DOS设备驱动程序与Windows进程的通信   随着计算机硬件的快速发展,功能强大的Microsoft Windows得到越来越广泛的 应用,软件环境由Windows替代DOS是大势所趋。增强模式的虚拟设备驱动棗序桘繍 VxD(.386文件)是人们能够编写的最强大的Windows应用程序,但却它是Windows编程 中一个艰难且高度专业化的分支,而且一般用户应用程序独享自制外部设备硬件资 源。Windows 3.x采用非抢先式多任务消息驱动运行机制,所以Windows下如何解决 实时高速数据采集问题,便显得非常重要。Windows 3.x应用程序虽然可以运行在保 护模式下,享有多于1M的内存,但它并未脱离实模式的DOS。在实时获取和控制系统 中,为快速响应外界变化而编写DOS设备驱动程序是简单而有效的选择。显而易见, 必须解决实模式的DOS设备驱动程序与保护模式的Windows进程之间的通信问题。 Windows API   能否既充分利用Windows统一、美观的交互界面,又能够保障数据采集的实时性 呢?答案是肯定的。利用Windows提供的DPMI(DOS保护模式接口)功能,以及内核函 数GlobalDOSAlloc(),就可以实现由DOS设备驱动程序直接访问Windows应用程序动 态申请的缓冲区的目的。这个函数可以分配既能够被Windows应用程序存取,也能被 DOS 设备驱动程序访问的内存块。   Windows 3.x通常运行于386增强模式,亦即对应着DOS环境下的保护模式。在增 强模式下,由于增加了虚拟内存管理,由描述符表(GDT和LDT)得到的线性地址与物 理地址之间并没有必然的联系。它们之间只有一种类似于表格的对应关系,所以需 要利用DOS保护模式接口(DPMI)来完成这项工作。 ①DWORD GlobalDosAlloc(cbAlloc)棗在 GWindows环境中分配一块能在MS- DOS实模 式下管理的全局内存。 DWORD cbAlloc; /* 指定要分配的内存字节数*/   函数GlobalDosAlloc()分配的内存保证位于第一个1M线性地址空间之内。因为 系统内存池是极为有限系统资源,所以,应用程序都应尽量压缩该函数所申请的内 存尺寸。函数返回值的高字是段值,而低字是相应的段选择符。DOS Driver可以利 用段值访问实模式内存,而Windows应用程序则利用段选择符访问保护模式内存。如 果Windows不能分配所要求大小的内存块,将返回零值。   注意:通过GlobalDosAlloc()函数分配的内存空间不需要象通常进行数据存取 那样,使用GlobalLock()函数来锁定该段内存空间。 ②UINT GlobalDosFree(uPMSelector)棗释放前面由sFGlobalDosAlloc函数分配的 全局内存对象。 UINT uPMSelector; /* 待释放的内存段选择符*/ ③WORD HIWORD(DWORD dwInteger)棗取出参数(DdwInteger所指定的32位整数值的 高字。 ④WORD LOWORD(DWORD dwVal)棗取出参数VadwVal所指定的32位整数值 的低字。 程序设计   下面列出的程序展示了具体的实现过程,它通过调用Windows内核函数 GlobalDosAlloc()申请内存块作为与DOS Driver进行通信的缓冲区。设备驱动程序 DOSDRV.EXE挂接中断77h(对应IRQ15),既可以等待来自硬件的中断,数据采集完成 之后,向Windows应用程序WINCOMM.EXE发送消息,通知其进行数据接收和处理;又 可以由Windows进程向DOS Driver发出服务请求,自主地完成数据的获取。   由于程序篇幅较长,下面只列出关键部分,并加以说明。 Ⅰ.DOSDRV.ASM: ①DOS Driver安装检查 入口: AX = 通信印鉴 BX = 0 出口: BX = 通信印鉴 ②DOS Driver数据处理 入口:     AX = 通信印鉴    BX = 1     CX = Segment => WORD    DX = Offset => WORD name dosdrv Vect_Num equ 77h Int_Flag equ 1234h _TEXT segment word public 'CODE' assume cs:_TEXT,ds:_TEXT oldint dd 0 ;原中断向量地址 handle proc [push ......] ;保护现场 cmp ax, Int_Flag ;鉴别通信印鉴 jnz short old_vect ;否,转数据采集 cmp bx, 0 ;申请类别鉴定 jnz short check mov bx, ax ;设置检查成功标志 jmp short return check: cmp bx, 1 ;申请类别鉴定 jnz short old_vect ;否,转数据采集 mov ds, cx mov bx, dx ;内存地址定位 ; ... ;用户编制的数据处理过程 ; ... jmp short return old_vect: ;数据采集,中断寄存器设置,判断是否有接受消息的窗口 return: [pop ......] ;恢复现场 iret handle endp ALIGN 16 init_resident: dosdrv proc far  [ ...... ] ;中断向量地址的保存、设置,以及程序驻留 dosdrv endp _TEXT ends  end dosdrv Ⅱ.WINCOMM.CPP: DWORD FAR PASCAL GlobalDosAlloc (DWORD); UINT FAR PASCAL GlobalDosFree (UINT); void IsTSR (void); void CallTSR (void); #include "windows.h" #define WM_DOSWINCOMM WM_USER+1 int TestTSR = 0; WORD PMSelector=0, SegAddr; WORD FAR *farPtr; DWORD windowsFlag; long CALLBACK __export MainWndProc(hWnd, message, wParam, lParam) HWND hWnd; UINT message; WPARAM wParam; LPARAM lParam; { DWORD memoryPtr; /*Windows进程消息处理*/ switch (message) {  case WM_CREATE: /*用户数据初始化过程*/ windowsFlag = GetWinFlags(); case WM_DOSWINCOMM: if (! (windowsFlag & WF_PMODE)) { /*Windows处于实模式的处理过程*/ break; } IsTSR(); if (0==TestTSR)        { /*DOS Driver未安装处理过程*/ } else if (0==PMSelector)        { /*申请内存失败的处理*/ } else { /*用户可根据需要申请内存*/ memoryPtr = GlobalDosAlloc(2); PMSelector = LOWORD (memoryPtr); SegAddr = HIWORD (memoryPtr); farPtr = (WORD FAR *) ( (DWORD)PMSelector << 16);          CallTSR(); /*用户编制的处理过程*/ if (0!=PMSelector) PMSelector = GlobalDosFree (PMSelector); break;  }    case WM_DESTROY: if (0!=PMSelector) PMSelector = GlobalDosFree (PMSelector); PostQuitMessage(0); break; default: return (DefWindowProc(hWnd, message, wParam, lParam)); } return (NULL); } 下面的程序段为保护模式与实模式的接口: void IsTSR() { _asm{ mov ax, 0200h mov bl, 77h int 31h ; DPMI调用 or cx, dx jz short notsr mov ax, 1234h mov bx, 0 int 77h ;鉴别通信印鉴 cmp bx, 1234h jnz short notsr mov TestTSR, -1 notsr: } } void CallTSR() { _asm{ mov ax, 1234h mov bx, 1 mov cx, SegAddr xor dx, dx int 77h } } 所谓DPMI,是由Microsoft、Intel、IBM等多家公司联合建立的一种约定,它规 定了一组服务程序,并可以在保护模式下使用INT 31h来调用它们。这些服务程序的 提供者(如Windows 3.1)被称为DPMI服务程序(Server);而这些服务程序的使用者 (如保护模式下的DOS扩展程序)被称为客户程序(Client)。在同一个程序里,当需要 由保护模式访问实模式的驱动程序或者TSR程序时,除了DPMI之外,别无选择。 在DOS提示符下运行DOSDRV.EXE程序,从而完成将被WINCOMM.EXE 调 用的设备驱动 程序的安装。一旦Windows 被加载,即可运行WINCOMM.EXE程序以实现通信。 结束语   利用Windows内核函数GlobalDosAlloc()所获得的内存对于Windows虚拟机来说 是局部的,因此其它的虚拟机不能够访问该段内存。这种方法作为DOS设备驱动程序 与Windows应用程序之间进行通信的一种模式,如经稍加改进,可以实现多个 Windows进程共同拥有一个DOS设备驱动程序,而相互之间互不干扰。当然也可以实 现Windows进程之间的通信。