Creating an MSI Package that Detects and Updates the .NET Compact Framework
Microsoft® .NET Compact Framework 1.0
Microsoft Visual Studio® .NET 2003
Microsoft Platform SDK
Summary: This document describes how one might create an MSI package which can detect the presence of the Microsoft .NET Compact Framework on a device, determine if the device needs to be upgraded with a newer version, and install or upgrade the Framework along with an application.
There are some mechanisms available now which can reduce the work involved. For instance, developers may include the .NET Compact Framework web redistributable (http://msdn.microsoft.com/mobility/downloads/updates/default.aspx) inside their own MSI and simply invoke it. Since this may not be sufficient for many, then a larger effort may be required.
Note this example is based on a deployment from a PC via RAPI. If you need to deploy from an SD card or other mechanism, it should be possible to extrapolate what might be required.
Download the accompanying sample code.
To successfully build this sample, you will need:
Visual Studio .NET 2003
The Microsoft Installer portion of the Microsoft Platform SDK (http://www.microsoft.com/msdownload/platformsdk/sdkupdate/)
The Windows CE .NET or Windows Mobile for Pocket PC SDK (Windows Mobile 2003 for Pocket PC SDK: http://www.microsoft.com/downloads/details.aspx?familyid=9996b314-0364-4623-9ede-0b5fbb133652&languageid=f49e8428-7071-4979-8a67-3cffcb0c2524&displaylang=en ).
If you wish to modify the SetupHelper.dll files, you will need the appropriate Platform Builder/Embedded Visual C++ tools to compile for all of the appropriate platforms.
By default the makefile is configured to pull the .cab files directly from the Visual Studio directories, however, it is written for the SP1 of the .NET Compact Framework. (SP1 replaced the ARMV4T cab with the ARMV4I.) For convenience, the SetupHelper.dll used by the sample includes binaries compiled for all of the platforms supported by the .NET Compact Framework.
You will need to create a .dll which can be invoked as a custom action from the MSI file.
Begin by connecting to the device using CeRapiInitEx.
Use CeFindAllFiles to search the device's Windows directory for GAC_mscorlib_*.dll. If that is present, some flavor of the .NET Compact Framework is installed on the device.
Query the device registry and enum the values in HKLM\SOFTWARE\Microsoft\.NETCompactFramework. You should find registry keys which represent the build of NETCF that is present. (e.g. 1.0.2268.00:REG_DWORD:0). This version is created by all .cab installs and is included on all future ROM installs. It was missing from the initial ROM install of the RTM .NET Compact Framework, so if you do not find it, assume the value is 1.0.2268.0.
At this point you should know whether the .NET Compact Framework is present, and whether you need to deploy your own version to the device. If you also need to deploy the SQL CE files, you can use a similar mechanism of searching the Windows directory for the GAC'd libraries. The SQL install does not post its version to the registry, so the only way to know the build version would be to read the Win32 resources of the .dll. In the sample I have opted to always install the SQL .cabs if they are required. This should be safe, because CE installer will prevent an inadvertent downgrade of the SQL packages on the device. A similar approach could be taken with the .NET Compact Framework as well.
To successfully choose the correct cab to deploy to the device, you will need the following information: the Windows CE OS version, platform string, processor type, and instruction set. Currently, not all of these are directly available via RAPI, so the only way to obtain them is to download a native 'SetupHelper' .dll to the device and call it via CeRapiInvoke. This is a bit of a chicken-and-egg problem; how to know what flavor native .dll to download to learn what flavor .cab is needed. The answer is to try them all. CeRapiInvoke will fail if the .dll is not able to run on the device. Since these .dlls are all about 4k in size, trying each one in turn is a relatively fast operation, and it's rare to have to try more than the first few. Following are the steps to learn precisely what platform we are dealing with:
CeGetVersionEx will give the Windows CE OS version.
If the device is Windows CE 3.0, retrieve the platform string via the SetupHelper.dll. The .NET Compact Framework will only work with Windows CE 3.0-based PocketPCs, and the PocketPC will have either "Palm PC2" or "PocketPC" for a platform string. Any other value indicates an unsupported platform for Windows CE 3.0.
If the device is Windows CE 3.0 and is PocketPC, call CeGetSystemInfo and use the dwProcessorType field to select the appropriate platform:
|x86||386, 486, 586, 686|
|SH3||10003, 10004, 103|
|ARM||2577, 1824, 2080, 70001|
The cabs for a Windows CE 3.0-based PocketPC are named netcf.core.ppc3.[Platform].cab, i.e. netcf.core.ppc3.arm.cab.
If the device is Windows CE 4.1 or newer, to correctly pick the right cab you need to know the devices instruction set (from SetupHelper.dll):
|Processor||Instruction Set Id|
|ARMV4I or ARMV4T||84082688, 84017152|
The cabs for Windows CE 4.1+ are named netcf.all.wce4.[Processor].cab.
To invoke a .cab file on the device, you must invoke wceload.exe with the full path name of the .cab as a command line parameter, which is easy to do with CeCreateProcess. However, since we do not want to invoke more than one .cab at a time, we need to wait for wceload to complete, and we are unable to use WaitForSingleObject on the process handle as with CreateProcess.
This is another case where our SetupHelper.dll comes in handy. It could be made to do the WaitFor… call on the device and simply block, but this makes your installer dependent on the device's behavior. Instead, we poll the device once a second to learn if the process has completed. This allows us to invoke all of the needed .cabs in turn, first the .NET Compact Framework, then the two SQL CE .cabs, and finally our application.
It should be noted that wceload is not guaranteed to be silent. Especially in the case of upgrading a ROM install of the .NET Compact Framework, dialogs may appear on the device which require user interaction. For this reason, it is best that your MSI direct the user's attention to the device, or alternately your SetupHelper.dll could be used to monitor wceload's process for popups, and use Win32 APIs to press the appropriate button.
As an added bonus, this sample demonstrates two additional aspects of managed application installation; making cab files and installing to the GAC.
The application itself is a simple "Hello World" form, but it includes a German-language satellite .dll which is placed in the device's Global Assembly Cache. As originally generated by Visual Studio, the satellite was placed in a subdirectory under the application directory and named de\MyApp.resources.dll. Normally it would not be necessary to place such a .dll into the GAC, as the .NET Compact Framework supports most of the same probing mechanisms as the desktop, and will look in localized subdirectories under the application directory to find resources. For the purposes of this sample, it was a simple enough .dll to create.
The satellite .dll is fully signed. It must be; the .NET Compact Framework GAC does not support delay signing. It has also been renamed from de\MyApp.resources.dll to MyApp.resources.de.dll. This is merely to prevent the installer from having to create the 'de' directory which would then be emptied when the .dll was moved into the GAC. Once inside the GAC, it will bear the original name 'MyApp.resources' as part of the strong name.
Next, the .gac file is created with an explicit path of where the .cab is to place the .dll to be moved. I chose \windows\, it could have also been \temp\ or even the root directory. What matters is that the path is consistent, and as of this writing the .gac installer does not support the %CE#% macros as are used in .cab file generation.
Finally, the .cab itself is generated. The sample places the .dll and .gac files into \windows\ (.gac files MUST be placed in the directory pointed to by %CE2%), the application goes under Program Files, and a shortcut is also created.