Q67845: Calling a DLL Written for Windows from a TSR for MS-DOS

Article: Q67845
Product(s): Microsoft Windows Device Driver Kit
Version(s): 3.0,3.1
Operating System(s): 
Keyword(s): 
Last Modified: 03-NOV-1999

-------------------------------------------------------------------------------
The information in this article applies to:

- Microsoft Windows Device Development Kit (DDK) for Windows, versions 3.0, 3.1 
-------------------------------------------------------------------------------

SUMMARY
=======

A TSR (terminate-and-stay-resident) program running under MS-DOS can call a
dynamic-link library (DLL) written for the Microsoft Windows environment. This
is possible by using the Interrupt 2Fh services provided by Windows and the
MS-DOS Protected Mode Interface (DPMI).

MORE INFORMATION
================

To call the TSR program, an application can use the DPMI Simulate Real Mode
Interrupt function or the DPMI Call Real Mode Procedure function with a FAR
return frame.

To call a DLL from a TSR, perform the following four steps:

1. Design the function to be called from the TSR similar to an interrupt service
  routine (ISR), and put it into a DLL. The module definition (DEF) file for
  the DLL must contain the statement CODE PRELOAD FIXED. Here is a code
  fragment for a callback function:

      ; My callback code in a CODE PRELOAD FIXED Windows DLL
     ;
     ; Note:  The real mode CS:IP in the real mode CallStruct must
     ;        be fixed up for IRET to work correctly, as shown below:

     My_Call_Back:                         ; The real mode callback entry

        ;==========================================================
        ; On entry to this routine:
        ;
        ;   DS:SI -> Real mode SS:SP
        ;   ES:DI -> Real mode call structure
        ;   Interrupts disabled
        ;
        ; On exit:
        ;   ES:DI -> Real mode call structure
        ;==========================================================

        call     Do_It                     ; Function that handles
                                           ; the callback

        cld
        lodsw                              ; Get real mode IP from stack
        mov      WORD PTR es:[di+2Ah], ax  ; Store IP in call struc
        lodsw                              ; Get real mode CS from stack
        mov      WORD PTR es:[di+2Ch], ax  ; Store CS in call struc
        add      WORD PTR es:[di+2Eh], 4   ; Bump real mode SP past CS:IP

        iret                               ; Exit My_Call_Back

2. Use the DPMI Allocate Real Mode callback function to set up a callback
  address to the function, as follows:

       mov      ax, 0303h               ; Allocate real mode callback
                                         ; address
        push     ds
        push     cs
        pop      ds
        mov      si, OFFSET My_Call_Back
        pop      es
        mov      di, OFFSET MyRMCS       ; Offset of real mode call
                                         ;   structure, used for DPMI
                                         ;   translation services
                                         ;   (see Chapter 11 of the
                                         ;   INTEL DPMI Spec. v 0.9)

        int      31h                     ; Call DPMI

        jc       CB_Error                ; If carry set, call failed
        jmp      Set_CB_Addr

     CB_Error:
        mov      ax, 1
        jmp      Set_CB_Exit

     Set_CB_Addr:
        mov      WORD PTR CB_Addr+2, cx   ; Store SEG:OFF of callback
        mov      WORD PTR CB_Addr, dx     ;   address in local variable

     Set_CB_Exit :                        ; Exit

3. Pass the real mode callback address, returned from the code above, to the TSR
  and store it, as follows:

     ; Call the TSR, passing the real mode callback in BX:CX

     Notify_TSR:

        mov      bx, WORD PTR CB_Addr+2    ; Real mode callback seg
        mov      cx, WORD PTR CB_Addr      ; Real mode callback off
        mov      ax, 8F00h                 ; ID and function number
        int      TSR_Int_No

        cmp      al, 00h
        jz       Notify_Success

     Notify_Failure:
        mov      al, 1                     ; Return failure in AL

     Notify_Success:

4. When it is time for the TSR to call the DLL, use Interrupt 2Fh to determine
  whether the current virtual machine (VM) is VM 1 (the Windows VM). If it is,
  use the assembly language statement CALL DWORD PTR dwCallBackAddr to call the
  real mode callback address. If the current VM is not VM 1, use the Interrupt
  2Fh service Switch VMs and Callback (Function 1685h). Specify the real mode
  callback address stored in the TSR in step 3 above for the ES:BX value in the
  call.

     ;
     ; Code in TSR, which is loaded before Windows
     ;

     Call_DLL_Entry:

        mov      ax, 1683h                 ; Windows Interrupt 2Fh
        int      2Fh                       ;    (Get Current VM)

        cmp      bx, 1                     ; Windows is always VM 1
                                           ; (version 3.0 *ONLY*)
        jz       Call_DLL                  ; We're in Windows VM

     Switch_VMs_And_Call_DLL:              ; We're not in Windows VM

        mov      ax, 1685h                 ; Windows Interrupt 2Fh
                                           ;   switch VMs and callback
        mov      bx, 1                     ; Windows 3.0 is always VM 1
        mov      cx, 0                     ; All flags off
        mov      dx, 0                     ; Priority boost not required
        mov      si, 0                     ;
        mov      di, seg SwitchProc        ; Segment of switch procedure
        mov      es, di                    ;
        mov      di, offset SwitchProc     ; Offset of switch procedure
        int      2Fh                       ;

        jnc      My_1685h_Success

     My_1685h_Error:

        ; AX = error code:   1 = Invalid VM ID
        ;                    2 = Invalid priority boost
        ;                    3 = Invalid flags

        .
        .
        .

        jmp      Call_DLL_Exit

     My_1685h_Success:

          mov     ax, 0
          jmp     Call_DLL_Exit

     Call_DLL:

        Call     DWORD PTR CB_Addr          ; FAR CALL to real mode
                                            ;   callback

     Call_DLL_Exit:

        .
        .
        .

        iret                               ; return to caller or
                                           ;   hardware interrupt

     SwitchProc proc

        pusha
        push    ds
        push    es

        call    DWORD PTR CB_Addr          ; FAR CALL to real mode
                                           ;   callback
        pop     es
        pop     ds
        popa

        iret                               ; Switches back to VM from
                                           ;   which it was called

     SwitchProc endp

The four steps above allocate a unique procedure callback address and pass it to
the TSR, which stores the address. When the TSR calls the DLL, if the Windows
virtual machine (VM) is running, the TSR calls the callback address directly.
Otherwise, the TSR calls the Switch VMs and Callback function to schedule the
Windows virtual machine. One parameter for this function is the address to call
once the requested VM is running.

In this case, the Windows scheduler switches VMs, then it calls the SwitchProc
function in the TSR. When SwitchProc calls the routine in the DLL that
corresponds to the allocated real mode callback address, the DLL receives
control to perform its processing.

Once the DLL completes its processing, it executes an IRET instruction to return
control to the SwitchProc. Then SwitchProc itself executes an IRET instruction.
At this point, the Windows scheduler switches back to the VM from which it was
called.

All Interrupt 2Fh functions are documented in Appendix C of the "Microsoft
Windows Device Driver Kit: Device Driver Adaptation Guide" for Windows 3.1 and
in Appendix D of the "Microsoft Windows Device Development Kit Virtual Device
Adaptation Guide" for Windows 3.0.

DPMI (Interrupt 31h) services are documented in the Intel document "MS-DOS
Protected Mode Interface Specification." A paper copy of the DPMI specification
is available free of charge from Intel Literature. In the United States, contact
Intel at (800) 548-4725. Outside the US, contact the Intel distributor for your
country.

Additional query words: 3.00 3.10 DDKDPMI DDKTSR

======================================================================
Keywords          :  
Technology        : kbAudDeveloper kbWin3xSearch kbWinDDKSearch kbWinDDK300 kbWinDDK310
Version           : :3.0,3.1

=============================================================================