tag:blogger.com,1999:blog-41434032771860656432024-02-07T07:23:15.729+01:00Mac in SoftMachttp://www.blogger.com/profile/12422354229331017688noreply@blogger.comBlogger15125tag:blogger.com,1999:blog-4143403277186065643.post-79835874737049113932013-05-10T15:48:00.001+02:002013-05-10T15:48:14.554+02:00Install node.js on a Windows Azure Cloud Service (II)<p>Last time I showed you <a href="http://macinsoft.blogspot.fr/2013/03/install-nodejs-on-windows-azure-cloud.html" target="_blank">how to modify a node.js Windows installer</a> so that you could install it on an Azure Cloud Service as part of a startup task.</p> <p>As it happens, I just found out another way to perform this task without modifying the installer itself: just create a Windows installer transform file. The process is quite similar, and only <a href="http://msdn.microsoft.com/en-us/library/windows/desktop/aa370557.aspx" target="_blank">Orca</a> is needed.</p> <ul> <li>Open the installer in Orca. </li> <li>Click the <em>Transform</em> > <em>New Transform</em> menu. </li> <li>Do your modifications. </li> <li>Click the <em>Transform</em> > <em>Generate Transform…</em> menu to create the transform file. </li> </ul> <p>As you will see, all your modifications are recorded, and appear in green in Orca:<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh91jx7axLohyphenhyphenfVxu3zhpobwMNZ54pQ5CY9xel42FcRSI4-CvIGUnFPwc9YmTZMqFRAbus_k3jbB5Upe0iwBMlFk-KgH98ZBuUXoaRs2zFTfOWirCAnkmzbP8SpiOAtWatHkP4Zj6XAZfQW/s1600-h/image%25255B5%25255D.png"><img title="image" style="border-left-width: 0px; border-right-width: 0px; background-image: none; border-bottom-width: 0px; float: none; padding-top: 0px; padding-left: 0px; margin-left: auto; display: block; padding-right: 0px; border-top-width: 0px; margin-right: auto" border="0" alt="image" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg6ohHTa9KQ9wobA9Cx2J1gac7HPfvnD4CADiemD-n3Nm7noU-lSDYEhry_6UZiQl4_7ZKoa8wbDAGL2zACZw20f14HEGLZ2eqQxokYA0_53MZCSYPjGPH2L2aZ-6cIcB4J4i2_Z83eNcgF/?imgmax=800" width="480" height="368" /></a></p> <p>To use this transform file, use the <a href="http://msdn.microsoft.com/en-us/library/windows/desktop/aa372085.aspx" target="_blank">TRANSFORMS property</a>. For instance: <tt>msiexec.exe /qn /i node-v0.10.3-x64.msi TRANSFORMS="node-v0.10.3-x64-azure.mst"</tt></p> Machttp://www.blogger.com/profile/12422354229331017688noreply@blogger.comtag:blogger.com,1999:blog-4143403277186065643.post-15214848811387047812013-03-05T11:30:00.001+01:002013-03-05T11:30:36.230+01:00Install node.js on a Windows Azure Cloud Service<p><a href="http://nodejs.org/" target="_blank">node.js</a> is <a href="http://www.windowsazure.com/en-us/develop/nodejs/tutorials/create-a-website-(mac)/" target="_blank">supported by default on a Windows Azure WebSite</a>. But there are cases when you might need to deploy an application on an heavier (and more expensive) Cloud Service. Such a case is when you need SSL on a custom domain, as Azure WebSites <a href="http://social.msdn.microsoft.com/Forums/en-US/windowsazurewebsitespreview/thread/0218204f-7be3-4586-bfdb-831cde6b7937" target="_blank">do not yet support custom certificates</a>.</p> <p>Cloud Services instances can be customized through <a href="http://msdn.microsoft.com/en-us/library/windowsazure/hh180155.aspx" target="_blank">startup tasks</a>. This is how you would install and configure node.js (and <a href="https://github.com/tjanczuk/iisnode" target="_blank">iisnode</a>, presumably): create a startup task that installs node.js, iisnode, and runs <em>npm –install</em> on your package. It all seems pretty straightforward (though it may be the subject of a future post).</p> <p>Except that node.js <strong>cannot</strong> be installed on a Cloud Service as is (<a href="https://github.com/joyent/node/commit/8164b2f" target="_blank">yet</a>): if you try to execute <a href="http://nodejs.org/download/" target="_blank">the installer</a> on a Cloud Service instance, it will fail at the “<em>Creating shortcuts</em>” stage (as you’ll see in the installer logs). The default node.js installer tries to create shortcuts in the Windows start menu, which is (quite understandably) a disabled functionality  in this version of Windows. And there is no way to tell the installer not to execute this step. So we’re doomed…</p> <p>Well, there is a way to get around this. We can edit the official installer with <a href="http://msdn.microsoft.com/en-us/library/windows/desktop/aa370557.aspx" target="_blank">Orca</a>, the database table editor for Windows installer packages. It needs to be installed first (the installer can be found along the <a href="http://www.microsoft.com/en-us/download/details.aspx?id=8442" target="_blank">Microsoft Windows SDK for Windows 7 and .NET Framework 4</a> binaries, in <em>%ProgramFiles%\Microsoft SDKs\Windows\v7.1\Bin</em>). Once done, you can right-click the node.js installer and select the <em>Edit with Orca</em> menu.</p> <p><img title="Pardon my French ;-)" style="float: none; margin-left: auto; display: block; margin-right: auto" alt="Edit with Orca" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhIs8XoAa2MeA9nsCP8O3NgYjxCTzSaWIZ9mHw0O9Hg_9Orxqte7I7dSDITYMllN_xeqjSths2OjEHupDbqlxYQG3sktJwXZcls9LtlcwiEurWjKAYBCvIntumJDG-q4t8kI-UtKClRpeuu/?imgmax=800" width="360" height="139" /></p> <p>As you can see, an installer package is nothing more than a database of all the components and actions performed during the install. Just select the <em>InstallExecuteSequence</em> table, and remove the 3 rows named <em>WixSchedInternetShortcuts</em>, <em>WixRollbackInternetShortcuts</em> and <em>WixCreateInternetShortcuts</em>.</p> <p><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgiM3HQe6hQx5MZwr3yRB0Zfqj5UT1lFrPWPF9HGJcMDKc_2STXv2soigIaw1yd2qgVpnNAmfH0D1q9dzjtea80Og1RydAWfmBdCes2RDgHSD5coR-OVN-gu2TNyEpWk7zwsVSisu37h-4R/s1600-h/image%25255B11%25255D.png"><img title="image" style="float: none; margin-left: auto; display: block; margin-right: auto" alt="image" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiRoE5D5JsRkMnqbFTqD2ShicXtQFLojY9ZPP_rRe3czTx03GgVtCIamKUTjB_i82d9pwv2XZ02bK4QLLNx6U1SCEU1wSsAqNpVEfH9kHv_ZGGhydEHp3Vkye-tJb8PS6PnwMG6dmj2l0it/?imgmax=800" width="640" height="396" /></a></p> <p>Just save your changes, and there you go: this package will install properly on a Windows Azure Cloud Service. Maybe one day will there be <a href="https://github.com/joyent/node/pull/4694" target="_blank">a command line option</a> to save us all this trouble…</p> Machttp://www.blogger.com/profile/12422354229331017688noreply@blogger.comtag:blogger.com,1999:blog-4143403277186065643.post-81067141463849992582012-02-08T18:33:00.001+01:002012-02-08T18:33:19.810+01:00Starting on GeoSIK<p>This blog has not been updated in a looong while now, but I thought I just mentioned I have started the development of a new open source project; namely: <strong>GeoSIK</strong>.</p> <p>GeoSIK stands for <strong>Geo</strong>spatial <strong>S</strong>ervices <strong>I</strong>mplementation <strong>K</strong>it, and it is a set of libraries that ease the development of OGC Web Services in .NET. This is part of the work I have been doing in the last few months in my new job at <a href="http://www.isogeo.fr/" target="_blank">Isogeo</a>.</p> <p>You can browse this project on <a href="http://geosik.codeplex.com/" target="_blank">CodePlex</a>, and I have just opened a <a href="http://geosikdev.blogspot.com/" target="_blank">new blog</a> dedicated to it that I will try to update regularly. Given my record here, it is very likely that the energy I will spend doing that means that this blog here will be even less updated in the future (if this is possible).</p> <p>But you never know.</p> Machttp://www.blogger.com/profile/12422354229331017688noreply@blogger.comtag:blogger.com,1999:blog-4143403277186065643.post-61967341500930856702009-09-18T11:38:00.002+02:002009-09-18T11:41:24.463+02:00Beware the Image object !<p>One requirement on the project I am working on (.NET 2.0, Windows Forms) was to make the end-user able to load a background image to an <code><a href="http://msdn.microsoft.com/en-us/library/microsoft.ink.inkpicture.aspx" target="_blank">InkPicture</a></code> (which is just a special <code><a href="http://msdn.microsoft.com/en-us/library/system.windows.forms.picturebox.aspx" target="_blank">PictureBox</a></code> control, with the ability to be drawn upon on a TabletPC). That did not seem very difficult at first : just create an <code><a href="http://msdn.microsoft.com/en-us/library/system.windows.forms.openfiledialog.aspx" target="_blank">OpenFileDialog</a></code> and there you go.</p> <pre class="csharp" name="code">// WRONG ! DO NOT USE THIS CODE !<br />if (_OpenFileDialog.ShowDialog()==DialogResult.OK)<br /> using (Stream ims=_OpenFileDialog.OpenFile())<br /> _InkPicture.Image=Image.FromStream(ims);</pre> <p>Clean, straightforward and ... wrong ! It took me quite some time to realize it though. My application started to throw occasional and rather erratic exceptions in completely unrelated places, and it kind of reminded me of the strange ways a native application could crash after a buffer had been overrun. But in a managed application ?...</p> <p>As often, the answer is <a href="http://msdn.microsoft.com/en-us/library/93z9ee4x.aspx" target="_blank">in the documentation</a> (check the <em>Remarks</em> section) :</p> <blockquote><p>You must keep the stream open for the lifetime of the Image.</p></blockquote> <p>Uh ? Any more information on why I should do this, and perhaps more importantly <em>how</em> I should do this ? Because my <code>Image</code> could be replaced from anywhere in the code. And even if I wanted to track the changes, there is no <code>ImageChanged</code> event on a <code>PictureBox</code>. And it feels weird to retain a reference to a <code>Stream</code> I never use...</p> <p>As for the first question, the answer lies in <a href="http://support.microsoft.com/?id=814675" target="_blank">KB 814675</a> : <code>Image</code> is just a wrapper around the native GDI+ library, which has specific requirements :</p> <blockquote><p>To retain access to the source bits, GDI+ locks any source file, and forces the application to maintain the life of any source stream, for the life of the Bitmap or the Image object.</p></blockquote> <p>As for the second question, tips can be found in the referenced article above. And here is my solution : use a copy of the stream based image that is unrelated to the original stream. Here is some code to create such a copy :</p> <pre class="csharp" name="code">public static Image Copy(Image original)<br />{<br /> // cf. http://support.microsoft.com/?id=814675<br /><br /> Image ret=new Bitmap(original.Width, original.Height);<br /> using (Graphics g=Graphics.FromImage(ret))<br /> {<br /> g.DrawImageUnscaled(original, 0, 0);<br /> g.Save();<br /> }<br /><br /> return ret;<br />}</pre> <p>And here is the correct code on how to load an <code>Image</code> into an <code>InkPicture</code> :</p> <pre class="csharp" name="code">if (_OpenFileDialog.ShowDialog()==DialogResult.OK)<br /> using (Stream ims=_OpenFileDialog.OpenFile())<br /> using (Image image=Image.FromStream(ims))<br /> _InkPicture.Image=Copy(image);</pre>Machttp://www.blogger.com/profile/12422354229331017688noreply@blogger.comtag:blogger.com,1999:blog-4143403277186065643.post-86171288924213591222009-05-15T16:28:00.002+02:002009-05-15T16:29:06.711+02:00Encrypt configuration file in a custom managed action on x64<p>When I install a .NET (or ASP .NET) application, I like to encrypt the sensitive parts of the configuration file like the connection strings. So I have been used to create a <a href="http://msdn.microsoft.com/en-us/library/system.configuration.install.installer.aspx" target="_blank">Custom Installer class</a> to achieve this. There are a few tricky things to take into account, like :</p> <ul> <li>when your configuration is loaded by the installer, it should be able to resolve embedded dependencies (like Enterprise Library assemblies) even when they are not in the GAC. </li> <li>this class accepts a <em>EXEPATH</em> parameter that contains the path of the application which application file is to be encrypted. It should then be terminated <a href="http://msdn.microsoft.com/en-us/library/2w2fhwzz.aspx" target="_blank">by an extra \ character</a>. </li> </ul> <p>For the record, here is how I do this :</p> <pre class="csharp" name="code">[RunInstaller(true)]<br />public partial class EncryptConfigInstaller:<br /> Installer<br />{<br /><br /> public EncryptConfigInstaller()<br /> {<br /> InitializeComponent();<br /> }<br /><br /> public override void Install(IDictionary stateSaver)<br /> {<br /> base.Install(stateSaver);<br /><br /> string exePath=Context.Parameters["exepath"].TrimEnd('\\');<br /><br /> AppDomain.CurrentDomain.SetData("EXEDIR", Path.GetDirectoryName(exePath));<br /> AppDomain.CurrentDomain.AssemblyResolve+=_AssemblyResolver;<br /> try<br /> {<br /> EncryptConfiguration(ConfigurationManager.OpenExeConfiguration(exePath));<br /> } finally<br /> {<br /> AppDomain.CurrentDomain.AssemblyResolve-=_AssemblyResolver;<br /> }<br /> }<br /><br /> private static Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)<br /> {<br /> AssemblyName name=new AssemblyName(args.Name);<br /><br /> return Assembly.LoadFile(Path.Combine((string)AppDomain.CurrentDomain.GetData("EXEDIR"), string.Concat(name.Name, ".dll")));<br /> }<br /><br /> private static void EncryptConfiguration(Configuration config)<br /> {<br /> EncryptSection(config.GetSection("connectionStrings"));<br /><br /> config.Save(ConfigurationSaveMode.Modified);<br /> }<br /><br /> private static void EncryptSection(ConfigurationSection section)<br /> {<br /> if ((section!=null) && (!section.SectionInformation.IsProtected))<br /> {<br /> section.SectionInformation.ProtectSection("DataProtectionConfigurationProvider");<br /> section.SectionInformation.ForceSave=true;<br /> }<br /> }<br /><br /> private static ResolveEventHandler _AssemblyResolver=new ResolveEventHandler(CurrentDomain_AssemblyResolve);<br /><br />}</pre> <p>Then even if I know that <a href="http://www.msifaq.com/a/1044.htm" target="_blank">managed actions are considered evil</a>, as I do not think I have a choice here, I am using the well known <a href="http://blogs.msdn.com/josealmeida/archive/2004/11/08/253831.aspx" target="_blank">WiX Custom Managed Actions</a> trick. So everything is fine in a perfect world.</p> <p>Except when a client tried to install one of my applications on a x64 platform (namely Windows Server 2008). Once solved the obvious <a href="http://macinsoft.blogspot.com/2009/05/oracle-not-so-instant-client.html">native libraries problem</a>, I was still puzzled by the fact that the user who had installed the application was the only one able to run it ! It was only after a couple of days of wrong conjectures, poor tricks and unproductive Google searches that I cornered the problem : the encryption process was broken.</p> <p>Extensive reasons behind that <a href="http://blogs.msdn.com/heaths/archive/2006/02/01/64-bit-managed-custom-actions-with-visual-studio.aspx">can be found here,</a> but basically it is because <em>InstallUtilLib.dll</em>, which is used to launch Installer classes, is a native library. As such, you should use the 64 bits version on a x64 platform. So I ended up copying both versions in an independent lib folder, and here is the magic corresponding WiX line :</p> <pre class="xml" name="code"><Binary Id="InstallUtilBinary" SourceFile="$(sys.CURRENTDIR)..\..\lib\$(sys.BUILDARCH)\InstallUtilLib.dll" /></pre> <p>Yes, I learned a lot about x64 lately. But I guess I am not done yet :-)</p>Machttp://www.blogger.com/profile/12422354229331017688noreply@blogger.comtag:blogger.com,1999:blog-4143403277186065643.post-28380767902328063072009-05-15T14:42:00.002+02:002009-05-15T14:43:38.025+02:00Oracle Instant Client in Visual Studio<p>In <a href="http://macinsoft.blogspot.com/2009/05/oracle-not-so-instant-client.html">my previous post</a>, I mentioned the fact that I added the Oracle <a href="http://www.oracle.com/technology/tech/oci/instantclient/index.html" target="_blank">Instant Client</a> files as Content files in a Visual Studio project. I would like to write more about this here.</p> <p>If you intend to use your application with Instant Client, you will want to be able to debug it with Instant Client. Which means that the libraries have to be copied along your generated application in the <em>bin\Debug</em> folder. The best way to achieve this is to include then as Content files in your project.</p> <p>But it gets more tricky if you want to be able to debug it on 32 bits platform as well as on a 64 bits platform : the source code is the same (I am obviously writing about a .NET application, here), you just have to pick the correct library depending on the platform you are debugging on.</p> <p>My way to do this is :</p> <ul> <li>I store the Instant Client libraries in an independent folder, say <em>lib\Oracle\Instant Client</em>. </li> <li>Each platform lies in a dedicated subfolder : <em>x86</em> for the 32 bits version, <em>x64</em> for the 64 bits version. </li> <li>I manually tweak the <em>.csproj</em> project file so that it picks the right version depending on the platform I am running Visual Studio on : <pre class="xml" name="code"><PropertyGroup><br /> <ProcessorArchitecture>x86</ProcessorArchitecture><br /> <ProcessorArchitecture Condition=" '$(PROCESSOR_ARCHITECTURE)' == 'AMD64' ">x64</ProcessorArchitecture><br /></PropertyGroup><br /><ItemGroup><br /> <Content Include="..\..\lib\Oracle\InstantClient\$(ProcessorArchitecture)\oci.dll"><br /> <Link>oci.dll</Link><br /> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory><br /> </Content><br /> <Content Include="..\..\lib\Oracle\InstantClient\$(ProcessorArchitecture)\orannzsbb11.dll"><br /> <Link>orannzsbb11.dll</Link><br /> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory><br /> </Content><br /> <Content Include="..\..\lib\Oracle\InstantClient\$(ProcessorArchitecture)\oraociei11.dll"><br /> <Link>oraociei11.dll</Link><br /> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory><br /> </Content><br /></ItemGroup><br /><ItemGroup Condition=" '$(ProcessorArchitecture)' == 'x86' "><br /> <Content Include="..\..\lib\Oracle\InstantClient\$(ProcessorArchitecture)\msvcr71.dll"><br /> <Link>msvcr71.dll</Link><br /> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory><br /> </Content><br /></ItemGroup></pre></li></ul> <p><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgbA4Z26k1XUAlYrPt5C6MXzYf3zceO9bxJrI36kxHnyx4WYPSjdkhcZ2OwGi8Y2E5BHeTit5w2cvbrVZtynFPkvGiXZbQHM2CIqBYbSkEs3jNeLMavxke1zLzVCsewp0_Q5bPfgJ02_tPn/s1600-h/image%5B4%5D.png"><img style="border-right-width: 0px; margin: 10px 10px 0px 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="image" border="0" alt="image" align="left" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgqi3PgloFAbCwVAN20ale5PSgBOfzVKKtGHFFkivge__pPEneLrby__EiKW9YzNodk7E3i2QiHOvIzbQdqztmXvM-fcS6dqfytCcQcQPdxVggsiyNfchGVI0VW-1q0ZBue7yIdQwD_-4Nq/?imgmax=800" width="172" height="86" /></a>This code is based on the <code>PROCESSOR_ARCHITECTURE</code> environment variable. This will be set to <em>x86</em> on a 32 bits platform, as expected, but <a href="http://support.microsoft.com/kb/888731" target="_blank">to AMD64 on a 64 bits platform</a>. That is why I am using a custom property to get the <em>x64</em> value back.</p> <p>I could also simply rename the library subfolder to <em>AMD64</em>, but I like <em>x64 </em>more (I know : I am picky). And besides, it makes my WiX files more straightforward…</p>Machttp://www.blogger.com/profile/12422354229331017688noreply@blogger.comtag:blogger.com,1999:blog-4143403277186065643.post-18818851221451691502009-05-15T11:55:00.002+02:002009-05-15T11:57:33.800+02:00Oracle (Not So) Instant Client<p>Developing database oriented .NET applications is quite a no brainer once you are used to your API (ADO.NET, Enterprise Library Data Application Block…) or your ORM (NHibernate…). Just pick your database vendor ADO .NET provider, which usually consists of one assembly that you distribute with your application, and that’s it. That is how it works with SQL Server (of course), but also <a href="http://www.teradata.com/td/page/149889" target="_blank">Teradata</a>,  <a href="http://dev.mysql.com/downloads/connector/net/6.0.html" target="_blank">MySQL</a>, <a href="http://pgfoundry.org/projects/npgsql/" target="_blank">PostgreSQL</a>, <a href="http://sqlite.phxsoftware.com/" target="_blank">SQLite</a>… You name it, that is the way it works.</p> <p>Oracle, you said ? Well, that must be the exception that confirms the rule (along with DB2, but I would like to focus my rant on Oracle, if I may). It works <em>quite</em> like this. You need an ADO .NET provider AND a native client. The problems with this are :</p> <ul> <li>it takes <a href="http://www.oracle.com/technology/software/products/database/oracle10g/htdocs/10201winsoft.html" target="_blank">more than 450Mb to download</a> ! </li> <li>you may have to install several clients on the same computer (a 11.1 client will not connect to a 8i server). </li> <li>good luck to you if you intended to <a href="http://support.microsoft.com/kb/255084" target="_blank">make it work from an ASP .NET application</a> (especially with <a href="http://msdn.microsoft.com/en-us/library/aa292118(VS.71).aspx" target="_blank">impersonation</a>) ! </li> </ul> <p>But thanks to the Oracle guys, there is another solution : Oracle <a href="http://www.oracle.com/technology/tech/oci/instantclient/index.html" target="_blank">Instant Client</a>. It consists of just a few DLLs that you can distribute along with your application and that allow connection to Oracle databases. If you are not too picky about character sets and supported languages, <a href="http://stackoverflow.com/questions/70602/what-is-the-minimum-client-footprint-required-to-connect-c-to-an-oracle-database/70901#70901" target="_blank">you can trim it down do 19Mb</a>.</p> <p>I became quite efficient with this : I add the necessary files as Content in my Visual Studio project, and a simple Setup project makes a good enough installer for my solution.</p> <p>Then this week, a client called me about how one of these applications just crashed after he installed it on a Windows 2008 Server. The fact was : it was a 64 bits Windows. As the .NET application was compiled as AnyCPU, it was automatically launched as a 64 bits application, and then you can guess by yourself what happened when the 32 bits Oracle client was loaded in memory. At this point, my options were :</p> <ul> <li>compile the application as x86, so that it would be launched as a 32 bits application even on a x64 platform. But why tweak my application when it is really Oracle that was at fault ? </li> <li>leave the application as AnyCPU, but distribute a different client depending on the platform it is installed on. This is the (more interesting) road I took. </li> </ul> <p>I decided to give up on the standard Setup project (had I any other option ?) and use <a href="http://wix.sourceforge.net/" target="_blank">WiX</a> 3.0 instead. My first attempt was to use a single installer to be used on both platforms (x86 and x64), and distribute the Oracle Instant Client 11 depending on the platform (I found that version 10 made my application crash on Windows Vista x64, while version 11 worked fine…). This is how I achieved this :</p> <pre class="xml" name="code"><Wix xmlns="http://schemas.microsoft.com/wix/2006/wi"><br /> <Fragment><br /> <DirectoryRef Id="INSTALLLOCATION"><br /><?if $(sys.BUILDARCH) = "x86" ?><br /> <Component Id="OracleInstantClientFiles_x86" Guid="{AA0076CE-F7B6-4cd8-9B67-199F665A8E77}" KeyPath="yes"><br /> <Condition><br /> <![CDATA[Installed OR NOT VersionNT64]]><br /> </Condition><br /> <File Id="OracleInstantClientFiles_x86_msvcr71.dll" Name="msvcr71.dll" Source="$(sys.CURRENTDIR)..\..\lib\Oracle\InstantClient\x86\msvcr71.dll" DiskId="1" /><br /> <File Id="OracleInstantClientFiles_x86_oci.dll" Name="oci.dll" Source="$(sys.CURRENTDIR)..\..\lib\Oracle\InstantClient\x86\oci.dll" DiskId="1" /><br /> <File Id="OracleInstantClientFiles_x86_orannzsbb11.dll" Name="orannzsbb11.dll" Source="$(sys.CURRENTDIR)..\..\lib\Oracle\InstantClient\x86\orannzsbb11.dll" DiskId="1" /><br /> <File Id="OracleInstantClientFiles_x86_oraociei11.dll" Name="oraociei11.dll" Source="$(sys.CURRENTDIR)..\..\lib\Oracle\InstantClient\x86\oraociei11.dll" DiskId="1" /><br /> </Component><br /><?endif ?><br /> <Component Id="OracleInstantClientFiles_x64" Guid="{3CCDBDB6-D45A-4523-8CC7-730D3A8851D3}" KeyPath="yes"><br /> <Condition><br /> <![CDATA[Installed OR VersionNT64]]><br /> </Condition><br /> <File Id="OracleInstantClientFiles_x64_oci.dll" Name="oci.dll" Source="$(sys.CURRENTDIR)..\..\lib\Oracle\InstantClient\x64\oci.dll" DiskId="1" /><br /> <File Id="OracleInstantClientFiles_x64_orannzsbb11.dll" Name="orannzsbb11.dll" Source="$(sys.CURRENTDIR)..\..\lib\Oracle\InstantClient\x64\orannzsbb11.dll" DiskId="1" /><br /> <File Id="OracleInstantClientFiles_x64_oraociei11.dll" Name="oraociei11.dll" Source="$(sys.CURRENTDIR)..\..\lib\Oracle\InstantClient\x64\oraociei11.dll" DiskId="1" /><br /> </Component><br /> </DirectoryRef><br /> </Fragment><br /><br /> <Fragment><br /> <ComponentGroup Id="OracleInstantClientFiles"><br /><?if $(sys.BUILDARCH) = "x86" ?><br /> <ComponentRef Id="OracleInstantClientFiles_x86" /><br /><?endif ?><br /> <ComponentRef Id="OracleInstantClientFiles_x64" /><br /> </ComponentGroup><br /> </Fragment><br /><br /></Wix></pre> <p>This works alright. Just reference the <code>OracleInstantClientFiles</code> component and there you have it. But I was annoyed by the fact that my application would install in the <i>Program Files (x86)</i> folder by default. So I went with the dual installer solution. And the source code became :</p> <pre class="xml" name="code"><Wix xmlns="http://schemas.microsoft.com/wix/2006/wi"><br /> <Fragment><br /> <DirectoryRef Id="INSTALLLOCATION"><br /> <Component Id="OracleInstantClientFiles" Guid="{AA0076CE-F7B6-4cd8-9B67-199F665A8E77}" KeyPath="yes"><br /><?if $(sys.BUILDARCH) = "x86" ?><br /> <File Id="OracleInstantClientFiles_x86_msvcr71.dll" Name="msvcr71.dll" Source="$(sys.CURRENTDIR)..\..\lib\Oracle\InstantClient\x86\msvcr71.dll" DiskId="1" /><br /><?endif ?><br /> <File Id="OracleInstantClientFiles_oci.dll" Name="oci.dll" Source="$(sys.CURRENTDIR)..\..\lib\Oracle\InstantClient\$(sys.BUILDARCH)\oci.dll" DiskId="1" /><br /> <File Id="OracleInstantClientFiles_orannzsbb11.dll" Name="orannzsbb11.dll" Source="$(sys.CURRENTDIR)..\..\lib\Oracle\InstantClient\$(sys.BUILDARCH)\orannzsbb11.dll" DiskId="1" /><br /> <File Id="OracleInstantClientFiles_oraociei11.dll" Name="oraociei11.dll" Source="$(sys.CURRENTDIR)..\..\lib\Oracle\InstantClient\$(sys.BUILDARCH)\oraociei11.dll" DiskId="1" /><br /> </Component><br /> </DirectoryRef><br /> </Fragment><br /><br /></Wix></pre> <p>So now I can distribute the correct Oracle files (worth 130Mb per platform) along with my less than 2Mb application !</p>Machttp://www.blogger.com/profile/12422354229331017688noreply@blogger.comtag:blogger.com,1999:blog-4143403277186065643.post-60756341936331125872008-10-29T18:44:00.002+01:002008-10-29T18:46:14.803+01:00How to install Oslo with SQL Server Express Edition ?<p>As I was delving into the <a href="http://msdn.microsoft.com/en-us/library/bb126235.aspx" target="_blank">Visual Studio DSL Tools</a> to design my first DSL for <a href="http://www.codeplex.com/salamanca" target="_blank" rel="tag">Salamanca</a>, I came across this new modeling platform by Microsoft : <a href="http://msdn.microsoft.com/en-us/oslo/default.aspx" target="_blank">Oslo</a>. In fact, it is so new that the first CTP has just <a href="http://code.msdn.microsoft.com/oslo/Release/ProjectReleases.aspx" target="_blank">been released</a>.</p> <p><a title="Haven van Oslo by cinevideogroup, on Flickr" href="http://www.flickr.com/photos/cinevideogroup/2947630330/"><img height="281" alt="Haven van Oslo" src="http://farm4.static.flickr.com/3139/2947630330_9f0f9e5ccc.jpg" width="500" align="right" /></a>As I already wrote on <a href="http://salamanca.nourysolutions.com/road/index.php?post/2008/10/29/To-Salamanca-via-Oslo" target="_blank">the blog of Salamanca</a>, the vision for this new tool seems to be exactly what I had been needing :</p> <blockquote>It's actually taking the kind of models that you're seeing arising in specific domains, like software management in System Center, or your data design over in SQL, or your process activities over in BizTalk and saying, we need to take all these domains and be able to put them into one model space. In fact, we need to let people create their own domains that aren't just isolated, but that exist in this one modeling space. And that's what Oslo is about.</blockquote> <p>That's it. I just quoted Bill Gates !</p> <p>So it was unavoidable that I tried to download and install the <a href="http://code.msdn.microsoft.com/oslo/Release/ProjectReleases.aspx?ReleaseId=1707" target="_blank">October 2008 CTP</a> on my computer. Everything was OK, except for the fact that the installer could not create the repository on my SQL Server 2008 Express Edition database. The message was :</p> <blockquote>Create repository database failed, please try later</blockquote> <p>So I tried later, as suggested in the installed ReadMe file. Indeed, my SQL Server instance not being the default instance on my system, but rather being a named instance (the name being "SQLEXPRESS"), I understand why the repository creation failed. The trouble is that the command lines included in the file to manually create the repository are incorrect. So here are the correct ones :</p> <pre>CreateRepository.exe /db:.\SQLEXPRESS<br />mx.exe -i:Models.mx -db:Repository -s:.\SQLEXPRESS</pre> <p>Please note that the second command failed at first with the following message :</p> <blockquote>There is insufficient system memory in resource pool 'internal' to run this query.</blockquote> <p>But, as suggested on <a href="https://connect.microsoft.com/SQLServer/feedback/ViewFeedback.aspx?FeedbackID=342696" target="_blank">this Microsoft Connect SQL Server issue</a>, letting the server "sit for a moment" solved the problem...</p>Machttp://www.blogger.com/profile/12422354229331017688noreply@blogger.comtag:blogger.com,1999:blog-4143403277186065643.post-567310435914398652008-08-25T14:40:00.001+02:002008-08-25T14:40:36.179+02:00The road to Salamanca<p>I haven't been around this blog for quite some time now. Don't take this as another useless and meaningless apology for not sticking to a self assigned schedule. The reason for this short post is just to tell the world that <a href="http://www.nourysolutions.com/" target="_blank">we</a> have decided to turn our software factory into an open source project, hosted on <a href="http://www.codeplex.com/" target="_blank">CodePlex</a> : <a href="http://www.codeplex.com/salamanca" target="_blank" rel="tag">Salamanca</a>. And there is even <a href="http://salamanca.nourysolutions.com/road/" target="_blank">a blog</a> attached to it, for which I am the main contributor. So that will be a excellent reason (I hope) for me not to find the time to update this one :-)</p> <p>Salamanca is a product I really grew found of, and which I think brings great promises, especially in the field of <a href="http://en.wikipedia.org/wiki/Separation_of_concerns" target="_blank" rel="tag">separation of concerns</a> with the concept of <a href="http://salamanca.nourysolutions.com/road/index.php?post/2008/08/20/Visiting-Salamanca" target="_blank">activities</a>, that can be thought of as a View-Controller pattern.</p> <p>Granted, as the size of the observable world seen from this blog is rather small, this post will also be used as a test for the trackback and reactions mechanisms on <a href="http://salamanca.nourysolutions.com/road/" target="_blank">The road to Salamanca</a>.</p> Machttp://www.blogger.com/profile/12422354229331017688noreply@blogger.comtag:blogger.com,1999:blog-4143403277186065643.post-25314046326744663032008-06-05T13:22:00.003+02:002008-06-05T13:28:52.253+02:00Missing<p>I got stuck with this error message yesterday :</p> <blockquote> <p>System.Data.OracleClient requires Oracle client software version 8.1.7 or greater</p> </blockquote> <p>Mmmh. Let me give you some context. I have been working for almost a year now on this ASP .NET application :</p> <ul> <li>connected to an Oracle 10g database. </li> <li>uses the Microsoft ADO .NET provider for Oracle (preferred over <a href="http://www.oracle.com/technology/tech/windows/odpnet/index.html" target="_blank">ODP .NET</a> 10g for its Visual Studio 2005 integration and its ability to work above Oracle <a href="http://www.oracle.com/technology/tech/oci/instantclient/index.html" target="_blank">Instant Client</a>). </li> <li>bundled Oracle Instant Client 10.2 (more on that later...). </li> <li>uses impersonation. </li> <li>tested on numerous development environments and a couple of servers without a glitch. </li> </ul> <p>This error occurred on a new and freshly installed Windows Server 2003 server. Without a clue, I looked for help on the Internet, basing my search on the error message. An I found <a href="http://www.google.com/notebook/public/01335025009615452606/BDRwbSwoQnIbT6rQh" target="_blank">a good one</a>, or so I thought. Let me get this straight : if you get the error above, it is very likely that you have to tweak your security rights on your Oracle client installation as suggested on the previous link. But not in my case, because of the way I had bundled Oracle Instant Client in my application :</p> <ul> <li>the DLLs are deployed in the application bin directory. </li> <li>the application bin directory is added in front of the %PATH% environment variable at application startup. This is how it is done : </li> </ul> <p>In the Global.asax file :</p> <textarea name="code" class="csharp" rows="13" cols="80"><br />protected void Application_Start(object sender, EventArgs e) <br />{<br /> // Code that runs on application startup<br /> SetEnvironmentVariable(<br /> "PATH",<br /> string.Format(<br /> CultureInfo.InvariantCulture,<br /> "{0};{1}",<br /> Path.Combine(HttpContext.Current.Request.PhysicalApplicationPath, "bin"),<br /> Environment.GetEnvironmentVariable("PATH")<br /> )<br /> );<br />}<br /><br />[DllImport("kernel32.dll", EntryPoint="SetEnvironmentVariableW", CharSet=CharSet.Unicode)]<br />public static extern bool SetEnvironmentVariable(string name, string val);<br /></textarea> <p>So in my case, the Oracle client installation was my application bin directory, which had the proper rights. This did not prevent me from trying to give everyone full access, just to make sure. But, very predictively, it failed to solve my problem.</p> <p>I began to sweat a little. Did I really need to install a full Oracle client on the server ? No : I proved otherwise. I thought...</p> <p>At last resort, I decided to use the indispensable <a href="http://www.aisto.com/roeder/dotnet/" target="_blank">Reflector for .NET</a> to check the piece of code responsible for throwing the exception. Nothing very fancy there, but I noticed after a while a peculiar trace mechanism. I tried to analyze it to find a way to enable it. And after a long while, I found out that Microsoft ADO .NET providers could be plugged into the <a href="http://msdn.microsoft.com/en-us/library/aa964124.aspx" target="_blank">Event Tracing for Windows</a> for debugging. Not very straightforward, but I managed to get it work. And here is the interesting piece of trace I got :</p> <blockquote> <p><oc|ERR|THROW> 'System.DllNotFoundException: Impossible de charger la DLL 'oci.dll': Le module spécifié est introuvable. (Exception de HRESULT : 0x8007007E) ,   à System.Data.Common.UnsafeNativeMethods.OCILobCopy2(IntPtr svchp  IntPtr errhp  IntPtr dst_locp  IntPtr src_locp  UInt64 amount  UInt64 dst_offset  UInt64 src_offset) ,   à System.Data.OracleClient.OCI.DetermineClientVersion()'</p> </blockquote> <p>Please pardon my french ;-) "Come on", I said to myself, "how can you not find this DLL when it is clearly in your %PATH% ?" Or is it not ? To be sure, I checked with <a href="http://technet.microsoft.com/en-us/sysinternals/bb896653.aspx" target="_blank">Process Explorer</a>. Indeed, it is.</p> <p>The truth is out there : what if it could find the said DLL, but could not load it for any reason ? Indeed, a search focusing more on DllImport than on Oracle <a href="http://forums.asp.net/t/1267713.aspx" target="_blank">confirmed this</a> : a missing dependency could trigger this exception. So I decided to check dependencies with the <a href="http://www.dependencywalker.com/" target="_blank">Dependency Walker</a>.</p> <p><img style="border-top-width: 0px; border-left-width: 0px; border-bottom-width: 0px; border-right-width: 0px" height="340" alt="image" src="http://lh6.ggpht.com/mcartoixa/SEfMhKqWOYI/AAAAAAAAABg/PDHH84XiSNc/image%5B12%5D.png?imgmax=800" width="565" border="0" /> </p> <p>So the Oracle Instant Client relies on MSVCR71.DLL, which is just not there. Isn't that DLL supposed to be by default in the C:\WINDOWS\system32 directory ? Well, it used to, but <a href="http://support.microsoft.com/kb/326922" target="_blank">not any more</a>.</p> <p>This is it. This dependency is so common that it had been somehow installed on all previous environments but not this one. I just added it to the application installation packages, alongside OCI.DLL, and that was it.</p> <p>Note : Oracle Instant Client 11.1 is delivered with all its dependencies, including MSVCR71.DLL...</p>Machttp://www.blogger.com/profile/12422354229331017688noreply@blogger.comtag:blogger.com,1999:blog-4143403277186065643.post-32546161777231257042008-02-20T13:51:00.004+01:002008-02-20T17:33:35.267+01:00About Visual Studio, SSIS and DSLs<p>I have had the opportunity to discover <a href="http://www.microsoft.com/sql/technologies/integration/default.mspx" target="_blank">Sql Server 2005 Integration Services</a> (SSIS) lately. That was a double blow. At first, I found it highly usable, though rough on the edges : still only a very promising tool. And then I found it was the perfect example of a software factory created with the <a href="http://msdn2.microsoft.com/en-us/vs2005/aa718368.aspx" target="_blank">Visual Studio Domain-Specific Language Tools</a>. It is great to see Microsoft eating its own dog food, and create a great tool with it.</p> <p><a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg8h6-5a_HbTJGoKMTaqDDg9LyYtiEdEkwQGUJR-dJfn1aASMuEVxkTYnD_hCxKn9ACgsDr-JpiRgAyqVsGaMgLf3vMa94azNKZr1rrSdTo3R9ZVKmA3TXUth266jCsRNQgbhUtQ_aTh9cp/s1600-h/ETL.png"><img style="float:right;" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg8h6-5a_HbTJGoKMTaqDDg9LyYtiEdEkwQGUJR-dJfn1aASMuEVxkTYnD_hCxKn9ACgsDr-JpiRgAyqVsGaMgLf3vMa94azNKZr1rrSdTo3R9ZVKmA3TXUth266jCsRNQgbhUtQ_aTh9cp/s320/ETL.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5169083840822853090" /></a>I kept <strike>playing</strike> working with it for a while and then I slightly became more and more frustrated by this experience. For instance, the process of modifying a data flow is tedious and laborious if a data source structure has been changed. And as happened with the Windows Forms designer, I soon felt the urge to bypass the designer to quickly mess with the code. My deep feeling is that designers are great to get started, editors are great to get finished. But in the case of SSIS, there is no such thing as code. All you can have is a XML file containing your flow design, messed up with presentation concerns, useless metadata, magical numbers... Great for machines, useless for humans.</p> <p>The problem here, is that you have no <a href="http://martinfowler.com/bliki/DomainSpecificLanguage.html" target="_blank">Domain-Specific Language</a> (DSL) per se. Or, better, that the DSL has been generated by a machine. Some people have identified this flaw and <a href="http://www.ayende.com/Blog/archive/2007/07/19/Idea-The-Boo-ETL-DSL.aspx" traget="_blank">felt the need for proper code</a>, even for ETLs. I have even found a <a href="http://activewarehouse.rubyforge.org/etl/" target="_blank">code only</a> ETL being developed. This, in my opinion, is the right thing to do, and I shall remember it when I design my own Software Factories : a DSL is mandatory, the designer is here for comfort.</p> <p>SSIS is a great tool. It just lacks a DSL.</p>Machttp://www.blogger.com/profile/12422354229331017688noreply@blogger.comtag:blogger.com,1999:blog-4143403277186065643.post-20991624839243059302008-02-12T13:48:00.005+01:002008-10-30T10:17:42.576+01:00HowTo : Log DDL events in your Oracle database<p><span style="font-weight:bold;">Update</span> (30/10/2008) : trigger code updated, due to a bug pointed out by Michel in the comments.</p> <p>Databases <a href="http://www.codinghorror.com/blog/archives/001050.html" target="_blank">should be under version control</a>. Not only that, but I believe that SQL scripts should be treated as regular code : <a href="http://www.joelonsoftware.com/articles/fog0000000043.html" target="_blank">can your database be built in one step</a> ?</p> <p>But if you do that, you will want to <a href="http://odetocode.com/Blogs/scott/archive/2008/02/02/11721.aspx" target="_blank">scripts database changes</a> too. This seems quite easy to do if you manage the whole application, but far less so if your it falls under the responsibility of, say, your customer DBA. But in any case, I think it is a good idea to track all the changes made on the structure of your database. So here is some simple scripts that I use on a daily basis to achieve this.</p> <p>First of all, create a table </p> <pre name="code" class="sql">CREATE TABLE log$ddlevents<br />(<br /> EventDate DATE DEFAULT SYSDATE NOT NULL,<br /> Owner VARCHAR2(30),<br /> EventType VARCHAR2(30) NOT NULL,<br /> ObjectType VARCHAR2(18) NOT NULL,<br /> ObjectName VARCHAR2(128),<br /> DatabaseUser VARCHAR2(30) NOT NULL,<br /> OsUser VARCHAR2(30),<br /> MachineName VARCHAR2(64),<br /> ProgramName VARCHAR2(64),<br /> SqlText VARCHAR2(4000)<br />);</pre> <p>This table can be automatically filled thanks to the appropriate trigger :</p> <pre name="code" class="sql">CREATE OR REPLACE TRIGGER trg_ad_logddlevent<br /> AFTER ALTER OR CREATE OR DROP OR RENAME ON SCHEMA<br />DECLARE<br /> sqlTextPart ora_name_list_t;<br /> sqlText VARCHAR2(4000);<br /> i PLS_INTEGER;<br />BEGIN<br /> FOR i IN 1 .. ora_sql_txt(sqlTextPart)<br /> LOOP<br /> -- Make sure that we only retrieve the first 4000 characters<br /> DECLARE<br /> l PLS_INTEGER; -- Length of the current string<br /> l1 PLS_INTEGER; -- Length of the concatenated string (can be >=4000)<br /> lp PLS_INTEGER; -- Length to be concatenated<br /> BEGIN<br /> SELECT NVL2(sqlText, LENGTH(sqlText), 0)<br /> INTO l<br /> FROM DUAL;<br /> SELECT l+NVL2(sqlTextPart(i), LENGTH(sqlTextPart(i)), 0)<br /> INTO l1<br /> FROM DUAL;<br /> SELECT DECODE(SIGN(l1-4000), -1, l1, 4000-l)<br /> INTO lp<br /> FROM DUAL;<br /> sqlText:=CONCAT(sqlText, SUBSTR(sqlTextPart(i), 1, lp));<br /><br /> IF (l1>=4000)<br /> THEN<br /> EXIT;<br /> END IF;<br /> END;<br /> END LOOP;<br /><br /> INSERT INTO log$ddlevents (<br /> Owner,<br /> EventType,<br /> ObjectType,<br /> ObjectName,<br /> DatabaseUser,<br /> OsUser,<br /> MachineName,<br /> ProgramName,<br /> SqlText<br /> ) VALUES (<br /> ora_dict_obj_owner,<br /> ora_sysevent,<br /> ora_dict_obj_type,<br /> ora_dict_obj_name,<br /> ora_login_user,<br /> SYS_CONTEXT('USERENV', 'OS_USER'),<br /> SYS_CONTEXT('USERENV', 'HOST'),<br /> SYS_CONTEXT('USERENV', 'MODULE'),<br /> sqlText<br /> );<br />END;</pre>Machttp://www.blogger.com/profile/12422354229331017688noreply@blogger.comtag:blogger.com,1999:blog-4143403277186065643.post-8035764844129923382007-11-30T18:22:00.001+01:002007-11-30T18:35:52.285+01:00Getting Better<p>I have had to spend the last few months of my life without ... a life ! Not that I had much of one before that, but this is not the point. We had this client we had signed a contract with in June for what we all believed to be a small project, due to be released in September. And we only managed to deliver our release last week : a web application (ASP .NET, C# 2.0, Oracle 10g) targeting IIS 6.0 on <a href="http://www.microsoft.com/windowsserver2003/default.mspx" target="_blank">Windows Server 2003</a> and a desktop application (Forms, C# 2.0, Oracle 10g) on <a href="http://www.microsoft.com/windowsxp/tabletpc/default.mspx" target="_blank">Windows XP Tablet PC Edition 2005</a>. The whole thing amounts to about 185,000 lines of code (according to <a href="http://cloc.sourceforge.net/" target="_blank">CLOC</a>), if that should ever mean something to you (please share your experience if it does ;-).</p> <p>The point is that we had to spend about twice as much resources on this project than was planned : we were 2 months late (on a 3 months project) and had to get additional developers involved at some point. So, what happened ? The answer is simple : a special blend of about all of the <a href="http://blogs.construx.com/blogs/stevemcc/archive/2007/06/15/Classic-Mistakes-Updated.aspx" target="_blank">36 Classic Mistakes</a> (except, notably, the Technology-Related Mistakes). The project at some point seemed so doomed, with blurry ever-changing requirements and about no visibility on our project, that we consider our achievement as a big success (and also as a proof that we are on the right tracks with our growing <a href="http://www.softwarefactories.com/" target="_blank">Software Factory</a>). But that does not mean we would accept to get through this again, and I know I, for one, would not.</p> <div style="float: left"><a href="http://www.amazon.fr/gp/product/0735605351?ie=UTF8&tag=mac09-21&linkCode=as2&camp=1642&creative=6746&creativeASIN=0735605351"><img src="http://ecx.images-amazon.com/images/I/21HB3P03XDL._AA_SL160_.jpg" border="0" /></a><img style="margin: 0px; border-top-style: none! important; border-right-style: none! important; border-left-style: none! important; border-bottom-style: none! important" height="1" alt="" src="http://www.assoc-amazon.fr/e/ir?t=mac09-21&l=as2&o=8&a=0735605351" width="1" border="0" /></div> <p>Everybody involved in this project can be blamed. Me, us and them. And for this never to happen again, we all will have to learn from our mistakes. Starting with me, obviously. But the thing is that I cannot know for sure that my coworkers will have the same attitude. And that if they do, they will do it in (what I, in my humble opinion, would call) a sensible way. And I know for a fact that our client will not. So where do I go from here ? As for me, I sense I have to become a more accurate estimator. There <a href="http://www.joelonsoftware.com/items/2007/10/26.html" target="_blank">seems to be a lot of work</a> incurred, such as better organization and a lot of (boring) track keeping activity, but I guess it is what it takes. I also have to give more visibility on my work to people who lack the most basic technical skills.</p> <div style="float: right"><a href="http://www.amazon.fr/gp/product/0735618798?ie=UTF8&tag=mac09-21&linkCode=as2&camp=1642&creative=6746&creativeASIN=0735618798"><img src="http://ecx.images-amazon.com/images/I/21TQYZD41ML._AA_SL160_.jpg" border="0" /></a><img style="margin: 0px; border-top-style: none! important; border-right-style: none! important; border-left-style: none! important; border-bottom-style: none! important" height="1" alt="" src="http://www.assoc-amazon.fr/e/ir?t=mac09-21&l=as2&o=8&a=0735618798" width="1" border="0" /></div> <p>And I committed myself to improve my skills in the fields that I sensed people I have had to work with lacked. Even if could have considered (probably erroneously) them as not directly related to my job. And if I can, at the same time, improve my communication skills (and there is much room for improvement here) so that I can share this knowledge with them, I think will have greatly reduced the risk of me having to live through this again. First things first, I will start with requirements gathering.</p> <p>It seems like a lot of work. And I think it is. But I guess it is what I like about this job. It's : getting better all the time.</p> <div id="{D28D3F3A-12E7-42cb-94F1-134D7EE0B244}" align="center"><a href="http://profile.imeem.com/IJqG7Zd/music/e0K9DoWA/the_beatles_getting_better/" target="_blank">Getting Better</a></div> <script type="text/javascript">var so=new SWFObject("http://media.imeem.com/m/swiFS59KKz/aus=false/", "vplayer-{D28D3F3A-12E7-42cb-94F1-134D7EE0B244}", "300", "80", "8", "#000000"); so.addParam("wmode", "transparent"); so.write("{D28D3F3A-12E7-42cb-94F1-134D7EE0B244}");</script>Machttp://www.blogger.com/profile/12422354229331017688noreply@blogger.comtag:blogger.com,1999:blog-4143403277186065643.post-32872525221208606952007-11-20T19:51:00.001+01:002007-11-21T17:06:15.846+01:00Software that works<p>I hate software that works. Deeply. From the bottom of my heart and soul. Let me try to explain why.</p> <p><a href="http://www.archives.gov/research/american-cities/" target="_blank"><img height="240" src="http://www.archives.gov/research/american-cities/images/american-cities-048.jpg" width="188" align="right" /></a>I have worked a lot with databases, and I have long been amazed at the ability <a href="http://www.oracle.com/" target="_blank">Oracle</a> has had to consistently ship software that works. Sure, they have <a href="http://www.argeniss.com/woodb.html" target="_blank">their share of bugs</a>, but who hasn't ? When you use their software, you can expect it to work. And nothing more. You can never expect consistency, nor OS integration, nor anything that could ease the pain of actually making it work. On the field of consistency for instance, I, for one, think that the procedures <code><a href="http://download.oracle.com/docs/cd/B19306_01/appdev.102/b14258/d_lob.htm#i998484" target="_blank">GETLENGTH</a></code>  and <code><a href="http://download.oracle.com/docs/cd/B19306_01/appdev.102/b14258/d_lob.htm#sthref3586" target="_blank">GET_STORAGE_LIMIT</a></code> (in the <code><a href="http://download.oracle.com/docs/cd/B19306_01/appdev.102/b14258/d_lob.htm" target="_blank">DBMS_LOB</a></code> package) are inconsistently named. But it works. <a href="http://en.wikipedia.org/wiki/RTFM" target="_blank">RTFM</a>. Have you ever tried to change the name of a computer with Oracle installed (<a href="http://www.oracle.com/technology/products/database/xe/index.html" target="_blank">Express Edition</a> on Windows XP for instance) ? Well, good luck if you want to see your database again.</p> <p>In the world of software that works, there is no such incongruity as <a href="http://www.refactoring.com/" target="_blank">refactoring</a>. Think of it as the activity of changing code that works into code that does the same thing, but in a different way. In other words, it is one of the most stupid things to do, the mother of all evils. One software I have had the opportunity to work on had at one time been ported from DOS (16 bit) to Windows NT (32 bit) (the source code was in C). Compiling it the first time resulted in hundreds of warnings (<code>int</code>s would not fit in <code>short</code>s). My coworkers confirmed it was normal behavior, and that nothing could be done while the software worked. And certainly not refactoring. As a result, the compiler <a href="http://www.artima.com/cppsource/codestandards.html" target="_blank">ceased to be my friend</a>.</p> <p>After a few years in the field, I have learn to develop a strong allergy to "software that works".</p> <p>First of all, I regard the expression itself as pure nonsense. Yes, of course, software should work. And boats should float too. How would you call <a href="http://en.wikipedia.org/wiki/Vasa_%28ship%29" target="_blank">a boat that does not float</a> ? Software that does not work is no software. Call it an attempt, a bunch of source code, junk, whatever. Software works.</p> <p>But what does it do exactly ? Simple as this : software should do what it is supposed to do. This may sound a lot like "software should work", except that this expression subtly introduces the concepts of <a href="http://en.wikipedia.org/wiki/Software_Requirements_Specification" target="_blank">software requirements</a> and of <a href="http://www.softwarearchitectures.com/go/Discipline/DesigningArchitecture/QualityAttributes/tabid/64/Default.aspx" target="_blank">quality attributes</a>.  What is your software supposed to do ? How efficient, usable or available is it supposed to be ? I realize now that almost none of the projects I have worked on in the past had a central repository (like a simple document) to gather answers to these simple questions. To be honest, nobody realized that these questions existed and could be asked. Unfortunately, and as far as I can tell, this is still the state of the art for the software industry.</p> <p>When you have requirements and attributes, you know what you can do with your code. If your software is a prototype, then it may not be worth refactoring it. If you design APIs, you might want to be consistent while naming them. The <a href="http://msdn2.microsoft.com/en-us/library/system.text.asciiencoding.aspx" target="_blank">ASCIIEncoding</a> class in the .NET framework does not follow the <a href="http://msdn2.microsoft.com/en-us/library/ms229043.aspx" target="_blank">capitalization conventions</a> set by Microsoft itself. The difference with Oracle here is that there is a rule, and that exceptions are few (and can be seen as mistakes). Try to guess in which environment developers get more productive, more quickly ?</p> <p>When you don't have requirements or attributes, your only sensible goal is to build software that works. Which means you don't really have a goal.</p> <p>I love software development. It is a world where I can express a lot of creativity in trying to imagine solutions that fit into a set of requirements. Sometimes, I can even come dangerously close to the quixotic dreams of perfection I made as a teenager. I guess I have to be thankful that "software that works" regularly kicks in to bring me back to reality.</p> Machttp://www.blogger.com/profile/12422354229331017688noreply@blogger.comtag:blogger.com,1999:blog-4143403277186065643.post-52324109835994789272007-11-15T10:55:00.001+01:002007-11-15T10:55:15.388+01:00Brief introduction<p>Hi all.  My name is Mac. Well, not really, but that is how my friends call me. I am 32 (still), I am a software developer, here in Paris, and have been so for the past 6 years. I have worked as a consultant in <a href="http://www.quaternove.fr/" target="_blank">Quaternove</a> for 5 years, developing software for a handful of diverse companies such as <a href="http://www.wincor-nixdorf.com/" target="_blank">Wincor-Nixdorf</a> (an ATM manufacturer), <a href="http://www.servier.com/" target="_blank">SERVIER</a> (a pharmaceutical laboratory) or <a href="http://www.sagem.com/mobiles/" target="_blank">SAGEM</a> (a mobile phone manufacturer), and developing skills (which I badly lacked ;-) in diverse technologies such as relational databases, C++, XML, Java or .NET.</p> <p>This experience taught me that ordinary developers have a lot to learn in many fields, ranging from the technologies they use to the communication skills that are needed to build better software. It taught me as well that in this respect (at least), I was a very ordinary developer. So I am reading a lot, from virtual blogs to real books, to try to become a better professional.</p> <p>After the discrepancies between how I was made things done and how I read (and came to believe) they could be done, I got myself a new job in a small company (we are 6 people), which I will keep unnamed (for now at least) as this blog is a personal initiative. I am considered a .NET expert, which I guess I can accept if it is taken as a relative notion. I am deeply involved in the realization of our vision of building our own <a href="http://www.softwarefactories.com/" target="_blank" rel="tag">Software Factory</a> to be able to develop better and cheaper software for our customers.</p> <p>This blog is destined as a means to keep track of my problems, my solutions and my various thoughts about software development. And maybe share some of them.</p> Machttp://www.blogger.com/profile/12422354229331017688noreply@blogger.com