淺談 VxD 回撥 Win32 應用程式 -- 趙世平 (轉)
淺談 VxD 回撥 應用
作者:趙世平
95 / 98 的虛擬外殼裝置( Virtual Device )提供了 VxD 直接回撥 Win16 應用程式的 VxD 服務,但是沒有提供 VxD 直接回撥 Win32 應用程式的 VxD 服務。不過 Windows 95 / 98 還是提供了兩種 VxD 回撥 Win32 應用程式的方法。方法之一是使用 VWIN32.VXD 提供的“非同步過程( APC )”功能。 Win32 應用程式首先動態載入 VxD ,並使用 DeviceIoControl 函式將回撥函式的地址傳遞給 VxD ,然後 Win32 應用程式呼叫 SleepEx / WaitForMultiplesEx / WaitForSingleObjectEx 函式,將 Win32 應用程式自身置為“掛起”狀態,這時 VxD 可以透過 VWIN32.VXD 提供的 _VWIN32_QueueUserApc 服務呼叫 Win32 應用程式的回撥函式。該方法較簡單,目前大多數回撥 Win32 應用程式的 VxD 均使用該方法[例如( RAV )的實時 VxD ( RAV_IO.VXD )即使用該方法回撥 Win32 查毒/防毒程式],《 Windows 95 System Programming 4 》一書的配套光碟中有一個名為 IFSMONITOR 的例項程式詳細講述了該方法,由於該書的配套光碟在國內很多 上都可以找到,此處就不再詳述了。
方法之二是一種比較靈活的方法,這種方法充分利用了 Win32 應用程式的多執行緒特點和執行緒間通訊的事件機制。 Win32 應用程式設定兩個執行緒並定義一個事件,主執行緒負責動態載入/解除安裝 VxD 和透過 DeviceIoControl 函式與 VxD 通訊,輔助執行緒透過 ResetEvent 函式和 WaitForSingleObject / WaitForSingleObjectEx 函式暫時掛起, VxD 可以透過 VWIN32.VXD 提供的 Win32 事件服務中的 _VWIN32_SetWin32Event 服務喚醒輔助執行緒,從而間接實現 VxD 回撥 Win32 應用程式。由於 VWIN32.VXD 提供了與 Win32 幾乎完全對應的 Win32 事件服務,所以該方法極其靈活,甚至可以透過定義兩個事件實現 VxD 在回撥 Win32 應用程式時與 Win32 應用程式完全同步。
筆者為了驗證上述方法,編寫了一個動態載入/解除安裝 VxD 的 Win32 應用程式和一個回撥 Win32 應用程式的 VxD 。其中 Win32 應用程式用 5.0 編寫,選用 Delphi 5.0 的原因是 Delphi 5.0 實現多執行緒非常容易,而且不必將大量程式碼用在 Win32 應用程式介面上; VxD 使用 VToolsD 2.03 編寫,是一個掛接實時鐘中斷( IRQ 8 )的 VxD ,該 VxD 參照 VToolsD 2.03 中的 CHIME 例項程式編寫。程式程式碼如下:
Win32 應用程式工程( TMR_TEST.DPR ):
program TMR_TEST;
uses
Forms,
TT_MAIN in 'TT_MAIN.pas' {TimerTestMain},
TMR_CLBK in 'TMR_CLBK.pas';
{$R *.RES}
begin
Application.Initialize;
Application.CreateForm(TTimerTestMain, TimerTestMain);
Application.Run;
end.
Win32 應用程式主窗體/主執行緒( TT_MAIN.PAS ):
unit TT_MAIN;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
Menus, SyncObjs, TMR_CLBK, StdCtrls;
type
TOpenVxDHandle=function(h:THandle):THandle;stdcall;
TTimerTestMain = class(TForm)
MainMenu1: TMainMenu;
File1: TMenuItem;
Exit1: TMenuItem;
Callback1: TMenuItem;
Start1: TMenuItem;
Label1: TLabel;
procedure FormShow(Sender: TObject);
procedure FormClose(Sender: TObject; var Action: TCloseAction);
procedure Exit1Click(Sender: TObject);
procedure Start1Click(Sender: TObject);
private
{ Private declarations }
Handle1:THandle;
TimerCallback1:TTimerCallback;
public
{ Public declarations }
end;
const
TIMER_DIOC_SET_VXD_EVENT=101;
var
TimerTestMain: TTimerTestMain;
implementation
{$R *.DFM}
procedure TTimerTestMain.FormShow(Sender: TObject);
begin
Event1:=TEvent.Create(nil,True,False,'');
Handle1:=CreateFile('.TIMER.VXD',0,0,nil,0,FILE_FLAG_DELETE_ON_CLOSE,0);
if Handle1=INVALID_HANDLE_VALUE then MessageBox(Handle,' 不能開啟 TIMER.VXD ! ',' 錯誤 ',MB_ICONSTOP or MB_OK)
end;
procedure TTimerTestMain.FormClose(Sender: TObject;
var Action: TCloseAction);
begin
if Handle1<>INVALID_HANDLE_VALUE then CloseHandle(Handle1)
end;
procedure TTimerTestMain.Exit1Click(Sender: TObject);
begin
Release;
Application.Tenate
end;
procedure TTimerTestMain.Start1Click(Sender: TObject);
var
Kernel32:THandle;
OpenVxDHandle:TOpenVxDHandle;
VxDEvent:THandle;
cb:Long;
begin
if Handle1<>INVALID_HANDLE_VALUE then
begin
TimerCallback1:=TTimerCallback.Create(True);
TimerCallback1.Resume;
Kernel32:=LoadLibrary('KERNEL32.DLL');
if Kernel32<>0 then
begin
OpenVxDHandle:=GetProcAddress(Kernel32,'OpenVxDHandle');
VxDEvent:=OpenVxDHandle(Event1.Handle);
DeviceIoControl(Handle1,TIMER_DIOC_SET_VXD_EVENT,@VxDEvent,SizeOf(VxDEvent),nil,0,cb,nil);
FreeLibrary(Kernel32)
end
end
end;
end.
Win32 應用程式輔助執行緒( TMR_CLBK.PAS ):
unit TMR_CLBK;
interface
uses
Classes, SysUtils, Windows, SyncObjs;
type
TTimerCallback = class(TThread)
private
{ Private declarations }
procedure UpdateLabelCaption;
protected
procedure Execute; overr;
end;
var
Event1:TEvent;
implementation
uses TT_MAIN;
{ Important: Methods and properties of objects in VCL can only be used in a
method called using Synchronize, for example,
Synchronize(UpdateCaption);
and UpdateCaption could look like,
procedure TTimerCallback.UpdateCaption;
begin
Form1.Caption := 'Updated in a thread';
end; }
{ TTimerCallback }
procedure TTimerCallback.UpdateLabelCaption;
begin
TimerTestMain.Label1.Caption:=Trim(IntToStr(StrToInt(TimerTestMain.Label1.Caption)+1))
end;
procedure TTimerCallback.Execute;
begin
{ Place thread code here }
while not Terminated do
begin
Event1.ResetEvent;
Event1.WaitFor(INFINITE);
Synchronize(UpdateLabelCaption)
end
end;
end.
VxD 標頭檔案( TIMER.H ):
// TIMER.h - include file for VxD TIMER
#include
#define DEVICE_CLASS TimerDevice
#define TIMER_DeviceID UNDEFINED_DEVICE_ID
#define TIMER_Init_Order UNDEFINED_INIT_ORDER
#define TIMER_Major 1
#define TIMER_Minor 0
class TimerInterrupt:public VHardwareInt
{
public:
TimerInterrupt(VOID);
~TimerInterrupt();
virtual VOID OnHardwareInt(VMHANDLE hVM);
private:
VOID (*m_callback)();
BYTE m_originalA;
BYTE m_originalB;
};
class TimerEvent:public VGlobalEvent
{
public:
TimerEvent(VOID);
virtual VOID handler(VMHANDLE hVM,CLIENT_STRUCT* pRegs,PVOID refData);
};
BYTE ReadRegister(BYTE reg);
VOID WriteRegister(BYTE reg, BYTE value);
VOID TimerHandler(VOID);
class TimerDevice : public VDevice
{
public:
virtual BOOL OnSysDynamicDeviceInit();
virtual BOOL OnSysDynamicDeviceExit();
virtual DWORD OnW32DeviceIoControl(PIOCTLPARAMS pDIOCParams);
private:
TimerInterrupt *pTimerInterrupt;
};
class TimerVM : public VVirtualMachine
{
public:
TimerVM(VMHANDLE hVM);
};
class TimerThread : public VThread
{
public:
TimerThread(THREADHANDLE hThread);
};
VxD 源程式( TIMER.CPP ):
// TIMER.cpp - main module for VxD TIMER
#define DEVICE_MAIN
#include "timer.h"
Declare_Virtual_Device(TIMER)
#undef DEVICE_MAIN
#include
#include
#define STATREG_A 0xA
#define STATREG_B 0xB
#define STATREG_C 0xC
#define ENABLE_INTERRUPT 0x40
#define TIMER_DIOC_SET_VXD_EVENT 101
static int Count=0;
static HANDLE VxDEvent=NULL;
TimerInterrupt::TimerInterrupt():VHardwareInt(8,0,0,0)
{
m_callback=TimerHandler;
m_originalA=ReadRegister(STATREG_A);
m_originalB=ReadRegister(STATREG_B);
}
TimerInterrupt::~TimerInterrupt()
{
WriteRegister(STATREG_A,m_originalA);
WriteRegister(STATREG_B,m_originalB);
forceDefaultOwner(8,0);
}
VOID TimerInterrupt::OnHardwareInt(VMHANDLE hVM)
{
if(m_callback!=NULL) m_callback();
ReadRegister(STATREG_C);
sendPhysicalEOI();
}
TimerEvent::TimerEvent():VGlobalEvent(NULL)
{
}
VOID TimerEvent::handler(VMHANDLE hVM,CLIENT_STRUCT* pRegs,PVOID refData)
{
dout<
if(VxDEvent!=NULL)
{
VWIN32_SetWin32Event(VxDEvent);
}
else
{
VSD_Bell();
}
}
VOID TimerHandler(VOID)
{
Count++;
if(Count==2000)
{
(new TimerEvent)->call();
Count=0;
}
}
BYTE ReadRegister(BYTE reg)
{
BYTE r;
_asm {
pushfd
cli
mov al, reg
or al, 80h
out 70h, al
jmp _1
}
_1:
_asm jmp _2
_2:
_asm {
in al, 71h
mov r, al
jmp _3
}
_3:
_asm jmp _4
_4:
_asm {
xor al, al
out 70h, al
popfd
}
return r;
}
VOID WriteRegister(BYTE reg, BYTE value)
{
_asm {
pushfd
cli
mov al, reg
or al, 80h
out 70h, al
jmp _1
}
_1:
_asm jmp _2
_2:
_asm {
mov al, value
out 71h, al
jmp _3
}
_3:
_asm jmp _4
_4:
_asm {
xor al, al
out 70h, al
popfd
}
}
TimerVM::TimerVM(VMHANDLE hVM) : VVirtualMachine(hVM) {}
TimerThread::TimerThread(THREADHANDLE hThread) : VThread(hThread) {}
BOOL TimerDevice::OnSysDynamicDeviceInit()
{
BYTE statreg;
pTimerInterrupt=new TimerInterrupt();
if(pTimerInterrupt!=NULL)
{
if(!pTimerInterrupt->hook())
{
pTimerInterrupt=NULL;
return FALSE;
}
else
{
statreg=ReadRegister(STATREG_B);
statreg|=ENABLE_INTERRUPT;
WriteRegister(STATREG_B,statreg);
ReadRegister(STATREG_C);
pTimerInterrupt->physicalUnmask();
VEvent::initEvents();
}
}
else
{
return FALSE;
}
return TRUE;
}
BOOL TimerDevice::OnSysDynamicDeviceExit()
{
BYTE statreg;
if(VxDEvent!=NULL)
{
VWIN32_CloseVxDHandle(VxDEvent);
VxDEvent=NULL;
}
statreg=ReadRegister(STATREG_B);
statreg&=~ENABLE_INTERRUPT;
WriteRegister(STATREG_B,statreg);
pTimerInterrupt->physicalMask();
if(pTimerInterrupt!=NULL) delete pTimerInterrupt;
return TRUE;
}
DWORD TimerDevice::OnW32DeviceIoControl(PIOCTLPARAMS pDIOCParams)
{
switch(pDIOCParams->dioc_IOCtlCode)
{
case TIMER_DIOC_SET_VXD_EVENT:
VxDEvent=*((HANDLE *)pDIOCParams->dioc_InBuf);
break;
default:
break;
}
return 0;
}
VxD 的基本原理與 VToolsD 2.03 中的 CHIME 例項程式相同,這裡不再詳述,只講述一下 VxD 回撥 Win32 應用程式的實現:
Win32 應用程式有一個主執行緒和一個輔助執行緒,並定義了一個事件(這裡透過 Delphi 5.0 的 TEvent 類定義),主執行緒負責動態載入/解除安裝 VxD ,輔助執行緒透過 ResetEvent 函式和 WaitForSingleObject / WaitForSingleObjectEx 函式暫時掛起(這裡透過 Delphi 5.0 的 TEvent 類的方法實現),但是 Win32 應用程式的事件控制程式碼不能直接被 VWIN32.VXD 提供的 Win32 事件服務使用,必須先透過 KERNEL32.DLL 中的未公開 API —— OpenVxDHandle 函式轉換成 VxD 事件控制程式碼(該函式在 SDK 文件中未公開,但是在 DDK 文件中公開了),然後透過 DeviceIoControl 函式傳遞給 VxD 。 Win32 SDK 沒有為未公開 API 提供標頭檔案和引入庫,但是可以透過 LoadLibrary 函式、 GetProcAddress 函式和 FreeLibrary 函式動態獲取 OpenVxDHandle 函式的入口地址,從而進行間接呼叫(注意! GetProcAddress 函式對於 KERNEL32.DLL 只能透過函式名獲取 API 入口地址,不能透過函式序號獲取 API 入口地址,原因是 公司在 KERNEL32.DLL 中加入了 Anti-ing 程式碼,而且大部分未公開 API 的引出函式名被去掉了,也就是說透過函式名也不能獲取這些未公開 API 的入口地址, OpenVxDHandle 函式是個例外)。 VxD 透過 VWIN32.VXD 提供的 Win32 事件服務中的 _VWIN32_SetWin32Event 服務(在 VToolsD 中是呼叫 VWIN32_SetWin32Event 函式)喚醒輔助執行緒,從而間接實現 VxD 回撥 Win32 應用程式。
該程式當中斷每發生 2000 次(大約 1 — 2 秒)時喚醒輔助執行緒, Win32 應用程式視窗中的計數器的計數值會不斷增大,該程式稍加修改,即可實現精度高達 1ms 的高精度定時。
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/10752043/viewspace-989859/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- JavaScript回撥函式應用淺析JavaScript函式
- VXD技術漫談(1) (轉)
- 大型直播平臺應用架構淺談應用架構
- 用 Java 實現回撥例程 (轉)Java
- 單頁應用SEO淺談(轉載)
- 淺談桌面應用程式的開發
- 應用安全淺談
- 用 Delphi 編寫 VxD 裝置驅動程式(轉) (轉)
- java閉包和回撥淺析Java
- 深入淺出Java回撥機制Java
- 類中靜態回撥函式應用函式
- 在應用程式中實現RAS撥號 (轉)
- 也談 Android 中的回撥Android
- FInClip開放平臺:淺談輕應用的發展
- 淺談webscoket原理及其應用Web
- 單頁應用SEO淺談
- 淺談例外表的應用
- 深入淺出: Java回撥機制(非同步)Java非同步
- 回撥函式快速使用 (轉)函式
- “淺談” Flutter 應用落地心得Flutter
- 淺談混合應用的演進
- 談用Delphi程式獲取撥號連線的動態IP地址 (轉)
- Dash應用瀏覽器端回撥常用方法總結瀏覽器
- 淺談聚合介面的應用場景
- [部落格搬家]淺談Vmware的應用
- 淺談 iOS 應用啟動過程iOS
- [JS]回撥函式和回撥地獄JS函式
- 多執行緒應用–Http請求阻塞回撥處理執行緒HTTP
- 多執行緒應用--Http請求阻塞回撥處理執行緒HTTP
- 觀察者模式在One Order回撥函式中的應用模式函式
- 【趙勳】在ASP.NET應用程式中上傳檔案ASP.NET
- 再談應用程式的例項問題 (轉)
- 淺談應用動態體驗設計
- Node.JS呼叫企業微信API:自建應用的回撥事件Node.jsAPI事件
- 用Kotlin實現極簡回撥Kotlin
- 用VC++6.0編寫撥號程式 (轉)C++
- 淺談移動應用的跨平臺開發工具(Xamarin和React Native)React Native
- 談Delphi程式設計中資原始檔的應用 (轉)程式設計