让非阻塞线程永久执行 "nothing" 的最佳方法是什么?
What's the best way to have a non-blocked thread permanently doing "nothing"?
我正在使用低级挂钩。
我做了这个 class:
class Kayz {
static int VKEY;
static void (*funcDown)();
static void (*funcUp)();
static HHOOK TheHook;
static KBDLLHOOKSTRUCT TheHookStruct;
static LRESULT _stdcall HookCallback(int, WPARAM, LPARAM);
public:
bool SetHook(int VKey, void(*FunctionDown)(), void(*FunctionUp)()) {
if (VKey < 0x07) {
if (!(TheHook = SetWindowsHookEx(WH_MOUSE_LL, &HookCallback, NULL, 0))) {
return false;
}
}
else if(VKey > 0x07){
if (!(TheHook = SetWindowsHookEx(WH_KEYBOARD_LL, &HookCallback, NULL, 0))) {
return false;
}
}
VKEY = VKey; funcDown = FunctionDown; funcUp = FunctionUp;
return true;
}
void UnSetHook() {
UnhookWindowsHookEx(TheHook);
}
};
int Kayz::VKEY;
void(*Kayz::funcDown)();
void(*Kayz::funcUp)();
HHOOK Kayz::TheHook;
KBDLLHOOKSTRUCT Kayz::TheHookStruct;
LRESULT _stdcall Kayz::HookCallback(int nCode, WPARAM wParam, LPARAM lParam) {
if (nCode >= 0) {
if (wParam == WM_KEYDOWN) {
TheHookStruct = *((KBDLLHOOKSTRUCT*)lParam);
if (TheHookStruct.vkCode == VKEY) {
(*funcDown)();
}
}
else if (wParam == WM_KEYUP)
{
TheHookStruct = *((KBDLLHOOKSTRUCT*)lParam);
if (TheHookStruct.vkCode == VKEY) {
(*funcUp)();
}
}
}
return CallNextHookEx(TheHook, nCode, wParam, lParam);
}
我放入 SetHook 的所有功能都是在主程序中更改一个 bool 变量,这样我就可以知道是否按下了键。在我看来,这是最佳方式,因为我不必每次在主程序中循环时都检查密钥的状态。
现在。
在主程序中使用阻塞定时器如Sleep()会阻塞程序,包括
return CallNextHookEx(TheHook, nCode, wParam, lParam);
这意味着,由于这是一个低级挂钩,所有其他程序只会在睡眠结束时获得输入。所以如果我在记事本中按下一个键,它只会在睡眠结束并且程序再次循环时被输入,如果我输入很多,它们很可能一次被输入一个。
我见过的唯一能够"bypass"这是
while(GetMessage(&msgVar, NULL, 0, 0)){}
GetMessage 从不或很少 returns,因此它不占用任何系统资源或处理能力。它不会阻塞,因为 while 正在等待它到达 return。所以基本上,它什么也没做,但也没有阻塞。
我需要一个线程来做类似的事情。该线程将接收按键 "events" 并执行更改主程序中变量的函数。
但这很脏。我不喜欢脏。
所以我很想知道:
我怎样才能以干净的方式实现无阻塞,消耗最少的资源?
谢谢。
编辑:
正如您所问:我正在制作一个记忆瞄准机器人,完全用于学习目的。
我现在花了相当多的时间阅读有关 MsgWaitForMultipleObjectsEx 的内容,显然您可以将前 2 个参数设为 null,这会派上用场。
我也在考虑以错误的方式做这件事,我打算为程序创建一个线程 "hold" 并且仍然从钩子接收异步输入(这就是我不想要的原因它会阻塞),然后另一个(总是-运行)线程将根据钩子调用的函数将改变的布尔值工作。
我现在意识到这是一个相当糟糕的设计,所以我正在考虑在主程序中使用 MsgWaitForMultipleObjectsEx,并用它检查 bool,如果需要暂停或恢复 aimbot 线程。
我现在开始理解@HarryJohnston 所说的意大利面条逻辑,因为我必须组织异步挂钩函数的作用与 MsgWaitForMultipleObjectsEx 之后的代码的作用,这些看起来有些困难决定。
我想跟随这些钩子并全面了解这一切是如何工作的,这就是为什么我不会立即使用原始输入的原因,不过谢谢@nikau6 告诉我这件事,我'当我完成 hooks 时,一定会研究它。
再次感谢大家
你需要在循环中使用 MsgWaitForMultipleObjectsEx
这对你来说是最强大的功能。有了这个,您将等待 windows(和挂钩)消息,对于多个事件(最多 63 个),您还可以接收用户模式 APC
调用并定期(通过超时执行相同的任务)。示例:
void ZApp::Run()
{
for (;;)
{
HANDLE* pHandles;
DWORD nCount = GetWaitHandles(&pHandles);
DWORD r = MsgWaitForMultipleObjectsEx(nCount, pHandles, GetTimeout(), QS_ALLINPUT, MWMO_ALERTABLE);
if (r < nCount)
{
OnSignalObject(r);
continue;
}
if (r == nCount)
{
BOOL bIdle = FALSE;
MSG msg;
while (PeekMessage(&msg, 0, 0, 0, PM_REMOVE))
{
if (!bIdle)
{
bIdle = IsIdleMessage(msg.message);
}
if (PreTranslateMessage(&msg)) continue;
if (msg.message == WM_QUIT)
{
return ;
}
if (!IsDialogMessageEx(&msg))
{
if (msg.message - WM_KEYFIRST <= WM_KEYLAST - WM_KEYFIRST)
{
TranslateMessage(&msg);
}
DispatchMessage(&msg);
}
}
if (bIdle)
{
OnIdle();
}
continue;
}
if (r - WAIT_ABANDONED_0 < nCount)
{
OnAbandonedObject(r - WAIT_ABANDONED_0);
continue;
}
switch(r)
{
case WAIT_TIMEOUT:
OnTimeout();
break;
case WAIT_IO_COMPLETION:
OnApcAlert();
break;
default: __debugbreak();
}
}
}
"It seems to me that it's the most optimal way because I don't have to check for the key's state every time I loop in the main program."
有一种比 hooks 更好的方法,但并不为人所知,它可以监视所有系统上的键盘事件。这是Raw Input。
使用原始输入,您的应用程序可以直接从 HID(人机设备接口)驱动程序获知每个键盘、鼠标等事件。这比钩子更有效,而且使用起来非常简单。您的应用程序不需要从 DLL 导出过程,并且由于原始输入不是挂钩,因此在处理消息后,无需将消息传递给另一个过程、另一个线程。 (请参阅下面关于 DefRawInputProc 程序的评论之一)。应用程序通过 WM_INPUT 消息获取原始输入。与钩子不同,必须创建一个 window,这是一项义务,需要一个句柄。
以下是我使用原始输入的方式:
编辑:你不会遇到关于非阻塞线程的问题。
#include <Windows.h>
#define HID_ISMOUSE(x) ((x).header.dwType == RIM_MOUSE)
#define HID_ISKEYBOARD(x) ((x).header.dwType == RIM_TYPEKEYBOARD)
#define HID_SCODE(x) ((x).data.keyboard.MakeCode) // scan code
#define HID_VKEY(x) ((x).data.keyboard.VKey) // virtual key code
#define HID_WMSG(x) ((x).data.keyboard.Message) // corresponding window message, WM_KEYDOWN, WM_SYSKEYDOWN, WM_KEYUP, WM_SYSKEYUP.
#define HID_ISKEYUP(x) ((x).data.keyboard.Flags & RI_KEY_BREAK)
#define HID_ISKEYDOWN(x) (((x).data.keyboard.Flags & 0x01) == RI_KEY_MAKE)
#define RAWINPUT_ERROR (UINT)-1
namespace HID
{
const USHORT MOUSE = 2;
const USHORT KEYBOARD = 6;
// Register a raw input device
bool RegisterDevice(HWND hTarget, USHORT usage)
{
RAWINPUTDEVICE hid;
hid.usUsagePage = 1; // generic desktop page
hid.usUsage = usage; // device id
hid.hwndTarget = hTarget; // window handle
hid.dwFlags = RIDEV_NOLEGACY | RIDEV_INPUTSINK ; // RIDEV_INPUTSINK to monitor all the system, RIDEV_NOLEGACY if you don't want legacy keyboard events.
return !!RegisterRawInputDevices(&hid, 1, sizeof(RAWINPUTDEVICE));
}
// Unregister a raw input device.
void UnregisterDevice(USHORT usage)
{
RAWINPUTDEVICE hid;
hid.usUsagePage = 1;
hid.usUsage = usage;
hid.dwFlags = RIDEV_REMOVE; // RIDEV_REMOVE to remove a device.
hid.hwndTarget = NULL; // NULL to remove a device.
RegisterRawInputDevices(&hid, 1, sizeof(RAWINPUTDEVICE));
}
// Get raw input data
bool GetInputData(HRAWINPUT hInput, RAWINPUT* RawInput)
{
UINT size = sizeof(RAWINPUT); // size = 40
if( GetRawInputData((HRAWINPUT)hInput, RID_INPUT, RawInput, &size, sizeof(RAWINPUTHEADER)) != RAWINPUT_ERROR )
return true;
else
return false;
}
}
int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE, LPWSTR cmd_line, int cmd_show)
{
WNDCLASSW wc = {0};
wc.lpfnWndProc = WindowProc;
...
HWND hwnd = ::CreateWindowW(...);
...
HID::RegisterDevice(hwnd, HID::KEYBOARD);
MSG msg;
while(GetMessageW(&msg, NULL, 0, 0))
{
DispatchMessageW(&msg);
}
HID::UnregisterDevice(HID::KEYBOARD);
return (int)msg.wParam;
}
LRESULT CALLBACK WindowProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
if(msg == WM_INPUT) // Raw input message.
{
RAWINPUT Input;
if(HID::GetInputData((HRAWINPUT)lParam, &Input))
{
if(HID_ISKEYBOARD(Input))
{
if(HID_ISKEYUP(Input))
{
return 0;
}
else // if(HID_ISKEYDOWN(Input))
{
return 0;
}
}
}
}
return ::DefWindowProc(hWnd, msg, wParam, lParam);
}
我已经意识到,在等待挂钩执行其他函数时永久拥有一个线程 "on hold" 只是一种糟糕的方式来实现我正在寻找的东西,您应该始终让每个线程都做某事。如果您遵循相同的路径,我建议您放弃它并以不需要这些的方式组织您的代码 "loose ends".
谢谢大家。主要是@RbMm who informed me of MsgWaitForMultipleObjectsEx and guided me through it, and @nikau6 who informed about RawInput,以后会用到
我还完成了 class 并包含了一个函数 returns 当你的键被按下或释放时(当 MsgWaitForMultipleObjectsEx returns 除了 WAIT_OBJECT_0),我想我会 post 它在这里以防万一有人需要它,因为大部分对话都是在评论中进行的,我经常在浏览 Whosebug 时跳过那些。
class Kayz {
static bool KDown[2];
static int VKEY;
static void (*funcDown)();
static void (*funcUp)();
static HHOOK TheHook;
static KBDLLHOOKSTRUCT TheHookStruct;
static LRESULT _stdcall HookCallback(int, WPARAM, LPARAM);
public:
bool SetHook(int VKey, void(*FunctionDown)(), void(*FunctionUp)()) {
if (VKey < 0x07) {
if (!(TheHook = SetWindowsHookEx(WH_MOUSE_LL, &HookCallback, NULL, 0))) {
return false;
}
}
else if(VKey > 0x07){
if (!(TheHook = SetWindowsHookEx(WH_KEYBOARD_LL, &HookCallback, NULL, 0))) {
return false;
}
}
VKEY = VKey; funcDown = FunctionDown; funcUp = FunctionUp;
return true;
}
void UnSetHook() {
UnhookWindowsHookEx(TheHook);
}
bool WaitOnKey()
{
MSG msg;
while (true) {
if (MsgWaitForMultipleObjectsEx(0, 0, INFINITE, QS_ALLINPUT, 0) == WAIT_OBJECT_0) {
while (PeekMessage(&msg, 0, 0, 0, PM_REMOVE)) {
if (msg.message != WM_QUIT) return false;
TranslateMessage(&msg); DispatchMessage(&msg);
}
if(KDown[0] == 0 && KDown[1] == 0){
continue;
}else if (KDown[0] == true) {
return true;
}else{
KDown[1] = false;
return true;
}
} else {
return false;
}
}
}
};
bool Kayz::KDown[2];
int Kayz::VKEY;
void(*Kayz::funcDown)();
void(*Kayz::funcUp)();
HHOOK Kayz::TheHook;
KBDLLHOOKSTRUCT Kayz::TheHookStruct;
LRESULT _stdcall Kayz::HookCallback(int nCode, WPARAM wParam, LPARAM lParam) {
if (nCode >= 0) {
if (wParam == WM_KEYDOWN) {
TheHookStruct = *((KBDLLHOOKSTRUCT*)lParam);
if (TheHookStruct.vkCode == VKEY) {
KDown[0] = true;
(*funcDown)();
}
}
else if (wParam == WM_KEYUP)
{
TheHookStruct = *((KBDLLHOOKSTRUCT*)lParam);
if (TheHookStruct.vkCode == VKEY) {
KDown[1] = true;
KDown[0] = false;
(*funcUp)();
}
}
}
return CallNextHookEx(TheHook, nCode, wParam, lParam);
}
我正在使用低级挂钩。 我做了这个 class:
class Kayz {
static int VKEY;
static void (*funcDown)();
static void (*funcUp)();
static HHOOK TheHook;
static KBDLLHOOKSTRUCT TheHookStruct;
static LRESULT _stdcall HookCallback(int, WPARAM, LPARAM);
public:
bool SetHook(int VKey, void(*FunctionDown)(), void(*FunctionUp)()) {
if (VKey < 0x07) {
if (!(TheHook = SetWindowsHookEx(WH_MOUSE_LL, &HookCallback, NULL, 0))) {
return false;
}
}
else if(VKey > 0x07){
if (!(TheHook = SetWindowsHookEx(WH_KEYBOARD_LL, &HookCallback, NULL, 0))) {
return false;
}
}
VKEY = VKey; funcDown = FunctionDown; funcUp = FunctionUp;
return true;
}
void UnSetHook() {
UnhookWindowsHookEx(TheHook);
}
};
int Kayz::VKEY;
void(*Kayz::funcDown)();
void(*Kayz::funcUp)();
HHOOK Kayz::TheHook;
KBDLLHOOKSTRUCT Kayz::TheHookStruct;
LRESULT _stdcall Kayz::HookCallback(int nCode, WPARAM wParam, LPARAM lParam) {
if (nCode >= 0) {
if (wParam == WM_KEYDOWN) {
TheHookStruct = *((KBDLLHOOKSTRUCT*)lParam);
if (TheHookStruct.vkCode == VKEY) {
(*funcDown)();
}
}
else if (wParam == WM_KEYUP)
{
TheHookStruct = *((KBDLLHOOKSTRUCT*)lParam);
if (TheHookStruct.vkCode == VKEY) {
(*funcUp)();
}
}
}
return CallNextHookEx(TheHook, nCode, wParam, lParam);
}
我放入 SetHook 的所有功能都是在主程序中更改一个 bool 变量,这样我就可以知道是否按下了键。在我看来,这是最佳方式,因为我不必每次在主程序中循环时都检查密钥的状态。
现在。
在主程序中使用阻塞定时器如Sleep()会阻塞程序,包括
return CallNextHookEx(TheHook, nCode, wParam, lParam);
这意味着,由于这是一个低级挂钩,所有其他程序只会在睡眠结束时获得输入。所以如果我在记事本中按下一个键,它只会在睡眠结束并且程序再次循环时被输入,如果我输入很多,它们很可能一次被输入一个。
我见过的唯一能够"bypass"这是
while(GetMessage(&msgVar, NULL, 0, 0)){}
GetMessage 从不或很少 returns,因此它不占用任何系统资源或处理能力。它不会阻塞,因为 while 正在等待它到达 return。所以基本上,它什么也没做,但也没有阻塞。
我需要一个线程来做类似的事情。该线程将接收按键 "events" 并执行更改主程序中变量的函数。
但这很脏。我不喜欢脏。 所以我很想知道:
我怎样才能以干净的方式实现无阻塞,消耗最少的资源?
谢谢。
编辑:
正如您所问:我正在制作一个记忆瞄准机器人,完全用于学习目的。 我现在花了相当多的时间阅读有关 MsgWaitForMultipleObjectsEx 的内容,显然您可以将前 2 个参数设为 null,这会派上用场。
我也在考虑以错误的方式做这件事,我打算为程序创建一个线程 "hold" 并且仍然从钩子接收异步输入(这就是我不想要的原因它会阻塞),然后另一个(总是-运行)线程将根据钩子调用的函数将改变的布尔值工作。
我现在意识到这是一个相当糟糕的设计,所以我正在考虑在主程序中使用 MsgWaitForMultipleObjectsEx,并用它检查 bool,如果需要暂停或恢复 aimbot 线程。
我现在开始理解@HarryJohnston 所说的意大利面条逻辑,因为我必须组织异步挂钩函数的作用与 MsgWaitForMultipleObjectsEx 之后的代码的作用,这些看起来有些困难决定。
我想跟随这些钩子并全面了解这一切是如何工作的,这就是为什么我不会立即使用原始输入的原因,不过谢谢@nikau6 告诉我这件事,我'当我完成 hooks 时,一定会研究它。
再次感谢大家
你需要在循环中使用 MsgWaitForMultipleObjectsEx
这对你来说是最强大的功能。有了这个,您将等待 windows(和挂钩)消息,对于多个事件(最多 63 个),您还可以接收用户模式 APC
调用并定期(通过超时执行相同的任务)。示例:
void ZApp::Run()
{
for (;;)
{
HANDLE* pHandles;
DWORD nCount = GetWaitHandles(&pHandles);
DWORD r = MsgWaitForMultipleObjectsEx(nCount, pHandles, GetTimeout(), QS_ALLINPUT, MWMO_ALERTABLE);
if (r < nCount)
{
OnSignalObject(r);
continue;
}
if (r == nCount)
{
BOOL bIdle = FALSE;
MSG msg;
while (PeekMessage(&msg, 0, 0, 0, PM_REMOVE))
{
if (!bIdle)
{
bIdle = IsIdleMessage(msg.message);
}
if (PreTranslateMessage(&msg)) continue;
if (msg.message == WM_QUIT)
{
return ;
}
if (!IsDialogMessageEx(&msg))
{
if (msg.message - WM_KEYFIRST <= WM_KEYLAST - WM_KEYFIRST)
{
TranslateMessage(&msg);
}
DispatchMessage(&msg);
}
}
if (bIdle)
{
OnIdle();
}
continue;
}
if (r - WAIT_ABANDONED_0 < nCount)
{
OnAbandonedObject(r - WAIT_ABANDONED_0);
continue;
}
switch(r)
{
case WAIT_TIMEOUT:
OnTimeout();
break;
case WAIT_IO_COMPLETION:
OnApcAlert();
break;
default: __debugbreak();
}
}
}
"It seems to me that it's the most optimal way because I don't have to check for the key's state every time I loop in the main program."
有一种比 hooks 更好的方法,但并不为人所知,它可以监视所有系统上的键盘事件。这是Raw Input。
使用原始输入,您的应用程序可以直接从 HID(人机设备接口)驱动程序获知每个键盘、鼠标等事件。这比钩子更有效,而且使用起来非常简单。您的应用程序不需要从 DLL 导出过程,并且由于原始输入不是挂钩,因此在处理消息后,无需将消息传递给另一个过程、另一个线程。 (请参阅下面关于 DefRawInputProc 程序的评论之一)。应用程序通过 WM_INPUT 消息获取原始输入。与钩子不同,必须创建一个 window,这是一项义务,需要一个句柄。
以下是我使用原始输入的方式:
编辑:你不会遇到关于非阻塞线程的问题。
#include <Windows.h>
#define HID_ISMOUSE(x) ((x).header.dwType == RIM_MOUSE)
#define HID_ISKEYBOARD(x) ((x).header.dwType == RIM_TYPEKEYBOARD)
#define HID_SCODE(x) ((x).data.keyboard.MakeCode) // scan code
#define HID_VKEY(x) ((x).data.keyboard.VKey) // virtual key code
#define HID_WMSG(x) ((x).data.keyboard.Message) // corresponding window message, WM_KEYDOWN, WM_SYSKEYDOWN, WM_KEYUP, WM_SYSKEYUP.
#define HID_ISKEYUP(x) ((x).data.keyboard.Flags & RI_KEY_BREAK)
#define HID_ISKEYDOWN(x) (((x).data.keyboard.Flags & 0x01) == RI_KEY_MAKE)
#define RAWINPUT_ERROR (UINT)-1
namespace HID
{
const USHORT MOUSE = 2;
const USHORT KEYBOARD = 6;
// Register a raw input device
bool RegisterDevice(HWND hTarget, USHORT usage)
{
RAWINPUTDEVICE hid;
hid.usUsagePage = 1; // generic desktop page
hid.usUsage = usage; // device id
hid.hwndTarget = hTarget; // window handle
hid.dwFlags = RIDEV_NOLEGACY | RIDEV_INPUTSINK ; // RIDEV_INPUTSINK to monitor all the system, RIDEV_NOLEGACY if you don't want legacy keyboard events.
return !!RegisterRawInputDevices(&hid, 1, sizeof(RAWINPUTDEVICE));
}
// Unregister a raw input device.
void UnregisterDevice(USHORT usage)
{
RAWINPUTDEVICE hid;
hid.usUsagePage = 1;
hid.usUsage = usage;
hid.dwFlags = RIDEV_REMOVE; // RIDEV_REMOVE to remove a device.
hid.hwndTarget = NULL; // NULL to remove a device.
RegisterRawInputDevices(&hid, 1, sizeof(RAWINPUTDEVICE));
}
// Get raw input data
bool GetInputData(HRAWINPUT hInput, RAWINPUT* RawInput)
{
UINT size = sizeof(RAWINPUT); // size = 40
if( GetRawInputData((HRAWINPUT)hInput, RID_INPUT, RawInput, &size, sizeof(RAWINPUTHEADER)) != RAWINPUT_ERROR )
return true;
else
return false;
}
}
int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE, LPWSTR cmd_line, int cmd_show)
{
WNDCLASSW wc = {0};
wc.lpfnWndProc = WindowProc;
...
HWND hwnd = ::CreateWindowW(...);
...
HID::RegisterDevice(hwnd, HID::KEYBOARD);
MSG msg;
while(GetMessageW(&msg, NULL, 0, 0))
{
DispatchMessageW(&msg);
}
HID::UnregisterDevice(HID::KEYBOARD);
return (int)msg.wParam;
}
LRESULT CALLBACK WindowProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
if(msg == WM_INPUT) // Raw input message.
{
RAWINPUT Input;
if(HID::GetInputData((HRAWINPUT)lParam, &Input))
{
if(HID_ISKEYBOARD(Input))
{
if(HID_ISKEYUP(Input))
{
return 0;
}
else // if(HID_ISKEYDOWN(Input))
{
return 0;
}
}
}
}
return ::DefWindowProc(hWnd, msg, wParam, lParam);
}
我已经意识到,在等待挂钩执行其他函数时永久拥有一个线程 "on hold" 只是一种糟糕的方式来实现我正在寻找的东西,您应该始终让每个线程都做某事。如果您遵循相同的路径,我建议您放弃它并以不需要这些的方式组织您的代码 "loose ends".
谢谢大家。主要是@RbMm who informed me of MsgWaitForMultipleObjectsEx and guided me through it, and @nikau6 who informed about RawInput,以后会用到
我还完成了 class 并包含了一个函数 returns 当你的键被按下或释放时(当 MsgWaitForMultipleObjectsEx returns 除了 WAIT_OBJECT_0),我想我会 post 它在这里以防万一有人需要它,因为大部分对话都是在评论中进行的,我经常在浏览 Whosebug 时跳过那些。
class Kayz {
static bool KDown[2];
static int VKEY;
static void (*funcDown)();
static void (*funcUp)();
static HHOOK TheHook;
static KBDLLHOOKSTRUCT TheHookStruct;
static LRESULT _stdcall HookCallback(int, WPARAM, LPARAM);
public:
bool SetHook(int VKey, void(*FunctionDown)(), void(*FunctionUp)()) {
if (VKey < 0x07) {
if (!(TheHook = SetWindowsHookEx(WH_MOUSE_LL, &HookCallback, NULL, 0))) {
return false;
}
}
else if(VKey > 0x07){
if (!(TheHook = SetWindowsHookEx(WH_KEYBOARD_LL, &HookCallback, NULL, 0))) {
return false;
}
}
VKEY = VKey; funcDown = FunctionDown; funcUp = FunctionUp;
return true;
}
void UnSetHook() {
UnhookWindowsHookEx(TheHook);
}
bool WaitOnKey()
{
MSG msg;
while (true) {
if (MsgWaitForMultipleObjectsEx(0, 0, INFINITE, QS_ALLINPUT, 0) == WAIT_OBJECT_0) {
while (PeekMessage(&msg, 0, 0, 0, PM_REMOVE)) {
if (msg.message != WM_QUIT) return false;
TranslateMessage(&msg); DispatchMessage(&msg);
}
if(KDown[0] == 0 && KDown[1] == 0){
continue;
}else if (KDown[0] == true) {
return true;
}else{
KDown[1] = false;
return true;
}
} else {
return false;
}
}
}
};
bool Kayz::KDown[2];
int Kayz::VKEY;
void(*Kayz::funcDown)();
void(*Kayz::funcUp)();
HHOOK Kayz::TheHook;
KBDLLHOOKSTRUCT Kayz::TheHookStruct;
LRESULT _stdcall Kayz::HookCallback(int nCode, WPARAM wParam, LPARAM lParam) {
if (nCode >= 0) {
if (wParam == WM_KEYDOWN) {
TheHookStruct = *((KBDLLHOOKSTRUCT*)lParam);
if (TheHookStruct.vkCode == VKEY) {
KDown[0] = true;
(*funcDown)();
}
}
else if (wParam == WM_KEYUP)
{
TheHookStruct = *((KBDLLHOOKSTRUCT*)lParam);
if (TheHookStruct.vkCode == VKEY) {
KDown[1] = true;
KDown[0] = false;
(*funcUp)();
}
}
}
return CallNextHookEx(TheHook, nCode, wParam, lParam);
}