Click here to Skip to main content
15,887,135 members
Articles / Programming Languages / C++

To Coinstallers Hell and Back Again

Rate me:
Please Sign up or sign in to vote.
4.50/5 (3 votes)
2 Feb 2007CPOL7 min read 31.5K   4   13  
Tips and possible pitfalls in device driver coinstallers development.
<h2>To Coinstallers Hell and Back Again</h2>
<p>
<i>Coinstallers</i> (in Microsoft jargon) are executable units optionally included in a driver distribution package. A coinstaller allows the driver provider to interact with the 'black box' driver installation services that are integral part of Windows (<i>Windows Device Management</i>).
</p>
<p>
If you are a driver developer who is tackling the issue of creating a driver distribution package, you have come to the right place. If you are not... well, I recommend you to engage more relaxing activities like saving whales or running a middle size state.
</p>
<p>
In this article I will elaborate on various issues that I had to resolve during the development of a coinstaller. Be advised this not a guide to coinstallers development - this subject is thoroughly covered in the DDK. This article only complements Microsoft documentation by highlighting some important issues and possible pitfalls.<br>
I recommend you to read the sections dealing with coinstaller development in the DDK before reading on.
</p>
 <h2>The Mission</h2>
My mission was to create a coinstaller to perform some registry cleanup operation when a driver is uninstalled. Since I am new to this subject, I decided to start with a simple prototype device-coinstaller which logs all calls to it in a textual log file.
<h2>Basic Architecture</h2>
The basic architecture of a coinstaller is clean and simple: a user mode DLL with a a single entry point.<br>
The entry point is a single exported function with a well specified interface:
<p>
<pre>
DWORD APIENTRY CoInstallerEntry (
   IN DI_FUNCTION InstallFunction,
   IN HDEVINFO DeviceInfoSet,
   IN PSP_DEVINFO_DATA DeviceInfoData OPTIONAL,
   IN OUT PCOINSTALLER_CONTEXT_DATA Context);
</pre>
</p>
<ul>
<li>InstallFunction: a device installation function (DIF) which is the action being performed over the device node ('devnode').</li>
<li> DeviceInfoSet,  DeviceInfoData: the devnode being handled.</li>
<li>Context: a context structure with some extra information and general purpose storage area.</li>
</ul>
Once the coinstaller is up and running Windows will call it for most DIF's it handles for the devnode.
<p>
To deploy the compiled coinstaller you need to include it in the driver installation package, together with the .sys, .inf, .cat and all the other files your package might contain. To make the coinstaller existence known  to Windows, some extra entries are required in the .inf file.
</p>
<p>
Once all this technical stuff is behind you, you're home free. After the .inf file has been selected for installation, Windows will issue calls to your coinstaller allowing you more control over the installation / uninstallation processes of your device.<br>
Simple enough. Or is it...
</p>
<h2>Decorations</h2>
<p>
The DDK claims coinstallers are standard <i>user mode DLL's</i>. Well, I have Visual Studio 2005 that can produce such DLL's, so why not use it?
</p>
<p>
I have created a new C++ project without support for MFC, CLR and all other 3 letter acronym Microsoft compilers piled up during the years. Just a good old Win32 DLL. I've added the required entry point, compiled, linked and added it to my package. The device went in, the device went out and no log file was to be seen anywhere.
</p>
<h4>Coinstaller rule number 1</h4>
<p>
<i>Make sure you are exporting the entry point function name undecorated.</i>
</p>
<p>
OK, my bad. I've fixed the code so the compiler will not decorate my function name (prefixing the entry point with the APIENTRY macro did the trick) and this time verified the exported function name using a DLL inspection tool. I've also rechecked that the entry point registration in the .inf file matches the one the tool is showing me. A perfect match now.
</p>
<h2>DEP</h2>
<p>
The device went in again and this time Windows did acknowledge my coinstaller, with a severe allergic reaction.
</p>
<p>
It appears that Windows has yet another security mechanism called DEP (Data Execution Prevention).<br>
Although I'm quite sure my coinstaller was innocent, DEP decided that it is a threat to world safety, engaged in illegal activity and should be shot on the spot.<br>
I don't want to go into all the gory details, but DEP decided the Windows executable running my DLL is executing code from a compiled data section rather than a code section (this is common to 'stack overflow' attacks). This suspicious behavior was  punished by a message box and immediate crash of the installation process. The only problem is that all my coinstaller ever tried to do is to write a few lines into a text file located in a directory shared between all users (no security issues here).
</p>
<h4>Coinstaller rule number 2</h4>
<p>
<i>Use the DDK Nmake compiler to build you coinstaller DLL.</i>
</p>
<p>
It appears that somehow the DLL's Visual Studio 2005 produces are not binary compatible with Windows Device Management. Oh well, we have Nmake.<br>
If any of you savvy readers can suggest a way to configure Visual Studio 2005 IDE to produce a compatible DLL, this would be helpful.
</p>
<h2>Fun with Nmake</h2>
<p>
I have always loved makefile's.<br>
I'm so bored at my work and have so much free time I don't care spending half a day composing a makefile for a single function DLL. Manually configuring everything is fun and constructive.
</p>
<h4>Coinstaller rule number 3</h4>
<p>
<i>Include the following in you 'sources' file (remove the lines starting with //):</i>
<pre>
// Required. Must be 8 characters or less (see below) and have a matching XXX.def file.
TARGETNAME=XXX
TARGETTYPE=DYNLINK
TARGETEXT=dll
// This is optional standard entry point in addition to the coinstaller's entry point..
DLLENTRY=DllMain
// This is taken from Microsoft's Toaster coinstaller sample. Don't ask me why this is needed.
DLLBASE=0x2000000
// This solves some annoying linking problems.
USE_MSVCRT=0
// This will give you a Unicode build.
C_DEFINES= $(C_DEFINES) -DUNICODE
</pre>
</p>
<p>
Before we go on, take notice of...
</p>
<h4>Coinstaller rule number 4</h4>
<p>
<i>Make sure there are no spaces in the path where your sources are.</i><br>
</p>
<p>
Nmake will not compile anything if you do have spaces (but of course it will not complain anything is wrong). Other DOS legacy issues will be presented shortly.
</p>
<h2>Long Names</h2>
<p>
So by now you have built a DEP friendly coinstaller. Say you partly ignored rule number 3 and decided to name our DLL <i>MyDeviceXpCoinst.dll</i>. This sounds reasonable. The problem is this is not.<br>
We have reached...
</p>
<h4>Coinstaller rule number 5</h4>
<p>
<i>The name of the coinstaller's DLL must be of 8 or less characters.</i>
</p>
<p>
Puzzled? Did you think that NTFS based Windows XP  with SP2 can handle the unthinkable16 characters name you have selected for your coinstaller DLL? Well, it can't.<br>
Of course all will work fine during the installation of the device. The coinstaller is called and the log is created. <i>The problem manifests itself when you uninstall the driver</i> - Windows suddenly gives the coinstaller a cold shoulder and refuses to call it with DIF_REMOVE.<br>
Renaming your DLL to something more readable like <i>MyDvXpCI.dll</i> will solve that.<br>
The only clue for this solution is the DDK's Toaster coinstaller example. A comfy 8 characters name is used for the DLL...
</p>
<h4>Coinstaller rule number 5 � remark 1</h4>
<p>
<i>Don't forget that inside every Windows core there is a little piece of enthusiastic, 100 years old DOS code just waiting for its 15 milliseconds of CPU time.</i>
</p>
<p>
By now you should have a coinstaller that actually works, but there are few more issues you should consider. 
</p>
<h2>Tips and Points to Consider</h2>
<ul>
<li>You can inspect and control the trace of Windows Device Manager. Read the following article by Microsoft: <a href='http:\\www.microsoft.com/whdc/driver/install/setupapilog.mspx'>"Troubleshooting Device Installation with the SetupAPI Log File"</a>. This will greatly improve your ability to debug your coinstaller.</li>
<li>During DIF_REMOVE post-processing, most of the driver registry entries have had already been deleted. Cache these values during pre-processing if they are required during post-processing.</li>
<li>You should define the "Unicode" flag in the sources file for a Unicode build. To signal the compiler to treat a constant string as a Unicode string, use the  TEXT(�abc�) macro. Do not use any other alternative macros (_T(�abc�) and so on) � they don't seem to work well.</li>
<li>The co-installer does not have full permissions during uninstallation. For example, it cannot access files located in system directories. If you have a log, place it in some public access directory such as %ALLUSERSPROFILE%\Application Data.</li>
<li>Notice that files copied from the distribution media to the system directory (among them, the coinstaller) inherit the file permissions of the system directory. This may become an issue if a non privileged user should ever access these files. You can override this using security descriptors in the .inf CopyFiles section (see DDK).</li>
</ul>
<h2>Conclusion</h2>
<p>
I've composed this article to assist any programmer out there that develops a coinstaller. I've tried to keep the article concise and to the point as much as possible. Unfortunately, I'm unable to provide any working code sample. The main reason for that is I cannot publish any code I've composed for my employer. Another reason is that I believe this is unnecessary due to the documentation provided in the DDK. If you encounter any of the problems I had, I hope you will benefit from my solutions. If you stumble upon other problems in this subject, feel free to complement my article.<br>
<b>Happy coding!</b>
</p>

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior) Runcom
Israel Israel
My name is Shay, born and raised in Jerusalem Israel although I'm currently living and working at Rishon Le'zion.
I've been around the programming world for 8 years now, 4 of them spent acquiring formal education as a Software Engineer at the Ben Gurion university in Israel.
After a modest start as an MS Access programmer and later as an IT developer, I've made a switch to low level system programming and end-to-end user applications design and implementation.
I'm currently employed in Runcom Technologies (a leading player in the WiMax industry) as a Windows drivers engineer.

I'm happy to be one of the very few who can make a decent living from their hobby. My true passion nevertheless, is music.

Comments and Discussions