好的,所以我正在尝试为 Direct X 游戏上的一些额外按钮做一些覆盖。
我在这里找到了一个很好地覆盖的 c++ 示例:http ://www.gamedev.net/topic/359794-c-direct3d-hooking-sample/
所以我开始将它转换为Delphi。通过一些日志记录,我可以看到它启动了正确进程的钩子并正确地钩子 Direct3DCreate9()。
接下来 TMyDirect3D9 创建成功。但是该过程从这里崩溃。
我有根据的猜测(基于 Ollydbg 中的一些调试),当我通过挂钩的 Direct3DCreate9() 将 MyDirect3D9 返回到原始进程并尝试调用其中一个类(接口)函数时,它失败了。
代码如下。如果我可以提供任何其他信息来帮助让我知道。
主DLL:
library LeagueUtilityBox;
{$R *.res}
{$DEFINE DEBUG}
uses
Windows,
APIHijack in 'APIHijack.pas',
Direct3D9 in '..\DirectX 9.0\Direct3D9.pas',
uSharedMem in '..\Misc\uSharedMem.pas',
MyDirect3D9 in 'MyDirect3D9.pas',
MyDirect3DDevice9 in 'MyDirect3DDevice9.pas',
{$IFDEF DEBUG}
SysUtils,
uLog in '..\Misc\uLog.pas',
{$ENDIF}
uMisc in 'uMisc.pas';
var
SharedMem : TSharedMem;
D3DHook: SDLLHook;
hHook : DWORD;
MyDirect3D9 : TMyDirect3D9;
function GetTargetProcess: String;
const
KeyBase : DWORD = HKEY_CURRENT_USER;
KeyLocation : String = 'Software\LeagueUtilityBox';
var
RegKey : HKEY;
TargetProcess : Array[0..511] Of Char;
Count : DWORD;
begin
Result := '';
If RegOpenKeyEx(KeyBase, PChar(KeyLocation), 0, KEY_QUERY_VALUE, RegKey) = ERROR_SUCCESS Then
begin
Count := 512;
If RegQueryValueEx(RegKey, nil, nil, nil, @TargetProcess[0], @Count) = ERROR_SUCCESS Then
begin
Result := String(TargetProcess);
end;
end;
end;
type
TDirect3DCreate9 = function(SDKVersion: LongWord): Pointer; stdcall;
function MyDirect3DCreate9(SDKVersion: LongWord): Pointer; stdcall;
var
OldFunc : TDirect3DCreate9;
D3D : PIDirect3D9;
begin
{$IFDEF DEBUG}
WriteToLog('C:\LeagueUtilityBox.log', 'MyDirect3DCreate9 called');
{$ENDIF}
Result := nil;
OldFunc := TDirect3DCreate9(D3DHook.Functions[0].OrigFn);
D3D := OldFunc(SDKVersion);
If D3D <> nil Then
begin
{$IFDEF DEBUG}
WriteToLog('C:\LeagueUtilityBox.log', 'D3D created: 0x' + IntToHex(DWORD(Pointer(D3D)), 8));
{$ENDIF}
New(MyDirect3D9);
MyDirect3D9 := TMyDirect3D9.Create(D3D);
{$IFDEF DEBUG}
WriteToLog('C:\LeagueUtilityBox.log', 'MyDirect3D9 Created');
{$ENDIF}
Result := @MyDirect3D9;
end;
end;
procedure InitializeHook;
var
Process : String;
I : Integer;
begin
SetLength(Process, 512);
GetModuleFileName(GetModuleHandle(nil), PChar(Process), 512);
For I := Length(Process) DownTo 1 Do
begin
If Process[I] = '\' Then Break;
end;
Process := Copy(Process, I + 1, Length(Process));
If CompareString(LOCALE_SYSTEM_DEFAULT, NORM_IGNORECASE, PChar(GetTargetProcess), -1, PChar(Process), -1) = 2 Then
begin
{$IFDEF DEBUG}
WriteToLog('C:\LeagueUtilityBox.log', 'Found target: ' + GetTargetProcess);
{$ENDIF}
With D3DHook Do
begin
Name := 'D3D9.DLL';
UseDefault := False;
DefaultFn := nil;
SetLength(Functions, 1);
Functions[0].Name := 'Direct3DCreate9';
Functions[0].HookFn := @MyDirect3DCreate9;
Functions[0].OrigFn := nil;
end;
{$IFDEF DEBUG}
WriteToLog('C:\LeagueUtilityBox.log', 'About to hook: ' + String(AnsiString(D3DHook.Name)));
{$ENDIF}
HookAPICalls(@D3DHook);
{$IFDEF DEBUG}
WriteToLog('C:\LeagueUtilityBox.log', 'Hook completed: ' + String(AnsiString(D3DHook.Name)));
{$ENDIF}
end;
end;
procedure InitializeDLL;
begin
SharedMem := TSharedMem.Create('LeagueUtilityBox', 1024);
Try
hHook := PDWORD(SharedMem.Buffer)^;
{$IFDEF DEBUG}
WriteToLog('C:\LeagueUtilityBox.log', 'Initializing DLL: ' + IntToStr(hHook));
{$ENDIF}
Finally
SharedMem.Free;
end;
end;
procedure UninitializeDLL;
begin
UnhookWindowsHookEx(hHook);
end;
function WindowsHookCallback(nCode: Integer; WPARAM: Integer; LPARAM: Integer): LRESULT; stdcall;
begin
Result := CallNextHookEx(hHook, nCode, WPARAM, LPARAM);
end;
procedure EntryPoint(Reason: DWORD);
begin
Case Reason Of
DLL_PROCESS_ATTACH:
begin
InitializeDLL;
InitializeHook;
end;
DLL_PROCESS_DETACH:
begin
UninitializeDLL;
end;
end;
end;
exports
WindowsHookCallback;
begin
DLLProc := @EntryPoint;
EntryPoint(DLL_PROCESS_ATTACH);
end.
自定义 IDirect3D9:
unit MyDirect3D9;
interface
uses Direct3D9, Windows, uMisc, uLog;
type
PMyDirect3D9 = ^TMyDirect3D9;
TMyDirect3D9 = class(TInterfacedObject, IDirect3D9)
private
fD3D: PIDirect3D9;
public
constructor Create(D3D: PIDirect3D9);
function QueryInterface(riid: REFIID; ppvObj: PPointer): HRESULT; stdcall;
function _AddRef: DWORD; stdcall;
function _Release: DWORD; stdcall;
function RegisterSoftwareDevice(pInitializeFunction: Pointer): HResult; stdcall;
function GetAdapterCount: LongWord; stdcall;
function GetAdapterIdentifier(Adapter: LongWord; Flags: DWord; out pIdentifier: TD3DAdapterIdentifier9): HResult; stdcall;
function GetAdapterModeCount(Adapter: LongWord; Format: TD3DFormat): LongWord; stdcall;
function EnumAdapterModes(Adapter: LongWord; Format: TD3DFormat; Mode: LongWord; out pMode: TD3DDisplayMode): HResult; stdcall;
function GetAdapterDisplayMode(Adapter: LongWord; out pMode: TD3DDisplayMode): HResult; stdcall;
function CheckDeviceType(Adapter: LongWord; CheckType: TD3DDevType; AdapterFormat, BackBufferFormat: TD3DFormat; Windowed: BOOL): HResult; stdcall;
function CheckDeviceFormat(Adapter: LongWord; DeviceType: TD3DDevType; AdapterFormat: TD3DFormat; Usage: DWord; RType: TD3DResourceType; CheckFormat: TD3DFormat): HResult; stdcall;
function CheckDeviceMultiSampleType(Adapter: LongWord; DeviceType: TD3DDevType; SurfaceFormat: TD3DFormat; Windowed: BOOL; MultiSampleType: TD3DMultiSampleType; pQualityLevels: PDWORD): HResult; stdcall;
function CheckDepthStencilMatch(Adapter: LongWord; DeviceType: TD3DDevType; AdapterFormat, RenderTargetFormat, DepthStencilFormat: TD3DFormat): HResult; stdcall;
function CheckDeviceFormatConversion(Adapter: LongWord; DeviceType: TD3DDevType; SourceFormat, TargetFormat: TD3DFormat): HResult; stdcall;
function GetDeviceCaps(Adapter: LongWord; DeviceType: TD3DDevType; out pCaps: TD3DCaps9): HResult; stdcall;
function GetAdapterMonitor(Adapter: LongWord): HMONITOR; stdcall;
function CreateDevice(Adapter: LongWord; DeviceType: TD3DDevType; hFocusWindow: HWND; BehaviorFlags: DWord; pPresentationParameters: PD3DPresentParameters; out ppReturnedDeviceInterface: IDirect3DDevice9): HResult; stdcall;
end;
implementation
uses MyDirect3DDevice9;
constructor TMyDirect3D9.Create(D3D: PIDirect3D9);
begin
{$IFDEF DEBUG}
WriteToLog('C:\LeagueUtilityBox.log', 'TMyDirect3D9.Create');
{$ENDIF}
fD3D := D3D;
end;
function TMyDirect3D9.QueryInterface(riid: REFIID; ppvObj: PPointer): HRESULT; stdcall;
begin
{$IFDEF DEBUG}
WriteToLog('C:\LeagueUtilityBox.log', 'TMyDirect3D9.QueryInterface');
{$ENDIF}
Result := fD3D^.QueryInterface(riid, ppvObj);
end;
function TMyDirect3D9._AddRef: DWORD; stdcall;
begin
{$IFDEF DEBUG}
WriteToLog('C:\LeagueUtilityBox.log', 'TMyDirect3D9._AddRef');
{$ENDIF}
Result := fD3D^._AddRef;
end;
function TMyDirect3D9._Release: DWORD; stdcall;
var
count : DWORD;
begin
{$IFDEF DEBUG}
WriteToLog('C:\LeagueUtilityBox.log', 'TMyDirect3D9._Release');
{$ENDIF}
count := fD3D^._Release;
If count = 0 Then
begin
Self.Free;
end;
Result := count;
end;
function TMyDirect3D9.RegisterSoftwareDevice(pInitializeFunction: Pointer): HResult; stdcall;
begin
{$IFDEF DEBUG}
WriteToLog('C:\LeagueUtilityBox.log', 'TMyDirect3D9.RegisterSoftwareDevice');
{$ENDIF}
Result := fD3D^.RegisterSoftwareDevice(pInitializeFunction);
end;
function TMyDirect3D9.GetAdapterCount: LongWord; stdcall;
begin
{$IFDEF DEBUG}
WriteToLog('C:\LeagueUtilityBox.log', 'TMyDirect3D9.GetAdapterCount');
{$ENDIF}
Result := fD3D^.GetAdapterCount;
end;
function TMyDirect3D9.GetAdapterIdentifier(Adapter: LongWord; Flags: DWord; out pIdentifier: TD3DAdapterIdentifier9): HResult; stdcall;
begin
{$IFDEF DEBUG}
WriteToLog('C:\LeagueUtilityBox.log', 'TMyDirect3D9.GetAdapterIdentifier');
{$ENDIF}
Result := fD3D^.GetAdapterIdentifier(Adapter, Flags, pIdentifier);
end;
function TMyDirect3D9.GetAdapterModeCount(Adapter: LongWord; Format: TD3DFormat): LongWord; stdcall;
begin
{$IFDEF DEBUG}
WriteToLog('C:\LeagueUtilityBox.log', 'TMyDirect3D9.GetAdapterModeCount');
{$ENDIF}
Result := fD3D^.GetAdapterModeCount(Adapter, Format);
end;
function TMyDirect3D9.EnumAdapterModes(Adapter: LongWord; Format: TD3DFormat; Mode: LongWord; out pMode: TD3DDisplayMode): HResult; stdcall;
begin
{$IFDEF DEBUG}
WriteToLog('C:\LeagueUtilityBox.log', 'TMyDirect3D9.EnumAdapterModes');
{$ENDIF}
Result := fD3D^.EnumAdapterModes(Adapter, Format, Mode, pMode);
end;
function TMyDirect3D9.GetAdapterDisplayMode(Adapter: LongWord; out pMode: TD3DDisplayMode): HResult; stdcall;
begin
{$IFDEF DEBUG}
WriteToLog('C:\LeagueUtilityBox.log', 'TMyDirect3D9.GetAdapterDisplayMode');
{$ENDIF}
Result := fD3D^.GetAdapterDisplayMode(Adapter, pMode);
end;
function TMyDirect3D9.CheckDeviceType(Adapter: LongWord; CheckType: TD3DDevType; AdapterFormat, BackBufferFormat: TD3DFormat; Windowed: BOOL): HResult; stdcall;
begin
{$IFDEF DEBUG}
WriteToLog('C:\LeagueUtilityBox.log', 'TMyDirect3D9.CheckDeviceType');
{$ENDIF}
Result := fD3D^.CheckDeviceType(Adapter, CheckType, AdapterFormat, BackBufferFormat, Windowed);
end;
function TMyDirect3D9.CheckDeviceFormat(Adapter: LongWord; DeviceType: TD3DDevType; AdapterFormat: TD3DFormat; Usage: DWord; RType: TD3DResourceType; CheckFormat: TD3DFormat): HResult; stdcall;
begin
{$IFDEF DEBUG}
WriteToLog('C:\LeagueUtilityBox.log', 'TMyDirect3D9.CheckDeviceFormat');
{$ENDIF}
Result := fD3D^.CheckDeviceFormat(Adapter, DeviceType, AdapterFormat, Usage, RType, CheckFormat);
end;
function TMyDirect3D9.CheckDeviceMultiSampleType(Adapter: LongWord; DeviceType: TD3DDevType; SurfaceFormat: TD3DFormat; Windowed: BOOL; MultiSampleType: TD3DMultiSampleType; pQualityLevels: PDWORD): HResult; stdcall;
begin
{$IFDEF DEBUG}
WriteToLog('C:\LeagueUtilityBox.log', 'TMyDirect3D9.CheckDeviceMultiSampleType');
{$ENDIF}
Result := fD3D^.CheckDeviceMultiSampleType(Adapter, DeviceType, SurfaceFormat, Windowed, MultiSampleType, pQualityLevels);
end;
function TMyDirect3D9.CheckDepthStencilMatch(Adapter: LongWord; DeviceType: TD3DDevType; AdapterFormat, RenderTargetFormat, DepthStencilFormat: TD3DFormat): HResult; stdcall;
begin
{$IFDEF DEBUG}
WriteToLog('C:\LeagueUtilityBox.log', 'TMyDirect3D9.CheckDepthStencilMatch');
{$ENDIF}
Result := fD3D^.CheckDepthStencilMatch(Adapter, DeviceType, AdapterFormat, RenderTargetFormat, DepthStencilFormat);
end;
function TMyDirect3D9.CheckDeviceFormatConversion(Adapter: LongWord; DeviceType: TD3DDevType; SourceFormat, TargetFormat: TD3DFormat): HResult; stdcall;
begin
{$IFDEF DEBUG}
WriteToLog('C:\LeagueUtilityBox.log', 'TMyDirect3D9.CheckDeviceFormatConversion');
{$ENDIF}
Result := fD3D^.CheckDeviceFormatConversion(Adapter, DeviceType, SourceFormat, TargetFormat);
end;
function TMyDirect3D9.GetDeviceCaps(Adapter: LongWord; DeviceType: TD3DDevType; out pCaps: TD3DCaps9): HResult; stdcall;
begin
{$IFDEF DEBUG}
WriteToLog('C:\LeagueUtilityBox.log', 'TMyDirect3D9.GetDeviceCaps');
{$ENDIF}
Result := fD3D^.GetDeviceCaps(Adapter, DeviceType, pCaps);
end;
function TMyDirect3D9.GetAdapterMonitor(Adapter: LongWord): HMONITOR; stdcall;
begin
{$IFDEF DEBUG}
WriteToLog('C:\LeagueUtilityBox.log', 'TMyDirect3D9.GetAdapterMonitor');
{$ENDIF}
Result := fD3D^.GetAdapterMonitor(Adapter);
end;
function TMyDirect3D9.CreateDevice(Adapter: LongWord; DeviceType: TD3DDevType; hFocusWindow: HWND; BehaviorFlags: DWord; pPresentationParameters: PD3DPresentParameters; out ppReturnedDeviceInterface: IDirect3DDevice9): HResult; stdcall;
var
hr : HRESULT;
begin
{$IFDEF DEBUG}
WriteToLog('C:\LeagueUtilityBox.log', 'TMyDirect3D9.CreateDevice');
{$ENDIF}
hr := fD3D^.CreateDevice(Adapter, DeviceType, hFocusWindow, BehaviorFlags, pPresentationParameters, ppReturnedDeviceInterface);
If Succeeded(hr) Then
begin
{$IFDEF DEBUG}
WriteToLog('C:\LeagueUtilityBox.log', 'fD3D^.CreateDevice Succeeded');
{$ENDIF}
ppReturnedDeviceInterface := TMyDirect3DDevice9.Create(PIDirect3D9(@Self), @ppReturnedDeviceInterface);
end;
Result := hr;
end;
end.
更新: 因此,由于 Delphi 接口的行为似乎与真正的接口不同(Delphi 有一个介于两者之间的中间,以便它成功地与其他接口对话)。所以我只是将接口转换为指针数组。
现在程序成功调用了 CreateDevice()。我可以在日志中看到这一点,也可以在 Ollydbg 中单步执行。
现在发生的情况是,当 CreateDevice 调用原始的 IDirect3D9.CreateDevice() 时,它再次崩溃。当我在 Ollydbg 中调试时,我注意到它对指针的取消引用太多了。
更新 2: 好的,修复了 PIDirect3D9 与 IDirect3D9 在不同位置的一些指针问题。所以原来的 IDirect3D9.CreateDevice() 被调用。但它与 D3DERR_INVALIDCALL 错误!
如此混乱。
更新3: 好的,经过更多调试,似乎当我调用该函数时,一个额外的参数被推入堆栈。这使得第一个参数无效。DirectX Debugging 进一步证明了这一点,它说 iAdapter 参数无效(第一个参数)。
更新 4: 使用 IntRefToMethPtr() 来获取指向原始 CreateDevice 调用的直接指针,我能够让它与堆栈相同。结果相同。看起来我在尝试将它挂在 Delphi 中时走错了路。
更新 5: 重写了挂钩方法。现在我只是在本质上挂钩 EndScene()。Hook 现在可以在测试程序中正常工作(Vertices.exe 随附于本文第一个 URL 中的 hook 演示)。但是在主游戏中它会导致游戏崩溃。无论哪种方式,我都学到了很多东西。