Load, unload or reload a driver at runtime

If you develop drivers for Windows CE or Embedded Compact you know it is a major PITA to have to reload the entire kernel every time you change something in the driver code. You need to do this even in a debug kernel because the driver binaries are in use (and thus you can't re-compile & link) when the driver is loaded.

To workaround this I created a tool that allows you to unload or (re)load a driver at runtime. The way I use it is as follows:

  1. I build a small debug kernel including the driver under development
  2. I add the driver dll to the list of modules that will be loaded from the Release Directory (in VS2K5 with PB go to menu "Target"->"Release Directory Modules..." and add the driver)
  3. I add my tool (see below) as a subproject to the OS Design

Now when I download the kernel the driver will load (if the correct Drivers\BuiltIn registry values are there of course). I can set breakpoints in and step through my driver code, etc. Now when I find a bug I just press F5 to continue execution, open a "Target Control" window (menu Target->Target Control) and use my tool to unload the driver, then fix the bug, recompile & link and then (re)load the driver without the need for rebuilding or even reloading the kernel:

reloaddrv [-u] drv1: [[-u] drv2: {etc}]

In Target Control you use the 's' command to start an executable: s reloaddrv -u drv1:

The drv1: is the name of your driver (a 3 letter prefix followed by an index and a colon). Make sure to specify the -u (unload) parameter before the driver name!

To just reload the driver (so a quick unload followed immediately by a load; great for debugging initialization and clean-up) you use the tool as follows: s reloaddrv drv1:

If the driver was not loaded when you executed the above command the tool will try to find the BuiltIn registry key for the driver and load it from there.

You can specify multiple drivers on the commandline as well (-u always applies to the driver directly following it): s reloaddrv -u drv1: -u drv2: drv3: -u drv4: drv5: drv6:

I'm sure this tool is useful to a lot of you developing drivers for Windows CE/Embedded Compact, so here it is:

int WINAPI WinMain(HINSTANCE hInstance,
                   HINSTANCE hPrevInstance,
                   LPWSTR    lpCmdLine,
                   int       nCmdShow)
{
    BOOL bLoad = TRUE;
    LPWSTR pszToken = wcstok(lpCmdLine, L" ");
    while (pszToken)
    {   // Process parameters
        if (wcsicmp(pszToken, L"-u")==0)
        {   // Reset load flag if only unload requested
            bLoad = FALSE;
            pszToken = wcstok(NULL, L" ");
            continue;
        }

        // See if the driver is loaded
        DEVMGR_DEVICE_INFORMATION devInfo = {0};
        LPWSTR pszDeviceKey = NULL;
        devInfo.dwSize = sizeof(devInfo);
        HANDLE hFind = FindFirstDevice(DeviceSearchByLegacyName, pszToken, &devInfo);
        if (INVALID_HANDLE_VALUE != hFind)
        {   // Clean up find handle
            FindClose(hFind);
            // Store driver registry key
            pszDeviceKey = wcsdup(devInfo.szDeviceKey);
            // Unload driver
            RETAILMSG(1, (L"Found driver \"%s\", unloading...", devInfo.szLegacyName));
            DeactivateDevice(devInfo.hDevice);
        }
        else
            RETAILMSG(1, (L"Driver \"%s\" not loaded.", pszToken));

        if (bLoad)
        {   // Load driver if requested
            if (NULL == pszDeviceKey)
            {   // Try to find driver key in registry
                WCHAR szPrefix[4] = {0};
                DWORD dwIndex = 0;
                wcsncpy(szPrefix, pszToken, 3);
                dwIndex = _wtol(&pszToken[3]);
                HKEY hKey;
                BOOL bFound = FALSE;
                if (ERROR_SUCCESS == RegOpenKeyEx(HKEY_LOCAL_MACHINE, L"Drivers\\BuiltIn", 0, NULL, &hKey))
                {   // Opened BuiltIn drivers key
                    DWORD i = 0;
                    WCHAR szDriverKey[MAX_PATH];
                    DWORD dwCount = _countof(szDriverKey);
                    while (ERROR_NO_MORE_ITEMS != RegEnumKeyEx(hKey, i++, szDriverKey, &dwCount, NULL, NULL, NULL, NULL))
                    {   // Enumerate all driver keys
                        HKEY hDriverKey;
                        if (ERROR_SUCCESS == RegOpenKeyEx(hKey, szDriverKey, 0, NULL, &hDriverKey))
                        {   // Check if the prefix matches the specified one
                            WCHAR szDriverPrefix[MAX_PATH] = {0};
                            dwCount = sizeof(szDriverPrefix);
                            RegQueryValueEx(hDriverKey, L"Prefix", NULL, NULL, (LPBYTE)szDriverPrefix, &dwCount);
                            if (0 == wcsicmp(szPrefix, szDriverPrefix))
                            {   // And check if the index matches the specified one
                                DWORD dwDriverIndex = 0;
                                dwCount = sizeof(dwDriverIndex);
                                RegQueryValueEx(hDriverKey, L"Index", NULL, NULL, (LPBYTE)&dwDriverIndex, &dwCount);
                                if (dwDriverIndex == dwIndex)
                                {   // We found our driver key!
                                    bFound = TRUE;
                                    RegCloseKey(hDriverKey);
                                    break;
                                }
                            }
                            RegCloseKey(hDriverKey);
                        }
                        dwCount = _countof(szDriverKey);
                    }
                    if (bFound)
                    {   // Copy driver key name into pszDeviceKey
                        pszDeviceKey = (LPWSTR)malloc(MAX_PATH * sizeof(WCHAR));
                        if (pszDeviceKey)
                            wsprintf(pszDeviceKey, L"Drivers\\BuiltIn\\%s", szDriverKey);
                    }
                    else
                        RETAILMSG(1, (L"Can't find driver key for \"%s\"...", pszToken));
                    RegCloseKey(hKey);
                }
            }
            if (pszDeviceKey)
            {   // Reload driver
                RETAILMSG(1, (L"Reloading from \"%s\"...", pszDeviceKey));
                ActivateDeviceEx(pszDeviceKey, NULL, 0, NULL);
            }
        }
        // Clean up and get ready for next parameter
        if (pszDeviceKey)
            free(pszDeviceKey);
        bLoad = TRUE;
        pszToken = wcstok(NULL, L" ");
    }
    return 0;
}

And for your convenience here's the tool 7-zipped up as an OS Design subproject: reloaddrv.7z

AttachmentSize
File reloaddrv.7z2.53 KB

Comments

A similar tool with a few more options can be found here: http://bogong.codeplex.com.

Cool, thanks! Only benefit my source code has is that you don't have to know the registry key name or long hex handle value, just the legacy prefix+index is enough. It's a bit easier to use I guess, but bogong definitely has its benefits too.