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.


December 01, 1993
URL:http://www.drdobbs.com/windows/undocumented-corner/184409133

DEC93: UNDOCUMENTED CORNER

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.

Chicago Looks Cool!

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.

A Slimy Hack

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.

Where Now?

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:

vxdlist %1 > nul
if errorlevel 2 goto not_found
if errorlevel 1 goto end
echo VxD %1 is loaded
.
.
.

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.

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]


#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


[LISTING THREE]


#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;
}



[LISTING 4]
: MAP_LIN.C excerpts

#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
}




Copyright © 1993, Dr. Dobb's Journal

Terms of Service | Privacy Statement | Copyright © 2024 UBM Tech, All rights reserved.