Windows 98 never sends an IRP_MN_SURPRISE_REMOVAL request. Consequently, a WDM driver needs to treat an unexpected IRP_MN_REMOVE_DEVICE as indicating surprise removal. The code samples I showed you in this chapter accomplish that by calling AbortRequests and StopDevice when they get this IRP out of the blue.
Windows 98 fails calls to the IoReportTargetDeviceChange function with STATUS_NOT_IMPLEMENTED. It doesn't export the symbol IoReportTargetDeviceChangeAsynchronous at all; a driver that calls that function will simply fail to load in Windows 98. Refer to Appendix A for information about how you can stub this and other missing support functions so as to be able to ship a single driver binary.
The architecture of Windows 98 doesn't lend itself at all well to blocking in kernel mode while waiting for user-mode programs to do things. This fact bit me especially hard in connection with one of my USB sample drivers (USBINT). The test program for this sample opens a handle and issues an asynchronous DeviceIoControl call. If you now unplug the device, what's supposed to happen is this: the driver receives an IRP_MN_SURPRISE_REMOVAL, whereupon it cancels the outstanding DeviceIoControl. The test program then closes its handle. Meanwhile, back in the driver, the REMOVE_DEVICE handler has blocked on a call to IoReleaseRemoveLockAndWait. When the IRP_MJ_CLOSE arrives, the driver will release the last claim on the remove lock and allow the device removal to proceed. This works just fine in Windows 2000, but it hangs Windows 98 because the test program never gets a chance to run in order to close its handle. (We don't get the SURPRISE_REMOVAL in Windows 98, but we do get a REMOVE_DEVICE that serves the same purpose.) A code path through QUERY_REMOVE does not hang the system, however. Moral: don't acquire the remove lock while a handle is open in Windows 98 if your device can be removed by the user without going through the Device Manager API.