程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> C++ >> 關於C++ >> C++語言的VxD與外界通信的所有接口

C++語言的VxD與外界通信的所有接口

編輯:關於C++

一、 什麼是VxD

從多任務操作系統Windows 3.1起,計算機中的任一物理設備x可同時被基於Dos或Windows的多個進程使用,這種一對多的關系稱為"設備虛擬化",各進程通過運行在核心層的VxD(虛擬x設備驅動程序)存取物理設備x。操作系統提供給用戶的軟件服務也可以用VxD實現。計算機中的其他資源,如CPU、內存等也可同時被多個進程使用,各進程在系統提供的虛擬機(VM)環境下存取這類資源。

VxD可由虛擬機管理器(VMM)在開機時裝入核心層(稱靜態裝入,即置VxD於c:\windows\system目錄下,在c:\windows\system.ini文件中,對節[386Enh]加一行"device=此VxD文件名"),或由應用程序實時裝入(稱動態裝入),而後,各進程便可存取鎖定在內存中的VxD數據區,以實時控制VxD的行為,VxD的內部結構可防止兩個進程同時存取其數據區。VxD通過響應VMM發給它的事件與外界交互。

Windows 95中,基於Dos的每個進程在單獨的VM中運行(稱在V86模式下運行),既可按Dos單進程方式,在640k低內存中運行(稱在實模式下運行),又可利用多進程環境的優點,在整個內存中運行(稱在保護模式下運行),通過95的DPMI接口存取內存高端的Windows圖形環境。其他16位或32位應用程序均在同一系統VM中運行。

下面只討論95環境下的VxD。

二、 VxD的創建

1. 由匯編語言創建VxD:需安裝微軟公司的Win32 SDK及DDK。

2. 由C或C++語言創建VxD:需安裝VC2.0或BC4.0,及Vireo Software公司的VToolsD軟件包。

VToolsD含3個實用工具:可創建VxD框架的QuickVxD;可動態裝卸VxD的VxD Loader;可顯示內存VxD特性的VxD Viewer。

QuickVxD含7個對話頁:

(1) Device Parameters頁

包括最多8個字符的VxD名,唯一標識號(ID),相對其他VxD的裝入順序(VxD Viewer可顯出某VxD的裝入順序值Init Order,若指定新VxD的裝入順序小於此Init Order,則新VxD將在此VxD前被裝入),實現語言(C或C++)靜、動態裝入方式等。

(2) VxD Services頁

可被其他VxD訪問的接口(稱為VxD服務),要求本VxD的ID>0,且未與內存各VxD的ID值沖突。

此ID可向微軟公司申請,也可使用Vireo公司的VIREO_TEST_ID(3180h)。下稱此類ID為接口ID。

(3) API頁

可被應用程序在實模式/V86模式下、保護模式下、DPMI的實模式/V86模式下、DPMI的保護模式下訪問的接口(統稱應用接口),前兩者要求本VxD提供接口ID,後兩者只要求本VxD提供以0結尾的唯一標識串;訪問前,先要靜態或動態裝入本VxD(第4者要求靜態裝入)。

第1、3者可被普通匯編程序訪問,第2、4者可被在BC的Windows 3-x(16)平台上生成的Windows程序訪問。

(4) Control Messages頁

對出現在Windows 3.1及Windows 95中各消息的響應,如靜態裝入時的DTNAMIC_INIT消息。

(5) Windows95 Control Messages頁

對只出現在Windows 95中各消息的響應,如動態裝入時的SYS_DYNAMIC_INIT消息。

(6) 用C++實現VxD時的Classes頁

從虛擬設備驅動程序類VDevice派生的類名(如MyDevice),此類的成員函數將接收(4)及(5)頁中出現的大多數消息。

從VM實例類VVirtualMachine派生的類名(如MyVM),此類的成員函數將接收貫穿在VM生命期中的各消息,如系統VM初啟消息Sys_VM_Init;

從線程實例類VThread派生的類名(如MyThread)。此類的成員函數將接收貫穿在線程生命期中的各消息,如新線程初啟消息THREAD_INIT。

(7) Output Files頁

體現以上內容的3個VxD文件(.h,.c或.cpp,.mak)將被存放的目錄位置。

三、 C++語言的VxD與外界通信的所有接口

我們將簡要實現my.VxD的應用接口及服務,它們均作為類的函數成員,存於my.h,my.cpp中。

1. 被32位C應用程序訪問的接口

應用程序先用CreateFile打開VxD,後用DeviceIoControl使VMM發送W32_DEVICEIOCONTROL消息給VxD:

HANDLE h;char ibuf[2],obuf[2];BOOL r;DWORD oc;OVERLAPPED o;

h=CreateFile("\\\\.\\my.vxd",0,0,0,0,FILE_FLAG_DELETE_ON_CLOSE,0);

//打開靜態my.VxD,或動態裝入my.VxD

r=DeviceIoControl(h,命令碼C,ibuf,sizeof(ibuf),obuf,sizeof(obuf),&oc,NULL或&o);

/*與my.VxD的事件過程OnW32DeviceIoControl交換數據,用ibuf向VxD傳數據,用obuf從VxD取數據,VxD傳回的數據總量放在oc中*/

CloseHandle(h);//關閉或動態卸下VxD

my.VxD應在Windows 95 control messages頁上選W32_DEVICEIOCONTROL事件,

在DWORD MyDevice::OnW32DeviceIoControl(PIOCTLPARAMS p)事件過程中寫:

switch(p->dioc_IOCtlCode){

case 命令碼C:

用p指向的IOCTLPARAMS結構,與應用程序交換數據;

if (成功) return(0); /*使DeviceIoControl的返回值r為TRUE*/
 else return(1);
 default:
 return(0);
}

以上做法要求VxD立即交換數據(同步通訊),值FILE_FLAG_DELETE_ON_CLOSE指明CloseHandle將不在內存中保留引用記數為0的VxD。

VxD也可延遲交換數據,此時,應用程序先傳值FILE_FLAG_DELETE_ON_CLOSE|FILE_FLAG_OVERLAPPED

到CreateFile,用o.hEvent=CreateEvent(0,TRUE,0,NULL)創建事件,再傳o的地址到DeviceIoControl,然後用GetOverlappedResult(h,&o,&oc,TRUE)在o上睡眠。

此時,p->lpoOverlapped一定大於0,VxD可用VMM服務_LinPageLock,按頁上鎖p->dioc_InBuf指向的應用程序ibuf區,p->dioc_OutBuf指向的obuf區,p->lpoOverlapped指向的o結構。要交換數據時,可置數據及數據總量到p->dioc_OutBuf及p->lpoOverlapped->O_InternalHigh,然後調用VMM服務VWIN32_DIOCCompletionRoutine(p->lpoOverlapped->O_Internal)喚醒應用程序。

VMM動態裝卸VxD時,以命令碼0及-1發送W32_DEVICEIOCONTROL消息給VxD,故Vireo公司建議命令碼C取[2048,4095]。

2. 被Real/V86模式下16位應用程序訪問的接口

my.VxD先要指定接口ID(如3180h),再在API頁上選Standard Application Entry Points框中的Real/V86 Mode標簽,即可生成MyDevice::V86_API_Entry入口,訪問它的匯編程序是:

entry dd ?
mov ax,1684h ;功能號
mov bx,3180h ;接口ID
int 2fh ;取入口的段/偏移到es/di,成功時,di及es返回非零值
mov ax,es
or ax,di
jz L0
mov word ptr [entry],di
mov word ptr [entry+2],es
mov ah,碼C
call [entry]
L0: 錯誤處理
MyDevice::V86_API_Entry(VMHANDLE hVM,CLIENT_STRUCT* p)入口可以是:
if (p->CBRS.Client_AH==碼C) p->CBRS.Client_AL=0;

3. 被保護模式下16位應用程序訪問的接口

與第2條類似,但選Protected Mode標簽,即可生成MyDevice::PM_API_Entry入口,訪問它的程序是:

int PASCAL WinMain(HANDLE h1,HANDLE h0,LPSTR lpCmdLine,int nCmdShow){
 
FARPROC entry; //32位
_asm{
mov ax,1684h
mov bx,3180h
int 2fh ;取入口的選擇符/偏移到es/di,成功時,di及es返回非零值
mov ax,es
or ax,di
jz L0
mov word ptr [entry],di
mov word ptr [entry+2],es
mov ah,碼C
call [entry]
}

對PM_API_Entry的處理如第2條。

4. 被DPMI的實模式/V86模式下16位應用程序訪問的接口

與第2條類似,但在API頁上選Vendor Specific Application Entry Points中的Real/V86 Mode標簽,然後在Vendor ID String中輸入唯一標識串my,即可生成My_V86VendorEntry::handler入口,訪問它的程序是:

str db ‘my',0 ;VxD的唯一標識串

entry dd ?

mov ax,168Ah ;功能號

lea si,str ;要求ds/si值是str的段值/偏移值

int 2Fh ;取入口的段/偏移到es/di,成功時,al返回0

cmp al,0

jne L0

mov word ptr [entry],di

mov word ptr [entry+2],es

...

call [entry]

對handler的處理如第二條。

5. 被DPMI的保護模式下16位應用程序訪問的接口

與第4條類似,但選Protected Mode標簽,即可生成My_ProtVendorEntry::handler入口,訪問它的程序是:

int PASCAL WinMain(HANDLE h1,HANDLE h0,LPSTR lpCmdLine,int nCmdShow){
char *id="my";
FARPROC entry;
_asm{
mov ax,168Ah
mov si,id
int 2Fh ;取入口的選擇符/偏移到es/di
cmp al,0
...
}
}

對handler的處理如第2條。

6. 可被其他VxD訪問的接口

若your.VxD欲調my.VxD的做兩數相減的minus接口,需在my.VxD的VxD service頁上輸入原型

DWORD _cdecl minus(DWORD i,DWORD j),再在MyDevice::minus中,寫return(i-j);

your.mak中,需處理中間文件wrap.cpp:

OBJECTS=your.OBJ wrap.obj

...

wrap.OBJ:wrap.cpp my.h

wrap.cpp中,對帶參數的VxD服務,需用VMM宏指令VxDJmp轉入,各參數進入wrap時,已按C的調用約定入棧;對不帶參數VxD服務,可調用VMM宏指令VxDCall(接口名):

#include "my.h"
DWORD _cdecl MyDevice::minus(DWORD i,DWORD j){
VxDJmp(minus);
}
your.cpp的某一函數f,可用VMM服務Get_DDB,查my.VxD是否已裝入,若未裝入,則用VxDLDR服務VxDLDR_LoadDevice將其裝入:
#define DEVICE_MAIN
#include "your.h"
Declare_Virtual_Device(YOUR)
#undef DEVICE_MAIN
#include "my.h" //此行需在DEVICE_MAIN外
VOID f(){
PDEVICEINFO pinfo;
PDDB pddb;
DWORD r;
pddb=Get_DDB(0,"MY
"); //用空格補全長度小於8的VxD名
if (pddb==0) {//未裝入
r=VxDLDR_LoadDevice("my.VxD",
VxDLDR_INIT_DEVICE,&pinfo,&pddb);
if (r!=0) //VxDLDR_LoadDevice未能成功裝入my.VxD
return;
}
MyDevice::minus(值1,值2);
}

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved