发信人: chenjr() 
整理人: smallhors(1999-07-22 22:58:34), 站内信件
 | 
 
 
      基于Windows软件平台的应用的迅速普及,使得开发DOS驱动程序与
 
 Windows应用程序接口的需求日益迫切。本文介绍了实模式DOS设备驱动程序访问 
 
 Windows保护模式进程动态申请的数据段,实现处于DOS与Windows不同工作模式的 进
 
 程之间的通信的一种方法。
 
 关键词 进程通信 保护模式 实模式 DOS Driver DPMI
 
 
 
   随着计算机硬件的快速发展,功能强大的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进程之间的通信。
 
  -- 小白小白。一洗就白
 表白表白。一清二白。。。。。。。。。
                  你忠实的朋友!!!!!
  ※ 来源:.月光软件站 http://www.moon-soft.com.[FROM: 202.104.34.196]
  | 
 
 
 |