Errata and Updates for "Surveying the New Win32 Driver Model for Windows 98 and Windows NT 5.0" (MSJ Nov & Dec 1997)
- New names for SETUPAPI functions
- How to test SIMPLE.SYS
- Missing CRS.H File
- Finding the top-level device object
Walter Oney Software
4 Longfellow Place
Boston, MA 02114Voice: (617) 227-5620
For more information, or to comment on one of my articles, send me e-mail at waltoney@oneysoft.com
I can't respond to telephone requests for assistance, but I will answer your e-mail. And please be considerate and not call my 800 number, which costs me over 20 cents per minute!
New names for SETUPAPI functions
NT 5.0 and Windows 98 (beta 2) use different names for two of the SETUPAPI functions used in the test program for locating a device interface. The second installment of the article contains a revised version of TEST.CPP that shows how to use GetProcAddress to work around the difference:HMODULE hSetupapi = GetModuleHandle("SETUPAPI.DLL"); if (!hSetupapi) { puts("SETUPAPI.DLL not loaded!"); exit(1); } typedef BOOL (WINAPI* ENUM)(HDEVINFO, PSP_DEVINFO_DATA, LPGUID, DWORD, PSP_INTERFACE_DEVICE_DATA); typedef BOOL (WINAPI* GETDETAIL)(HDEVINFO, PSP_INTERFACE_DEVICE_DATA, PSP_INTERFACE_DEVICE_DETAIL_DATA, DWORD, PDWORD, PSP_DEVINFO_DATA); ENUM SetupDiEnumDeviceInterfaces; // new name for SetupDiEnumInterfaceDevice GETDETAIL SetupDiGetDeviceInterfaceDetail; // new name for SetupDiGetInterfaceDeviceDetail SetupDiEnumDeviceInterfaces = (ENUM) GetProcAddress(hSetupapi, "SetupDiEnumDeviceInterfaces"); if (!SetupDiEnumDeviceInterfaces) SetupDiEnumDeviceInterfaces = (ENUM) GetProcAddress(hSetupapi, "SetupDiEnumInterfaceDevice"); if (!SetupDiEnumDeviceInterfaces) { puts("Can't find SetupDiEnumDeviceInterfaces in SETUPAPI.DLL"); exit(1); } SetupDiGetDeviceInterfaceDetail = (GETDETAIL) GetProcAddress(hSetupapi, "SetupDiGetDeviceInterfaceDetailA"); if (!SetupDiGetDeviceInterfaceDetail) SetupDiGetDeviceInterfaceDetail = (GETDETAIL) GetProcAddress(hSetupapi, "SetupDiGetInterfaceDeviceDetailA"); if (!SetupDiGetDeviceInterfaceDetail) { printf("Can't find SetupDiGetDeviceInterfaceDetail in SETUPAPI.DLL"); exit(1); }
How to test SIMPLE.SYS
We didn't have space in the magazine to discuss how to test SIMPLE.SYS. Here's a sketch of the procedure I've used under Windows 98 build 1546:
- Hopefully you're using Soft-Ice/W version 3, so use LOADER32 to load the SIMPLE.NMS symbol file that got built by the build procedure. It would be useful to set a breakpoint at DriverEntry at this point, just so you can verify that the driver gets loaded when you install a device in the next step.
- Use the Add New Hardware wizard to install a device of class "Other" using the WCO.INF file that's in the project directory.
- Run TEST.EXE with the project directory (and not the DEBUG subdirectory) as the current directory. TEST should run a few IOCTL's. It will also dynamically load MYVXD.VXD and use it to perform an IOCTL using the NTKERN services described in the 2d installment of the article.
- If you run TEST with the "-dowrite" option, it will try to write the string "Hi!" to the device. You'll need to simulate the operation of the fake device to make this work. In Soft-Ice, use the BPIO command to set an I/O breakpoint on whatever port ended up being assigned to the device. (Use Device Manager to find out.) When the breakpoint trips, you can "DB AL" to see the data byte that the HAL has just written to the port. Then hit F5, hot-key back into Soft-Ice, and do a GENINT command with an interrupt number equal to 50h plus whatever IRQ got assigned to the device. That will simulate a hardware interrupt for the device, whereupon the driver will send the next data byte. In my experience, issuing the GENINT while in the HAL after tripping the I/O breakpoint will crash Windows.
Missing CRS.H File
I omitted to include the file CRS.H with the rest of the source code for the article. You need it if you want to build MYVXD.VXD. Click here to download it.
Finding the top-level device object
As shown in the article, the SendDeviceSetPower function sends a SET_POWER request to the FDO. If there are any upper filter devices, they won't see the request. It's much better to send the request to the top-level driver in the stack by calling IoGetAttachedDeviceReference as shown here:NTSTATUS SendDeviceSetPower(IN PDEVICE_OBJECT fdo, IN DEVICE_POWER_STATE state, ULONG context) { // SendDeviceSetPower NTSTATUS status; // final status PDEVICE_OBJECT tdo = IoGetAttachedDeviceReference(fdo); ASSERT(tdo); __try { // generate set power request PIRP Irp = IoAllocateIrp(tdo->StackSize, FALSE); if (!Irp) return STATUS_INSUFFICIENT_RESOURCES; PIO_STACK_LOCATION stack = IoGetNextIrpStackLocation(Irp); stack->MajorFunction = IRP_MJ_POWER; stack->MinorFunction = IRP_MN_SET_POWER; stack->Parameters.Power.SystemContext = context; stack->Parameters.Power.Type = DevicePowerState; stack->Parameters.Power.State.DeviceState = state; KEVENT event; KeInitializeEvent(&event, NotificationEvent, FALSE); IoSetCompletionRoutine(Irp, (PIO_COMPLETION_ROUTINE) OnRequestComplete, (PVOID) &event, TRUE, TRUE, TRUE); status = PoCallDriver(tdo, Irp); if (status == STATUS_PENDING) { // wait for completion KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL); status = Irp->IoStatus.Status; } // wait for completion IoFreeIrp(Irp); } // generate set power request __finally { ObDereferenceObject((PVOID) tdo); } return status; } // SendDeviceSetPower