Porting VxDs from Windows 3.1 to Windows 95

Don't throw away your Windows 3.1 VxDs just because Windows 95 is available. Don presents a VxD that works with both Windows 3.1 and Windows 95, then describes the different techniques for calling its services.


November 01, 1995
URL:http://www.drdobbs.com/windows/porting-vxds-from-windows-31-to-windows/184409664

NOV95: Porting VxDs from Windows 3.1 to Windows 95

Don is president of Nexus Technologies, a company specializing in SCSI networking software for Windows. He can be reached at [email protected].


Do you have a 16-bit Windows 3.1 application that communicates with a VxD of your own design? It would certainly be nice if you could use the same VxD and port the application to Windows 95 by simply recompiling it with a 32-bit compiler. Unfortunately, there's a little more to it. The methods for obtaining the entry point to the VxD and for calling its services have changed, requiring modifications to both the calling application and the VxD itself.

In this article, I'll present a VxD that works with both Windows 3.1 and Windows 95 applications and describe the different techniques for calling its services. I'll also describe an alternate approach to one of the most common VxD programming problems: posting a message to an application. The application and driver presented here support multiple cards, shared-memory allocation, and interrupt processing.

Take a look at PORTAGE, a simple, 16-bit Windows 3.1 application that drives the VxD and get acquainted with the various project files and directories; see Table 1. (The complete PORTAGEsystem, including executables, source, and the files in Table 1, is available electronically; see "Availability," page 3.) All these projects work well in the Visual C++ environment, even though they use external makefiles. Using an external makefile makes it easier to understand the build process and the tools being used, but it also makes it more difficult to manage large projects. If you're adapting this work to a project with many files, I recommend letting Visual C++ create and maintain the build environment for you.

The Test Harness

PORTAGE.EXE is a simple Windows application, modeled after the GENERIC sample in the Windows 3.1 Programmer's Guide. It contains a standard window procedure that processes messages and calls other procedures in response to user input, and it illustrates the concepts required for servicing multiple hardware devices with a single VxD. To service multiple devices, instance data--data that is unique to each instance of the hardware--must be maintained down in the VxD. Here, that data is allocated by the calling application (or DLL) and then passed down to the VxD during a registration process. This makes it possible to share data between an application or DLL running in Ring 3 and a VxD running in Ring 0 so that multiple instances of the application may be executed. Each time a new instance is run, the application allocates a data structure that holds all the data of interest to both the VxD and the application. When an instance is shut down, the VxD is notified and the memory is freed.

The user interface for PORTAGE.EXE is easily extensible. There are two pull-down menus: "VxD" and "Simulate." The VxD menu lets the developer call various routines in the VxD; the Simulate menu artificially generates conditions for testing purposes, without actually calling into the VxD. For example, the VxD described in this article (PORTAGE.386) provides an API routine that posts a message. Selecting "Post" from the VxD menu calls the associated API routine in the VxD. But selecting Post from the Simulate menu causes the application itself to call PostMessage() without invoking the VxD services. When using a debugger, this allows you to isolate a problem to a particular VxD or application-level routine.

The VxD provides the application with an API that consists of three routines: one to register the instance of the application with the VxD, another to post a message, and a third to shut down the instance. The application-level interface to the VxD routines consists of three functions in Listing One. The first function, portage_vxd_register(), is called when the application processes the WM_CREATE message. This function first allocates the memory to hold the shared data structure. This is accomplished via a call to GlobalAlloc(). This seemingly innocuous bit of programming is actually somewhat tricky due to undocumented and/or misunderstood behavior of Windows 3.1 memory-management functions. For example, the call to GlobalAlloc() uses the GPTR attribute, which is the same as using both the GMEM_FIXED and the GMEM_ZEROINIT attributes. You might, therefore, presume that the memory is fixed in place, but it is fixed only if the call is made from a DLL and not from an application. GlobalLock() does not lock the memory in place either--it only translates a memory handle into a pointer. The memory is "locked" only in the sense that its <selector:offset> value does not change. Only a call to GlobalFix() will keep the memory from moving around. In a DLL, this call would be redundant when used with the GMEM_FIXED attribute.

But why should we even worry about whether or not the memory is fixed? Since the memory is being shared with a VxD, the VxD will map the Ring 3 address, which is of the <selector:offset> form, into a Ring 0 address, which is linear. If Windows moves the data by changing the selector's linear base address, the VxD will no longer point to the same data as the application, resulting in major problems. And that will definitely happen if the data is not fixed in place. Furthermore, if the data will be touched at interrupt time, it must be page-locked. In our case, there is no interrupt processing down in the VxD, but there likely would be if the VxD dealt with actual hardware devices--which is why it is shown here.

The VxD entry point is obtained through a call to INT 2Fh with AX=1684h. That call provides the entry point to the VxD's Protected Mode API, which the application then uses to call into the VxD and register this instance of the application. The address of the shared-data structure is passed in as shown, and the VxD returns a status code indicating success or failure. If successful, the VxD will have written an index value, or instance-data handle, into the shared-data structure. This instance-data handle is used in subsequent calls to the VxD so that it can locate the correct instance data, and it is not to be confused with the application-instance handle passed into WinMain() when an application is started.

The portage_vxd_post() function instructs the VxD to post a message. Only the function number and the instance-data handle must be sent to the VxD. The application registers all other parameters for the call to PostMessage() in the shared data structure in response to the Post selection in the VxD menu.

Finally, portage_vxd_shutdown() notifies the VxD that a particular instance is being closed, or shut down. This routine is called in response to the WM_DESTROY message and must also provide the VxD with only the function number and instance-data handle. The VxD is told that the memory for this instance is going away shortly, so after the call into the VxD, the application frees the memory.

A Schizoid Driver

The source code for the VxD is contained in the file PORTAGE.ASM. This VxD contains a Protected Mode (PM) API and a Virtual-86 Mode (V86) API, although the V86 API only provides the GetVersion routine. Listing Two lists the three PM API routines described earlier.

The first VxD routine invoked by the application is PORTAGE_PM_Register(). The VxD maintains an array containing all the instance-data pointers for all active application instances. It searches this array for the first entry equal to zero. The resulting value is the instance-data handle, which is then returned to the calling application. If no open slots are available, a status value indicating failure is returned.

After finding an open slot, the PM address of the instance data is converted to a linear address with the VMM service Map_Flat. The instance-data handle is then written into the shared data structure, and the linear address is stored in the table. The VxD can access members of the shared data structure simply by placing the structure's linear address in EDI and using an offset. The offsets are defined in an assembly-language data structure in the include file PORTAGE.INC. It is extremely important to keep the assembly-language structure definition in sync with the C-language structure definition. If a structure member is added or removed from one but not the other, the communication mechanism breaks down. When two different code sets attempt to access members of the shared data structure using different offsets, results are unpredictable. If you have many shared structures, or structures changed by many different people, you should use a tool that addresses this problem, like H2INC.

When an application closes, it relinquishes its instance-data handle by calling PORTAGE_PM_Shutdown(). This routine simply places a zero in the array of instance-data pointers maintained by the VxD, making that slot available for a subsequent instance of the application.

Typically, VxDs interface with some hardware device, detecting certain conditions or transferring data. Events of interest to the application layer are usually communicated via a Windows message sent to a window registered with the VxD. One common technique is to register the address of a DLL that calls PostMessage(), but this requires that the DLL routine be in a fixed code segment. What if your design doesn't include a DLL? Windows does not allow you to place a routine in a fixed code segment in your application or statically linked library, so why not eliminate the "helper" function altogether and make the call to PostMessage() directly from the VxD? The code excerpt in Listing Three illustrates this technique.

The code in PORTAGE_Schedule_System_VM() shows how to set up a callback to another routine that performs a nested execution block for the purpose of calling Ring 3 code. One neat trick is to pass the instance-data pointer--initially in EDI--to the callback routine as reference data stored in EDX. The callback occurs when the system is running in the context of the system VM and the other specified conditions are satisfied. This routine, PORTAGE_Schedule_System_VM(), may safely be called as-is from within an interrupt service routine (ISR) executing in Ring 0.

The callback routine, PORTAGE_Do_Ring3_Callback(), sets up and executes a nested execution block. But first it retrieves the reference data passed in EDX and stuffs it into EDI. Now this routine has access to the shared data structure. The application includes the address of PostMessage() in the shared data structure, along with other information. If this VxD were an interface to real hardware, some of this data (such as the particular Windows message or other parameters) could be determined on the fly down in the VxD in response to various hardware conditions.

The Harness Revisited

We'll now port the test application by building it with 32-bit, Visual C++ 2.x rather than 16-bit, Visual C++ 1.5. The problems of recompiling your application in the 32-bit world are usually fairly minor. The electronic listings give details on setting compiler and linker command-line switches. Be aware that the format for the link command has changed with the new set of tools and that integer size has changed from 16 to 32 bits. This may not be a problem in some places in your code, but if you use an integer in a data structure that communicates with a VxD or some other process running on a hardware device, be very careful. I recommend never using integers in these cases; instead use some data type that precisely specifies the length of the data, such as WORD or DWORD.

Listing Four shows the new method for obtaining the entry point to the VxD (see portage_vxd_register()). Instead of an explicitly called entry point, the application obtains a handle to the VxD that is used in subsequent calls to DeviceIOControl(). With the advent of the Win32 API, you can no longer use inline assembly and INT 2Fh to obtain the entry point of a VxD; only C code will do. Parameters that need to be passed into the VxD are specified as parameters to the DeviceIOControl() routine. Those parameters are then placed in a data structure, a pointer to which is passed into the VxD. The VxD can then access the parameters as offsets from the pointer that is passed in.

The first two parameters to DeviceIOControl() are the device handle and the function number. The documentation states that the rest of the parameters are free-format; that is, you can place anything in those parameters, and they will not be checked. However, this is only partially true. If you pass pointers, you should place them in the positions in the parameter list where casting is done to a pointer type. If not, the call to DeviceIOControl() will fail. In fact, if you have many parameters, you are better off putting them in a structure and simply passing a pointer to that structure, as shown in the SDK. Also, it is impossible to call function zero in your VxD. That function is called during the processing of the CreateFile() routine, but subsequent attempts to call it with DeviceIOControl() will fail. If the call does fail, useful error information can be obtained by calling GetLastError(). The Win32 SDK documents how to interpret that error information.

Completing the Port

To use your VxD in Windows 95, you must use the new VMM include files in the DDK and the new assembler, MASM 6.11. (These files will not assemble using the old assembler, MASM 5.10B.) See the electronic listings for the exact command-line switches.

Calling API routines in a Windows 95 VxD has changed significantly from Windows 3.1. Rather than calling directly into an API routine whose entry point was furnished to an application, a W32_DEVICEIOCONTROL message is now sent to the VxD control procedure when the application calls DeviceIOControl(). The handler for that message, as specified in the control procedure, is the new W32 API handler down in the VxD. When W32_DEVICEIOCONTROL is sent to the VxD, ESI contains a pointer to a data structure, called DIOCParams, that contains all the parameters specified in the call to DeviceIOControl(). This new W32 API handler is very much like the old PM API handler, except that it must first extract the function number from DIOCParams before dispatching control to the desired API routine. It also must return a successful completion when given -1 as the function number; see CloseFile() (which is automatically sent by the system when the application exits).

The code for PORTAGE_W32_Register() is very similar to its PM counterpart; see Listing Five. One major difference is that the address of the shared data structure passed in from the application need not be converted to a linear address. It's already a 32-bit address that can be used by the VxD directly. This address is extracted from the DIOCParams structure and stored for future use, just as before.

Finally, the code for posting a message has changed; see Listing Six. There is now a SHELL service for posting a message, so you needn't explicitly schedule the system VM and perform a nested execution block. The service _SHELL_PostMessage() takes care of those details, providing a much simpler interface for the VxD programmer.

Conclusion

A VxD written for Windows 3.1 can be modified so that it will work with both 16-bit Windows 3.1 applications and 32-bit Windows 95 applications. This VxD also incorporates key concepts necessary for the support of multiple hardware devices.

The porting process isn't so daunting once you realize that much of the current VxD can be reused. There are just some additional messages that the control procedure can respond to; some new services; and some new features, like dynamically loading a VxD.

Table 1: PORTAGE projects and directories.

Project              Description

\portage\w31\app     16-bit      Ring-3 Windows 3.1 application.
\portage\w31\vxd     32-bit      Ring-0 Windows 3.1 VxD.
\portage\w95\app     32-bit      Ring 3 Windows 95 application.
\portage\w95\vxd     32-bit      Ring-0 Windows 95 VxD.

Listing One

/**********************************************************************
 * PORTAGE application API Routines (Win 3.1 version), by Don Matthews.
 **********************************************************************/
HGLOBAL           hMem;       /* global memory handle                 */
SharedDataFormat *SharedData; /* data shared between the app and VxD  */
DWORD             VxD_Entry;  /* VxD entry point                      */
BOOL portage_vxd_register( void )
{
        BOOL Result = FAILURE;
        hMem        = NULL;
        SharedData  = NULL;
        VxD_Entry   = NULL;
        hMem        = GlobalAlloc( GPTR, sizeof(SharedDataFormat) );
        if( !hMem )        return( Result );
        SharedData  = (SharedDataFormat *)GlobalLock( hMem );
        if( !SharedData )  return( Result );
        GlobalFix( hMem );
        GlobalPageLock( hMem );
        _asm
        {   mov  ax, 1684h
            mov  bx, PORTAGE_Device_ID
            int  2Fh
            mov  word ptr [VxD_Entry + 2], es
            mov  word ptr [VxD_Entry + 0], di
        }
        if( VxD_Entry )
        {   _asm
            {   mov  ax, API_PORTAGE_PM_Register
                mov  bx, word ptr [SharedData + 2]
                mov  cx, word ptr [SharedData + 0]
                call dword ptr [VxD_Entry]
                mov  Result, ax
            }
        }
        return( Result );
}
/****************************************************************/
BOOL portage_vxd_post( void )
{
        BOOL Result = FAILURE;
        WORD InstanceIndex;
        if( VxD_Entry )
        {
            InstanceIndex = (WORD)SharedData->InstanceIndex;
            _asm
            {   mov  ax, API_PORTAGE_PM_Post
                mov  bx, word ptr [InstanceIndex]
                call dword ptr [VxD_Entry]
                mov  Result, ax
            }
        }
        return( Result );
}
/****************************************************************/
BOOL portage_vxd_shutdown( void )
{
        BOOL Result = FAILURE;
        WORD InstanceIndex;
        if( VxD_Entry &&
            (SharedData->InstanceIndex != PORTAGE_Max_Instances) )
        {
            InstanceIndex = (WORD)SharedData->InstanceIndex;
            _asm
            {   mov  ax, API_PORTAGE_PM_Shutdown
                mov  bx, word ptr [InstanceIndex]
                call dword ptr [VxD_Entry]
                mov  Result, ax
            }
        }
        if( hMem )
        {   GlobalPageUnlock( hMem );
            GlobalUnfix( hMem );
            GlobalUnlock( hMem );
            GlobalFree( hMem );      
        }
        return( Result );
}
/****************************************************************/

Listing Two

;-------------------------------------------------------------------
;  PORTAGE VxD API Routines (Windows 3.1 version), by Don Matthews
;-------------------------------------------------------------------
BeginProc PORTAGE_PM_Register
        Trace_Out "PORTAGE_PM_Register"
        ;
        ; Determine the next available instance index.
        mov       eax, 0
RegisterLoop:
        mov       edi, PORTAGE_Instance_Table[ eax * 4 ]
        or        edi, edi
        jz        short RegisterContinue
        inc       eax
        cmp       eax, PORTAGE_Max_Instances
        jne       short RegisterLoop
RegisterContinue:
        ; Convert the SharedData pointer to a linear address.
        push      eax
        mov       ah, Client_BX
        mov       al, Client_CX
        VMMcall   Map_Flat
        mov       edi, eax
        pop       eax
        cmp       edi, -1
        je        short RegisterError1
        Trace_Out "Map_Flat - SUCCESS!"
        ;        
        ; Save the instance index in the SharedData structure.
        mov       dword ptr [edi.InstanceIndex], eax
        cmp       eax, PORTAGE_Max_Instances
        je        short RegisterError2
        Trace_Out "Found Instance Index #EAX"
        ;
        ; Save the SharedData pointer.
        mov       PORTAGE_Instance_Table[ eax * 4 ], edi
RegisterExit:
        jmp       PORTAGE_PM_API_Success
RegisterError1:
        Trace_Out "Map_Flat - FAILURE!"
        jmp       PORTAGE_PM_API_Failure
RegisterError2:
        Trace_Out "ERROR - No available instance index!"
        jmp       PORTAGE_PM_API_Failure
EndProc PORTAGE_PM_Register
;----------------------------------------------------------
BeginProc PORTAGE_PM_Post
        Trace_Out "PORTAGE_PM_Post"
        ;
        ; Load EDI with a pointer to the SharedData structure.
        ; Subsequent accesses to the members of the structure will
        ; be done using offsets into the structure from EDI, where
        ; EDI is an offset from DS.
        movzx     eax, [ebp.Client_BX]
        mov       edi, PORTAGE_Instance_Table[ eax * 4 ]
        call      PORTAGE_Schedule_System_VM
        jmp       PORTAGE_PM_API_Success
EndProc PORTAGE_PM_Post
;----------------------------------------------------------
BeginProc PORTAGE_PM_Shutdown
        Trace_Out "PORTAGE_PM_Shutdown"
        ;
        ; Load EDI with a pointer to the SharedData structure. Subsequent
        ; accesses to the members of the structure will be done using offsets 
        ; into the structure from EDI, where EDI is an offset from DS.
        movzx     eax, [ebp.Client_BX]
        mov       edi, PORTAGE_Instance_Table[ eax * 4 ]
        ; Zero out the instance data pointer
        mov       edi, 0
        mov       PORTAGE_Instance_Table[ eax * 4 ], edi
        jmp       PORTAGE_PM_API_Success
EndProc PORTAGE_PM_Shutdown

Listing Three

;-------------------------------------------------------------------
; Calling PostMessage() from a VxD (Win 3.1 version), by Don Matthews
;-------------------------------------------------------------------
BeginProc PORTAGE_Schedule_System_VM
        mov       eax, Cur_Run_VM_Boost
        VMMcall   Get_Sys_VM_Handle
        mov       ecx, PEF_Wait_For_STI or PEF_Wait_Not_Crit
        mov       edx, edi
        mov       esi, offset32 PORTAGE_Do_Ring3_Callback
        xor       edi, edi
        VMMcall   Call_Priority_VM_Event
        clc
        ret
EndProc PORTAGE_Schedule_System_VM
;------------------------------------------------------------
BeginProc PORTAGE_Do_Ring3_Callback
        Push_Client_State
        VMMcall   Begin_Nest_Exec
        ; Retrieve the reference data
        mov       edi, edx
        ; Check the window handle
        cmp       dword ptr [edi.hWnd], 0
        je        short Do_Ring3_Callback_Exit
        ; Push the PostMessage parameters for the Ring 3 callback
        mov       eax, dword ptr [edi.hWnd]
        VMMcall   Simulate_Push
        mov       ax,   word ptr [edi.msg]
        VMMcall   Simulate_Push
        mov       ax,   word ptr [edi.wParam]
        VMMcall   Simulate_Push
        mov       ax,   word ptr [edi.lParam + 2]
        VMMcall   Simulate_Push
        mov       ax,   word ptr [edi.lParam + 0]
        VMMcall   Simulate_Push
        mov       cx,   word ptr [edi.PostMessageAddr + 2]
        movzx     edx,  word ptr [edi.PostMessageAddr + 0]
        VMMcall   Simulate_Far_Call
        VMMcall   Resume_Exec
Do_Ring3_Callback_Exit:
        VMMcall   End_Nest_Exec
        Pop_Client_State
        clc
        ret
EndProc PORTAGE_Do_Ring3_Callback

Listing Four

/**********************************************************************
 * PORTAGE application-level routines (Win 95 version) by Don Matthews
 **********************************************************************/
HGLOBAL           hMem;       /* global memory handle                 */
SharedDataFormat *SharedData; /* data shared between the app and VxD  */
HANDLE            hVxD;       /* VxD handle                           */
BOOL portage_vxd_register( void )
{       BOOL  Result = FAILURE;
        BOOL  DeviceIOResult;
        DWORD Error;
        hMem        = NULL;
        SharedData  = NULL;
        hVxD        = NULL;
        hMem        = GlobalAlloc( GPTR, sizeof(SharedDataFormat) );
        if( !hMem )        return( Result );
        SharedData  = (SharedDataFormat *)GlobalLock( hMem );
        if( !SharedData )  return( Result );
        GlobalFix( hMem );
        hVxD = CreateFile( "\\\\.\\PORTAGE",
                           (DWORD)NULL, (DWORD)NULL, NULL,
                           (DWORD)NULL, (DWORD)NULL, NULL );
        if( hVxD == INVALID_HANDLE_VALUE )  return( Result );
        if( hVxD )
        {   SetLastError( 0 );
            DeviceIOResult = DeviceIoControl(
                                 hVxD,
                                 API_PORTAGE_PM_Register,
                                 (LPVOID)       SharedData,
                                 (DWORD)        NULL,
                                 (LPVOID)       NULL,
                                 (DWORD)        NULL,
                                 (LPDWORD)      NULL,
                                 (LPOVERLAPPED) NULL );
            Error = GetLastError();
            if( DeviceIOResult )  Result = SUCCESS;
            else                  Result = FAILURE;
        }
        return( Result );
} 
/****************************************************************/
BOOL portage_vxd_post( void )
{       BOOL  Result = FAILURE;
        DWORD InstanceIndex;
        BOOL  DeviceIOResult;
        DWORD Error;
        if( hVxD )
        {   InstanceIndex = SharedData->InstanceIndex;
            SetLastError( 0 );
            DeviceIOResult = DeviceIoControl(
                                 hVxD,
                                 API_PORTAGE_PM_Post,
                                 (LPVOID)       InstanceIndex,
                                 (DWORD)        NULL,
                                 (LPVOID)       NULL,
                                 (DWORD)        NULL,
                                 (LPDWORD)      NULL,
                                 (LPOVERLAPPED) NULL );
            Error = GetLastError();
            if( DeviceIOResult )  Result = SUCCESS;
            else                  Result = FAILURE;
        }
        return( Result );
}
/****************************************************************/
BOOL portage_vxd_shutdown( void )
{       BOOL  Result = FAILURE;
        DWORD InstanceIndex;
        BOOL  DeviceIOResult;
        DWORD Error;
        if( hVxD &&
            (SharedData->InstanceIndex != PORTAGE_Max_Instances) )
        {   InstanceIndex = SharedData->InstanceIndex;
            SetLastError( 0 );
            DeviceIOResult = DeviceIoControl(
                                 hVxD,
                                 API_PORTAGE_PM_Shutdown,
                                 (LPVOID)       InstanceIndex,
                                 (DWORD)        NULL,
                                 (LPVOID)       NULL,
                                 (DWORD)        NULL,
                                 (LPDWORD)      NULL,
                                 (LPOVERLAPPED) NULL );
            Error = GetLastError();
            if( DeviceIOResult )  Result = SUCCESS;
            else                  Result = FAILURE;
        }
        if( hMem )
        {   GlobalUnfix( hMem );
            GlobalUnlock( hMem );
            GlobalFree( hMem );      
        }
        return( Result );
}
/****************************************************************/

Listing Five

;---------------------------------------------------------------
;  Portage VxD API Routines (Win 95 version), by Don Matthews.
;---------------------------------------------------------------
BeginProc PORTAGE_W32_Register
        Trace_Out "PORTAGE_W32_Register"
        ; Determine the next available instance index.
        mov       eax, 0
W32_RegisterLoop:
        mov       edi, PORTAGE_Instance_Table[ eax * 4 ]
        or        edi, edi
        jz        short W32_RegisterContinue
        inc       eax
        cmp       eax, PORTAGE_Max_Instances
        jne       short W32_RegisterLoop
        ;
W32_RegisterContinue:
        ; Retrieve the pointer to the SharedData structure.
        mov       edi, [esi.lpvInBuffer] ; SharedDataFormat *SharedData
        ; Save the instance index in the SharedData structure.
        mov       [edi.InstanceIndex], eax
        cmp       eax, PORTAGE_Max_Instances
        je        short W32_RegisterError
        Trace_Out "Found Instance Index #EAX"
        ; Save the SharedData pointer.
        mov       PORTAGE_Instance_Table[ eax * 4 ], edi
W32_RegisterExit:
        jmp       PORTAGE_W32_API_Success
W32_RegisterError:
        Trace_Out "ERROR - No available instance index!"
        jmp       PORTAGE_W32_API_Failure
EndProc PORTAGE_W32_Register
;---------------------------------------------------------------
BeginProc PORTAGE_W32_Post
        Trace_Out "PORTAGE_W32_Post"
        ; Load EDI with a pointer to the SharedData structure.
        ; Subsequent accesses to the members of the structure will
        ; be done using offsets into the structure from EDI, where
        ; EDI is an offset from DS.
        mov       eax, [esi.lpvInBuffer] ; DWORD InstanceIndex
        mov       edi, PORTAGE_Instance_Table[ eax * 4 ]
        call      PORTAGE_W32_Do_Ring3_Callback
        jmp       PORTAGE_W32_API_Success
EndProc PORTAGE_W32_Post
;---------------------------------------------------------------
BeginProc PORTAGE_W32_Shutdown
        Trace_Out "PORTAGE_W32_Shutdown"
        ; Load EDI with a pointer to the SharedData structure.
        ; Subsequent accesses to the members of the structure will
        ; be done using offsets into the structure from EDI, where
        ; EDI is an offset from DS.
    mov       eax, [esi.lpvInBuffer] ; DWORD InstanceIndex
        mov       edi, PORTAGE_Instance_Table[ eax * 4 ]
        ; Zero out the instance data pointer.
        mov       edi, 0
        mov       PORTAGE_Instance_Table[ eax * 4 ], edi
        jmp       PORTAGE_W32_API_Success
EndProc PORTAGE_W32_Shutdown

Listing Six

;--------------------------------------------------------------------
; PORTAGE VxD Posting Routines (Windows 95 version), by Don Matthews.
;--------------------------------------------------------------------
BeginProc PORTAGE_W32_Do_Ring3_Callback
        ;
        ; Check the window handle
        cmp       dword ptr [edi.hWnd], 0
        je        short W32_Do_Ring3_Callback_Exit
        ;
        ; Load the PostMessage parameters for the Ring 3 callback
        mov       eax, dword ptr [edi.hWnd]
        movzx     ebx,  word ptr [edi.msg]
        movzx     ecx,  word ptr [edi.wParam]
        mov       edx, dword ptr [edi.lParam]
        ;
        VxDcall   _SHELL_PostMessage, < eax, ebx, ecx, edx, 0, 0 >
        ;
W32_Do_Ring3_Callback_Exit:
        clc
        ret
EndProc PORTAGE_W32_Do_Ring3_Callback


Copyright © 1995, Dr. Dobb's Journal

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