首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> C++ >> 關於C++

Windows 9x VxD 開發:C++ 語言虛擬裝置驅動程式與外界通訊的五大介面完整解析

     編輯:關於C++

虛擬裝置驅動程式 (VxD) 是 Windows 9x 核心架構的關鍵,運行於 Ring 0,負責硬體虛擬化、系統服務與多任務仲裁。使用 Vireo VToolsD 輔助 C++ 開發 VxD 時,理解其與外界 (應用程式、其他 VxD、DOS/Win16/Win32 程式) 通訊的五種標準介面至關重要。本文系統整理這五大介面的建立方式、呼叫慣例與程式碼範例。

VxD 通訊介面總覽

介面類型呼叫端VToolsD 精靈頁籤核心機制適用場景
1. Win32 應用程式介面 (W32_DeviceIoControl)32-bit 應用程式 (VC/BCB/Delphi)API 頁 → Protected Mode (DPMI)CreateFile + DeviceIoControl → VMM 發送 W32_DEVICEIOCONTROL 訊息現代 Win32 應用與 VxD 雙向資料交換、控制 (首選)
2. Real/V86 模式介面 (標準應用程式入口點)16-bit DOS/Win16/Win3.1 應用 (組合語言)API 頁 → Standard Application Entry Points → Real/V86 ModeINT 2Fh, AX=1684h 取得入口 → CALL FAR 進入 V86_API_Entry相容舊版 DOS/Win16 程式呼叫 VxD
3. 16-bit 保護模式介面 (標準應用程式入口點)16-bit Windows 應用 (BC 3.x/Win16)API 頁 → Standard Application Entry Points → Protected ModeINT 2Fh, AX=1684h 取得選擇子:偏移 → CALL FAR 進入 PM_API_Entry16-bit Windows 應用呼叫 VxD
4. DPMI Real/V86 廠商專用介面DPMI 客戶端 (DOS 延伸器/Win32s)API 頁 → Vendor Specific Application Entry Points → Real/V86 ModeINT 2Fh, AX=168Ah + 廠商 ID 字串 → 進入 My_V86VendorEntry::handler不想佔用標準 ID、需唯一字串識別的 VxD
5. DPMI 保護模式 廠商專用介面DPMI 保護模式客戶端API 頁 → Vendor Specific Application Entry Points → Protected ModeINT 2Fh, AX=168Ah + 廠商 ID 字串 → 進入 My_ProtVendorEntry::handler32-bit DPMI 客戶端呼叫 VxD
6. VxD 服務 (VxD-to-VxD)其他 VxDVxD Services 頁宣告服務表 + VxDCall/VxDJmp 巨集 → 透過 INT 20h 動態連結VxD 間共享核心功能、資料結構、硬體存取

介面 1:Win32 應用程式介面 (最常用、最現代)

應用程式端 (C/C++ Win32)

HANDLE h = CreateFile("\\\\.\\MYVXD.VXD", 0, 0, NULL, 0, FILE_FLAG_DELETE_ON_CLOSE, NULL);
// 同步呼叫
char inBuf[64], outBuf[64]; DWORD bytesRet;
BOOL ok = DeviceIoControl(h, MY_IOCTL_CODE, inBuf, sizeof(inBuf), outBuf, sizeof(outBuf), &bytesRet, NULL);
// 非同步呼叫 (Overlapped)
OVERLAPPED ov = {0}; ov.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
DeviceIoControl(h, MY_IOCTL_CODE, inBuf, sizeof(inBuf), outBuf, sizeof(outBuf), &bytesRet, &ov);
GetOverlappedResult(h, &ov, &bytesRet, TRUE); // 等待完成
CloseHandle(h);

VxD 端實作 (MyDevice.cpp)

// 1. QuickVxD 勾選 Windows 95 Control Messages → W32_DEVICEIOCONTROL
// 2. 實作 OnW32DeviceIoControl
DWORD MyDevice::OnW32DeviceIoControl(PIOCTLPARAMS p) {
    switch (p->dioc_IOCtlCode) {
        case MY_IOCTL_CODE:
            // p->dioc_InBuf  : 輸入緩衝區 (使用者模式位址)
            // p->dioc_OutBuf : 輸出緩衝區
            // p->dioc_CBInBuf  / CBOutBuf : 緩衝區大小
            // 同步模式:直接複製資料
            memcpy(p->dioc_OutBuf, "Response from VxD", 18);
            p->dioc_Olp->O_InternalHigh = 18; // 回傳位元組數
            return 0; // 成功 -> DeviceIoControl 回傳 TRUE
        case 0: case -1: // VMM 動態載入/卸載通知
            return 0;
        default:
            return 0; // 未知代碼也回傳 0 避免應用程式掛住
    }
}

// 非同步模式處理 (p->lpoOverlapped != 0):
// 1. _LinPageLock 鎖定使用者緩衝區頁面
// 2. 處理完成後,填入 p->dioc_OutBuf 與 p->lpoOverlapped->O_InternalHigh
// 3. VWIN32_DIOCCompletionRoutine(p->lpoOverlapped->O_Internal) 喚醒應用程式

介面 2/3:標準應用程式入口點 (Real/V86 & Protected Mode)

需在 Device Parameters 指定 Device ID (如 3180h,可向 Microsoft 申請或用 Vireo 測試 ID)。

呼叫端組合語言 (Real/V86 Mode)

EntryPoint dd ?
mov  ax, 1684h
mov  bx, 3180h      ; Device ID
int  2Fh            ; ES:DI 返回入口點
mov  ax, es
or   ax, di
jz   Error
mov  word ptr [EntryPoint], di
mov  word ptr [EntryPoint+2], es
mov  ah, MY_FUNC_CODE
call [EntryPoint]   ; 進入 VxD V86_API_Entry
Error: ...

VxD 端處理 (同一入口函式)

// V86_API_Entry / PM_API_Entry 簽名相同
void MyDevice::V86_API_Entry(VMHANDLE hVM, CLIENT_STRUCT *p) {
    if (p->CBRS.Client_AH == MY_FUNC_CODE) {
        // 處理邏輯,結果回傳於 Client_AL/Client_AX/Client_EAX
        p->CBRS.Client_AL = 0; // 成功
    }
}

介面 4/5:廠商專用應用程式入口點 (DPMI)

不需 Device ID,改用唯一廠商 ID 字串 (如 "MYVXD") 識別。

呼叫端 (DPMI Real/V86)

VendorID db 'MYVXD',0
EntryPoint dd ?
mov  ax, 168Ah
lea  si, VendorID   ; DS:SI 指向 ID 字串
int  2Fh            ; AL=0 成功,ES:DI 入口點
cmp  al, 0
jne  Error
mov  word ptr [EntryPoint], di
mov  word ptr [EntryPoint+2], es
call [EntryPoint]   ; 進入 My_V86VendorEntry::handler

介面 6:VxD 服務 (VxD-to-VxD 核心層通訊)

提供最高效、無模式切換開銷的核心層 API 共享。

提供者 VxD (MYVXD) 定義服務

// MYVXD.H (QuickVxD VxD Services 頁產生)
#define MYVXD_DeviceID 0x3180
#define MYVXD_Minus_Service 1
_declspec(naked) DWORD _cdecl MYVXD_Minus(DWORD a, DWORD b); // 宣告

// MYVXD.CPP 實作
DWORD _cdecl MyDevice::MYVXD_Minus(DWORD a, DWORD b) {
    _asm { mov eax, a; sub eax, b; ret } // 直接回傳 a-b
}

// 服務表宣告 (自動產生於 .h)
Begin_Service_Table(MYVXD)
MYVXD_Service(MYVXD_Get_Version, Local)
MYVXD_Service(MYVXD_Minus, Local)
End_Service_Table(MYVXD)

消費者 VxD (YOURVXD) 呼叫服務

// YOURVXD.MAK 需包含 wrap.cpp
// wrap.cpp (QuickVxD 產生的中介層)
#include "myvxd.h"
DWORD _cdecl MyDevice::MYVXD_Minus(DWORD a, DWORD b) {
    VxDJmp(MYVXD_Minus); // 透過 VMM 服務表跳轉
}

// YOURVXD.CPP 使用
#define DEVICE_MAIN
#include "yourvxd.h"
Declare_Virtual_Device(YOUR)
#undef DEVICE_MAIN
#include "myvxd.h" // 必須在 DEVICE_MAIN 區塊外

void SomeFunction() {
    PDDB pddb = Get_DDB(0, "MYVXD   "); // 名稱補空格至 8 字元
    if (!pddb) { // 未載入,嘗試動態載入
        PDEVICEINFO pi; PDDB pd;
        DWORD r = VxDLDR_LoadDevice("MYVXD.VXD", VxDLDR_INIT_DEVICE, &pi, &pd);
        if (r) return; // 載入失敗
    }
    DWORD result = MyDevice::MYVXD_Minus(100, 28); // 呼叫服務 -> 72
}

關鍵開發提醒

  1. 命令碼範圍:Vireo 建議 W32_DEVICEIOCONTROL 的自訂命令碼使用 2048~4095,避免與系統保留值 (0, -1) 衝突。
  2. 同步 vs 非同步:簡單資料交換用同步;大量資料、耗時操作必須用非同步 (Overlapped) 避免凍結系統訊息迴圈。
  3. 緩衝區鎖頁:非同步模式下,VxD 存取使用者模式緩衝區前,必須呼叫 _LinPageLock 鎖定實體頁面,防止分頁錯誤。
  4. ID 衝突:標準介面 Device ID 必須唯一,建議向 Microsoft 申請正式 ID;測試階段可用 VIREO_TEST_ID (3180h)。
  5. 除錯工具:Ring 0 除錯需使用 SoftICE for Windows 95/98,一般使用者模式除錯器無法中斷 VxD 程式碼。

掌握這六種介面機制,即可根據呼叫端環境 (Win32/Win16/DOS/DPMI/VxD) 選擇最適合的通訊方式,構建強健的 Windows 9x 系統級解決方案。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved