Many PC programmers have written, or at least seen, a program to list all the DOS device drivers installed on a machine. Probably every PC system administrator has, at one time or another, used such a program. MS-DOS keeps device-driver headers in a linked list, called the device "chain" and, while Microsoft doesn't document how to find this chain, doing so is extremely simple. More because of this combination of semi-illicitness with extreme simplicity, and less because of any genuine utility, programs to walk DOS device-chain are ubiquitous.
But this is the '90s! Whether you like it or not, real-mode DOS memory-resident programs (TSRs) and DOS device drivers are becoming less important. Three years ago, Art Rothstein wrote a program to walk the OS/2 device chain (see "Opening OS/2's Backdoor," DDJ, October 1990). What matters more and more these days are Windows virtual device drivers (VxDs). Think of VxDs as 32-bit protected-mode TSRs. Just as many programmers were able to use TSRs to accomplish the otherwise impossible under MS-DOS, likewise VxDs will be where programmers "push the envelope" of Windows. What TSRs were in the mid-eighties, VxDs will be in the mid-nineties.
Since VxDs are 32-bit protected-mode code, Windows need not make an expensive mode switch when a VxD rather than a real-mode TSR or driver provides some service. For example, programs that hook DOS INT 21h should (if only for performance reasons) be written as VxDs rather than as TSRs. This will be particularly true in Microsoft's forthcoming "Chicago" operating system (Windows 4/DOS 7), in which the presence of even one real-mode TSR or driver may seriously impact the whole system. Furthermore, VxDs are allocated out of extended memory, so they don't occupy any precious conventional memory below one megabyte. Finally, Windows VxDs have available hundreds of services that plain-vanilla DOS just doesn't provide.
In this month's "Undocumented Corner," I'll throw out the traditional program to walk the DOS device chain, and write a new program, VXDLIST, that walks the Windows Enhanced mode VxD chain and displays the names of all VxDs loaded on the system. This program is similar to the VXD command in Nu-Mega's Soft-ICE/Windows debugger. In particular, VXDLIST will be useful as a tool for exploring Chicago, which is based on VxDs.
While Microsoft has failed to document the Linear Executable (LE) file format used by VxDs, and the W3 file format used by the Enhanced mode WIN386.EXE file (DOS386.EXE in Chicago), you can bypass this problem by examining VxDs in memory. Microsoft's Windows Device Driver Kit (DDK) documents almost everything needed to implement a VXDLIST program, and the remaining pieces are readily inferred from the documentation.
While VXDLIST depends almost entirely on documented interfaces, it is useful for uncovering undocumented interfaces. For example, while Microsoft clearly documents how a VxD can provide a programming interface to applications running under Windows, the documentation says next to nothing about which built-in Windows VxDs actually do provide APIs. VXDLIST shows which ones do.
Figure 1 shows sample output from VXDLIST running under Windows 3.1 Enhanced Mode. Figure 2 shows sample output when running under the August 1993 prerelease of Chicago.
What do we see here? Just a list of VxDs. In one sense, the VXDLIST output looks like a slightly complicated version of the [386Enh] section of the Windows SYSTEM.INI file, which uses device= statements to specify the VxDs to be loaded. So, if you want to find out which VxDs are loaded on a system, why not just print out the [386Enh] section of SYSTEM.INI? Because the user might have modified the .INI file without restarting Windows. In addition, TSRs and drivers running before Windows can bypass SYSTEM.INI by hooking an INT 2Fh AX=1605h broadcast to tell Windows to load a VxD (which might be embedded right inside the TSR or driver). For example, this is how MS-DOS 5 and 6 forced Windows 3.0 to load WINA20.386.
You can learn a lot by staring at the VXDLIST output. First, there's the sheer number of VxDs that happened to be loaded in this standard configuration: about 30 in Windows 3.1 and almost 50 in Chicago. Many of these (including VMM, the Windows Virtual Machine Manager) are built into WIN386.EXE or DOS386.EXE; others are supplied as *.386 files, or built into DOS programs such as EMM386.EXE or INTERLNK.EXE.
In addition to VMM, there are VPICD (the Virtual Programmable Interrupt Controller [PIC] Device), VTD (Virtual Timer Device), VDD (Virtual Display Device), and so on. DOSMGR is the Enhanced Mode DOS extender and Windows' (largely undocumented) interface to MS-DOS. PageFile and PageSwap manage the virtual-memory swap file; the Reboot VxD virtualizes Ctrl-Alt-Del.
In Figure 2, you can see that Chicago tends to rely more heavily than Windows 3.1 on VxDs. Chicago includes many new VxDs, including VFAT (a 32-bit protected mode version of the DOS FAT file system), IFSMGR (an installable file-system manager), VXDLDR (a dynamic VxD loader), VSHARE (a 32-bit protected mode version of SHARE), and so on. Microsoft is providing an early look at many of these new Chicago VxDs in version 3.11 of Windows for Workgroups (WfW).
The order in which VxDs appear in this list reflects the order in which the VxDs initialized. For example, it is significant that SHELL appears last, as this VxD is responsible for starting the Windows kernel (KRNL386.EXE), which in turn boots the rest of what we normally think of as Windows (USER, GDI, and so on). SHELL is an interface between the upper and lower parts of Windows Enhanced mode.
The third column in Figure 1 and 2 shows VxD identification numbers. For example, VMM is considered VxD #1, VPICD is #3, VTD is #5, and so on. Microsoft issues these numbers to third-party VxD developers ([email protected]). Only VxDs that provide APIs require an ID number.
One VxD calls functions in another VxD by issuing an INT 20h, specifying the VxD ID and a function number. Windows and DOS programs can call down to a VxD through a callback address; programs obtain this address by calling INT 2Fh AX=1684h with BX set to the VxD ID.
The VxD can provide separate APIs for protected mode (PM) and Virtual-86 (V86) mode. A real-mode DOS program running under Windows could use the V86 API; a Windows application would use the PM API, of course. Figure 1 and 2 show which VxDs provide APIs in each mode. An asterisk next to the address of the API indicates that it has a corresponding callback address, which in turn indicates that some program has probably used the API. To determine what the API actually does, you would need to disassemble at the address displayed by VXDLIST, or consult the documentation. For an examination of a sample VxD API, see "Fast Interrupt Handling Without VxDs" by Karen Hazzah (Windows/DOS Developer's Journal, June 1993).
The last VXDLIST column indicates the size of the VxD's "service table." This is the table of function pointers used to service API calls from other VxDs. For example, in Windows 3.1, VMM provides 242 services, VPICD provides 21, VTD provides 8, and so on. For the most part, these are documented in the Windows DDK Virtual Device Adaptation Guide.
Notice in Figure 2 that Chicago provides many new functions: VMM in Chicago provides 350 services, over 100 more than in Windows 3.1. These new VMM services include calls for threads, mutexes, memory commit/uncommit, and all the other low-level functionality you would expect VMM to provide, given that Microsoft intends for Chicago to support all the same Win32 calls as Windows NT, except for security and Unicode. And in four megabytes, instead of twelve, or whatever ridiculous amount of memory NT requires.
Other important new areas of functionality in Chicago include IFSMGR, which appears to provide 61 services. According to a developer at Microsoft, IFSMGR will be a full-blown, documented manager for installable file systems--greatly preferable to using the undocumented DOS network redirector.
As seen in the VXDLIST output, the SHELL VxD provides only six services in Windows 3.1, but 25 in Chicago. The new SHELL seems to include services for accessing the Windows API (PostMessage, WinExec, the clipboard, and DLL calling) from a DOS box; this presumably underlies Chicago's ability to start a Windows program from the C:\> prompt in a "unified" command shell.
VXDLIST has a -verbose option which will output the number, address, and name for each function in the service table; Figure 3 shows an example. Unfortunately, the function names are not built into the VxDs themselves. VXDLIST includes a list of function names that were culled from DDK header files. Since I didn't have a Chicago DDK, VXDLIST doesn't know the names of the new Chicago services.
VXDLIST.C is unfortunately too long to reprint here; full source code is available electronically (see "Availability," page 3). Because VxDs reside in extended memory, walking the VxD list is best accomplished by a protected-mode program. This can either be a DOS program that uses the DOS Protected Mode Interface (DPMI) services provided by Windows, or it can be a Windows application (which is really just a certain type of protected-mode DOS program). VXDLIST is a DOS program that uses DPMI to switch itself into protected mode. DPMISHEL.C, also available electronically, provides a C interface to DPMI INT 2Fh and INT 31h services. Incidentally, in Windows Enhanced Mode the DPMI server is located in VMM, and in many ways is just a thin layer on top of VMM services.
The key portion of VXDLIST is the loop that runs over the VxD chain; this is shown in Listing One (page 137) along with an extract from VXDLIST's display function to printf selected fields from each VxD's header. The Windows DDK documents the VxD header structure, not in the manual, but in the VMM.INC file. Listing Two (page 137) shows a C version of this structure, called the Device Descriptor Block (DDB).
As you can see in Listing One, VXDLIST locates the root of the VxD chain by calling a function, Get_First_VxD. It moves from one VxD to the next by calling Get_Next_VxD. Unfortunately, there are no VMM functions to walk the VxD chain. Instead, Get_First_VxD and Get_Next_VxD are provided by another module, VXDCHAIN.C, which is also unfortunately too long to reprint here. However, Listing Three (page 137) shows the key Get_First_VxD and Get_Next_VxD functions from VXDCHAIN.C.
Get_Next_VxD is simple. The Windows VMM keeps all VxD headers in a linked list, in the order in which they initialized. The DDB has a "reserved" field, DDB_Next. It is not explained in the documentation, but this field is obviously a pointer to the next VxD in the chain.
So, all you need is the root of the VxD chain (Get_First_VxD). This will always be the VMM itself, which initializes before all VxDs. While VMM really isn't a VxD (among other responsibilities, it is the VxD manager and supplier of most functions that VxDs call), it has a standard VxD DDB. Its DDB_Name field will contain the string "VMM," followed by five spaces. Its VxD ID number (see DDB_Req_Device_Number in Listing Two) will be 1.
Implementing Get_First_VxD is the only tricky or undocumented part of this code. Frankly, the function uses brute force: it searches in memory for the string "VMM", and sees if any such strings are indeed VMM's DDB_Name field. Windows doesn't contain an undocumented Get_First_VxD function; instead, we have to implement one, by any means necessary. Thus, this month's "Undocumented Corner" has revealed, not so much an undocumented interface, as a slimy hack.
Where does Get_First_VxD look for the VMM DDB? We know it's in extended memory, but where exactly? At the VxD level, Windows deals in what are called "linear" addresses: the machine is treated as a linear (that is, flat) 4-gigabyte array of bytes. This is a sparse array, in that, except on a dream machine with four gigabytes of physical memory or swap-file disk space, most of the addresses are empty. Windows leaves empty slots in the page directory and page tables. As presently constructed, Windows Enhanced Mode reserves one page table (four megabytes of linear address space) for VxDs, starting at address 80000000h (two gigabytes).
To find the start of the VxD chain, Get_First_VxD must search in the range 8000000h-80400000h for the string "VMM". It can't start the search right at 80000000h, however, because Windows seems to use the first 4K as a "guard" page to catch null pointers. Thus, it starts searching at 80001000h (see Listing Two).
Linear addresses such as 80001000h are not immediately useful to a 16-bit program such as VXDLIST. To turn this linear address into a useful selector:offset pointer that it can dereference, VXDLIST must "map" the address into its address space. This is done using Get_VxD, which calls the map_linear function, which in turn uses DPMI equivalents of the AllocSelector, SetSelectorBase, and SetSelectorLimit Windows API functions (see Listings Three and Four, page 137). To turn a linear address into a selector:offset pointer, map_linear allocates a selector/descriptor and sets its base address to the desired linear address. This is explained in detail in the new "Undocumented DOS Meets Windows" chapter of Undocumented DOS, second edition.
Besides helping reveal how Windows Enhanced Mode and Chicago work, does walking the VxD chain have any practical use? Surprisingly, it does. I was first alerted to the need for this utility by a system administrator who needed to incorporate a program into a batch file to tell whether a named VxD was running. Placing a VxD name or ID number on VXDLIST's command line makes it search for the specified VxD. VXDLIST will return a DOS error code based on whether the VxD is loaded. A batch file ERRORLEVEL statement can then test this:
The code to search for a specified VxD can be found in the Find_VxD and Find_VxD_ID functions in VXDCHAIN.C, available electronically.
The VxD manipulation functions in VXDCHAIN.C might also be useful as the basis for a dynamic VxD loader for Windows 3.1, similar to VXDLDR in Chicago. It's also interesting to consider how a non-VxD program might call functions such as those displayed in Figure 3. One possibility would be a 32-bit Ring 0 call gate (see Matt Pietrek's "Run Privileged Code from Your Windows-based Program Using Call Gates," Microsoft Systems Journal, May 1993).
That's it for this month's "Undocumented Corner." Future columns will take up the undocumented LE and W3 file formats I mentioned earlier. I've already received a useful write-up of LE from Doug McIntyre. This material will be useful in Chicago as well as in Windows 3.1. I've also just received a fascinating two-part article by Kelly Zytaruk on the Windows Virtual Machine Control Block (VM CB) structure, most of whose fields are undocumented. It looks, then, like we're in for several months of coverage for Windows Enhanced Mode and Chicago. Please send your suggestions to me at 76320,302 on CompuServe.
Copyright © 1993, Dr. Dobb's JournalChicago Looks Cool!
A Slimy Hack
Where Now?
vxdlist %1 > nul
if errorlevel 2 goto not_found
if errorlevel 1 goto end
echo VxD %1 is loaded
.
.
.
Figure 1: VXDLIST output under Windows 3.1 Enhanced Mode
Name Vers ID DDB Control V86 API PM API #Srvc
-------- ---- ----- -------- -------- -------- -------- -----
VMM 3.10 0001h 80011A74 8000ADFF 242
VPICD 3.10 0003h 80020EFC 8001FF10 80020624 80020624* 21
VTD 3.10 0005h 80022404 8002199E 80021933 80021933 8
PageFile 2.00 0021h 80036514 80035EB8 800363DF 7
PageSwap 2.10 0007h 8002D028 8002C6D0 7
PARITY 1.00 0008h 8003692C 80036884 0
Reboot 2.00 0009h 800230B8 800224FC 80022825* 0
VDD 2.00 000Ah 8001C580 80016060 80019EC7* 14
VSD 2.00 000Bh 80025078 80024F04 2
VCD 3.10 000Eh 80037960 80036E7F 80036EC7 4
VMD 3.00 000Ch 80016000 80015DBC 80015F49 80015F49* 3
VKD 2.00 000Dh 8001F44C 8001DB28 8001E05E* 15
BLOCKDEV 3.10 0010h 80035E70 80035BB8 7
INT13 3.10 0020h 800151EC 80014F3C 5
VFD 2.00 001Bh 80036818 800366C8 0
PharLap 1.00 80014550 80013E14 0
VMCPD 1.02 0011h 80037D20 80037B84 3
BIOSXLAT 1.00 0013h 80036D80 800369C0 0
DOSMGR 1.00 0015h 80030E6C 8002EB33 8002E977* 12
VMPOLL 3.10 0018h 80031680 80031433 3
Vpfd 1.04 1022h 80014BF4 800147D4 80014B2A 80014B2A 1
VXD 1.01 28C0h 80014ED8 80014C88 80014C8A 80014C8A 0
LANMAN10 3.00 800154FC 800154AC 0
VTDAPI 3.00 0442h 8001FB60 8001FB52 8001F728* 0
COMBUFF 1.00 800380CC 80037D70 0
TDDebug 1.00 001Dh 8001478C 8001466A 8001475A 0
VDMAD 2.00 0004h 80024B88 8002323C 24
V86MMGR 1.00 0006h 8002C67C 8002AD13 21
SHELL 3.00 0017h 80035988 8003429E 80032C88* 6
Figure 2: VXDLIST output under Chicago (August 1993 Prerelease)
Name Vers ID DDB Control V86 API PM API #Srvc
-------- ---- ----- -------- -------- -------- -------- -----
VMM 4.00 0001h 80014020 80004097 80004BE1 80004BE1* 350
VCACHE 3.01 048Bh 8003DEF4 8003DC0C 8003DEAF 8003DEAF 14
CONFIGMG 4.00 0033h 8001FAE0 8001BEBF 8001BE20 8001BE20 57
FAKEIDE 3.10 00FDh 8001B1CC 8001B1B8 0
VPICD 3.10 0003h 800466D4 80044F38 800459C0 800459C0 22
VTD 4.00 0005h 80048DD8 80048080 80230B5F 80230B5F 12
VXDLDR 3.00 0027h 80043CA0 800431C0 800432EB 800432EB 6
ISAPNP 4.00 0051h 80047D00 800475A4 0
IOS 3.10 0010h 80036080 8003495C 802244B8 13
PAGEFILE 4.00 0021h 800442E8 80044130 8004422C 7
PAGESWAP 2.10 0007h 80050EBC 80050DB0 10
PARITY 1.00 0008h 800527C4 80052704 0
REBOOT 2.00 0009h 800493C8 8004902C 802310C2* 0
VDD 2.00 000Ah 800599E8 80053140 80054735 80054735* 15
VSD 2.00 000Bh 8004B6C0 8004B4F0 4
VCD 3.10 000Eh 80027AD4 80026F4F 80026F97 9
VMD 4.00 000Ch 8005B104 8005AA38 802370F0 802370F0* 7
VKD 2.00 000Dh 8005CCA4 8005B380 8005B953* 20
VFBACKUP 4.00 0036h 80022788 80022212 80022405 80022406 5
INT13 3.10 0020h 80043094 80042A8E 5
VMCPD 1.02 0011h 80052F34 80052C34 7
BIOSXLAT 1.00 0013h 80052BC4 80052808 0
VNETBIOS 3.00 0014h 8005E89C 8005D9B8 4
DOSMGR 4.00 0015h 800511A4 80050F04 80232BA4 13
VSHARE 1.00 0483h 80022BC0 80022AA3 80022A78 80022A78 7
VMPOLL 3.10 0018h 800515B8 80051363 3
VXD 2.00 28C0h 8001B144 8001AE64 8001AE8E 8001AE8E 0
VWIN32 1.02 002Ah 80021E68 80020844 80020936* 7
VCOMM 1.00 002Bh 80024EBC 80024554 80223530 80223530* 29
SERIAL 1.00 80025F64 80024FAC 0
LPT 1.00 8002652C 800260AC 0
COMBUFF 1.00 80026C44 800268A0 0
VCOND 1.00 0038h 8003EDD8 8003DFA8 8003DFBB 8003E042* 2
VTDAPI 4.00 0442h 800530EC 800530D6 802363A4* 0
UNIMODEM 1.00 0460h 8005D1E4 8005CF74 8005D025 0
DiskTSD 3.10 81ED4720 81ED44A8 0
voltrack 3.10 0090h 81EDD3A4 81EDCDD4 0
RMM 3.10 81EDE3B8 81EDDD2C 0
NEC 3.10 81EEB95C 81EEABA0 0
ESDI_506 3.10 008Dh 81EECA50 81EEC0A8 0
VDMAD 2.00 0004h 8004B230 800495D8 26
V86MMGR 1.00 0006h 80050C38 8004F42B 23
VFAT 3.00 0486h 8003D688 8003A364 8003A398 8003A398 0
CDFS 3.00 800428D8 800423CB 0
VDEF 3.00 800440F4 80043FD8 0
IFSMgr 3.00 0484h 8002A630 80027BDC 8002A134 61
SHELL 4.00 0017h 8005251C 80051F18 80235F0F 80235F0F* 25
WSHELL 4.00 80044820 800443C8 8022F1E0* 0
Figure 3: The VXDLIST verbose option
C:\DDJ\VXDCHAIN>vxdlist -verbose PageFile
Name Vers ID DDB Control V86 API PM API #Srvc
-------- ---- ----- -------- -------- -------- -------- -----
PageFile 2.00 0021h 8007495C 80074300 80074827 7
Init order: 18000000
Demand Paging Swap Device Services
210000h @ 80074318h PageFile_Get_Version
210001h @ 80291002h PageFile_Init_File
210002h @ 80074316h PageFile_Clean_Up
210003h @ 8007435Ah PageFile_Grow_File
210004h @ 80074496h PageFile_Read_Or_Write
210005h @ 80074709h PageFile_Cancel
210006h @ 80074337h PageFile_Test_IO_Valid
[LISTING ONE]
#include "vxdchain.h"
DWORD vxd_lin, next;
int err;
if ((err = Get_First_VxD(&vxd_lin)) != 0)
fail("Can't locate VxD chain");
for (;;)
{
display(vxd_lin);
if ((err = Get_Next_VxD(vxd_lin, &next)) == 0)
vxd_lin = next;
else if (err == ERROR_END_OF_CHAIN)
break; // successfully got to end of VxD chain
else
fail("Can't walk VxD chain");
}
// ...
void display(DWORD vxd_lin)
{
unsigned char name[9];
DDB far *pddb;
if (Get_VxD(vxd_lin, &pddb) != 0)
fail("Can't get VxD");
_fstrncpy(name, pddb->DDB_Name, 8); /* printf %8.8Fs not reliable */
name[8] = '\0';
printf("%s ", name);
printf("%u.%02u ",
pddb->DDB_Dev_Major_Version, pddb->DDB_Dev_Minor_Version);
if (pddb->DDB_Req_Device_Number)
printf("%04Xh", pddb->DDB_Req_Device_Number);
// ... etc.
}
[LISTING TWO]<a name="039c_000d">
#pragma pack(1)
typedef struct {
DWORD DDB_Next; // addr of next VxD in chain, or 0
WORD DDB_SDK_Version;
WORD DDB_Req_Device_Number; // the VxD ID number
BYTE DDB_Dev_Major_Version;
BYTE DDB_Dev_Minor_Version;
WORD DDB_Flags;
BYTE DDB_Name[8]; // padded with spaces
DWORD DDB_Init_Order; // also order within list (SHELL last)
DWORD DDB_Control_Proc;
DWORD DDB_V86_API_Proc;
DWORD DDB_PM_API_Proc;
void (far *DDB_V86_API_CSIP)(); // V86 mode seg:ofs callback addr
void (far *DDB_PM_API_CSIP)(); // prot mode sel:ofs callback addr
DWORD DDB_Reference_Data;
DWORD DDB_Service_Table_Ptr;
DWORD DDB_Service_Table_Size;
} DDB; // from ddk include/vmm.inc
<a name="039c_002d"><a name="039c_000e"><a name="039c_000f">
[LISTING THREE]<a name="039c_000f">
#include "map_lin.h"
int Get_First_VxD(DWORD *proot)
{
DWORD vmm_ddb_lin = 0;
DWORD lin;
DDB far *pddb;
char far *vmm_str;
unsigned char far *fp;
// try to find string "VMM " in first megabyte
// can't start at 0x800000000L: page fault!
for (lin=0x80001000L; lin<0x80400000L; lin += 0x10000L)
{
if (! (fp = (char far *) map_linear(lin, 0x10000L)))
return ERROR_CANT_MAP_LINEAR;
if (vmm_str = fmemstr(fp, VMM_STR, 0xFFFF)) // search for string
{
// back up to get linear address of possible VMM_DDB
vmm_ddb_lin = lin + (vmm_str - fp) - offsetof(DDB, DDB_Name);
free_mapped_linear(fp);
// map VMM_DDB (hopefully) into our address space
if (! (pddb = (DDB far *) map_linear(vmm_ddb_lin, sizeof(DDB))))
return ERROR_CANT_MAP_LINEAR;
// make sure it really is VMM_DDB
if ((_fstrncmp(pddb->DDB_Name, VMM_STR, 8) == 0) &&
(pddb->DDB_Req_Device_Number == 1) &&
(pddb->DDB_Init_Order == 0) && // VMM_Init_Order
(pddb->DDB_Next > 0x80000000L) &&
(pddb->DDB_Control_Proc > 0x80000000L))
{
Free_VxD(pddb);
*proot = vmm_ddb_lin;
return SUCCESS;
}
}
// still here: keep looking
free_mapped_linear(fp);
}
// still here: didn't find it
return ERROR_CANT_FIND_VMM;
}
int Get_Next_VxD(DWORD vxd, DWORD *pnext)
{
DDB far *pddb;
DWORD next;
int err;
if ((err = Get_VxD(vxd, &pddb)) != 0)
return err;
next = pddb->DDB_Next; // should verify with an Is_VxD() function?
Free_VxD(pddb);
*pnext = next;
return next ? SUCCESS : ERROR_END_OF_CHAIN;
}
int Get_VxD(DWORD vxd, DDB far* *ppddb)
{
if (*ppddb = (DDB far *) map_linear(vxd, sizeof(DDB)))
return SUCCESS;
else
return ERROR_CANT_MAP_LINEAR;
}
int Free_VxD(DDB far *pddb)
{
free_mapped_linear(pddb);
return SUCCESS;
}
<a name="039c_002e"><a name="039c_0010"><a name="039c_0011">
[LISTING 4]: MAP_LIN.C excerpts<a name="039c_0011">
#ifdef DPMI_APP
// DPMISHEL.H includes DPMI equivalents for AllocSelector, etc.
#include "dpmishel.h"
#else
#include "windows.h"
#endif
void far *map_linear(DWORD lin_addr, DWORD num_bytes)
{
WORD sel;
// allocate a data selector similar to our current DS
_asm mov sel, ds
if ((sel = AllocSelector(sel)) == 0) // INT 31h AX=0 CX=1
fail("Cannot allocate a selector!");
// set the base and limit of the new selector
SetSelectorBase(sel, lin_addr); // INT 31h AX=7 BX=sel CX:DX=lin_addr
SetSelectorLimit(sel, num_bytes - 1); // INT 31h AX=8 BX=sel CX:DX=limit
return MK_FP(sel, 0); // turn sel into a far pointer
}
.NET
Undocumented Corner
Andrew presents VXDLIST, a program that walks the Windows Enhanced mode VxD chain and displays the names of all VxDs loaded on the system. VXDLIST will be particularly useful for exploring the upcoming "Chicago" operating environment, which is based on VxDs.Related Reading
More Insights
INFO-LINK
To upload an avatar photo, first complete your Disqus profile. | View the list of supported HTML tags you can use to style comments. | Please read our commenting policy. | |