Click here to Skip to main content
15,118,063 members
Articles / Mobile Apps
Posted 7 Jun 2003


29 bookmarked

Manual uninstaller for PocketPC

Rate me:
Please Sign up or sign in to vote.
4.75/5 (14 votes)
7 Jun 2003Public Domain5 min read
If you don't want to use CAB, this is how.


Most CE apps use Microsoft's CABWIZ tool to make their installers and uninstallers. I think it's an ugly tool, with a clunky user-interface. This article discusses how to write a manual installer/uninstaller instead.

Actually, the short answer is "You can't." CABWIZ is still needed. However, what you can do is to use CABWIZ only as a skeleton, whose only task is to execute your own manual uninstaller. This article is therefore about that skeleton.

I have one very important piece of advice: always check errors, and display the precise error message on-screen. Use GetLastError() and FormatMessage() to get the error text. And when you display it on-screen, note precisely where the error came from. Do not lump several errors into a generic "some failure occurred" message. This is because -- I guarantee -- there will be errors in your setup program, and end-users will write to you to complain, and unless you have a complete diagnostic then you'll never know what went wrong. (I have omitted error-checking in this article for simplicity).


CABWIZ is a tool that comes bundled with eMbedded Visual Tools 3.0 (free) from Microsoft. Normally, you write an .INF file and compile it with CABWIZ. This generates a .CAB file. The .CAB file can be executed on the PocketPC, whereupon it installs the application and adds an entry into the Settings > RemovePrograms.

It's easy to write an installer manually - all you have to do is copy your executable into the folder \Program, and add a shortcut to it in \Windows\Programs. And maybe a shortcut in the start menu, and maybe register some registry keys.

The problem is how to register your program so it shows up under Start > Settings > System > RemovePrograms. Well, the system populates the RemovePrograms dialog with entries from the registry keys.


Within these keys, the dialog looks for a DWORD value Instl; if it is set to 1, then the app is currently installed, and should be listed in the dialog. If it is 0, then the app used to be installed but is no longer, and so won't be listed.

When the user elects to remove an installed program, it looks in the registry key for the values CmdFile and IsvFile. The first points to a .DAT file, usually \Windows\AppMgr\<Provider> <AppName>.DAT, which was put there by the installer. It is written in a proprietary .DAT format, such as is generated by CABWIZ. The second points to a .DLL file with four particular exports, which we will discuss.

The system's built-in uninstaller executes the exported function Uninstall_Init from the DLL, then parses and executes the .DAT file, then executes Uninstall_Exit. Therefore, for our manual-uninstaller purposes, our manual-installer would have to use CABWIZ to generate a .DAT file, and to place it in that directory, and to place our .DLL there as well.

But there's a problem. There are several other related registry keys which the system placed there when it executed the initial CAB file (i.e. when it installed the software in the first place). These extra registry keys are shared between all installed applications. And they're not documented. Therefore, it is not safe to do the install manually, since we mightn't set up these registry keys properly. Instead, we must use a .CAB file to do the install.

All is not lost. We hate CAB-based installers, and even though we're forced to use them, we can at least use them in a minimal way: so that the ONLY task of the CAB-installer is to set up these registry keys, and nothing more. Then we can put the real brains of our installer inside our own application, and the real brains of our uninstaller inside our custom-written .DLL file.

Incidentally, when the user clicks the 'Remove' button, the system pauses for some time with its CE-logo hourglass (between half a second and twenty seconds, usually closer to one second) before executing the setup.dll. There's no earthly reason for this. Doh!

A brief note: Our installer will invoke the CAB purely for minimal purposes, and so we want to invoke it completely silent -- without any dialogs. Here's how:

// How to install a CAB completely silently

#include <windows.h>

  // get the current application directory
  wchar_t fn[MAX_PATH]; GetModuleFileName(NULL,fn,MAX_PATH);
  wchar_t *c=fn, *lastslash=c;
  while (*c!=0) {if (*c=='\\') lastslash=c+1; c++;}

  // First, if cabwiz thinks that the app was already
  // installed, then it will complain. Hence we lie to it.
  //   Provider <space> AppName
  // as defined in ce_setup.inf and used by cabwiz.
  HKEY hkey; LONG lres;
                  L"SOFTWARE\\Apps\\Lu ce_setup",0,0,&hkey);
  if (lres==ERROR_SUCCESS)
  { DWORD val=0;

  // The act of running cabwiz will also delete the cab.
  // I think that's horrible. So I make it readonly
  DWORD attr = GetFileAttributes(fn);
  if (attr==0xFFFFFFFF)
  { return MessageBox(NULL,fn,L"Not found",MB_OK);

  // Now run the cab using wceload, telling it to be quiet:
  const wchar_t *cmd = L"\\windows\\wceload.exe";
  wchar_t arg[MAX_PATH+40];
  wsprintf(arg,L"/noaskdest /noui \"%s\"",fn);
  BOOL res=CreateProcess(cmd,arg,0,0,0,0,0,0,0,&pi);
  if (!res) MessageBox(NULL,L"Couldn't",L"ce_setup",MB_OK);
  { WaitForSingleObject(pi.hProcess,50000);

  // Really, we should wait until the cabwiz has finished
  // before restoring the file attributes. But I put a
  // timeout of 50s in there just in case something went
  // wrong, since it's bad in a PocketPC to leave
  // blocked processes.

  return 0;


As discussed, when the user clicks on 'Remove', the system will invoke a custom DLL. And this is where we put the brains of our uninstaller. Here is the form of this DLL. The two exported installer-functions are as follows:

#include <windows.h>


extern "C" __declspec(dllexport) codeINSTALL_INIT
Install_Init(HWND hpar, BOOL fFirstCall,
             BOOL fPreviouslyInstalled,
             LPCTSTR pszSuggestedInstallDir)
{ // can either return "continue", or abort before we've
  // even started the install

extern "C" __declspec(dllexport) codeINSTALL_EXIT
Install_Exit(HWND hpar, LPCTSTR pszChosenInstallDir,
             WORD cFailedDirs, WORD cFailedFiles,
             WORD cFailedRegKeys, WORD cFailedRegVals,
             WORD cFailedShortcuts)
{ // can either return "okay", or "uninstall what we've just
  // installed". But we're going to return "okay" since it
  // succeeded, and then delete some dummy files. The
  // dummy files were just a workaround around a bug in
  // cabwiz, not a sign of failure.
  wchar_t buf[MAX_PATH]; wcscpy(buf,pszChosenInstallDir);
  int len=wcslen(buf)-5;
  buf[len]='1'; DeleteFile(buf);
  buf[len]='2'; DeleteFile(buf);
  buf[len]='3'; DeleteFile(buf);
  buf[len]='4'; DeleteFile(buf);
  // And continue with other installation work if we want:
  MessageBox(hpar,L"Installing...",L"My App",MB_OK);
  // ...

  return codeINSTALL_EXIT_DONE;

The ugly issue with the temporary files will be discussed below.

The two exported uninstaller-functions are given below. Note that we make use of a global variable install_dir to record the install directory. That's because this information is given to us when the system calls Uninstall_Init(), but not when it calls Uninstall_Exit() (which is when we want it).

wchar_t install_dir[MAX_PATH];

extern "C" __declspec(dllexport) codeUNINSTALL_INIT
Uninstall_Init(HWND hpar, LPCTSTR pszInstallDir)
{ // we can either return "continue", or "abort before we
  // even start the uninstall". We will abort if our
  // application is already open. Note: but first, take
  // this opportunity to record the install_dir.
  // That's because we'll need to refer to it in
  // Uninstall_Exit, but the system doesn't to tell us it
  // again. Hence the need to remember. It's safe to 
  // remember in a global variable, since Uninstall_Init
  // and _Exit are invoked in the same instance of the DLL.
  HWND halready = FindWindow(L"MyMainClass",L"My App");    
  if (!halready) return codeUNINSTALL_INIT_CONTINUE;
  MessageBox(hpar,L"Quit program before uninstalling it",
             L"My App",MB_OK);

extern "C" __declspec(dllexport) codeUNINSTALL_EXIT
Uninstall_Exit(HWND hpar)
{ // Here we do the main work of our uninstalling:
  MessageBox(hpar,L"Uninstalling...",L"My App",MB_OK);
  // ...


The CAB file

As discussed, our installer will execute a CAB file (silently) to set up the registry keys. This CAB file will include the setup.dll that we built in the previous section. To build the CAB file, we will use Microsoft's CABWIZ. This is the control file ce_setup.inf:

Signature   = "$Windows NT$"
Provider    = "Lu"
CESignature = "$Windows CE$"

AppName     = "ce_setup"
InstallDir  = %CE1%

The system concatenates Provider and AppName (with a space in between), and this concatenation is what appears in the RemovePrograms dialog. The %CE1% stands for \Program Files -- other %CE#% values are listed in the online help documentation under the help topic "Destination Directory Macro Reference". You can also use e.g. InstallDir = %CE1%\Subdirectory.

CopyFiles = FileList
AddReg    = 
CESetupDLL  = ce_setup.dll

; Bug! If using a dll, then less than four files
; generate an error when running the cab. ???

1 = ,"source directory",,
2 = ,"bin directory",,ARMRel


FileList = 0,%InstallDir%

Here CESetupDLL refers to the setup.dll that we built previously. [SourceDisksFiles] associates it with an index number, and CABWIZ looks under [SourceDisksNames] to find where this file is located in the development machine.

[FileList] is a list of all the files that will be installed in the PocketPC. You might expect this to be empty. But there's a bug in CABWIZ whereby, if you use a setup.dll but have less than four items, it crashes. That's why we include four dummy files (each with size 1 byte!). Anyway, our Install_Exit function deletes them again immediately after installation has finished. Doh!

To invoke CABWIZ on this .inf file, and so build our .CAB, use this command. (it's all just a single line. Write it in a batch file!)

c:\progra~1\windows ce tools\wce300\pocket pc 
    2002\support\activesync\windows ce 
    application installation\cabwiz\cabwiz.exe" ce_setup.inf /err cabwiz.log


This article, along with any associated source code and files, is licensed under A Public Domain dedication


About the Author

Technical Lead
United States United States
Lucian studied theoretical computer science in Cambridge and Bologna, and then moved into the computer industry. Since 2004 he's been paid to do what he loves -- designing and implementing programming languages! The articles he writes on CodeProject are entirely his own personal hobby work, and do not represent the position or guidance of the company he works for. (He's on the VB/C# language team at Microsoft).

Comments and Discussions

GeneralRegistry Editing issue Pin
zeroone23-May-08 7:08
Memberzeroone23-May-08 7:08 
Questionregistry keys are removed [modified] Pin
kumar_vinit30-Aug-06 20:51
Memberkumar_vinit30-Aug-06 20:51 
Generalmobile5 Pin
KennethA12-Jul-06 5:35
MemberKennethA12-Jul-06 5:35 
GeneralExit appliction before install Pin
raj_rr76-Feb-06 2:09
Memberraj_rr76-Feb-06 2:09 
Generaluninstall from PC Pin
yc8415-Jun-05 7:28
Memberyc8415-Jun-05 7:28 
Generaluninstalling problem Pin
SandhyaPillai6-Mar-05 19:00
MemberSandhyaPillai6-Mar-05 19:00 
GeneralRe: uninstalling problem Pin
ljw10047-Mar-05 3:24
Memberljw10047-Mar-05 3:24 
GeneralRe: uninstalling problem Pin
raj_rr76-Feb-06 2:12
Memberraj_rr76-Feb-06 2:12 
GeneralInstalling an MDB Pin
Alex Evans18-Apr-04 19:35
MemberAlex Evans18-Apr-04 19:35 
GeneralRe: Installing an MDB Pin
João Paulo Figueira25-Apr-04 2:02
professionalJoão Paulo Figueira25-Apr-04 2:02 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.