| 赞 | 14 |
| VIP | |
| 好人卡 | |
| 积分 | 18 |
| 经验 | |
| 最后登录 | 2022-6-5 |
| 在线时间 | 133 小时 |
- 梦石
- 0
- 星屑
- 1803
- 在线时间
- 133 小时
- 注册时间
- 2013-10-6
- 回帖
- 181
|
发表于 2018-1-13 23:52:53
|
显示全部楼层
本帖最后由 不死鸟之翼 于 2018-1-13 23:54 编辑
这是一个典型的脚本语言和原生代码的互操作问题,回调参数一定需要是原生代码。
因为Win32 API层是C/C++的天下,所以我们用C/C++处理这类问题显然比手写机器指令舒服得多(我特么也脑仁儿疼啊)。
主要思路是Ruby到C++用Win32API call通信,回来就用RGSSEval。
大概写了一段,编译成DLL并导出Inject函数即可。
[pre lang="ruby" line="1"]using RGSSEval_t = int(__cdecl*)(const char*);
RGSSEval_t RGSSEval = nullptr;
WNDPROC OldWindowProc = nullptr;
string rubyCallbackFunc;
LRESULT CALLBACK CustomWindowsProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg) {
case WM_MOUSEWHEEL: {
int zDelta = GET_WHEEL_DELTA_WPARAM(wParam);
int xPos = GET_X_LPARAM(lParam);
int yPos = GET_Y_LPARAM(lParam);
int fwKeys = GET_KEYSTATE_WPARAM(wParam);
POINT clientPt = { xPos,yPos };
ScreenToClient(hWnd, &clientPt);
stringstream expr;
expr << rubyCallbackFunc << "(" << zDelta << "," << clientPt.x << "," << clientPt.y << "," << fwKeys << ")";
RGSSEval(expr.str().c_str());
return CallWindowProcA(OldWindowProc, hWnd, uMsg, wParam, lParam);
}
default:
return CallWindowProcA(OldWindowProc, hWnd, uMsg, wParam, lParam);
}
}
INT WINAPI Inject(const char* callbackName)
{
HMODULE hModule=GetModuleHandleA("RGSS202E.dll"); //Change this if necessary
if (hModule) {
RGSSEval = (RGSSEval_t)GetProcAddress(hModule, "RGSSEval");
if (RGSSEval) {
HWND hWnd = GetActiveWindow();
if (hWnd) {
OldWindowProc = (WNDPROC)SetWindowLongA(hWnd, GWL_WNDPROC, (LONG)CustomWindowsProc);
rubyCallbackFunc = callbackName;
return TRUE;
}
}
}
return FALSE;
}[/pre]
Ruby侧提供回调函数
[pre lang="ruby" line="1"]def on_mouse_wheel(zDelta,xPos,yPos,fwKeys)
p "delta=#{zDelta} mouse_x=#{xPos} mouse_y=#{yPos} modifiers=#{fwKeys}"
end
fun=Win32API.new('WndProc.dll','Inject','p','i')
if fun.call('on_mouse_wheel')==0
p 'Failed'
end[/pre]
fwKeys当特定键按下时特定位为1,详情参考MSDN。
效果图
如果需要处理其他消息的话在窗口过程里增加即可。
因为窗口过程是在主消息循环中分发消息时调用的,所以不会有线程同步问题。 |
|