VxD Example: MessageBoxIn the previous tutorials, you learn about mechanics of VxD programming. Now is the time to apply what you have learned. In this tutorial, we will create a simple static VxD which will display a message box whenever a VM is created/destroyed.
Trapping VM creation and termination eventsWhen a VM is created, the VMM sends Create_VM control message to all VxDs. Also when a VM is terminated normally, it sends VM_Terminate and VM_Terminate2 to all VxDs. Our job is easy: Process Create_VM and VM_Terminate2 messages in our device control procedure. When our VxD receives those two control messages, it displays a message box on the screen.
When our VxD receives Create_VM or VM_Terminate2 message, ebx contains the handle of the VM. A VM handle can be considered as the unique ID of the VM. Each VM has its unique ID (VM handle). You can use VM handle in the same manner as you use a process ID, by passing it as a parameter to the services that need it.
On closer examination, a VM handle is actually the 32-bit linear address of the VM control block (VMCB).
VM Control Block is a structure that contains several important items about the VM. It's defined as:
cb_s STRUC
CB_VM_Status DD ?
CB_High_Linear DD ?
CB_Client_Pointer DD ?
CB_VMID DD ?
CB_Signature DD ?
cb_s ENDS
CB_VM_Status contains the bit flags that you can examine to find out about the state of the VM.
CB_High_Linear is the starting linear address of the mirror of the VM in the shared system region (above 3 GB). This concept requires an explanation. Under Windows 95, a VxD should not touch the V86 region directly instead the VMM maps the whole V86 region of every VM to the shared system region. When a VxD wants to modify/touch the memory in V86 region of the VM, it should do so to the high-linear area of the VM. For example, if the video memory is at 0B8000h and your VxD needs to touch that area, it should add the value in CB_High_Linear to 0B8000h and touch that area instead. The changes you made to the high-linear mirror will be reflected to the VM because both areas share the same page directory entry. Using the high-linear mirror is better in most situation because you can modify the VM even if it's not the current VM.
CB_Client_Pointer contains the address of the client register structure. The client register structure contains the values of all registers of the interrupted V86 or protected mode application in the VM. If your VxD wants to know/modify the state of the V86 or PM application, it can modify the members of the client register structure and the changes will propagate to the application when the VMM resumes its execution.
CB_VMID The numeric identifer of the VM. The VMM assigns this number when it creates the VM. The system VM has the VMID of 1.
CB_Signature contains the string "VMcb". This member is used in checking if the VM handle is valid.
Displaying a MessageBoxA VxD can use Virtual Shell Device services to communicate to the users. One such service we will use in this example is SHELL_Message.
SHELL_Message is a register-based service. You pass parameters to it via registers.
ebx Handle of the VM that is responsible for the message
eax MessageBox flags. You can look them up in shell.inc. They start with MB_.
ecx 32-bit linear address of the message to display
edi 32-bit linear address of the message box caption
esi 32-bit linear address of the callback function in case you need to know the response of the user to the message box. If you don't want to know, use NULL.
edx Reference data that will be passed to your callback (if you specify one in esi) On return, the carry flag is clear if the call is successful. The carry flag is set otherwise.
The example
.386p
include vmm.inc
include shell.inc
DECLARE_VIRTUAL_DEVICE MESSAGE,1,0, MESSAGE_Control, UNDEFINED_DEVICE_ID, UNDEFINED_INIT_ORDER
Begin_control_dispatch MESSAGE
Control_Dispatch Create_VM, OnVMCreate
Control_Dispatch VM_Terminate2, OnVMClose
End_control_dispatch MESSAGE
VxD_PAGEABLE_DATA_SEG
MsgTitle db "VxD MessageBox",0
VMCreated db "A VM is created",0
VMDestroyed db "A VM is destroyed",0
VxD_PAGEABLE_DATA_ENDS
VxD_PAGEABLE_CODE_SEG
BeginProc OnVMCreate
mov ecx, OFFSET32 VMCreated
CommonCode:
VMMCall Get_sys_vm_handle
mov eax,MB_OK+MB_ICONEXCLAMATION
mov edi, OFFSET32 MsgTitle
xor esi,esi
xor edx,edx
VxDCall SHELL_Message
ret
EndProc OnVMCreate
BeginProc OnVMClose
mov ecx,OFFSET32 VMDestroyed
jmp CommonCode
EndProc OnVMClose
VxD_PAGEABLE_CODE_ENDS
end
Analysis:
Begin_control_dispatch MESSAGE
Control_Dispatch Create_VM, OnVMCreate
Control_Dispatch VM_Terminate2, OnVMClose
End_control_dispatch MESSAGEThe VxD processes two control messages, Create_VM and VM_Terminate2. When Create_VM control message is received, it calls OnVMCreate procedure. And when it receives VM_Terminate2 message, it calls OnVMClose procedure.
VxD_PAGEABLE_DATA_SEG
MsgTitle db "VxD MessageBox",0
VMCreated db "A VM is created",0
VMDestroyed db "A VM is destroyed",0
VxD_PAGEABLE_DATA_ENDSWe put the data in the pageable data segment.
BeginProc OnVMCreate
mov ecx, OFFSET32 VMCreated
CommonCode:
VMMCall Get_sys_vm_handle
mov eax,MB_OK+MB_ICONEXCLAMATION
mov edi, OFFSET32 MsgTitle
xor esi,esi
xor edx,edx
VxDCall SHELL_Message
ret
EndProc OnVMCreateOnVMCreate procedure is created using BeginProc and EndProc macros. It puts the parameters for SHELL_Message service into the registers. Since we want to display the message box in the system VM, we cannot use the value in ebx (which is the handle of the VM that is being created). Instead, we use a VMM service, Get_Sys_VM_Handle, to obtain the VM handle of the system VM. This service returns the VM handle in ebx. We put the addresses of the message and the caption into ecx and edi, respectively. We don't want to know the response of the user, so we zero out esi and edx. When all parameters are in the appropriate registers, we call SHELL_Message to display the message box.
BeginProc OnVMClose
mov ecx,OFFSET32 VMDestroyed
jmp CommonCode
EndProc OnVMCloseOnVMCloseprocedure is simplicity in itself. Since it uses identical code as OnVMCreate, it initializes ecx with the address of the different message and then jumps to the code inside OnVMCreate.
Module Definition File
VXD MESSAGE
SEGMENTS
_LPTEXT CLASS 'LCODE' PRELOAD NONDISCARDABLE
_LTEXT CLASS 'LCODE' PRELOAD NONDISCARDABLE
_LDATA CLASS 'LCODE' PRELOAD NONDISCARDABLE
_TEXT CLASS 'LCODE' PRELOAD NONDISCARDABLE
_DATA CLASS 'LCODE' PRELOAD NONDISCARDABLE
CONST CLASS 'LCODE' PRELOAD NONDISCARDABLE
_TLS CLASS 'LCODE' PRELOAD NONDISCARDABLE
_BSS CLASS 'LCODE' PRELOAD NONDISCARDABLE
_LMGTABLE CLASS 'MCODE' PRELOAD NONDISCARDABLE IOPL
_LMSGDATA CLASS 'MCODE' PRELOAD NONDISCARDABLE IOPL
_IMSGTABLE CLASS 'MCODE' PRELOAD DISCARDABLE IOPL
_IMSGDATA CLASS 'MCODE' PRELOAD DISCARDABLE IOPL
_ITEXT CLASS 'ICODE' DISCARDABLE
_IDATA CLASS 'ICODE' DISCARDABLE
_PTEXT CLASS 'PCODE' NONDISCARDABLE
_PMSGTABLE CLASS 'MCODE' NONDISCARDABLE IOPL
_PMSGDATA CLASS 'MCODE' NONDISCARDABLE IOPL
_PDATA CLASS 'PDATA' NONDISCARDABLE SHARED
_STEXT CLASS 'SCODE' RESIDENT
_SDATA CLASS 'SCODE' RESIDENT
_DBOSTART CLASS 'DBOCODE' PRELOAD NONDISCARDABLE CONFORMING
_DBOCODE CLASS 'DBOCODE' PRELOAD NONDISCARDABLE CONFORMING
_DBODATA CLASS 'DBOCODE' PRELOAD NONDISCARDABLE CONFORMING
_16ICODE CLASS '16ICODE' PRELOAD DISCARDABLE
_RCODE CLASS 'RCODE'
EXPORTS
MESSAGE_DDB @1
Assembling process ml -coff -c -Cx -DMASM6 -DBLD_COFF -DIS_32 message.asm
link -vxd -def:message.def message.obj
VxD Installation
Put message.vxd in \system folder
add the following line inside [386enh] section of system.ini
device=message.vxd
reboot your computer
Testing the VxDCreate a DOS box. You will see the message box, displaying the message, "A VM is created". When you close the DOS box, a message box appears with the message, "A VM is destroyed".