本帖最后由 十二 于 2011-4-21 10:48 编辑
FXTZ的控制流程被包含在DirectInput中。虽然IAT中没有DirectInputCreate这样类似的助手函数(大部分程序IAT中都会导入d3d9.dll、dinput.dll其中会导入DirectInputCreate、Direct3DCreate9之类的助手函数。)方便返回接口的指针来方便调用。
以前一直以为所有的windows程序都是用windows消息循环来执行call back function来实现自定义的逻辑,直到主席提到dxinput,才改变了我的看法。
windows是以API为基础用消息来驱动的。但是消息并不只是WM_规范的这些,消息很广义可以为一个信号,也可以是一个值,这对程序流程而言都可以看做一个消息。并非只指WIN规范WM_消息。
从消息队列取走消息然后处理,效率是相当低下的所以DXinput出现了。
以上全是废话,下面进入正题。
FXTZ的IAT中并没有导入dinput.dll和其助手函数DirectInputCreate来得到IDirectInput接口的指针,(吐槽那你怎么知道用了dxinput)。
dx组件的调用并非必须用助手函数来得到接口指针,dx组件是基于COM的详见,http://bbs.nyasama.com/forum.php?mod=viewthread&tid=1887&extra=page%3D1
如果拥有组件的CLSID和IID可以通过CoCreateInstance函数来得到IDirectInput接口,很巧FXTZ就用的这种方法。
F3载入th123.exe
bp CoCreateInstance
栈信息如下:

0012FB30处是CLSID可在注册表中找到。
0012FB3C处是IID可在 http://bbs.nyasama.com/forum.php?mod=viewthread&tid=1887&extra=page%3D1 此贴10L中的附件中找到
0012FB40处记录的指针0088D17C中就是IDirectInput8W接口的指针了。
Alt+F9返回。
- 0040D20B |. 85C0 TEST EAX,EAX
- 0040D20D |. 7D 1E JGE SHORT th123_汉.0040D22D
- 0040D20F |. 8B0D 78D18800 MOV ECX,DWORD PTR DS:[88D178]
- 0040D215 |. 6A 00 PUSH 0 ; /Style = MB_OK|MB_APPLMODAL
- 0040D217 |. 68 D0E38500 PUSH th123_汉.0085E3D0 ; |Title = "DInput-Error"
- 0040D21C |. 68 80E38500 PUSH th123_汉.0085E380 ; |Text = "DirectInput对象创建失败#0"
- 0040D221 |. 51 PUSH ECX ; |hOwner = 002EAA64
- 0040D222 |. FF15 4C628400 CALL DWORD PTR DS:[<&USER32.MessageBoxA>>; \MessageBoxA
- 0040D228 |. 32C0 XOR AL,AL
- 0040D22A |. C2 0800 RETN 8
- 0040D22D |> A1 7CD18800 MOV EAX,DWORD PTR DS:[88D17C]
- 0040D232 |. 8B4C24 08 MOV ECX,DWORD PTR SS:[ESP+8] ; th123_汉.00400000
- 0040D236 |. 8B10 MOV EDX,DWORD PTR DS:[EAX]
- 0040D238 |. 8B52 1C MOV EDX,DWORD PTR DS:[EDX+1C]
- 0040D23B |. 68 00080000 PUSH 800
- 0040D240 |. 51 PUSH ECX
- 0040D241 |. 50 PUSH EAX
- 0040D242 |. FFD2 CALL EDX
- 0040D244 |. 85C0 TEST EAX,EAX
- 0040D246 |. 7D 1D JGE SHORT th123_汉.0040D265
- 0040D248 |. A1 78D18800 MOV EAX,DWORD PTR DS:[88D178]
- 0040D24D |. 6A 00 PUSH 0 ; /Style = MB_OK|MB_APPLMODAL
- 0040D24F |. 68 D0E38500 PUSH th123_汉.0085E3D0 ; |Title = "DInput-Error"
- 0040D254 |. 68 A8E38500 PUSH th123_汉.0085E3A8 ; |Text = "DirectInput对象创建失败#1"
- 0040D259 |. 50 PUSH EAX ; |hOwner = NULL
- 0040D25A |. FF15 4C628400 CALL DWORD PTR DS:[<&USER32.MessageBoxA>>; \MessageBoxA
- 0040D260 |. 32C0 XOR AL,AL
- 0040D262 |. C2 0800 RETN 8
- 0040D265 |> B0 01 MOV AL,1
- 0040D267 \. C2 0800 RETN 8
复制代码
可以看到会出现判断EAX的值来确定是否获得IDirectInput8接口指针。
0088D17C 中也返回了 IDirectInput8 的接口指针,右键数据跟随00126AA1C。

来到这里

再跟随一次。

这里就是 IDirectInput8接口下的方法了对照http://bbs.nyasama.com/forum.php?mod=viewthread&tid=1887&extra=page%3D1此贴10L中的附件
DECLARE_INTERFACE_(IDirectInput8W, IUnknown)
{
/*** IUnknown methods ***/
STDMETHOD(QueryInterface)(THIS_ REFIID riid, LPVOID * ppvObj) PURE;
STDMETHOD_(ULONG,AddRef)(THIS) PURE;
STDMETHOD_(ULONG,Release)(THIS) PURE;
/*** IDirectInput8W methods ***/
STDMETHOD(CreateDevice)(THIS_ REFGUID,LPDIRECTINPUTDEVICE8W *,LPUNKNOWN) PURE;
STDMETHOD(EnumDevices)(THIS_ DWORD,LPDIENUMDEVICESCALLBACKW,LPVOID,DWORD) PURE;
STDMETHOD(GetDeviceStatus)(THIS_ REFGUID) PURE;
STDMETHOD(RunControlPanel)(THIS_ HWND,DWORD) PURE;
STDMETHOD(Initialize)(THIS_ HINSTANCE,DWORD) PURE;
STDMETHOD(FindDevice)(THIS_ REFGUID,LPCWSTR,LPGUID) PURE;
STDMETHOD(EnumDevicesBySemantics)(THIS_ LPCWSTR,LPDIACTIONFORMATW,LPDIENUMDEVICESBYSEMANTICSCBW,LPVOID,DWORD) PURE;
STDMETHOD(ConfigureDevices)(THIS_ LPDICONFIGUREDEVICESCALLBACK,LPDICONFIGUREDEVICESPARAMSW,DWORD,LPVOID) PURE;
};
和上面的地址指针的顺序是一样的。
判断得到IDirectInput8后会调用Initialize进行初始化。然后会调用一个重要的方法。CreateDevice来得到LPDIRECTINPUTDEVICE8的接口指针。调试的时候别数错了同上一样跟踪。
顺道就全部贴出来了。
DECLARE_INTERFACE_(IDirectInputDevice8W, IUnknown)
{
/*** IUnknown methods ***/
STDMETHOD(QueryInterface)(THIS_ REFIID riid, LPVOID * ppvObj) PURE;
STDMETHOD_(ULONG,AddRef)(THIS) PURE;
STDMETHOD_(ULONG,Release)(THIS) PURE;
/*** IDirectInputDevice8W methods ***/
STDMETHOD(GetCapabilities)(THIS_ LPDIDEVCAPS) PURE;
STDMETHOD(EnumObjects)(THIS_ LPDIENUMDEVICEOBJECTSCALLBACKW,LPVOID,DWORD) PURE;
STDMETHOD(GetProperty)(THIS_ REFGUID,LPDIPROPHEADER) PURE;
STDMETHOD(SetProperty)(THIS_ REFGUID,LPCDIPROPHEADER) PURE;
STDMETHOD(Acquire)(THIS) PURE;
STDMETHOD(Unacquire)(THIS) PURE;
STDMETHOD(GetDeviceState)(THIS_ DWORD,LPVOID) PURE;
STDMETHOD(GetDeviceData)(THIS_ DWORD,LPDIDEVICEOBJECTDATA,LPDWORD,DWORD) PURE;
STDMETHOD(SetDataFormat)(THIS_ LPCDIDATAFORMAT) PURE;
STDMETHOD(SetEventNotification)(THIS_ HANDLE) PURE;
STDMETHOD(SetCooperativeLevel)(THIS_ HWND,DWORD) PURE;
STDMETHOD(GetObjectInfo)(THIS_ LPDIDEVICEOBJECTINSTANCEW,DWORD,DWORD) PURE;
STDMETHOD(GetDeviceInfo)(THIS_ LPDIDEVICEINSTANCEW) PURE;
STDMETHOD(RunControlPanel)(THIS_ HWND,DWORD) PURE;
STDMETHOD(Initialize)(THIS_ HINSTANCE,DWORD,REFGUID) PURE;
STDMETHOD(CreateEffect)(THIS_ REFGUID,LPCDIEFFECT,LPDIRECTINPUTEFFECT *,LPUNKNOWN) PURE;
STDMETHOD(EnumEffects)(THIS_ LPDIENUMEFFECTSCALLBACKW,LPVOID,DWORD) PURE;
STDMETHOD(GetEffectInfo)(THIS_ LPDIEFFECTINFOW,REFGUID) PURE;
STDMETHOD(GetForceFeedbackState)(THIS_ LPDWORD) PURE;
STDMETHOD(SendForceFeedbackCommand)(THIS_ DWORD) PURE;
STDMETHOD(EnumCreatedEffectObjects)(THIS_ LPDIENUMCREATEDEFFECTOBJECTSCALLBACK,LPVOID,DWORD) PURE;
STDMETHOD(Escape)(THIS_ LPDIEFFESCAPE) PURE;
STDMETHOD(Poll)(THIS) PURE;
STDMETHOD(SendDeviceData)(THIS_ DWORD,LPCDIDEVICEOBJECTDATA,LPDWORD,DWORD) PURE;
STDMETHOD(EnumEffectsInFile)(THIS_ LPCWSTR,LPDIENUMEFFECTSINFILECALLBACK,LPVOID,DWORD) PURE;
STDMETHOD(WriteEffectToFile)(THIS_ LPCWSTR,DWORD,LPDIFILEEFFECT,DWORD) PURE;
STDMETHOD(BuildActionMap)(THIS_ LPDIACTIONFORMATW,LPCWSTR,DWORD) PURE;
STDMETHOD(SetActionMap)(THIS_ LPDIACTIONFORMATW,LPCWSTR,DWORD) PURE;
STDMETHOD(GetImageInfo)(THIS_ LPDIDEVICEIMAGEINFOHEADERW) PURE;
};
可以看到这下面有很多方法,想折腾的人可以去MSDN找原型,或者去百度找找DXinput的具体流程,这里不熬述。
这里有两个重要的方法GetDeviceState,GetDeviceData。这两个函数是把键盘数据送给缓冲区然后做为消息让FXTZ实现键盘按键后的游戏逻辑。
FXTZ没有用GetDeviceData,因为格斗游戏要求即使操作,所以用GetDeviceState来得到即时缓冲。
断到GetDeviceState方法内部。
|