The concept of a virtual device driver arose in Windows 3.0 386 Enhanced
mode as a way to "virtualize" hardware devices so that multiple
DOS and Windows applications could share them. If I type on the keyboard,
for example, my keystrokes might at one time belong to the active Windows
application, and at another, to a character-mode program running in a DOS
box. Microsoft's designers built a multitasking operating system--WIN386.EXE--around
the idea of "virtual machines," a familiar paradigm for academics
and others who had long ago used IBM's own CP-67 (later VM/370). Handling
the virtual keyboard attached to a virtual machine calls for a virtual keyboard
device (VKD) which can direct the actual keystrokes from physical hardware
to the correct program in such a way that each consumer of keystrokes believes
it's dealing directly with hardware. Handling some other hypothetical "x"
device calls for a Virtual "x" Device--a VxD (in the cyberspeak
shorthand of the folks from Redmond).
Since VxDs are 32-bit, flat-model programs running in the same privileged
ring-0 world of the true operating system, programmers who need to do hardcore
systems programming will gravitate toward this level of programming. Do
you need to control math-coprocessor emulation in a 3.0 system, where the
DPMI 0Exx series of services hadn't yet been implemented? Simple. Just write
a VxD that intercepts software interrupt 31h and provides the necessary
virtualization of the processor's CR0 control register. Do you want to provide
demand paging of executables for a 32-bit Windows extender? A VxD is part
of the solution.
Writing virtual device drivers is generally the arcane specialty of trained
stunt programmers--and David Thielen and Bryan Woodruff's Writing Windows
Virtual Device Drivers does nothing to dispel that notion. Organizationally,
the book shows great initial promise. The first three sections of the book,
comprising 13 chapters and 170 pages, contain tutorial material aimed at
teaching how to combine the myriad of possible services into usable components.
The remaining three quarters of the book contain reference material, including
register-by-register instructions about how to use those services. The reference
material duplicates Microsoft's own documentation but, at least in the section
on VMM services, follows an obvious, alphabetic plan that seems to have
escaped the Microsoft writers as a preferable organizing principle. The
book breaks down, unfortunately, in precisely the area of tutorial exposition
for which potential readers have been thirsting for years.
Regrettably, Thielen and Woodruff don't develop the theme of why anyone
might want or need to write a VxD in any straightforward way. In the first
two pages, you are confronted with: the abbreviation VxD without any explanation
of where the "x" comes from; the gratuitous proposal that VMM
(mysteriously equated to WIN386.EXE without further explanation) might launch
COMMAND.COM instead of KRNL386.EXE (which is what, exactly?); advice not
to tamper directly with the IDT; the acronym IRQ; and many other low-level
concepts that belong somewhere in the book, but not right at the start.
As a programmer who's written many VxDs and who teaches VxD programming
on occasion, I wasn't startled by anything in this introductory material,
of course; but right from the start, I knew that this is a book by and for
programmers who aren't afraid to trap I/O ports, deal with a virtualized
programmable interrupt controller, or impale themselves on the "suicide"
fence of device initialization.
If the introduction made me feel that I had stepped into the middle of a
manuscript, the ensuing discussion of the mechanics of building a VxD left
me hopelessly confused. The Microsoft DDK's Virtual Device Adaptation Guide
explains that a VxD can contain a real-mode initialization part, a protected-mode
initialization part, a set of handlers for noteworthy events in the life
of a virtual machine, and a collection of service routines for use by other
VxDs and by application programs. Using macros in VMM.INC (a DDK component),
a programmer creates an assembly-language program that contains one USE16
segment for real-mode initialization and several USE32 code and data segments
for everything else. You assemble the program nowadays with MASM 6.1 and
link it with a LINK386 left over from the early beta program of OS/2 2.0.
The resulting LE signature file is then postprocessed to make it usable
by the VxD loader in WIN386 and by the WDEB386 debugger. None of the actual
mechanics of building a VxD are discussed in Writing Windows Virtual Device
Drivers, however. Even a make script with some minimal commentary would
be helpful.
The potentially complex subject of real-mode initialization becomes two
paragraphs in the book. The first paragraph reminds you that it's actually
V86-mode initialization, if you happen to be using a memory manager such
as EMM386, 386Max, or QEMM. The other paragraph supplies the information
that certain segment sizes cause unspecified "problems" within
VMM. I was glad to know this (although I would have appreciated more information
about what the "problems" were, so I could diagnose failures in
my own code better), but I've never had a real-mode initialization section
that was large enough to trip on the restrictions. On the other hand, I
didn't read about any of the things I've actually done with real-mode initializers.
How would I learn the potential benefits of the 2F/1607 device callout function?
How to claim owned pages, prevent a duplicate driver from being loaded,
or halt the startup of Windows altogether? How would I learn about passing
reference data to the protected-mode initialization part of the driver,
or about communicating with a TSR whose 2F/1605 hook caused me to be loaded?
Thielen and Woodruff have fallen into the common trap of programmer/authors,
in which they assume that their readers know almost as much about the subject
as they do. Hence, they leave out many steps of reasoning and explanation.
Since their readers won't, in fact, know very much about the subject (why
buy the book otherwise?), this becomes the book's major failing. Another
example, drawn from Chapter 3 on memory management, illustrates the problem:
The MMGR manages instance data for VMs. Instance data is a range of V86 address space that VMM maintains separately for each VM. It is used frequently for MS-DOS and some TSRs. For example, if an MS-DOS device driver maintains an input buffer, it may be useful to have the buffered input directed to the VM that was active when the buffer was filled. In this case, the VxD would query the device driver for the buffer address and maximum size and add an instance data area as shown here...This text, which is the entire description provided of instance data, is followed by an example (in C) that calls the authors' own VMM_AddInstanceItem function. I find several problems with this snippet, primarily in regard to what isn't said. "Instance data" is data that must be private to each virtual machine even though it has the same real-mode address in every machine. This is conceptually similar to automatic data in a reentrant subroutine or to thread local storage in NT. The buffer used by DOSKEY is a good example: It won't do for a command typed in one DOS box to show up in the recall buffer of another.