Friday, May 10, 2013

Install node.js on a Windows Azure Cloud Service (II)

Last time I showed you how to modify a node.js Windows installer so that you could install it on an Azure Cloud Service as part of a startup task.

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 Orca is needed.

  • Open the installer in Orca.
  • Click the Transform > New Transform menu.
  • Do your modifications.
  • Click the Transform > Generate Transform… menu to create the transform file.

As you will see, all your modifications are recorded, and appear in green in Orca:image

To use this transform file, use the TRANSFORMS property. For instance: msiexec.exe /qn /i node-v0.10.3-x64.msi TRANSFORMS="node-v0.10.3-x64-azure.mst"

Tuesday, March 5, 2013

Install node.js on a Windows Azure Cloud Service

node.js is supported by default on a Windows Azure WebSite. 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 do not yet support custom certificates.

Cloud Services instances can be customized through startup tasks. This is how you would install and configure node.js (and iisnode, presumably): create a startup task that installs node.js, iisnode, and runs npm –install on your package. It all seems pretty straightforward (though it may be the subject of a future post).

Except that node.js cannot be installed on a Cloud Service as is (yet): if you try to execute the installer on a Cloud Service instance, it will fail at the “Creating shortcuts” 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…

Well, there is a way to get around this. We can edit the official installer with Orca, the database table editor for Windows installer packages. It needs to be installed first (the installer can be found along the Microsoft Windows SDK for Windows 7 and .NET Framework 4 binaries, in %ProgramFiles%\Microsoft SDKs\Windows\v7.1\Bin). Once done, you can right-click the node.js installer and select the Edit with Orca menu.

Edit with Orca

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 InstallExecuteSequence table, and remove the 3 rows named WixSchedInternetShortcuts, WixRollbackInternetShortcuts and WixCreateInternetShortcuts.

image

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 command line option to save us all this trouble…

Wednesday, February 8, 2012

Starting on GeoSIK

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: GeoSIK.

GeoSIK stands for Geospatial Services Implementation Kit, 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 Isogeo.

You can browse this project on CodePlex, and I have just opened a new blog 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).

But you never know.

Friday, September 18, 2009

Beware the Image object !

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 InkPicture (which is just a special PictureBox control, with the ability to be drawn upon on a TabletPC). That did not seem very difficult at first : just create an OpenFileDialog and there you go.

// WRONG ! DO NOT USE THIS CODE !
if (_OpenFileDialog.ShowDialog()==DialogResult.OK)
using (Stream ims=_OpenFileDialog.OpenFile())
_InkPicture.Image=Image.FromStream(ims);

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 ?...

As often, the answer is in the documentation (check the Remarks section) :

You must keep the stream open for the lifetime of the Image.

Uh ? Any more information on why I should do this, and perhaps more importantly how I should do this ? Because my Image could be replaced from anywhere in the code. And even if I wanted to track the changes, there is no ImageChanged event on a PictureBox. And it feels weird to retain a reference to a Stream I never use...

As for the first question, the answer lies in KB 814675 : Image is just a wrapper around the native GDI+ library, which has specific requirements :

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.

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 :

public static Image Copy(Image original)
{
// cf. http://support.microsoft.com/?id=814675

Image ret=new Bitmap(original.Width, original.Height);
using (Graphics g=Graphics.FromImage(ret))
{
g.DrawImageUnscaled(original, 0, 0);
g.Save();
}

return ret;
}

And here is the correct code on how to load an Image into an InkPicture :

if (_OpenFileDialog.ShowDialog()==DialogResult.OK)
using (Stream ims=_OpenFileDialog.OpenFile())
using (Image image=Image.FromStream(ims))
_InkPicture.Image=Copy(image);

Friday, May 15, 2009

Encrypt configuration file in a custom managed action on x64

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 Custom Installer class to achieve this. There are a few tricky things to take into account, like :

  • 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.
  • this class accepts a EXEPATH parameter that contains the path of the application which application file is to be encrypted. It should then be terminated by an extra \ character.

For the record, here is how I do this :

[RunInstaller(true)]
public partial class EncryptConfigInstaller:
Installer
{

public EncryptConfigInstaller()
{
InitializeComponent();
}

public override void Install(IDictionary stateSaver)
{
base.Install(stateSaver);

string exePath=Context.Parameters["exepath"].TrimEnd('\\');

AppDomain.CurrentDomain.SetData("EXEDIR", Path.GetDirectoryName(exePath));
AppDomain.CurrentDomain.AssemblyResolve+=_AssemblyResolver;
try
{
EncryptConfiguration(ConfigurationManager.OpenExeConfiguration(exePath));
} finally
{
AppDomain.CurrentDomain.AssemblyResolve-=_AssemblyResolver;
}
}

private static Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
{
AssemblyName name=new AssemblyName(args.Name);

return Assembly.LoadFile(Path.Combine((string)AppDomain.CurrentDomain.GetData("EXEDIR"), string.Concat(name.Name, ".dll")));
}

private static void EncryptConfiguration(Configuration config)
{
EncryptSection(config.GetSection("connectionStrings"));

config.Save(ConfigurationSaveMode.Modified);
}

private static void EncryptSection(ConfigurationSection section)
{
if ((section!=null) && (!section.SectionInformation.IsProtected))
{
section.SectionInformation.ProtectSection("DataProtectionConfigurationProvider");
section.SectionInformation.ForceSave=true;
}
}

private static ResolveEventHandler _AssemblyResolver=new ResolveEventHandler(CurrentDomain_AssemblyResolve);

}

Then even if I know that managed actions are considered evil, as I do not think I have a choice here, I am using the well known WiX Custom Managed Actions trick. So everything is fine in a perfect world.

Except when a client tried to install one of my applications on a x64 platform (namely Windows Server 2008). Once solved the obvious native libraries problem, 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.

Extensive reasons behind that can be found here, but basically it is because InstallUtilLib.dll, 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 :

<Binary Id="InstallUtilBinary" SourceFile="$(sys.CURRENTDIR)..\..\lib\$(sys.BUILDARCH)\InstallUtilLib.dll" />

Yes, I learned a lot about x64 lately. But I guess I am not done yet :-)

Oracle Instant Client in Visual Studio

In my previous post, I mentioned the fact that I added the Oracle Instant Client files as Content files in a Visual Studio project. I would like to write more about this here.

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 bin\Debug folder. The best way to achieve this is to include then as Content files in your project.

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.

My way to do this is :

  • I store the Instant Client libraries in an independent folder, say lib\Oracle\Instant Client.
  • Each platform lies in a dedicated subfolder : x86 for the 32 bits version, x64 for the 64 bits version.
  • I manually tweak the .csproj project file so that it picks the right version depending on the platform I am running Visual Studio on :
    <PropertyGroup>
    <ProcessorArchitecture>x86</ProcessorArchitecture>
    <ProcessorArchitecture Condition=" '$(PROCESSOR_ARCHITECTURE)' == 'AMD64' ">x64</ProcessorArchitecture>
    </PropertyGroup>
    <ItemGroup>
    <Content Include="..\..\lib\Oracle\InstantClient\$(ProcessorArchitecture)\oci.dll">
    <Link>oci.dll</Link>
    <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
    </Content>
    <Content Include="..\..\lib\Oracle\InstantClient\$(ProcessorArchitecture)\orannzsbb11.dll">
    <Link>orannzsbb11.dll</Link>
    <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
    </Content>
    <Content Include="..\..\lib\Oracle\InstantClient\$(ProcessorArchitecture)\oraociei11.dll">
    <Link>oraociei11.dll</Link>
    <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
    </Content>
    </ItemGroup>
    <ItemGroup Condition=" '$(ProcessorArchitecture)' == 'x86' ">
    <Content Include="..\..\lib\Oracle\InstantClient\$(ProcessorArchitecture)\msvcr71.dll">
    <Link>msvcr71.dll</Link>
    <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
    </Content>
    </ItemGroup>

imageThis code is based on the PROCESSOR_ARCHITECTURE environment variable. This will be set to x86 on a 32 bits platform, as expected, but to AMD64 on a 64 bits platform. That is why I am using a custom property to get the x64 value back.

I could also simply rename the library subfolder to AMD64, but I like x64 more (I know : I am picky). And besides, it makes my WiX files more straightforward…

Oracle (Not So) Instant Client

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 TeradataMySQL, PostgreSQL, SQLite… You name it, that is the way it works.

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 quite like this. You need an ADO .NET provider AND a native client. The problems with this are :

But thanks to the Oracle guys, there is another solution : Oracle Instant Client. 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, you can trim it down do 19Mb.

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.

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 :

  • 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 ?
  • 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.

I decided to give up on the standard Setup project (had I any other option ?) and use WiX 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 :

<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
<Fragment>
<DirectoryRef Id="INSTALLLOCATION">
<?if $(sys.BUILDARCH) = "x86" ?>
<Component Id="OracleInstantClientFiles_x86" Guid="{AA0076CE-F7B6-4cd8-9B67-199F665A8E77}" KeyPath="yes">
<Condition>
<![CDATA[Installed OR NOT VersionNT64]]>
</Condition>
<File Id="OracleInstantClientFiles_x86_msvcr71.dll" Name="msvcr71.dll" Source="$(sys.CURRENTDIR)..\..\lib\Oracle\InstantClient\x86\msvcr71.dll" DiskId="1" />
<File Id="OracleInstantClientFiles_x86_oci.dll" Name="oci.dll" Source="$(sys.CURRENTDIR)..\..\lib\Oracle\InstantClient\x86\oci.dll" DiskId="1" />
<File Id="OracleInstantClientFiles_x86_orannzsbb11.dll" Name="orannzsbb11.dll" Source="$(sys.CURRENTDIR)..\..\lib\Oracle\InstantClient\x86\orannzsbb11.dll" DiskId="1" />
<File Id="OracleInstantClientFiles_x86_oraociei11.dll" Name="oraociei11.dll" Source="$(sys.CURRENTDIR)..\..\lib\Oracle\InstantClient\x86\oraociei11.dll" DiskId="1" />
</Component>
<?endif ?>
<Component Id="OracleInstantClientFiles_x64" Guid="{3CCDBDB6-D45A-4523-8CC7-730D3A8851D3}" KeyPath="yes">
<Condition>
<![CDATA[Installed OR VersionNT64]]>
</Condition>
<File Id="OracleInstantClientFiles_x64_oci.dll" Name="oci.dll" Source="$(sys.CURRENTDIR)..\..\lib\Oracle\InstantClient\x64\oci.dll" DiskId="1" />
<File Id="OracleInstantClientFiles_x64_orannzsbb11.dll" Name="orannzsbb11.dll" Source="$(sys.CURRENTDIR)..\..\lib\Oracle\InstantClient\x64\orannzsbb11.dll" DiskId="1" />
<File Id="OracleInstantClientFiles_x64_oraociei11.dll" Name="oraociei11.dll" Source="$(sys.CURRENTDIR)..\..\lib\Oracle\InstantClient\x64\oraociei11.dll" DiskId="1" />
</Component>
</DirectoryRef>
</Fragment>

<Fragment>
<ComponentGroup Id="OracleInstantClientFiles">
<?if $(sys.BUILDARCH) = "x86" ?>
<ComponentRef Id="OracleInstantClientFiles_x86" />
<?endif ?>
<ComponentRef Id="OracleInstantClientFiles_x64" />
</ComponentGroup>
</Fragment>

</Wix>

This works alright. Just reference the OracleInstantClientFiles component and there you have it. But I was annoyed by the fact that my application would install in the Program Files (x86) folder by default. So I went with the dual installer solution. And the source code became :

<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
<Fragment>
<DirectoryRef Id="INSTALLLOCATION">
<Component Id="OracleInstantClientFiles" Guid="{AA0076CE-F7B6-4cd8-9B67-199F665A8E77}" KeyPath="yes">
<?if $(sys.BUILDARCH) = "x86" ?>
<File Id="OracleInstantClientFiles_x86_msvcr71.dll" Name="msvcr71.dll" Source="$(sys.CURRENTDIR)..\..\lib\Oracle\InstantClient\x86\msvcr71.dll" DiskId="1" />
<?endif ?>
<File Id="OracleInstantClientFiles_oci.dll" Name="oci.dll" Source="$(sys.CURRENTDIR)..\..\lib\Oracle\InstantClient\$(sys.BUILDARCH)\oci.dll" DiskId="1" />
<File Id="OracleInstantClientFiles_orannzsbb11.dll" Name="orannzsbb11.dll" Source="$(sys.CURRENTDIR)..\..\lib\Oracle\InstantClient\$(sys.BUILDARCH)\orannzsbb11.dll" DiskId="1" />
<File Id="OracleInstantClientFiles_oraociei11.dll" Name="oraociei11.dll" Source="$(sys.CURRENTDIR)..\..\lib\Oracle\InstantClient\$(sys.BUILDARCH)\oraociei11.dll" DiskId="1" />
</Component>
</DirectoryRef>
</Fragment>

</Wix>

So now I can distribute the correct Oracle files (worth 130Mb per platform) along with my less than 2Mb application !