Dr. Dobb's is part of the Informa Tech Division of Informa PLC

This site is operated by a business or businesses owned by Informa PLC and all copyright resides with them. Informa PLC's registered office is 5 Howick Place, London SW1P 1WG. Registered in England and Wales. Number 8860726.


Channels ▼
RSS

.NET

Building VxDs in Windows 95


SP 96: Building VxDs in Windows 95

John is the author of Embedded Systems Programming in C and Assembly (Van Nostrand Reinhold, 1994). He can be reached at [email protected].


Writing a virtual device driver (VxD) for Windows 95 should not be a massive task. After all, the operating system is reasonably solid, the device being interfaced is likely to be a debugged product, and Microsoft provides all the necessary tools and documentation in the Windows 95 SDK and device driver development kit (DDK). Neophyte VxD writers, however, often have difficulty navigating the plethora of Microsoft documentation, filtering out unneeded information, and learning the OS-to-VxD and VxD-to-application interface frameworks. To help you through this process, I've built a very simple assembly shell for creating dynamically loadable VxDs.

Because of problems I encountered in the C materials supplied with the MSDN Development Platform Device Driver Kit (October '95 release), I do not recommend using C exclusively for creating VxDs. While the supplied documentation provides a robust discussion of assembly interfaces, it is relatively unsophisticated in its treatment of C. After studying the DDK materials, I found it easier to write a generic VxD shell. The assembly shell, called VANYDEV.VXD, calls C routines to do the real work and uses assembly language to take advantage of the DDK-supplied routines and macros. The working examples provided were written using the MSDN Development Platform SDK, DDK, and the Visual C++ (VC++) integrated development environment (IDE). The assembly shell should flatten the VxD learning curve, and should provide a functioning VxD framework that can be used in most situations. Even if you're a seasoned VxD developer, the information I present here enhances the Microsoft DDK documentation.

Approaching the VxD

Figure 1 presents an overview of the VxD interfaces addressed in this article. When a Win32 application attempts to load a dynamically loadable VxD, control is passed to the Windows 95 virtual machine manager (VMM) to supervise the loading process. The VMM is the heart of Windows 95 and is essentially a preemptive, multitasking operating system; it supervises virtual machines (VM) and VxDs. Under Windows 95, each application executes in its own VM. During the execution life cycle of a dynamically loaded VxD, there are only three pertinent messages initiated from the VMM to the VxD: Sys_Dynamic _Device_Init, Sys_Dynamic_Device_Exit, and W32_DeviceIoControl. By convention, these three messages are fielded from a control dispatch table within the VxD.

The first two messages are used during the VxD load and unload processes. The third is used when device I/O control (DIOC) requests are made to the device.

DIOC requests are typically generated by an application using the Microsoft-provided C function DeviceIoControl. Function arguments include the VxD handle (which is created when the VxD is loaded), and pointers to input and output parameters. Input parameters include a desired service number that is programmer defined. All DIOC information contained in the DDK is clear and concise, and I've provided an example DIOC call in the test program (see Listing Four).

Writing the VxD Shell

Listing One presents VANYDEVD.ASM, the main assembly module for a dynamically loadable VxD for the ANYDEV board. Listing One includes three files from the DDK: VMM.INC, VWIN32.INC, and VPICD.INC. In addition to providing standardized constants, these INCLUDE files contain assembly macros that save the programmer some work. There also are three similarly named header files used by VANYDEVC.C; see Listing Three.

Listing One first calls the Declare_Virtual_Device macro to transparently create a device descriptor block (DDB) for the VxD. The DDB is a data block that serves as the entry point for the VxD. In the call to Declare_Virtual_Device, VANYDEVD is the name of the VxD, 1 and 0 indicate the major and minor revision levels of the VxD, VANYDEVD_Control is the address of the control procedure that fields the three messages from the VMM, Undefined_Device_ID is the ID used for all devices not registered with Microsoft, and Undefined_Init_Order specifies the order in which devices are initialized (in this case, the order is not important).

Working in conjunction with this macro are the VANYDEVD_Control procedure and the Control_Dispatch macro. VANYDEVD_Control will generate a control dispatch table. Within the control procedure, the Control_Dispatch macro precedes a message number and the address of a routine to process that message. The macro ensures proper building of the dispatch table. Any message sent to the VxD other than one of the three defined in the control procedure will be ignored, and a "good" return code will be returned to the caller.

The three message-processing assembly routines specified in the control procedure should be defined in the same module as the dispatch table. In addition, their code segments must be of the VxD_LOCKED_CODE_SEG type. As written in Listing One, these routines call C functions for actual processing. Primitive C functions can be put into a separate module using VC++, and they can be compiled using default IDE settings. Typical C routines are contained in VANYDEVC (see Listings Two and Three).

Expanding the VxD Shell

As an example, assume that an input board exists that can be plugged into a PC's ISA bus. External to the PC, the board interfaces with a wheelbarrow that is filled with bits. The board's purpose is to sort the bits into a readable format and provide 4 KB (one page) of readable data at the adapter board's base address (under 1 MB). Each time the wheelbarrow is filled with bits, an interrupt occurs that signals the presence of readable data at the board's base address. To expand the VxD shell into a driver for this board, an interrupt-service routine (ISR) must be written to handle the board's interrupts. Also, a shared memory area would be desirable to speed communication between the VxD and the application.

Unlike earlier versions of Windows, Windows 95 splits the 4 GB of linear address space into four distinct areas. Two areas under the 2-GB boundary are reserved for DOS and for applications. Above the 2-GB boundary, there are two 1-GB areas reserved for global (system-wide) and application sharing, respectively. System DLLs and memory-mapped files are mapped to the application-shared area. The global area is used for system page tables, VxDs, and globally mapped addresses.

Since VANYDEVD's data area is locked and globally mapped, addresses of data structures defined within the VxD can be passed to an application via a DIOC request. Using these globally mapped addresses, the application can directly access data structures contained in the VxD. Access to the board's base address (under 1 MB) also is required. Using the Microsoft-supplied MapPhysToLinear function from within the VxD provides a global address usable by both the application and the VxD. Again, the application can obtain the requisite global address from the VxD by issuing a DIOC request using the DeviceIoControl function. To facilitate all of the aforementioned operations, the assembly module must contain the MapPhysToLinear interface as shown in Listing One; and the C module must be expanded to handle the device-specific requests, define the shared area, and store the global addresses needed by the application; see Listing Three.

In adapting the VxD shell to drive the wheelbarrow board, the IRQ known to the board is virtualized using the services of the Windows 95 virtual device driver for the PC's programmable interrupt controller (VPICD). To converse with VPICD, more routines are added to the assembly module. These routines constitute a shell interface to VPICD and it is from this shell that VxD calls are issued to VPICD. The assembly module also includes an IRQ descriptor block (IDB), a necessary part of the VPICD service protocol. Both the IRQ number and ISR address are specified within the IDB. In addition, when using a virtualized IRQ, the ISR must use a specific exit protocol. The reason is that the VMM actually fields interrupt requests and calls the ISR specified in the IDB. The ISR must issue the physical end of interrupt (EOI) to VPICD and then return (not iret) to VMM. The routine required for exiting the ISR is included in Listing One.

Finally, the ISR might do no more than set flags and status indicators in the shared data structure. The flags could indicate to the application that a page of data is available for processing at the board's reserved area below 1 MB. The application might poll the flags until data is available and then display it, store it, or whatever. The C module's initialization and uninitialization routines could be fleshed out to initialize the board and preclude more than one successful loading of the VxD. These areas are commented accordingly in the example source code. Listing Three shows a testable C module and indicates how the C routines might be used to drive the wheelbarrow board.

Conclusion

TEXT1.CPP (Listing Four) is a test program which can be stepped through using the debugger in VC++. The complete source code and project files are available electronically; see "Availability" on page 3. Listing Four loads the VxD and provides viewable data via the shared VxD-resident structure. Results of various operations can be determined from the supplied data. Of course, IRQ selection is environment-specific. Although there is no wheelbarrow board available, an unused IRQ should be specified in VANYDEV's IDB in order to avoid conflicts returned by the DeviceInit routine (which calls CVirt_IRQ). As supplied, the test program looks for the VxD in the C:\xyz directory. The link batch file that I've supplied (see "Availability") will copy the VxD to C:\xyz\ after successfully linking the objects.

To build the test program, first ensure that drive C: contains a directory called "c:\xyz." Next, use the VANYDEVD.BAT file to assemble VANYDEVD.ASM. You can then compile VANYDEVC.C using VC++. The LVANYDEV.BAT file can be used to link the two modules (VANYDEVD.DEF must exist). Note that LVANYDEV will copy the VANYDEVD.VXD to c:\xyz\VANYDEVD.VXD. Finally, use VC++ to compile and link the test program in Listing Four. You should then be able to step through the TEXT1 program using the integrated debugger, and check values inserted into the global structure after the VxD is loaded.

Figure 1: Essential ANYDEV system software-construction overview.

Listing One

;;VANYDEVD.asm
name VANYDEVD
.386p
;; ------------------------------------------------
;; VANYDEVD.asm -- Assembly module, MAIN module for
;; Dynamically loadable VxD for ANYDEV board
;; ------------------------------------------------
;; -----------------------------------
;; INCLUDE files needed by this module
;; -----------------------------------
include <\ddk\inc32\vmm.inc>
include <\ddk\inc32\vwin32.inc>
include <\ddk\inc32\vpicd.inc>
;; ------------------------------------
;; C routines/data used by this module
;; ------------------------------------
extrn _CVANYDEVD_Device_Init:near
extrn _CVANYDEVD_Device_UNInit:near
extrn _CVANYDEVD_Device_IOctrl:near
extrn _ISR_ANYDEV:near
;; -------------------------------------
;; Routines/data called from this module
;; -------------------------------------
public _GET_BD_MEM
public _Virt_IRQ
public _End_ISR
public _Physically_Mask_IRQ
public _Physically_UNMask_IRQ
public _UNVirt_IRQ
public _Get_IRQ_Status
; ===========================================================================
;; Misc VANYDEVD-specific Equates -- NOTE: This is where you change
;; the REV level or put any MS-assigned DEVICE ID (if DEVICE registered with 
;; MS). Also change here the address & length of the below 1mb area used by 
;; ANYDEV board and the IRQ used.
;; ---------------------------------------------------------------------------
VANYDEVD_MajoREV equ 1       ;ANYDEV's MAJOR revision level
VANYDEVD_MinoREV equ 0       ;decimal number of revision
VANYDEVD_DeviceID equ Undefined_Device_ID ;no need for device number assignment
VANYDEVD_IRQ_Used equ 10         ;IRQ used by ANYDEV board
VANYDEVD_1mb_adr  equ 0c0000h ;Below 1mb address used by ANYDEV board   
VANYDEVD_1mb_len  equ 01000h  ;Length of below 1mb area used by ANYDEV board
; ============================================================================
;; ---------------------------------------------
;; Virtual Device Declaration (Required). (Declares this code as virtual 
;; device driver). Also creates the Device Data Block
;; ---------------------------------------------
 
Declare_Virtual_Device VANYDEVD,VANYDEVD_MajoREV,VANYDEVD_MinoREV,
                        VANYDEVD_Control,VANYDEVD_DeviceID,Undefined_Init_Order
VxD_LOCKED_CODE_SEG
;; --------------------------------------------
;; Control Dispatch Table & Proc (Required)
;; Used to dispatch supported messages sent by
;; VMM -- clears carry for unsupported mssgs.
;; --------------------------------------------
;; Only 3 VMM messages are recognized and processed
;; by this routine -- all DIOC interface messages
;; translate to W32_DeviceIoControl mssgs from the VMM.
;; "Control_Dispatch" precedes MSSG NUMBER, PROCEDURE
BeginProc VANYDEVD_Control
    Control_Dispatch Sys_Dynamic_Device_Exit, VANYDEVD_Device_UNInit
    Control_Dispatch Sys_Dynamic_Device_Init, VANYDEVD_Device_Init
    Control_Dispatch W32_DeviceIoControl,     VANYDEVD_Device_IOctrl
    xor eax,eax  ;;return 0 (required in some instances)
    clc          ;;clear carry flg for GOOD indicator   
    ret
EndProc VANYDEVD_Control
;; -------------------------------------------------------------
;; NOTE: "BeginProc & EndProc" are needed in conjunction with
;; the above dispatch table -- below routines facilitate C fcns
;; -------------------------------------------------------------
;; =======================================================================
;; Routines below are VXD interface (load, unload, process) ROUTINES
;; =======================================================================
;; --------------------------------------------
;; Routine to jump to C routine for processing
;; SYS_DYNAMIC_DEVICE_INIT message
;; --------------------------------------------
BeginProc VANYDEVD_Device_Init
    call _CVANYDEVD_Device_Init
    ret
EndProc VANYDEVD_Device_Init
;; --------------------------------------------
;; Routine to jump to C routine for processing
;; SYS_DYNAMIC_DEVICE_EXIT message
;; --------------------------------------------
BeginProc VANYDEVD_Device_UNInit
        call _CVANYDEVD_Device_UNInit
        ret
EndProc VANYDEVD_Device_UNInit
;; --------------------------------------------
;; Routine to jump to C routine for processing
;; W32_DEVICEIOCONTROL messages -- These are
;; VxD requests from the application.
;; At entry, esi points to the DIOC interface
;; structure passed by the application
;; --------------------------------------------
BeginProc VANYDEVD_Device_IOctrl
        push esi
        call _CVANYDEVD_Device_IOctrl
        pop esi
        ret
EndProc VANYDEVD_Device_IOctrl
;; ======================================================
;; Routines below are miscellaneous assembly interfaces
;; ======================================================
;; -------------------
;; GET MEM BELOW 1MB
;; -------------------
BeginProc _GET_BD_MEM
         VMMcall _MapPhysToLinear <VANYDEVD_1mb_adr,VANYDEVD_1mb_len,0>
         ret
EndProc _GET_BD_MEM
;; -------------------
;; Handle EOI for ISR
;; -------------------
BeginProc _End_ISR
         VxDcall VPICD_Phys_EOI
         ret
EndProc _End_ISR
;; =======================================================================
;; Routines below are IRQ specific - for virtualization/unvirtualization
;; =======================================================================
;; -------------------
;; Virtualize IRQ
;; -------------------
BeginProc _Virt_IRQ
         push edi
         mov edi, OFFSET32 _VIRQdat
         VxDcall VPICD_Virtualize_IRQ
         jnc VIRQEXIT
         mov eax, 0ffffffffh  ;set ERR if appro
VIRQEXIT:
         pop edi
         ret
EndProc _Virt_IRQ
;; -------------------
;; UN Virtualize IRQ
;; -------------------
BeginProc _UNVirt_IRQ
         VxDcall VPICD_Force_Default_Behavior
         ret
EndProc _UNVirt_IRQ
;; ---------------
;; Get IRQ Status
;; ---------------
BeginProc _Get_IRQ_Status
         push ecx
         xor eax,eax
         mov ax,[_VIRQdat]
         VxDcall VPICD_Get_IRQ_Complete_Status
         jc statusbad
         xor eax,eax
         jmp statusex
statusbad:
         push ecx
         pop eax
statusex:
        pop ecx
         ret
EndProc _Get_IRQ_Status
;; -------------------
;; Physically Mask IRQ
;; -------------------
BeginProc _Physically_Mask_IRQ
         VxDcall VPICD_Physically_Mask
         ret
EndProc _Physically_Mask_IRQ
;; ----------------------
;; Physically UN-Mask IRQ
;; ----------------------
BeginProc _Physically_UNMask_IRQ
         VxDcall VPICD_Physically_Unmask
         ret
EndProc _Physically_UNMask_IRQ
VxD_LOCKED_CODE_ENDS
;; --------------------------------
;; VPICD IRQ Descriptor Block
;; --------------------------------
VxD_LOCKED_DATA_SEG
_VIRQdat dw VANYDEVD_IRQ_Used       ;; IRQ# 
         dw 0
         dd OFFSET32 _ISR_ANYDEV    ;; ISR ADDRESS
         dd 0
         dd 0
         dd 0
         dd 0
         dd 500
         dd 0
         dd 0
         dd 0
VxD_LOCKED_DATA_ENDS
        END

Listing Two

//VANYDEVD.H
#define BOGUSADDRESS 0xffffffff //Returned by MS routines
// These constants define the services of the VXD required by the application
// They are named from the perspective of the application (originator)
// NOTE: Any code numbers may be used except -1 & 0
#define VANYDEVD_GRAB_ADDRESSES      10 
                            //Obtains an IN CONTEXT address of the global areas
#define VANYDEVD_INIT_ADEV_HARDWARE  11 
        //Requests the VXD to INIT the ANYDEV hardware using current parameters
#define VANYDEVD_ENABLE_ANYDEV_HDWR  14 //Requests the VXD to ENABLE ANYDEV 
#define VANYDEVD_DISABLE_ANYDEV_HWR  15 //Requests the VXD to DISABLE ANYDEV 
// SHARED by the application and the ANYDEV virtual device driver. It contains 
// board configuration information, board status, and flags used by
// both software ends, This structure is built by the VXD and resides at ring-0
typedef struct ANYDEVprimary
    {
    DWORD flags;                //Status Flags, etc
    DWORD Global_addr_1MB;      //Address of board's memory below 1mb
    DWORD IRQhandle;            //IRQ handle
    DWORD IRQflags;             //User defined
    DWORD IRQcount;             //User defined
    DWORD IRQstatus;            //Set by Get IRQ Status routine
                                //What ever the user desires goes here
    } ADEV, *ADEVPTR;
#define IRQ_VIRT_OK 1   

Listing Three

//// VANYDEVDc.c
// --------------------------------------------
// Dynamically loadable VxD for ANYDEV board
// --------------------------------------------
#define WIN32_LEAN_AND_MEAN // Excludes un-needed parts of windows.h
#include "windows.h"
#include <\ddk\inc32\vmm.h>
#include <\ddk\inc32\vwin32.h>
#include <\ddk\inc32\debug.h>
#include "VANYDEVD.H"
// -------------------------------------
// Externs defined in assembly module 
// -------------------------------------
// These defined in assembly for the VMM.INC or VPICD.INC file inclusion
extern DWORD GET_BD_MEM(void);
extern DWORD Virt_IRQ(void);
extern DWORD Get_IRQ_Status(void);
extern void UNVirt_IRQ(DWORD IRQhandle);
extern void End_ISR(DWORD IRQhandle);
extern void Physically_Mask_IRQ(DWORD IRQhandle);
extern void Physically_UNMask_IRQ(DWORD IRQhandle);
// ------------------------
// PRAGMA for this DATA 
// ------------------------
// Establish segment
#pragma data_seg ( "_LDATA","_LCODE")
// ------------------------------------
// Data structures MUST be INITIALIZED
// ------------------------------------
ADEV ANYDEVX = {0}; // Main structure for ANYDEV -- shared by app 
// ------------------------
// PRAGMAS for this CODE 
// ------------------------
// Establish segment
#pragma code_seg ( "_LTEXT", "_LCODE" )
//No stack checking for routines in this module
#pragma check_stack(off)
// ----------------
// Disable hardware
// ----------------
void Disable_AnyDev(void)
{
    //This would likely be a port WRITE to DISABLE the board's interrupt            
}
// ----------------
// Enable hardware
// ----------------
void Enable_AnyDev(void)
{
    //This would likely be a port WRITE to ENABLE the board's interrupt         
}
// --------------------------
// ISR Processing for ANYDEV
// --------------------------
void Process_ISR(void)
{
    //Where the user might SET FLAGS and indicators in the ANYDEVX structure
    //in order to notify the application that data is available below 1MB
}
// -----------------
// ISR for ANYDEV
// -----------------
void _declspec(naked)ISR_ANYDEV(void)
{
    // Save registers
    _asm sti
    _asm pushad
    _asm pushfd
    //Process the ISR 
    Process_ISR();
    //End ISR
    _asm clc
    End_ISR(ANYDEVX.IRQhandle);
    //Set GOOD return code
    _asm clc
    //Restore saved registers
    _asm popfd
    _asm popad
    _asm ret;
}
// -------------------
// Virtualize the IRQ
// -------------------
DWORD CVirt_IRQ(void)
{
    // If in use by an instance of this program, RETURN with BAD code
    if (ANYDEVX.IRQcount)
        return (BOGUSADDRESS);
    // If in use by another program, RETURN with BAD code
    ANYDEVX.IRQstatus = Get_IRQ_Status();
    if (ANYDEVX.IRQstatus)
        return (BOGUSADDRESS);
    // If IRQ NOT in use this point is reached
    // Set BAD return code
    ANYDEVX.IRQhandle = BOGUSADDRESS;
    // Disable ANYDEV hardware  
    Disable_AnyDev();
    // Get global memory address below 1mb
    ANYDEVX.Global_addr_1MB = GET_BD_MEM();
    if (ANYDEVX.Global_addr_1MB != BOGUSADDRESS)
        {
        // Virtualize the IRQ
        ANYDEVX.IRQhandle = Virt_IRQ(); 
        if (ANYDEVX.IRQhandle != BOGUSADDRESS)
            {
            // unmask the IRQ, set OK flag & increment IRQ count
            Physically_UNMask_IRQ(ANYDEVX.IRQhandle);
            ANYDEVX.IRQflags |= IRQ_VIRT_OK;
            ++(ANYDEVX.IRQcount);
            }
        }
    return(ANYDEVX.IRQhandle);
}
// ----------------------
// UN Virtualize the IRQ
// ----------------------
void CUNVirt_IRQ(DWORD IRQhandle)
{
 // if IRQ has been successfully virtualized
 if ((ANYDEVX.IRQhandle != 0)
 &&  (ANYDEVX.IRQhandle != BOGUSADDRESS))
    {
    // Physically mask the IRQ and UN virtualized it
    Physically_Mask_IRQ(ANYDEVX.IRQhandle);
    UNVirt_IRQ(ANYDEVX.IRQhandle);
    }
// Set UNvirtualized flags and indicators
ANYDEVX.IRQhandle = BOGUSADDRESS;
ANYDEVX.IRQflags &= ~IRQ_VIRT_OK;
    return;
}
// ---------------------------------------
// Set Good Return code for DIOC requests
// ---------------------------------------
void _declspec(naked)GoodReturnDIOC(void)
{
    // Clear eax and carry flag for GOOD return
    _asm xor eax,eax
    _asm clc
    _asm ret;
}
// ---------------------------------------
// Set Bad Return code for DIOC requests
// ---------------------------------------
void _declspec(naked) BadReturnDIOC(void)
{
    // NOTE: 50 is a FCN NOT SUPPORTED code -- ok to use
    // SET carry flag for BAD return
    _asm mov eax,50
    _asm stc
    _asm ret;
}
// ------------------------------
// Routine for ANYDEV Device UNINIT  
// ------------------------------
void CVANYDEVD_Device_UNInit()
{
    // Disable ANYDEV, Unvirtualize IRQ, set GOOD return code
    Disable_AnyDev();           
    CUNVirt_IRQ(ANYDEVX.IRQhandle); 
    GoodReturnDIOC();           
    return;
}
// ------------------------------
// Routine for ANYDEV Device INIT  
// ------------------------------
void CVANYDEVD_Device_Init()
{
DWORD retcode;
    // Try to virtualize the IRQ
    retcode = CVirt_IRQ();
    // Set GOOD or BAD return code based on success
    if (retcode == BOGUSADDRESS)
        BadReturnDIOC();
    else
        GoodReturnDIOC();
    return;
}
// --------------------------------
// Routine for ANYDEV Device IO ctrl  
// --------------------------------
void CVANYDEVD_Device_IOctrl(PDIOCPARAMETERS ptr)
{
DWORD *obuf1;
    // Field the DEV IO requests from VMM
    switch(ptr->dwIoControlCode)
        {
        case(VANYDEVD_INIT_ADEV_HARDWARE):
            ANYDEVX.flags = 0;
            //User likely to require other initialization here
        break;
        case(VANYDEVD_GRAB_ADDRESSES):
            // Point to Output buffer
            obuf1 = (DWORD *) ptr->lpvOutBuffer;
            // Return GLOBAL 1MB addr, addr of data structure
            // and return indicators of IRQ virtuaization request
            *obuf1 = ANYDEVX.Global_addr_1MB;
            *(obuf1+1) = (DWORD) &ANYDEVX;
            *(obuf1+2) = (DWORD) ANYDEVX.IRQhandle;
            *(obuf1+3) = (DWORD) ANYDEVX.IRQflags;
            //User might want to return other/different values here
        break;
        case(VANYDEVD_ENABLE_ANYDEV_HDWR):
            //Call routine to enable interrupt
            Enable_AnyDev();
        break;
        case(VANYDEVD_DISABLE_ANYDEV_HWR):
            //Call routine to disable interrupt
            Disable_AnyDev();
        break;
        //The below DIOC_GETVERSION is a part of the dynamic load protocol
        //It MUST return a GOOD code (all codes here use GoodReturnDIOC()
        case(DIOC_GETVERSION):
        case(DIOC_CLOSEHANDLE):
        default:
        break;
        }
    GoodReturnDIOC();
    return;
}

Listing Four

// Text1.cpp -- Test routine for VANYDEVD.VXD interface
// A way to test dynamic load of VXD and VERIFY global addresses and data
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <\ddk\inc32\vmm.h>
#include <\ddk\inc32\vwin32.h>
#include <stdio.h>
#include <string.h>
#include <malloc.h>
#include <\msvc20\bin\VANYDEVD\VANYDEVD.H>
//NOTE: "\\\\.\\" indicates to CreateFile that VXD is being loaded dynamically
char VxDpathName[] = "\\\\.\\c:\\xyz\\vanydevd.vxd";
//Init values show something other than 0 during debug sessions
DWORD vxd_outbuff[0x1024]={1,2,3,4,5,6,7,0,0,};
//Main interface test routine for VANYDEVD.VXD
int main(int argc, char *arg[])
{
HANDLE vxd_Handle;
int errx;
DWORD DIOC_count;
DWORD *AddrBelow1MB;
DWORD  DataToReview[10];
ADEVPTR SharedMemPtr;
    errx = 0;
    DIOC_count = 0;
    //Load the VXD dynamically
    vxd_Handle=CreateFile(&VxDpathName[0],0,0,NULL,0,
                                             FILE_FLAG_DELETE_ON_CLOSE,NULL);
    if (vxd_Handle == INVALID_HANDLE_VALUE)
            errx=GetLastError();
     else
            {
            //If VxD LOAD was SUCCESSFUL, GRAB global Addresses and IRQ Status
            DeviceIoControl(vxd_Handle,VANYDEVD_GRAB_ADDRESSES,
                    NULL,0,&vxd_outbuff[0],(DWORD) sizeof(vxd_outbuff),
                                                             &DIOC_count,NULL);
            AddrBelow1MB = (DWORD *) vxd_outbuff[0];
            //Walk through here with debugger to verify addresses and data
            if (((DWORD) AddrBelow1MB) != BOGUSADDRESS)
                {
                DataToReview[0] = *AddrBelow1MB;
                SharedMemPtr = (ADEVPTR) vxd_outbuff[1];
                DataToReview[1] = SharedMemPtr->IRQcount;
                DataToReview[2] = SharedMemPtr->IRQhandle;
                DataToReview[3] = SharedMemPtr->IRQstatus;
                }
            else
                printf("\nBOGUS address received for 1MB area");
            }
    printf("\nERR %d \n",errx);
    if (vxd_Handle != INVALID_HANDLE_VALUE)
            CloseHandle(vxd_Handle);
    return(errx);
}


Related Reading


More Insights






Currently we allow the following HTML tags in comments:

Single tags

These tags can be used alone and don't need an ending tag.

<br> Defines a single line break

<hr> Defines a horizontal line

Matching tags

These require an ending tag - e.g. <i>italic text</i>

<a> Defines an anchor

<b> Defines bold text

<big> Defines big text

<blockquote> Defines a long quotation

<caption> Defines a table caption

<cite> Defines a citation

<code> Defines computer code text

<em> Defines emphasized text

<fieldset> Defines a border around elements in a form

<h1> This is heading 1

<h2> This is heading 2

<h3> This is heading 3

<h4> This is heading 4

<h5> This is heading 5

<h6> This is heading 6

<i> Defines italic text

<p> Defines a paragraph

<pre> Defines preformatted text

<q> Defines a short quotation

<samp> Defines sample computer code text

<small> Defines small text

<span> Defines a section in a document

<s> Defines strikethrough text

<strike> Defines strikethrough text

<strong> Defines strong text

<sub> Defines subscripted text

<sup> Defines superscripted text

<u> Defines underlined text

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task. However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

 
Disqus Tips 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.