[Microsoft Home][Products][Search][Support][Shop][Write Us] [Microsoft Home] [Image] Technology Technology [Home Page for the Driver and Hardware Development Site] Areas Initiatives [Technology Areas - Link to detailed information][Hardware Technology Initiatives - Link to detailed information][Search MSDN, Knowledge Base and this site] File Date: July 7, 1998 Adding a VxD Interface to a SYS Driver Contents: The Doctor’s Solution The Doctor’s Example How to Build and Run the Doctor’s Sample Final Bits of Advice Hello again, Hardware Development community: Today the doctor will be discussing how to add a VxD interface to a Win32® Driver Model (WDM) driver in order to support legacy applications. Microsoft® Windows® 98 driver developers generally create WDM drivers that take advantage of Windows 98 support of newer hardware classes, and to create drivers that run under both Windows NT® 5.0 and Windows 98. However, there are instances when newer hardware needs to support legacy applications. For example, consider a theoretical device that initially had a parallel port interface and was driven by a Windows 95 VxD. For the sake of this example, assume that this device is currently used by several third-party applications that communicate with this device by making calls directly into the device’s VxD. Now, with the advent of Windows 98 and Universal Serial Bus (USB), the manufacturer has decided to sell the device with a USB connection. Obviously, the manufacturer wants to take advantage of USB support in Windows 98 by developing a WDM driver, but must also provide a VxD interface in order to support legacy third-party software. How this can be done is the topic of today’s visit. The Doctor’s Solution [Back to Top] The recommended approach to support a legacy VxD interface without destroying the driver’s ability to run on both Windows 98 and Windows NT is to a create a small VxD and a *.sys-style mapper driver to map calls from the original VxD interface to calls inside the WDM driver. Figure 1 illustrates a conventional VxD interface. Figure 2 illustrates how a single set of drivers can support a legacy VxD interface, or run in a purely WDM environment and support newer applications that know how to interface with WDM drivers. The WDM driver should also run under Windows NT 5.0. Notice that (depending on the structure of the original VxD) you may be able to reuse much original VxD code to construct the new WDM driver. As shown in Figure 1, in a legacy VxD solution, third-party applications call into Legacy.vxd using the DeviceIoControl API. [Legacy VxD Solution] Figure 1. Legacy VxD Solution In Figure 2, you can see that the application calls are routed to the new WDM driver (Wdmdrv.sys) by going through the VxD interface exported by the mapper *.sys driver (Mapsys.sys). The Mapsys.sys driver creates the VxD interface by using the VMM_Add_DDB call. Mapsys.sys is able to call into Wdmdrv.sys by using the simple dynamic linking mechanism used by DLLs. [WDM Mapper Solution] Figure 2. WDM Mapper Solution Let me warn you now, the prescription for interfacing VxDs with WDM drivers is not an easy one to take. It involves some assembly language with fairly complicated macros that are easily misused. Also, because Windows NT does not support VxDs, you must make sure that your small mapper *.sys driver is never run on a Windows NT system—attempting to call VMM_Add_DDB on a Windows NT system will cause an instant blue screen! Enough with the warnings, on to the example. The Doctor’s Example [Back to Top] download MouseVxD, the example code described in this article. (Zip file; file size: 21KB; file date: December 17, 1997) The doctor’s example uses a USB mouse to simulate a device with a newly-added USB connection. In this example, assume the original device was a serial mouse that was controlled by an ordinary Windows 95 VxD driver that only allowed applications to detect button presses. Also, assume the original VxD driver only provided three operations: Open, ReadButtonStatus, and Close, which were respectively accessed through the CreateFile, DeviceIoControl, and CloseHandle APIs. This sample creates a new VxD driver, a mapper *.sys-style driver, and WDM driver that together provide the same user-mode interface as the original serial mouse VxD driver. Enough talk about theory, time to look at the code. Let’s start with the sample app, Mousemon.exe. Assume Mousemon.exe is the "original" third party application that we need to maintain compatibility with. As you can see from examining Mousemon.c, the application is very simple: open the mouse with CreateFile, read the button status and loop until the user presses a ‘q’ at which time the mouse is closed and the application terminates. Moving onto Mousebtn.vxd, we see things are nearly as simple. Mapvxd.c simply forwards the Open, Read, and Close requests from Mousemon.exe to Mapsys.sys.vxd via the VXDCALL mechanism. To see the details of the VxD call from ‘C,’ simply look at the Mousebtn.h file. But there’s not any magical medicine here; the Doctor is using the same technique described in Walter Oney’s book, System Programming for Windows 95. Things get a little more complicated with the Mapsys.sys driver. First, examine the Mapsysa.asm and Mapsysa.inc files. Toward the beginning of Mapsysa.asm, you can see the "Create_MAPSYS_Service_Table = 1" statement that creates a service table from the list of entries included in the Mapsysa.inc file. Notice that the "Create_MAPSYS_Service_Table = 1" statement must come before the "Declare_Virtual_Device" statement. Otherwise, you’ll end up with an empty service table and things will quietly not work. Also, notice that the "Create_MAPSYS_Service_Table = 1" statement by itself does not make the Mapsys.sys code accessible to other VxDs. To make Mapsys.sys accessible, you also need to make the VMM_Add_DDB call. The Windows 95 DDK documentation warns not to use the VMM_Add_DDB call, but in this case, you need to. Before calling VMM_Add_DDB, store the address of the VxD_Desc_Block (a.k.a. DDB) for your mapper sys driver into the EDI register. The name of your mapper sys DDB can be constructed by appending the string "_DDB" to the device name parameter used in the Declare_Virtual_Device statement. In this example, the VMM_Add_DDB macro is called from the MAPSYS_Init_VxD procedure, which is called from DriverEntry() when the Mapsys.sys driver is loaded. Also included in the Mapsysa.asm file are simple procedures to implement all the Service functions. These Service functions first convert parameters passed via registers from the Mousebtn.vxd into parameters passed via the standard ‘C’ convention on the stack. Then the Service function calls the appropriate routine in the Mapsys.c file. These routines in the Mapsys.c function log the event and then call the appropriate function in Wdmdrv.sys. Notice that the Mapsys.c routines are able to directly call into Wdmdrv.sys because Wdmdrv.sys exported these functions using the same technique used by DLLs. For more information on how DLLs can use exported functions, consult the Win32 SDK documentation and look at the SOURCES file for Mapsys.sys. The Wdmdrv.c file contains a lot of code, but don’t let that scare you. We’ve finished all the mapping stuff. Most of the code in this file has to do with communicating with HID to access the mouse and isn’t as relevant to this discussion. However, the code is well commented and you may find a few tips, especially if you’re working with a HID device. The only interesting thing to note (which many of you will find obvious) is that Wdmdrv.sys is a WDM driver (see Figure 2). This means you have access to all the power of WDM. It also means that you have the responsibility of running under Windows NT 5.0. So, don’t go making VXDCALLs in this driver. You need to keep all references to VxDs in either the base VxD driver (Mousebtn.vxd) or the mapper sys file (Mapsys.sys). How to Build and Run the Doctor’s Sample [Back to Top] Now that you have a basic understanding of the theory behind the sample, let’s get the sample running. The sample was tested with prototype USB mice using the Windows 98 Beta 3 and Microsoft Visual C++® version 5.0. Notice that to actually execute the sample you’ll need a USB mouse, which are a little hard to come by at this time. However, you should be able to understand what’s happening just by looking at the source code. Those of you fortunate enough to have a USB mouse can step through the code using Soft-ICE. The sample consists of four separate executables. The sample application (Mousemon.exe) is built from a Visual C++ environment (run Vcvars32.bat from a command window) by running nmake /f mousemon.mak. The Mousebtn.vxd, Mapsys.sys, and Wdmdrv.sys drivers are built from the WDM / Windows 98 DDK checked build environment. Notice that Wdmdrv.sys must be built before Mapsys.sys because Mapsys.sys needs to link with Wdmdrv.lib. Make sure that you also have the Ml.exe assembler on your path. This assembler is included with the Windows NT 4.0 DDK. To build the Mousevxd.vxd driver, simply run nmake from the Mousevxd directory. (Build.exe doesn’t currently support making VxDs, so the Doctor has provided a simple, ordinary makefile.) To build the Mapsys.sys executable, run build -ew from the Sys subdirectory. Likewise, to build the Wdmdrv.sys driver, run build -ew from the Wdmdrv subdirectory. After you have the driver binaries built, copy Mapsys.sys and Wdmsys.sys to the Windows/System32/Drivers directory, and copy Mousebtn.vxd to the Windows/System directory. For simplicity, instruct Windows 98 to load the Mapsys.sys driver during startup by merging the Mapsys.reg file into your test machine’s registry. You can do this by double-clicking the Mapsys.reg file and choose Yes to the registry change dialogs. In a production driver example, you probably don’t want to load your *.sys driver during startup; instead, you would let the Windows 98 Plug and Play manager load your *.sys file after detecting the corresponding hardware. Before starting the Mousemon.exe application, make sure your USB mouse is working properly. Then, remove or rename the Mouhid.vxd file in the Windows/System directory to prevent Windows 98 from trying to use the USB mouse at the same time as Wdmdrv.sys is using it. After all this is done, reboot the test computer. Now, execute the Mousemon.exe program, and you should see print statements indicating which buttons are pressed on the USB mouse. Final Bits of Advice [Back to Top] As I mentioned earlier, VxD-to-SYS mapping is tricky and it’s very easy to make a mistake. For example, putting the service table macros in the wrong order will cause the VxD interfaces to quietly not be exported. By quietly, I mean that you won’t get any error messages or crashes, your VXDCALLs into the mapper *.sys file will simply return before executing any code. So, take small steps when developing a VxD to SYS mapper by adding only little bits of code before testing and debugging. Most importantly, remember that you should only be using this technique when you absolutely have to—that is, when you must support an existing application that cannot be modified to call WDM. Finally, make every effort to keep the WDM driver compliant with the WDM rules. Don’t make VXDCALLs in the WDM driver, and be sure to test it under both Windows 98 and Windows NT 5.0 with applications that know how to access WDM drivers. By doing this, you’ll have less driver executable files to produce and maintain, which is easier on you and easier on your customers. That’s all for this visit. Remember to send your comments and suggestions for future articles to DrIver@microsoft.com. Until next month … Watch the way you DrIve! Disclaimer for Working Documents © 1997 - 1998 Microsoft Corporation. All rights reserved. Legal Notices.