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

ID Number: Q67845

3.00 3.10

WINDOWS

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 CS:IP

; ES:DI -> Real mode SS:SP

; Interrupts disabled

;

; On exit:

; ES:DI -> Real mode call structure

;==========================================================

call Do_It ; Function that handles

; the callback

cld

lodsw

mov WORD PTR es:[di+2Ah], ax ; Real mode IP

lodsw

mov WORD PTR es:[di+2Ch], ax ; Real mode CS

lodsw

mov WORD PTR es:[di+20h], ax ; Real mode FLAGS

add WORD PTR es:[di+2Eh], 4 ; Bump real mode stack down

; to remove old 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 reference words: 3.00 3.10 DDKDPMI DDKTSR