//hook 键盘钩子 import win; import key; var SetWindowsHookEx = ::User32.api("SetWindowsHookExW","pointer(int idHook,pointer lpfn, pointer hmod,INT dwThreadId)"); var CallNextHookEx = ::User32.api("CallNextHookEx","int( POINTER hhk,int nCode,INT wParam,int lParam)"); var UnhookWindowsHookEx = ::User32.api("UnhookWindowsHookEx","int(pointer hhk)"); var WH_KEYBOARD_LL = 13 var KBDLLHOOKSTRUCT = class{ INT vkCode; INT scanCode; INT flags; INT time; INT extraInfo; } var LowLevelKeybdProc = function( nCode, wParam, lParam){ if ( nCode == 0/*_HC_ACTION*/ ) { var ll = KBDLLHOOKSTRUCT(); ll = ..raw.convert(topointer(lParam),ll ) if( ( type(owner.proc) == type.function) && (owner.proc(wParam,ll.vkCode,ll.scanCode ,ll.flags & 0x10/*_LLKHF_INJECTED*/, ll.flags,ll.time,ll.extraInfo) ) ){ return 1; } } return CallNextHookEx(owner._hhk, nCode, wParam, lParam); } namespace key class hook{ ctor(){ this._proc_c = ..raw.tostdcall( LowLevelKeybdProc,"int(int nCode, INT wParam, int lParam)",this) this.hhk = SetWindowsHookEx(WH_KEYBOARD_LL,this._proc_c,_HINSTANSE,0); this._hhk = this.hhk; ..table.gc(this,"close"); } close = function(){ if(this.hhk){ UnhookWindowsHookEx(this.hhk); this.hhk = null; } } mapKey = function(map,methodOwner,hwnd){ var down={}; var up={}; for(k,v in map.down){ down[..key.getCode(k)] = methodOwner[v]; } for(k,v in map.up){ up[..key.getCode(k)] = methodOwner[v]; } this.proc = function(msg,vkcode,scancode,injected,flags,timeStamp,extraInfo){ if( injected ) return; if( hwnd && hwnd != ..win.getForeground() ) { return; } select(msg) { case 0x100/*_WM_KEYDOWN*/ ,0x104/*_WM_SYSKEYDOWN*/ { var method = down[vkcode]; if(method){ method(); return true; } } case 0x101/*_WM_KEYUP*/,0x105/*_WM_SYSKEYUP*/ { var method = up[vkcode]; if(method){ method(); return true; } } } } } } /**details(重要说明) 低级键盘钩子只能用于界面线程,并且依赖界面线程创建的消息循环。 低级键盘钩子所在界面线程的任何耗时操作执行时间不应超过 200 毫秒。 耗时操作可能导致遗漏部分键盘消息,如果耗时操作阻塞键盘钩子消息超过一秒或超过注册表限制的更小时间, 系统可能会直接删除键盘钩子(导致超级热键不可用)。 建议将阻塞消息循环的耗时操作放到后台工作线程内执行, 请参考:[创建多线程](../../../guide/language/thread.md)。 勿滥用低级键盘钩子, 普通快捷键不必要使用此钩子,可参考: aardio 范例 » Windows 窗口 » 快捷键。 end details**/ /**intellisense() key.hook = 低级键盘钩子。\n\n低级键盘钩子只能用于包含窗口消息循环的界面线程。\n键盘钩子所在的界面线程如果有阻塞钩子消息的耗时操作,可能导致遗漏键盘消息。\n耗时操作阻塞键盘钩子消息超过一秒或注册表限制的更小时间,系统可能导致钩子失效。\n建议将阻塞消息循环的耗时操作应当放到后台工作线程内执行。 key.hook() = 创建低级键盘钩子\n!stdKeyHook. !stdKeyHook.proc = @.proc = function(msg,vkCode,scanCode,injected,flags,timeStamp,extraInfo){ if( injected ) return;//忽略模拟按键 var vkName = key.getName( vkCode ); select(msg) { case 0x100/*_WM_KEYDOWN*/ ,0x104/*_WM_SYSKEYDOWN*/{ io.print("按下",vkName) } case 0x101/*_WM_KEYUP*/,0x105/*_WM_SYSKEYUP*/{ io.print("弹起",vkName) } } __/*取消按键调用 return true;\n注意钩子回调函数不要做耗时操作。\n耗时超过一秒或超过注册表限制的更小时间,\n系统会直接删除钩子,没有任何方法可以检查到删除操作。\n可用 win.setTimeout 或创建线程执行耗时操作*/ } !stdKeyHook.mapKey( = 创建一个映射按键的 proc 。\n用法请参考:范例/自动化/adb/安卓投屏 !stdKeyHook.mapKey(.(map,owner,hwnd) = 参数 @map 可在 down 字段指定按键映射表,在 up 字段指定弹起键映射表。\n按键映射表的键应当为 key.VK 定义的虚拟键名,值为 @owner 的成员函数名。\n可选用 @hwnd 限定映射按键的窗口句柄 !stdKeyHook.close() = 释放按键录制钩子 _LLKHF_EXTENDED=@1/*_LLKHF_EXTENDED*/ _LLKHF_INJECTED=@0x10/*_LLKHF_INJECTED*/ _LLKHF_ALTDOWN=@0x20/*_LLKHF_ALTDOWN*/ _LLKHF_UP=@0x80/*_LLKHF_UP*/ end intellisense**/