Windows Mobile .NET version detection at install

Question: When I install my Windows Mobile application on device not having NET CF 3.5, it fails to run. What to do? When you create a cabinet (.cab) file, you can choose to add an optional tailor-made setup.dll file to check out the version of OS and .NET framework. See http://msdn.microsoft.com/en-us/library/aa924308.aspx. Packaging a Smart Device Solution for Deployment: http://msdn.microsoft.com/en-us/library/zcebx8f8(VS.80).aspx Insert a version detection function into yours Install_Init() method. See our sample code below:
// Setup.cpp : Defines the entry point for the DLL application.
//

#include "stdafx.h"
#include "ce_setup.h"
#include "Setup.h"

#define MIN_DOTNETCF_VERSION    3

BOOL APIENTRY DllMain( HANDLE hModule,
                       DWORD  ul_reason_for_call,
                       LPVOID lpReserved
                     )
{
    switch (ul_reason_for_call)
    {
    case DLL_PROCESS_ATTACH:
    case DLL_THREAD_ATTACH:
    case DLL_THREAD_DETACH:
    case DLL_PROCESS_DETACH:
        break;
    }
    return TRUE;
}

// ----------------------------------------------------------
// Variables

TCHAR ErrorMessageTitle[] = _T("RandomFacts Setup Error");

TCHAR ErrorMessageNet[] =  _T("Microsoft Windows Mobile 6.0+ and NET CF 3.5+ are required to run our applications.\n")
                           _T("You can download the latest updates from Microsoft.\n")
                           _T("Visit http://www.microsoft.com/windowsmobile for details.\n");

// ----------------------------------------------------------
// This function will be called when the user
// tries to install the cab. According to its return
// value the installation continues or is cancelled.
// As this could be called more than once
// (i.e. if there is not enough space on the target)
// we should take care about fFirstCall parameter
// to show the dialog only once.
// To continue installation, return codeINSTALL_INIT_CONTINUE
// If you want to cancel installation,
// return codeINSTALL_INIT_CANCEL

codeINSTALL_INIT Install_Init(HWND hwndParent, BOOL  fFirstCall, BOOL fPreviouslyInstalled, LPCTSTR pszInstallDir )
{

      if( fFirstCall ){
        if( ::IsDotNetInstalled(MIN_DOTNETCF_VERSION) == false ){
            ::MessageBox(hwndParent, ErrorMessageNet, ErrorMessageTitle, MB_OK);
            return codeINSTALL_INIT_CANCEL;
        }
    }

    return codeINSTALL_INIT_CONTINUE;
}

// ----------------------------------------------------------
// Queries the registry to see if .NET 3.0 or above is installed
bool IsDotNetInstalled(int MinMajorVer)
{
    HKEY        regKey;
    TCHAR       achKey[MAX_KEY_LENGTH];   // buffer for subkey name
    DWORD       cbName;                   // size of name string
    TCHAR       achClass[MAX_PATH] = TEXT("");  // buffer for class name
    DWORD       cchClassName = MAX_PATH;  // size of class string
    DWORD       cSubKeys=0;               // number of subkeys
    DWORD       cbMaxSubKey;              // longest subkey size
    DWORD       cchMaxClass;              // longest class string
    DWORD       cValues;              // number of values for key
    DWORD       cchMaxValue;          // longest value name
    DWORD       cbMaxValueData;       // longest value data
    DWORD       cbSecurityDescriptor; // size of security descriptor
    FILETIME    ftLastWriteTime;      // last write time
    LPDWORD     data;
 
    DWORD       i, retCode;
 
    TCHAR       achValue[MAX_VALUE_NAME];
    DWORD       cchValue = MAX_VALUE_NAME;

    LONG result = RegOpenKeyEx(
                              HKEY_LOCAL_MACHINE,
                              _T("Software\\Microsoft\\.NETCompactFramework"),
                              0,
                              0,
                              &regKey
                               );

    if (result == ERROR_SUCCESS){                      
        // Get the class name and the value count.
        retCode = RegQueryInfoKey(
                                    regKey,                  // key handle
                                    achClass,                // buffer for class name
                                    &cchClassName,           // size of class string
                                    NULL,                    // reserved
                                    &cSubKeys,               // number of subkeys
                                    &cbMaxSubKey,            // longest subkey size
                                    &cchMaxClass,            // longest class string
                                    &cValues,                // number of values for this key
                                    &cchMaxValue,            // longest value name
                                    &cbMaxValueData,         // longest value data
                                    &cbSecurityDescriptor,   // security descriptor
                                    &ftLastWriteTime);       // last write time                    
   
        // Enumerate the key values.
        if (cValues)
        {          
            for (i=0, retCode=ERROR_SUCCESS; i<cValues; i++)
            {                  
                cchValue = MAX_VALUE_NAME;
                achValue[0] = '\0';
                retCode = RegEnumValue(regKey, i,
                                        achValue,
                                        &cchValue,
                                        NULL,
                                        NULL,
                                        NULL,
                                        NULL); 

                if (achValue[0] >= '0' && achValue[0] <= '9')
                {
                    int digit = achValue[0] - '0';
                    if (digit >= MinMajorVer)      // Above MinMajorVer
                    {
                        return true;
                    }
                }
            }
        }
    }
    return false;
}
If you want to sell your app on the Windows Marketplace, you don’t need to take care about the required .NET CF versions. Microsoft did a great job here.