11. Fusion and the Compact Framework
The Compact Framework is the name of the version of the .NET framework that is available on what Microsoft calls 'smart devices' or 'mobile devices'. This includes PocketPC PDAs and mobile phones. These devices have two important criteria in the context of this workshop: firstly they do not use hard discs and so they use flash memory for storage, and secondly, they tend to have limited amounts of memory (both for run time usage and for storage). For these reasons, these devices run a cut down version of Windows that has been ported to one of several non-Intel platforms and is stored in flash ROM.
At this point, the talk of developing for multiple platforms may make you think 'write once, run anywhere', but before you get carried away remember that 1) that is a catchphrase of Java (and is inaccurate anyway, it should be 'write once, test and rewrite everywhere') and 2) just because a piece of code runs on the desktop version of the framework it does not mean that it will run under the Compact Framework. The reason why I say this, is because the Compact Framework has a limited number of classes and the classes it has often do not have the full range of methods available on the desktop. The Compact Framework classes have a subset of the methods available on the desktop and usually they do not have the full range of overloaded methods. Further, since the version of the Windows API is limited (for example there is no concept of security or cryptography) then the number of classes in the CF are also limited.
Fusion is very different in the CF than it is on the desktop. The .NET Compact Framework (in version 1.0) does not use configuration files, so there is no concept of binding redirects in Fusion. This lack of config files also affects probing - the process of locating assemblies - and probing is further hampered by the fact that Windows CE does not have a concept of 'current directory'. Finally, the GAC itself is implemented in a totally different way to the desktop version and this means that there is a different way to add assemblies to the GAC. You'll learn about all these issues later on this page.
11.1 CF Development
Before you start this part of the workshop there is an important issue to consider. Throughout the workshop I have given examples using applications compiled for the console. The reason why I do this is because I want to show you that there is no magic involved. I think that wizards and RAD hide you away from technology and make it more difficult for you to fully understand the technology. Using the console means that I can show you all of the command line parameters and give you a clear understanding how each tool works. I also want you to be able to develop applications using the .NET Framework SDK which is a free download, after all, you should only need to pay Microsoft money when you are actually earning some.
However, there are some other issues. The first issue is that 'smart devices' do not have a command line and so this means that the example must be compiled as GUI application. The issue here is that a typically there will be more GUI code than the actual code that I am trying to demonstrate. There really isn't any way around this, and in the text below I have tried to keep the UI as simple as possible. Another issue is the limited memory and storage on a smart device. This means that it is not practical to have a development platform on the devices, and so instead the development is carried out on the desktop and the application is then deployed to the device.
Thus any development of Compact Framework applications means that you have to have the deployment and debugging tools that will access the device. This means that there is a compelling argument to use Visual Studio because it has all of these tools and makes their use seamless. Visual Studio has a project type for PocketPC and Smart Devices and this code will compile against the Compact Framework. Visual Studio also allows you to deploy to either a device attached with ActiveSync or to an emulator. Deployment to an attached device uses RAPI, the remote API. Microsoft provides other tools as part of their Power Toys for mobile devices that also use RAPI, as I'll explain in a moment.
However, it is possible to create applications without using Visual Studio.
The C# compiler that you use for the desktop is also used for Compact Framework
applications, the only difference is the assemblies that the compiler will
reference when it creates an assembly. If you have Visual Studio installed you
will find that the Compact Framework SDK is installed under the Visual Studio
folder. This SDK contains the metadata assemblies for the framework, so
compiling a Compact Framework application merely involves using /nostdlib,
so you don't get the desktop libraries and then use a response file or the
/r switch to point to the CF assemblies that you use. The details are
given
here.
Microsoft does provide the Compact Framework redistributable for free, so with some tweaking it is possible to use these to install the Compact Framework on the desktop. Details of what you need to do are given here, for an older version of the SDK.
Once you have created an application you need to distribute it. Microsoft provides ActiveSync as a free download. This software provides the tools to allow you to connect to a device in its docking station. It also provides the Remote API (RAPI) which you can use to write applications to access your device. Microsoft provides the Windows Mobile Developer Power Toys which contains tools that use RAPI. I use the following tools on this page:
| Tool | Description |
|---|---|
| ActiveSync Remote Display | This Power Toy will display the screen of the docked device on the desktop machine. This is great for taking screen shots and makes it easier to see what is being displayed on your device. |
| CeCopy | Uses RAPI to copy files from the desktop to the device |
| RAPIStart | Runs code on the device. This allows you to start a program from your desktop, and it allows you to pass command line parameters to that device. |
If you don't have a connected device then you can explore the Compact Framework GAC using the emulator device. This is available as part of the PocketPC SDK. To use the Compact Framework on the emulator you must first install the framework, and the details are given here. Note that if you use Visual Studio you are given the option of deploying to the device or the emulator and the first time that you deploy a CF application (either to the emulator or to a device) Visual Studio will install the Compact Framework runtime.
All of the tools I have mentioned provide you with a way to develop Compact Framework applications for free. While I applaud the ability to learn about the Compact Framework without having to pay Microsoft any money, there are too many downloads, and the fact that there is no official support means that I cannot recommend that you use them for my workshop so I will wimp out and provide a Visual Studio project. (My point of view - for those interested - is that if you are developing a product for sale then you should expect to pay for the tools that you use. If you are learning how to develop a product then clearly you have no revenue.)
On this page I will use just one example. To create this, use Visual Studio New Project wizard to create a new Smart Device Application:

This will start the project wizard that gives you the option of the target device and the type of project to create. For these examples I used Pocket PC for the device and Windows Application for the project. One of the reasons that I have not used Visual Studio for the rest of the examples in this workshop is because the project wizards bloat the code with unnecessary code and comments, another reason is the dumb names it uses. To tidy up the code follow these steps:
- Use the solution explorer to rename the main code file (
Form1.cs) toMainForm.cs - View the code for
MainForm.csand remove all comments. Then rename the class toMainForm, change the name of the constructor and changeMainso that it creates an instance of this class. - Remove
System.DataandSystem.Xmlassemblies from the References folder and any using statements for the associated namespaces. We won't use these assemblies, so there's no point in referencing them. - Remove
AssemblyInfo.csfrom the project, I want to keep the project compact and use just one source file.
As with the other examples, this example will use a library. So use the
project wizard to add a new Smart Device Application called lib
and select Pocket PC for the target and Class Library for the
project type. Again, clean up the project:
- Rename the class to
LibraryCodeand rename the file tolib.cs. - Remove the constructor and comments.
- Remove
System.DataandSystem.Xmlassemblies from the References folder. Also remove theusingstatement forSystem.Data. - Remove
AssemblyInfo.csfrom the project and add a using statement forSystem.Reflectiontolib.cs. The assembly attributes will be placed in this file. - Finally, add a reference to the
libproject to the References folder of the application project.
The start up files for this project can be found here.
11.2 Private Assemblies
In this example you will investigate the locations of assemblies. To do this
add a method called GetVersion:
{
Assembly a = Assembly.GetExecutingAssembly();
Module m = a.GetModules()[0];
return String.Format("version: {0}\r\nfile: {1}",
a.GetName().Version.ToString(),
m.FullyQualifiedName);
}
Note that the desktop has a property called Assembly.Codebase
that returns the address of the assembly, however, this property is not
available in the Compact Framework. The workaround is to get access to the first
module in the assembly (which will be the assembly file that contains the
manifest) and access the FullQualifiedName property.
Next add a button and a text box to the form of the application. Change the properties of the text box so that it is multiline and has a vertical scrollbar. Double click on the button so that the designer adds a click handler. These changes are shown here (note that I have also changed the button's name too):
{
this.mainMenu = new System.Windows.Forms.MainMenu();
this.btnLoad = new System.Windows.Forms.Button();
this.txtResults = new System.Windows.Forms.TextBox();
this.btnLoad.Location = new System.Drawing.Point(80, 8);
this.btnLoad.Size = new System.Drawing.Size(80, 20);
this.btnLoad.Text = "Load Library";
this.btnLoad.Click += new System.EventHandler(this.btnLoad_Click);
this.txtResults.Location = new System.Drawing.Point(8, 40);
this.txtResults.Multiline = true;
this.txtResults.ScrollBars = System.Windows.Forms.ScrollBars.Vertical;
this.txtResults.Size = new System.Drawing.Size(224, 224);
this.txtResults.Text = "";
this.Controls.Add(this.txtResults);
this.Controls.Add(this.btnLoad);
this.Menu = this.mainMenu;
this.Text = "Fusion Test";
}
private void btnLoad_Click(object sender, System.EventArgs e)
{
}
Add the following code to the click handler:
{
lib.LibraryCode lib = new lib.LibraryCode();
this.txtResults.Text += String.Concat(lib.GetVersion(), "\r\n");
}
catch(Exception ex)
{
this.txtResults.Text += String.Concat("Exception:\r\n", ex.ToString(), "\r\n");
}
.NET will delay load library assemblies. So it is only when the button is clicked that .NET will attempt to find the library assembly. So the first time you click on this button .NET will attempt to locate the assembly and if the assembly cannot be found an exception will be thrown. As you can see, this code will either give the assembly version, or the exception text, to the textbox.
Build the example, then right click on the solution in the Solution
Explorer and select deploy. Next, you'll be asked where you want to deploy the
application to (a connected device, of the emulator), so select whichever is
appropriate and then click on the Deploy button. Next, move to the device
(or use asrdisp.exe from the desktop) and use the Pocket PC File Explorer
from the Programs folder to navigate to \Program Files\Fusion
(where Fusion is the name of the application project) to locate the
application and start it. On my machine I get the following:

The library does not have a strong name and so versioning is not used. Notice that the library is loaded from the same folder as the application.
11.3 Locating Libraries
Next, we will change the library so that it has a version. Before we do that we'll learn another difference between the desktop and CE edition of Windows.
Click on the close button in the top right hand corner. Now view the applications running on your machine. To do this open the Settings folder, then select the System tab, select the Memory applet and then the Running Programs tab. You'll find that the example program is still running:
![]() |
This might confuse you because you appear to have already closed the application. In fact, the close button on mobile applications does not close the application, it merely hides the application. To close the application you have to use the Memory application . To do this, select Fusion Test and click on Stop. You must stop the application like this because if you try to replace the application or a library it uses while the application is running you'll get a sharing violation. However, it gets irritating to have to use the Memory screen to close the application, so to get round this issue add another button to the form, double click on it to add a handler and add the following:
{
Application.Exit();
}
Now you can shut down the application simply by clicking on the new button.
The desktop version of .NET has various probing rules, so let's see if they apply to the
Compact Framework. Switch to ActiveSync and click on the Explore button.
Next, navigate to \Program Files\Fusion by first double clicking on
My Pocket PC. Use explorer to rename lib.dll to lib.exe.
Now run the application again and click on the Load Library button.
You'll get a TypeLoadException. The desktop version will probe for
an assembly using the short name and either dll or exe
for the extension, however, the Compact Framework will only probe for libraries
with the extension of dll. Use Windows Explorer to rename the file
back to lib.dll.
Notice that on the desktop you can use
fuslogvw to get information about why the attempt to load an assembly
failed. The Compact Framework does not store information about a binding or
probing failure, so this tool is not available,and neither is there a
FusionLog property on the FileNotFoundexception.
Now use Windows Explorer to create a folder called lib under the
application folder and move the library to that folder. Repeat the previous
test. Again, you'll find that a TypeLoadException will be thrown.
The desktop version of the framework allows you to put library assemblies in a
subfolder that has the same name as the assembly, but the Compact Framework does
not allow this. Use Windows Explorer to move the library to the root of the
device. The easiest way to do this is to navigate to the lib folder,
select lib.dll and then select Cut from the context menu.
Now use Explorer's up button until you get to the root (you'll see the
Windows and Program Files folders there) and paste the file.
Repeat the previous test. You'll see something like this:

This time the application worked, which means that the file was loaded from the root. This is quite concerning because it means that multiple applications can share an assembly without a strong name simply by placing the assembly in the root folder. All the advantages of .NET versioning have disappeared.
| Be warned about this. .NET versioning has been designed to prevent the problems of DLL Hell. If you install library assemblies in the root you will return back to the bad old days of DLL Hell. |
Close down the application and move the library from the root to the
\Windows folder and repeat the test. Again, you'll get a
TypeLoadException, indicating that this folder is not used to share
assemblies. The rules for the Compact Framework are that Fusion will look for private
assemblies in the application folder or in the root.
Move the library back to the application folder and delete the subfolder that you created earlier.
11.4 Versioning
The next action is to change the library so that it has a version. To do this
the library must be given a strong name. Visual Studio isn't too good as far as
strong naming goes. I find this a very serious deficiency, and proof that
Microsoft cannot use their own development tools to develop their own .NET code.
I would have expected a menu item that would generate a key-pair for you and a
project setting to indicate that the project uses a key-pair (in a key container
or file). However, Visual Studio 2003 does not have such features, so you have
to do much of the work by hand. The first action is to obtain a key file and to do
this you use the sn utility or simply copy the key file that you used
earlier in this workshop. The trick is
to copy the key file to a location where the compiler will find it and such a
location is under the library's folder. Use Windows Explorer to navigate to the
library's project folder then paste the key file into obj\Debug or obj\Release
depending on the build configuration you are using. Once you have done this you
can add the following to the library file:
using System.Reflection;
[assembly: AssemblyKeyFile("key.snk")]
[assembly: AssemblyVersion("1.0.0.0")]
Recompile the solution and deploy it to the device, then run the application to confirm that it now reports the correct version.
Use the desktop Explorer to access the device and create a temporary folder under the
application folder. Move the library to that location. You are keeping this
version of the file for a later test. Now change the version of
the library to 2.0.0.0 and rebuild and re-deploy the application.
Run the application to confirm that it is loading the new version and then close
the application. Now use Explorer to make a copy of the old version of the
library that you copied to the temporary folder a few moments ago and use it to
replace the new version of the library. Run the application again. You should get a
TypeLoadException. The reason is that even though the library is a
private assembly versioning is used because the assembly has a strong name. The
runtime looks for version 2.0.0.0 but the only version that can be
found is version 1.0.0.0, and since the version is different to the
required version the exception is thrown.
At this point you should have a copy of version 1.0.0.0 in the
temporary folder, so now change the source code so that the library is version
1.0.0.1, that is, only the final (revision) figure has changed.
Compile and re-deply the code and repeat the test: run the application to confirm that it
picks up version 1.0.0.1, close the application and then replace
the new version with the 1.0.0.0 version of the library before running the
application again.
This time you'll find that the old version of the library will be loaded. This conflicts with the desktop version of the runtime. On the desktop, if a library has a strong name then applications that use the library will have the version stored in them and Fusion will attempt to load the exact version specified, or not at all. With the Compact Framework the runtime will try to load the version stored in the assembly that uses the library, but it will ignore the revision setting. If there are two libraries with the same major, minor and build values then the runtime will load the library that has the highest revision.
Change the source code back to version 1.0.0.0, build and deploy
it and then use the desktop Windows Explorer to delete the temporary version of the library and the
temporary folder that you created.
11.5 The GAC
There are two ways to add assemblies to the GAC and I'll demonstrate both
here. The first way is to use the cgacutil.exe utility. This takes
the name of the library file as the parameter, and since Pocket PCs do not allow
you to launch an application through the UI with a parameter, we will use the
RapiStart utility to access the Pocket PC device remotely. This Power Toy is run on the desktop in a
console and it uses RAPI to launch the process, and any command line parameters
to the device.
Open a console and type the following:
Now run the application and load the library. You will see something like this:

Notice that the library has been picked up from the \Windows
folder and that the name of the file is:
Clearly the Compact Framework uses a different strategy than the desktop. The
desktop version uses nested folders to store different versions of an assembly
in the GAC, whereas the CF renames the files using the version and culture. Use
Explorer on the desktop to navigate to the \Windows folder. You
should see something like this:

As you can see, all of the framework assemblies have been renamed. The general format is:
where <version string> is in the format v<major>_<minor>_<build>_<revision>,
<culture string> is in the format c<culture> with
neutral for a file that does
not have a culture and <ref> is used to differentiate between
multiple versions of the same file and is typically 1. Note that
the public key token of the assembly is not used in the naming scheme,
but it is used by cgacutil; if you try to add the same assembly to
the GAC twice, you'll find that the request will be ignored the second time. If
you add two assemblies which only differ by their public key token, then you'll
find they'll be added but with different <ref> values. I think this
is confusing. It would not have taken much to extract the public key token and
use it in the name of the GAC file so that it has an obviously different name to
an assembly with the same short name from a different publisher.
Another solution is to make
sure that you give your assemblies a unique name, especially if you intend to
put them in the GAC. The best way to do this is to code your company's name
into the assembly name, for example, acme.lib.dll.
|
To remove the file from the GAC you should use cgacutil with the
/uf switch and the complete name of the assembly including the
public key token. Also note that you must use
/uf for the switch and not -uf. You can use sn -Tp
on the desktop on the file created by Visual Studio (ie the file in the
bin\Debug folder under the library's project folder) to get the public
key token.
Stop the application on the device and remove the library from the GAC. For my machine I type the following at the command line:
Now re-run the application and load the library. You should find that the library will be loaded from the application's folder.
There is another way to install items into the GAC and this diverges completely from the desktop.
On the desktop machine create a file called Fusion.gac (copy con
Fusion.gac then the press Ctrl-Z.) Edit this file with notepad
(type notepad Fusion.gac) and add the following text and then save the
file:
What you have done here is created a text file with the short name of the
application and the extension gac; then you have added the full
path of a library assembly used by the application that you want to add to the
GAC.
Now copy this file to the \Windows folder on the device. There
are two ways to do this. The first way is to copy the file using the desktop Windows
Explorer: select the file you created and copy it, then navigate to the My Device item in Windows Explorer
(it is under My Computer), move to the \Windows folder and paste
the file. The other way is to use cecopy Power Toy:
The dev: prefix indicates that the folder is on the device.
Use Explorer to check that lib.dll is not in the GAC. If
it is, then remove it with cgacutil. Now run the application, but
do not click the Load Library button. Use the desktop Explorer to browse
\Windows on the device, you should find that lib.dll has
been added to the GAC, and you can confirm this by clicking on the Load
Library button. What has happened here is that when an application starts
the Compact Framework looks for an appropriate .gac file, and if
this file exists it reads the file and adds the specified assemblies to the GAC.
Now close down the application. Use the desktop Explorer to locate Fusion.gac
in \Windows and delete it. Run the application again and load the
library. The
documentation for the Compact Framework says that if you delete a .gac
file, or if you remove the entry for a library in such a file, then the assembly
will be removed from the GAC. On my Pocket PC 2003 machine (CF version
1.0.2268.00) this does not work, and I still have to use cgacutil
to remove a file from the GAC. It may work on your machine. Incidentally, if you run cgacutil on
the device without a command line it will give a dialog with the current version
of the framework.
At this point, close down the application and use rapistart to
run cgacutil to remove the library from the GAC, if it has not
already been removed.
If you browse \Windows on the device you'll find that there are
two other .gac files:
System.SR.ENU.gac
The first contains most of the framework assemblies like mscorlib
and system, the second contains the name of a resource assembly
called System.SR.dll. It is interesting that although this second .gac
file appears to be localised to the English locale, the actual assembly is not
localised (its name is GAC_System.SR_v1_0_5000_0_cneutral_1.dll,
that is, the culture is neutral).
11.6 Programmatically Adding and Removing Assemblies From the GAC
If you would like to add assemblies to, or remove them from, the GAC using
code you
should do so by launching cgacutil from your application. I mention this here because it is
not as trivial as it first appears. Yet again the problem is that the Compact
Framework is a cut down version of the framework. On the desktop you would launch
an application by calling System.Diagnostics.Process, but this
class is not available in the Compact Framework. Instead, you have to use
platform invoke to call an API to launch the utility.
The following code requires a using statement for
System.Runtime.InteropServices:
struct PROCESS_INFORMATION
{
public uint hProcess;
public uint hThread;
public uint dwProcessId;
public uint dwThreadId;
}
[DllImport("coredll")]
private static extern int CreateProcess(
string lpszImageName, string lpszCmdLine, IntPtr lpsaProcess,
IntPtr lpsaThread, int fInheritHandles, uint fdwCreate,
IntPtr lpvEnvironment, IntPtr lpszCurDir, IntPtr lpsiStartInfo,
ref PROCESS_INFORMATION lppiProcInfo);
private static void StartProcess(string proc, string cmdLine)
{
PROCESS_INFORMATION procInfo = new PROCESS_INFORMATION();
CreateProcess(proc, cmdLine, IntPtr.Zero, IntPtr.Zero,
0, 0, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, ref procInfo);
}
CreateProcess which is part
of Windows CE API. You can now call StartProcess passing the name
of cgacutil and the appropriate command line to install or remove
an assembly from the GAC.
11.7 Strong Name Verification
On the desktop version of the .NET framework strong names are verified only at install time when they are added to the GAC. The strong name of an assembly is not checked when the assembly is loaded for performance reasons, and anyway, the check is unnecessary because the GAC on the desktop is secure so the file can not be changed by someone other than an administrator or power user since it was installed.
The PocketPC file system lacks many of the features of NTFS, and principally in this discussion, there are no permissions set on folders so the GAC cannot be viewed as a secure location. For this reason the compact framework always checks the strong name signature of a shared assembly every time the assembly is used. Although this does affect performance, it ensures that assemblies are not tampered.
11.8 Concluding Remarks
The Compact Framework is a cut down version of the desktop framework, the Windows CE API is a cut down version of the Windows API. Both of these facts explain the differences between the way that Fusion works on mobile devices and how it works on the desktop. However, I have to admit that I am a little disappointed in some of its 'features'. In many cases it would take very little to change these problems.
I am disappointed that the root is used when Fusion probes for private
assemblies. This is a bad mistake for the CF team to make because it means that
lazy developers (and if we are honest most of us would admit to being lazy) will
use the root as a mechanism to share assemblies just as Win32 developers use
System32 to share DLLs. If one developer uses the root this way,
you can be sure that almost all developers will, and so install programs will
end up writing over other assemblies and we have returned to DLL Hell. It would be simple
for the CF team to remove this option: private assemblies reside in the
application folder; shared assemblies reside in the GAC. There should not be an
intermediate limbo.
I am disappointed that the files in the GAC are not named using the public key token. This would make the names of files much more unique. .NET developers are told that giving a file a strong name distinguishes it from an assembly created by another publisher, and this should be apparent in the CF GAC too. It is true that if an assembly with the same short name, the same version and the same culture exists in the GAC as the one you are installing, then the new assembly will be added with a different ref parameter (the number at the end of the renamed file). However, I think this is rather lame and it would make more sense simply to append the public key token to the renamed file's name.
| I hope that you enjoy this tutorial and value the knowledge that you will gain from it. I am always pleased to hear from people who use this tutorial (contact me). If you find this tutorial useful then please also email your comments to mvpga@microsoft.com. |
Errata
If you see an error on this page, please contact me and I will fix the problem.
