Why I should not be writing applications ;)

The first driver I owned when I started at Microsoft in 1997 was i8042prt.sys, the driver that controls your PS2 mouse and keyboard.  I had the job of upgrading it from an NT4 legacy style driver to a PnP enabled Windows 2000 driver.  Of course my dad asked "didn't keyboards and mice work before you got there?  what are you doing over there anyways?"  My manager at the time said all I had to do was get the mouse and keyboard working after resume from Sx and that would be it.  I ended up owning the driver for 5+ years and not the advertised couple of months ;), but I learned a lot.


One of things I did while owning this driver was unify crash dump support for all builds.  Previous to my owning the driver, Microsoft Far East Asia PSS added the ability to crash (aka crash dump) the machine for any given key and modifier combination (see I8xServiceCrashDump() for initialization of state and I8xProcessCrashDump() on processing the keys at runtime in 6000\src\input\pnpi8042 in the WDK for the gory details).  This code was under #ifdefs and was only live for far east builds of the driver.  This was a universally useful feature and I was asked to make it available for all builds.  I decided to make it easy for folks to enable it as well.  Configuration was not easy. First, you had to know the scan code for the trigger key (instead of the character itself).  Second you had to describe the modifiers using a set of flags that were only documented in the driver's header.  Third, you had to know where to put these registry values (which typically meant you also had to create a reg key as well). Yuck, not friendly.

To fix this I created one registry value (CrashOnCtrlScroll) that setup the default crash dump key sequence for you, right ctrl + scroll lock.  This KB article describes how to set the key. That worked well ... except that many laptops did not have a right control key!  I didn't want to create another key for crashing on left ctrl + scroll lock (or something as arbitrary but completely different) so I created a GUI application that let you type the key and select the modifiers.  Not rocket science, but functional.  Here is a snapshot


Issue #1:  While functional, it's ugly

Issue #2:  I named it cdsetup.exe (c[rash]d[ump]setup.exe).  Logical name except that many cdrom setup applications are also called cdsetup.exe so there was a bit of confusion.  To add to the confusion, I think my version of cdsetup.exe was a part of the Windows 200 resource kit.  The one saving grace to this name is that it automatically requests elevation on Vista (probably because it has "setup" its name).

Issue #3:  After recompiling the application yesterday to test it on Vista the red X, Cancel and OK buttons all stopped working which meant I could not dismiss the dialog box.  Weird.  I found an old build and it worked just fine so something I did during the recompile altered the behavior.  To get the application to build and run I had to add the following 2 lines to the sources file


Specifying the taret version has no effect on the runtime so it must have been the conversion to the MS VC runtime instead of the old C runtime that shipped with Windows. I decided to debug the application's DlgProc.  It is rather simple:

LRESULT CALLBACK CrashDumpSetup::s_DlgProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
CrashDumpSetup *cdi;

if (message == WM_INITDIALOG) {
cdi = new CrashDumpSetup(hDlg);

if (!cdi)
return FALSE;

SetWindowLongPtr(hDlg, DWLP_USER, (ULONG_PTR) cdi);
return (LRESULT) cdi->Initialize();

cdi = (CrashDumpSetup *) GetWindowLongPtr(hDlg, DWLP_USER);
if (cdi) {
return cdi->DialogProc(message, wParam, lParam);

return FALSE;

LRESULT CrashDumpSetup::DialogProc(UINT message, WPARAM wParam, LPARAM lParam)
switch (message) {
switch (LOWORD(wParam)) {
ToggleAltUI(LOWORD(wParam) == IDC_DUMP_ALT);

case IDOK:
if (Apply() == FALSE) {
return FALSE;

delete this;
EndDialog(m_hDlg, LOWORD(wParam));


return FALSE;

When pressing the red X, Cancel or OK keys my WM_COMMAND processing code ran and called EndDialog() as I expected, but like I said before, the dialog didn't dismiss itself.  I looked at the docs for EndDialog and saw that it returns a BOOL, so I rewrote EndDialog to capture the return value and it was returning FALSE!  Ugh.  Since I was single stepping through the function, !gle came to the rescue

0:000> !gle
LastErrorValue: (Win32) 0x578 (1400) - Invalid window handle.
LastStatusValue: (NTSTATUS) 0xc0000034 - Object Name not found.

An invalid window handle? m_hDlg worked before, maybe some component was overwriting it. So dumped the variable and then the hDlg from s_DlgProc,

0:000> dt this m_hDlg
Local var @ 0x1ff798 Type CrashDumpSetup*
+0x000 m_hDlg : 0x00a064d0 HWND__
0:000> .f+
01 001ff7cc 775af512 cdsetup!CrashDumpSetup::s_DlgProc+0x8f [d:\work\cdsetup\cdsetup.cpp @ 576]
0:000> dt hDlg
Local var @ 0x1ff7d4 Type HWND__*
+0x000 unused : 49595140

Looking at the 2 values, it is obvious that m_hDlg was altered. But how? After staring at the code I wrote 7 years ago, I thought about what I changed (using the MS C runtime) and I immediate saw it. I was deleting the object and then touching freed memory. The old C runtime left the freed memory as it was before itw as freed so the old code had the bug the entire time, it was just that the new runtime caught made the mistake apparent immediately. The fix was simple, capture the value before deleting the object and then calling EndDialog (I guess I could have called EndDialog in the destructor but that made cleanup in the failure to initialize the object potentially problematic). After applying the fix, all of the buttons now worked as expected ;).

        case IDCANCEL:
HWND h = m_hDlg;
delete this;
EndDialog(h m_hDlg, LOWORD(wParam));

Comments (5)

  1. satya@winprogger says:

    Nice post as always 🙂

    Is there a plan to ship to cdsetup in future then ? Also where do you store the alternate key codes ?

  2. i don’t think that cdsetup is going to go into any resource kit unfortunately.  the alt keys are setup in hklmsystemcurrentcontrolsetservicesi8042prtparametersCrashdump

    Dump1Keys : REG_DWORD controls the modifier keys to use.  it is a bitwise set of flags, as defined in i8042prt.h

    #define CRASH_R_SHIFT  0x01
    #define CRASH_R_CTRL   0x02
    #define CRASH_R_ALT    0x04
    #define CRASH_L_SHIFT  0x10
    #define CRASH_L_CTRL   0x20
    #define CRASH_L_ALT    0x40

    Dump2Key : REG_DWORD controls the key to be pressed. This value is the index into the std table of scancodes for a keyboard.  There could not be a worse interface IMHO, but i didn’t come up with it.  here is the table, as defined in IxServiceCrashDump

        const UCHAR keyToScanTbl[134] = {
            0x00,0x7B,0x79,0x70 };


  3. Igor Levicki says:

    There are two points to make out of all this

    1. You wouldn’t make those errors today.

    2. Even if you did that by accident you would catch them

    3. Unlike many others you are devoted to fixing the errors you find

    That IMO means you should write whatever code you want.

  4. Igor Levicki says:

    Oh, and the fourth point is — I can’t count 🙂

Skip to main content