module Kernel
class << self
GetWindowLong = Win32API.new("user32", "GetWindowLong", "ii", "i")
SetWindowLong = Win32API.new("user32", "SetWindowLong", "iii", "i")
CallWindowProc = Win32API.new("user32", "CallWindowProc", "iiiii", "i")
def hwnd
Win32API.new("user32", "GetActiveWindow", "", "i").call
end
def wm_proc(hwnd, msg, wparam, lparam)
if msg == 522 # WM_MOUSEWHEEL
p 1
else
CallWindowProc.call(@old_proc_addr, hwnd, msg, wparam, lparam)
end
end
def handle
@old_proc_addr = GetWindowLong.call(hwnd, -4)
@new_proc_addr = method(:wm_proc).object_id * 2
SetWindowLong.call(hwnd, -4, @new_proc_addr)
end
def unhandle
SetWindowLong.call(hwnd, -4, @old_proc_addr)
end
end
end
Kernel.handle
module Kernel
class << self
GetWindowLong = Win32API.new("user32", "GetWindowLong", "ii", "i")
SetWindowLong = Win32API.new("user32", "SetWindowLong", "iii", "i")
CallWindowProc = Win32API.new("user32", "CallWindowProc", "iiiii", "i")
def hwnd
Win32API.new("user32", "GetActiveWindow", "", "i").call
end
def wm_proc(hwnd, msg, wparam, lparam)
if msg == 522 # WM_MOUSEWHEEL
p 1
else
CallWindowProc.call(@old_proc_addr, hwnd, msg, wparam, lparam)
end
end
def handle
@old_proc_addr = GetWindowLong.call(hwnd, -4)
@new_proc_addr = method(:wm_proc).object_id * 2
SetWindowLong.call(hwnd, -4, @new_proc_addr)
end
def unhandle
SetWindowLong.call(hwnd, -4, @old_proc_addr)
end
end
end
Kernel.handle
这是一个典型的脚本语言和原生代码的互操作问题,回调参数一定需要是原生代码。
因为Win32 API层是C/C++的天下,所以我们用C/C++处理这类问题显然比手写机器指令舒服得多(我特么也脑仁儿疼啊)。
主要思路是Ruby到C++用Win32API call通信,回来就用RGSSEval。
大概写了一段,编译成DLL并导出Inject函数即可。
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;
}
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;
}
Ruby侧提供回调函数
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
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
fwKeys当特定键按下时特定位为1,详情参考MSDN。
效果图
如果需要处理其他消息的话在窗口过程里增加即可。
因为窗口过程是在主消息循环中分发消息时调用的,所以不会有线程同步问题。