5 handle everything from file systems to sound cards to network systems.
Perh
aps you haven't considered the fact that VxDs, though 32-bit in nature, were birthed in a 16-bit, nonthreaded, nonpreemptive OS. Now they're expected--required even--to participate in an OS that has all these characteristics. This is not a simple metamorphosis.
The Grand Illusion
A virtual machine (VM) is an illusion. Specifically, the illusion is that a given process "thinks" it has exclusive access to all a computer's hardware. This includes memory, I/O ports, interrupts, and whatever other components the process chooses to manipulate. VxDs help to create this illusion.
Two kinds of VMs existed in Windows 3.1: DOS boxes and the Windows VM itself. (The latter is called the "System VM"--within which runs all Windows applications.) A third software entity, the VM manager (VMM), though not a VM itself, acted as the chief supervisor for active VMs and VxDs. For example, the VMM had the job of handling the preemptive time-slicing among the executing VMs.
Additionally, any Vx
D that was to serve as the virtualizing mechanism for an I/O device had to register itself with the VMM. So, if a VxD wanted to act as the handler for a particular I/O port, it had to ask the VMM to "hook" that port. Subsequently, whenever a Windows application
attempted access
on that port, an exception was sent to the VMM, which would pass the access request along to the appropriate VxD.
Under Win 95, life is much the same, but better in key ways. There's still one VM per DOS box and still one VM for all the Windows processes. But now some of the Windows processes are Win32 applications with more capabilities than the Win32s programs of Windows 3.x.
This produces a variety of new twists that VxD designers must be aware of. For example, Win32 applications in Win 95 can be multithreaded. No longer must a VxD be aware only of which VM requires service; there are occasions wherein a VxD must discover which thread within a particular VM needs help.
By the way, some read
ers might suspect that each Win32 application is its own VM in Win 95, as I first did. This turns out not to be the case. Each Win32 application runs as a member of the System VM, though with its own address space.
More important, a
successful VxD
in Win 95 had better be prepared to cooperate with old 16-bit Windows applications as well as with new 32-bit Win32 applications. And when it comes to VxDs, "cooperation" can mean a number of different things.
The Old Way
Although VxDs can hook I/O ports and interrupts and perform other ring 0 feats to virtualize the hardware, this turns out to be only part of what VxDs do on behalf of applications. VxDs can also provide callable APIs so that an application can directly petition a VxD for services.
In Windows 3.x, it's possible to get at a VxD's API via interrupt 2Fh. I demonstrated how to do this in
"The Software Stopwatch"
(April 1995 BYTE), where I described how you can access the API of Window
s' virtual timing device.
This mechanism still works in Win 95, provided you use it from a 16-bit application. In fact, Win 95 allows a variation: Rather than loading the device ID in the BX register, you set the BX register to zero and place a pointer in the ES:DI register pair. This pointer references an eight-character, blank-padded, uppercase string, which is the name of the VxD. As before, after your program issues an INT 2Fh instruction, the address of the VxD's API is returned in ES:DI.
Unfortunately, you can't use the INT 2Fh technique from a Win32 application. In fact, Win32 applications can't execute software interrupts, period; they'll crash if they try.
Does this mean that Win32 applications and VxDs live on opposite sides of an uncrossable digital chasm? A VxD can still do such things as virtualizing I/O ports on behalf of a 32-bit application, but what if you want to allow your VxD to expose its API to Win32 programs? Thankfully, your VxD can provide both a 16-bit and a 32-b
it API, and thus play happily with 16-bit Windows applications as well as Win32 applications in the Win 95 environment.
The New VxD
Whenever an event occurs of which a VxD must be notified, that VxD is sent a control message. These messages can come from the VMM or other VxDs, and they are processed by the VxD much as a Windows program processes Windows events. Ordinarily, these messages tell the VxD things such as, "An application is trying to access an I/O port that you said you'd handle. Here's the infotake care of it."
Win 95 adds a new message, W32_DEVICEIOCONTROL. This message is sent to the VxD in response to a Win32 application's issuing a
DeviceIoControl( )
to the VxD, and it's the mechanism whereby a Win32 application can call a VxD directly.
From the Win32 application's standpoint, it must first obtain a handle to the particular VxD by calling (oddly enough) the
CreateFile( )
routine. Ordinarily, this function is used to create or open disk f
iles. But if your program prefixes the filename with
\\.\
when making the call, the system recognizes that the filename corresponds to a VxD name. (Of course, in C or C++, backslash characters appearing in a string must be prefixed with more backslash characters, so
\\.\
becomes
\\\\.\\
. Oh, well.)
The
CreateFile( )
call returns a handle--in this case, it's a handle to the VxD. The application can then use this handle in subsequent calls to
DeviceIoControl( )
to send messages to the VxD. (Calling
DeviceIoControl( )
actually calls an "intermediary" VxD, VWIN32, which in turn calls the control-dispatch-entry point of the VxD on behalf of the application.)
DeviceIoControl( )
provides parameters for informing the VxD of which function to perform, as well as input and output buffer pointers for transferring data between the application and the VxD. (We won't show
DeviceIoControl( )
's function prototype here; you can easily find i
t in any Win32 reference manual.)
Dynamic VxDs
There's an additional benefit that comes from using
DeviceIoControl( )
to communicate with a VxD. If you step back into Windows 3.1 for a moment, you discover that VxDs are statically loaded. That is, all the VxDs a system will use are loaded when Windows starts up, and they hang around for the lifetime of Windows' execution.
Win 95 (and Windows for Workgroups 3.11) allows for dynamically loaded (and unloaded) VxDs. When an application issues a
CreateFile( )
call to access a VxD, the system keeps track of how many handles are open on each VxD. When an application is terminating, it should issue a
CloseHandle( )
call to release the VxD's handle, which causes the system to decrement the handle count. (It's important to note that the handles associated with a process are automatically called when the process exits.)
Once a VxD's handle count reaches zero, the system issues to the VxD the control messa
ge SYS_DYNAMIC_DEVICE _EXIT, telling it in essence: "You're about to be unloadedplease clean up before you leave." Once the VxD processes this message, the system unloads it.
VxDs Forever
Of course, we haven't quite closed the loop here. For any of this to work, the VxD authors have to craft the additional handlers to process the W32_DEVICEIOCONTROL message. Although building a separate route into the VxD's API might be something of a pain, it will likely be a well-trodden route as the number of Win32 programs multiplies.
Actually, this separate route is not really separate at all. A VxD will already have a control-dispatch handler in place to capture incoming messages sent to it by the VMM. Typically, this amounts to a jump table that pairs event codes with handler routines. Once the code to deal with W32_DEVICEIOCONTROL messages is written, it should be a simple matter to insert an additional entry into the jump table to complete the wiring.
With the
DeviceIoContro
l( )
calling mechanism, there is no reason why Win32 applications have to be left out in the virtual cold.
Acknowledgment
Thanks to Fred Hewett and Stephen Lewin-Berlin of Vireo Software for their technical assistance.
illustration_link (9 Kbytes)

illustration_link (4 Kbytes)

How does Windows know where the VxD's entry point is? The DDB (device descriptor block) is stored in the VxD and examined when the VxD is loaded. It contains, among other things, the VxD's API entry-point address.
Rick Grehan is a BYTE senior technical editor with an M.S. in mathematics/computer science. You can reach him by sending e-mail to
rick_g@bix.com
.