Related
I'm currently writing a function to get the current line/column of an EDIT control, and I'm stuck on a problem:
If I use WM_KEYUP to handle the caret position, the coordinates are valid but it can't be updated every "frame" since it waits for the user to release the pressed key
If I use WM_KEYDOWN, GetCaretPos returns the "previous" position of the caret (well, it's an obvious issue since it hasn't moved yet.)
Is there anything I can do to guess the next position of a caret? is it efficient if I just use EM_GETSEL?
LRESULT Edit::HandleMessage(UINT msg, WPARAM wParam, LPARAM lParam)
{
switch (msg)
{
case WM_LBUTTONDOWN:
{
// Get character position from the mouse cursor's position (currently, there's no conversion of the coordinates if the mouse cursor is out of bound)
POINT pntMousePos{ 0 };
pntMousePos.x = GET_X_LPARAM(lParam);
pntMousePos.y = GET_Y_LPARAM(lParam);
LRESULT notifyValue = this->Notify(EM_CHARFROMPOS, 0, MAKELPARAM(pntMousePos.x, pntMousePos.y));
int lineIdx = static_cast<int>(this->Notify<int, LPARAM>(EM_LINEINDEX, -1));
if (lineIdx == NULL) {
DWORD beg, end;
lineIdx = Notify(EM_GETSEL, &beg, &end);
}
m_caretPos.line = HIWORD(notifyValue) + 1;
m_caretPos.column = (LOWORD(notifyValue) - lineIdx) + 1;
// Send a custom message to the main window
SendMessage(GetParent(m_parent) /*The EDIT control is actually a child of a tab control that is itself a child of the main window, ignore this */, CEM_GETLINEINFO, MAKEWPARAM(m_caretPos.line, m_caretPos.column), 0);
}
break;
case WM_KEYDOWN:
{
// Get Character position from the carret's position
// Get text metric (doesn't work so I removed some lines)
TEXTMETRIC tm{0};
HDC hdc = GetDC(m_self);
SelectObject(hdc, this->m_fnt);
GetTextMetricsW(hdc, &tm);
ReleaseDC(m_self, hdc);
POINT caretPos{ 0 };
GetCaretPos(&caretPos);
LRESULT notifyValue = this->Notify(EM_CHARFROMPOS, 0, MAKELPARAM(caretPos.x, caretPos.y));
int lineIdx = static_cast<int>(this->Notify<int, LPARAM>(EM_LINEINDEX, -1));
m_caretPos.line = HIWORD(notifyValue) + 1;
m_caretPos.column = (LOWORD(notifyValue) - lineIdx) + 1;
SendMessage(GetParent(m_parent), CEM_GETLINEINFO, MAKEWPARAM(m_caretPos.line, m_caretPos.column), 0);
}
break;
}
return DefSubclassProc(m_self, msg, wParam, lParam);
}
On Windows 10 I have been experimenting with replacing the "Window snap" feature to work better with ultra wide monitors. While I have had no problem capturing the Windows Key+arrow cursors to handle the keyboard shortcut, I now want to detect when another application Window has been dragged to the top/right/left/bottom of the current monitor.
Current code:
#include <iostream>
#include <Windows.h>
HHOOK _hook_keyboard;
KBDLLHOOKSTRUCT kbdStruct;
CONST int HORIZONTAL_SLOTS = 4;
CONST int VERTICAL_SLOTS = 1;
// horizontalPosition/verticalPosition specifies which "slot" starting at 0 to place Window in
// horizontalSlots/verticalSlots specifies how many slots to divide the screen into
void MoveAndResizeActiveWindow(int horizontalPosition, int verticalPosition, int horizontalSlots, int verticalSlots)
{
// get work area on primary monitor
HWND currentWindow = GetForegroundWindow();
if (currentWindow != NULL)
{
HMONITOR currentMonitor = MonitorFromWindow(currentWindow, MONITOR_DEFAULTTONEAREST);
MONITORINFO monitorInfo;
monitorInfo.cbSize = sizeof(MONITORINFO);
if (GetMonitorInfo(currentMonitor, &monitorInfo))
{
long width = monitorInfo.rcWork.right - monitorInfo.rcWork.left;
long height = monitorInfo.rcWork.bottom - monitorInfo.rcWork.top;
long snappedWidth = width / horizontalSlots;
long snappedHeight = height / verticalSlots;
long snappedLeft = (snappedWidth * horizontalPosition) + monitorInfo.rcWork.left;
long snappedTop = (snappedHeight * verticalPosition) + monitorInfo.rcWork.top;
MoveWindow(currentWindow, snappedLeft, snappedTop, snappedWidth, snappedHeight, true);
}
}
}
LRESULT __stdcall HookCallbackKeyboard(int nCode, WPARAM wParam, LPARAM lParam)
{
BOOL bEatkeystroke = false;
short keyState;
if (nCode >= 0)
{
kbdStruct = *((KBDLLHOOKSTRUCT*)lParam);
switch (wParam)
{
case WM_KEYDOWN:
keyState = GetAsyncKeyState(VK_LWIN);
if (keyState)
{
switch (kbdStruct.vkCode)
{
case VK_LEFT:
bEatkeystroke = true;
break;
case VK_RIGHT:
bEatkeystroke = true;
break;
case VK_UP:
bEatkeystroke = true;
break;
case VK_DOWN:
bEatkeystroke = true;
break;
};
};
break;
case WM_KEYUP:
keyState = GetAsyncKeyState(VK_LWIN);
if (keyState)
{
switch (kbdStruct.vkCode)
{
case VK_LEFT:
MoveAndResizeActiveWindow(0, 0, 4, 1);
bEatkeystroke = true;
break;
case VK_RIGHT:
MoveAndResizeActiveWindow(3, 0, 4, 1);
bEatkeystroke = true;
break;
break;
case VK_UP:
MoveAndResizeActiveWindow(1, 0, 4, 1);
bEatkeystroke = true;
break;
case VK_DOWN:
MoveAndResizeActiveWindow(2, 0, 4, 1);
bEatkeystroke = true;
break;
};
}
break;
};
}
if (bEatkeystroke)
{
return 1;
}
else
{
return CallNextHookEx(_hook_keyboard, nCode, wParam, lParam);
}
}
void SetHook()
{
if (!(_hook_keyboard = SetWindowsHookEx(WH_KEYBOARD_LL, HookCallbackKeyboard, NULL, 0)))
{
MessageBox(NULL, L"Failed to install hook on keyboard!", L"Error", MB_ICONERROR);
}
}
int main(int argc, char** argv[])
{
SetHook();
MSG msg;
while (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return msg.wParam;
}
Any suggestions how to identify when Windows have been dragged to a particular location on the screen?
As per advice in replies to original question I have tried used SetWinEventHook with the following code, planning to restrict EVENT_MIN and EVENT_MAX once correct events to watch for worked out.
g_hook_winevent = SetWinEventHook(
EVENT_MIN, EVENT_MAX,
NULL, // Handle to DLL.
HandleWinEvent, // The callback.
0, 0, // Process and thread IDs of interest (0 = all)
WINEVENT_OUTOFCONTEXT | WINEVENT_SKIPOWNPROCESS); // Flags.
}
void CALLBACK HandleWinEvent(HWINEVENTHOOK hook, DWORD event, HWND hwnd,
LONG idObject, LONG idChild,
DWORD dwEventThread, DWORD dwmsEventTime)
{
// process event here
}
While this easily tracks start or end of a Windows move with EVENT_SYSTEM_MOVESIZESTART and EVENT_SYSTEM_MOVESIZEEND I can't see an event here that tracks the moving of Window prior to EVENT_SYSTEM_MOVESIZEEND.
While that will work if only good option, ideally I want to be able to detect Window location from start of EVENT_SYSTEM_MOVESIZESTART until EVENT_SYSTEM_MOVESIZEEND completes. Testing with notepad the only event getting raised during the move is EVENT_OBJECT_NAMECHANGE, which seems to constantly trigger during Window move, at least with Notepad. However based on description in documentation I'm not sure if this is suitable for my use case: "An object's Name property has changed. The system sends this event for the following user interface elements: check box, cursor, list-view control, push button, radio button, status bar control, tree view control, and window object. Server applications send this event for their accessible objects."
I'm using raw input to handle input for generic devices, thus far all of my test cases have worked (keyboards, game pads and mice), but my laptops track pad is giving me a weird problem. When I get a WM_INPUT message from the track pad (movement or button presses) I receive almost all the correct information except for the hDevice in the RAWINPUT header
I'm getting all available HID devices through GetRawInputDeviceList (with RID_DEVICE_INFO) and WM_INPUT_DEVICE_CHANGE messages. I believe the track pad is found by the first method (mouse HID with 2 buttons, index 6).
HID: [0x00020043] active
HID: [0x00020047] active
HID: [0x00020049] active
HID: [0x0002004B] active
keyboard: [0x00010041] active
mouse: [0x0001003B] active
mouse: [0x00010039] active
mouse: [0x0001003B] added
mouse: [0x00010039] added
keyboard: [0x00010041] added
#ifndef UNICODE
#define UNICODE
#endif
#include <array>
#include <vector>
#include <Windows.h>
bool active = true;
const char* getTypeStr(DWORD type)
{
if (type == RIM_TYPEMOUSE) return "mouse";
else if (type == RIM_TYPEKEYBOARD) return "keyboard";
else return "HID";
}
LRESULT CALLBACK wndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
if (msg == WM_INPUT)
{
if (GET_RAWINPUT_CODE_WPARAM(wParam) == RIM_INPUT) // Only handle foreground events.
{
const HRAWINPUT hRawInput = reinterpret_cast<HRAWINPUT>(lParam);
// Get the size of the data package.
UINT32 size = 0;
GetRawInputData(hRawInput, RID_INPUT, nullptr, &size, sizeof(RAWINPUTHEADER));
// Ignore empty packets.
if (size > 0)
{
PRAWINPUT input = reinterpret_cast<PRAWINPUT>(malloc(size));
GetRawInputData(hRawInput, RID_HEADER, input, &size, sizeof(RAWINPUTHEADER));
GetRawInputData(hRawInput, RID_INPUT, input, &size, sizeof(RAWINPUTHEADER));
printf("Received WM_INPUT from 0x%p.\n", input->header.hDevice);
free(input);
return 0;
}
}
}
else if (msg == WM_INPUT_DEVICE_CHANGE)
{
const HANDLE hDevice = reinterpret_cast<HANDLE>(lParam);
RID_DEVICE_INFO info;
info.cbSize = sizeof(RID_DEVICE_INFO);
UINT cbSize = info.cbSize;
GetRawInputDeviceInfo(hDevice, RIDI_DEVICEINFO, &info, &cbSize);
if (wParam == GIDC_ARRIVAL) printf("%s: [0x%p] added\n", getTypeStr(info.dwType), hDevice);
else printf("%s: [0x%p] removed\n", getTypeStr(info.dwType), hDevice);
}
else if (msg == WM_CLOSE)
{
active = false;
return 0;
}
return DefWindowProc(hWnd, msg, wParam, lParam);
}
int main()
{
// Create the window.
const HINSTANCE hInstance = GetModuleHandle(nullptr);
WNDCLASSEX wndEx =
{
sizeof(WNDCLASSEX),
CS_DBLCLKS,
wndProc,
0,
0,
hInstance,
nullptr,
LoadCursor(nullptr, IDC_ARROW),
(HBRUSH)(COLOR_WINDOW + 1),
nullptr,
L"TestWindow",
nullptr
};
RegisterClassEx(&wndEx);
const HWND hWnd = CreateWindow(L"TestWindow", L"TestWindow", WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU, 0, 0, 600, 600, nullptr, nullptr, hInstance, nullptr);
ShowWindow(hWnd, SW_SHOW);
// Log the connected devices.
UINT32 deviceCnt;
GetRawInputDeviceList(nullptr, &deviceCnt, sizeof(RAWINPUTDEVICELIST));
std::vector<RAWINPUTDEVICELIST> devices{ deviceCnt };
GetRawInputDeviceList(devices.data(), &deviceCnt, sizeof(RAWINPUTDEVICELIST));
for (const RAWINPUTDEVICELIST cur : devices)
{
printf("%s: [0x%p] active\n", getTypeStr(cur.dwType), cur.hDevice);
}
// Register the raw input devices we want to get notifications from.
std::array<RAWINPUTDEVICE, 3> rawInputDevices
{
RAWINPUTDEVICE {
0x1,
0x2, // Mouse
RIDEV_DEVNOTIFY,
hWnd
},
RAWINPUTDEVICE {
0x1,
0x6, //Keyboard
RIDEV_DEVNOTIFY,
hWnd
},
RAWINPUTDEVICE {
0x1,
0x5, // Gamepad
RIDEV_DEVNOTIFY,
hWnd
}
};
RegisterRawInputDevices(rawInputDevices.data(), rawInputDevices.size(), sizeof(RAWINPUTDEVICE));
// Update loop.
MSG msg;
while (active)
{
while (PeekMessage(&msg, hWnd, 0, 0, PM_REMOVE))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
// Finalize.
DestroyWindow(hWnd);
UnregisterClass(L"TestWindow", hInstance);
}
I'm expecting the WM_INPUT message to give me a valid device handle but it doesn't.
Received WM_INPUT from 0x00000000.
The result for a move
input:
header:
dwType = 0
dwSize = 48
hDevice = 0x0000000000000000
wParam = 0
data (mouse):
usFlags = 0
usButtons = 0
usButtonData = 0
ulRawButtons = 0
ILastX = 5
ILastY = -6
uIExtraInformation = 0
HID that's probably my track pad
hDevice = 0x0000000000010039
cbSize = 32
dwType = 0
mouse:
dwId = 128
dwNumberOfButtons = 2
dwSampleRate = 0
fHasHorizontalWheel = 0
Notifications from my integrated keyboard seem to be correct
Received WM_INPUT from 0x00010041.
The problem was that my touchpad is a precision touchpad, which means that there are some filters/transformations applied to it before the WM_INPUT notification. This was intended behaviour by the API but not documented (as far as I know).
Massive thanks to Eric Brown for answering this questions! Check the comment he left for the answer.
I have a combobox in that I want to display different string on selecting an item in Combo.
My combo box is a dropdown combobox.
For eg: I have following in my combobox.
Alex - Manager
Rain - Project Lead
Shiney - Engineer
Meera - Senior Engineer
OnSelecting an item in combobox I want to diaply only name i.e. Alex.
I tried below code
struct details{
CString name;
CString des;
};
BOOL CComboTestDlg::OnInitDialog()
{
CDialog::OnInitDialog();
details d1;
d1.name = _T("alex");
d1.des =_T("manager");
m_vec.push_back(d1);
details d2;
d2.name = _T("Rain");
d2.des =_T("Engineer");
m_vec.push_back(d2);
// TODO: Add extra initialization here
for(int i=0;i<m_vec.size();i++)
{
m_ctrlCombo.AddString(m_vec[i].name+m_vec[i].des);
m_ctrlCombo.SetItemData(i,(DWORD_PTR)&m_vec[i]);
}
m_ctrlCombo.SelectString(-1,m_vec[0].name);
m_ctrlCombo.SetWindowText(m_vec[0].name);
return TRUE; // return TRUE unless you set the focus to a control
}
void CComboTestDlg::OnCbnSelchangeCombo1()
{
int nItem = m_ctrlCombo.GetCurSel();
details* det = (details*)m_ctrlCombo.GetItemData(nItem);
PostMessage(SETCOMBOTEXT,IDC_COMBO1,(LPARAM)(LPCTSTR)det->name);
}
BOOL CComboTestDlg::PreTranslateMessage(MSG* pMsg)
{
MSG msg1=*pMsg;//I am loosing the value after checking ..so storing temp.
MSG msg;
CopyMemory(&msg, pMsg, sizeof(MSG));
HWND hWndParent = ::GetParent(msg.hwnd);
while (hWndParent && hWndParent != this->m_hWnd)
{
msg.hwnd = hWndParent;
hWndParent = ::GetParent(hWndParent);
}
if (pMsg->message==SETCOMBOTEXT && (pMsg->wParam == IDC_COMBO1))
SetDlgItemText(IDC_COMBO1, (LPCTSTR)pMsg->lParam);
if(pMsg->message==WM_KEYDOWN)
{
if(pMsg->wParam==VK_RETURN && msg.hwnd ==m_ctrlCombo.m_hWnd )
{
OnCbnSelchangeCombo1();
}
}
return CDialog::PreTranslateMessage(pMsg);
}
I am able to achieve my requirement OnComboSelChange() and Arrow Keys event but on pressing enter key after using arrow keys in combo box, it is not showing formatted text in combo box.
I think the most reliable and easy to implement solution is to subclass the edit control of the combobox. Intercept the WM_SETTEXT message and change the text as you like before forwarding it to the rest of the chain (finally the original window proc).
Install the sub class proc in OnInitDialog():
COMBOBOXINFO cbi{ sizeof(cbi) };
if( m_ctrlCombo.GetComboBoxInfo( &cbi ) )
{
SetWindowSubclass( cbi.hwndItem, ComboEditSubClassProc, 0, 0 );
}
ComboEditSubClassProc() could look like this:
LRESULT CALLBACK ComboEditSubClassProc( HWND hWnd, UINT uMsg, WPARAM wParam,
LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData )
{
switch( uMsg )
{
case WM_SETTEXT:
{
CString text = reinterpret_cast<LPCTSTR>( lParam );
// Extract the name (everything before "-").
CString name = text.SpanExcluding( _T("-") );
name.TrimRight();
// Forward the modified text to any other sub class procs, aswell
// as the original window proc at the end of the chain.
return DefSubclassProc( hWnd, uMsg, 0, reinterpret_cast<LPARAM>( name.GetString() ) );
}
case WM_NCDESTROY:
{
// We must remove our subclass before the subclassed window gets destroyed.
// This message is our last chance to do that.
RemoveWindowSubclass( hWnd, ComboEditSubClassProc, uIdSubclass );
break;
}
}
return DefSubclassProc( hWnd, uMsg, wParam, lParam );
}
Notes:
Contrary to my original solution of processing CBN_SELCHANGE, the current solution also works correctly if the combobox drop-down list is closed by pressing Return or is dismissed.
I think it is in general more reliable because we don't have to rely on the order of the notifications. The combobox has to finally call WM_SETTEXT to change the content of the edit control so this message will always be received.
There will also be no flickering as in the original solution where the text was first changed by the combobox and then modified by our code only after the fact.
I'm using low level hooks.
I have made this 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);
}
All the functions I put into SetHook do is change a bool variable in the main program so I can know whether or not the key is pressed. 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.
Now.
Using a blocking timer such as Sleep() in the main program will block the program, including
return CallNextHookEx(TheHook, nCode, wParam, lParam);
That means that, as this is a low level hook, every other program is only gonna get the input when sleep ends. So if I press a key while in notepad, it's only gonna get typed when sleep ends and the program loops again, if I type a lot, they're gonna get typed most likely one at a time.
The only thing I've seen that is able to "bypass" this is
while(GetMessage(&msgVar, NULL, 0, 0)){}
GetMessage never or rarely returns, so it doesn't take up any system resources or processing power. It doesn't block because while is waiting for it to return. So basically, it's not doing anything but it isn't blocking either.
I need to have a thread that is doing something similar to this. That thread will be receiving the key press "events" and executing the functions that change the variables in the main program.
But this is dirty. I don't like dirty.
So I'd very much like to know:
How can I achieve non-blocking nothing, consuming the least possible resources, in a clean way?
Thank you.
EDIT:
As you asked: I'm making a memory aimbot strictly for learning purposes.
I have now spent quite a bit of time reading about MsgWaitForMultipleObjectsEx, and apparently you can just null the first 2 parameters, which comes in handy.
I was also thinking of doing this the wrong way, I was going to make a thread for the program to "hold" and still receive the async input from the hooks(which is why I didn't want it to block), and then the other(always-running) thread would work based on the bools that the functions the hook called would change.
I've now realized that's a rather bad design, so I'm thinking of using MsgWaitForMultipleObjectsEx in the main program, and checking that bool with it, pausing or resuming the aimbot thread if needed.
I'm now beginning to understand what #HarryJohnston said about the spaghetti logic, because I've got to organize what the async hook functions do with what the code that comes after MsgWaitForMultipleObjectsEx does, and those seem some rather difficult decisions.
I want to follow these hooks and get a full understanding of how this can all work, which is why I won't be using raw input right away, though thank you #nikau6 for informing me about it, I'll surely look into it when I'm done with hooks.
Once again thank you everyone.
"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."
There's a better way than hooks, not well known, to monitor the keyboard events on all the system. This is Raw Input.
With raw inputs, your application get informed of each keyboard, mouse, etc.., event, straight from the HID (Human Device Interface) driver. This is more efficient than hooks, and very simple to use. Your application don't need to export a procedure from a DLL, and because raw inputs are not hooks, no message have to be passed to an another procedure, to a another thread, after it was treated. (see one of my comments below about the DefRawInputProc procedure). The application gets the raw input through the WM_INPUT message. Unlike hooks, a window must be created, that's an obligation, a handle is asked.
Here's how I use Raw Input :
EDIT : And you'll not get the problem you have about the non-blocking thread.
#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);
}
you need use MsgWaitForMultipleObjectsEx in loop this is most power function for you. with this you will be wait for windows(and hooks) messages, for multiple events (up to 63) also you can receiver user mode APC calls and periodically (by timeout do same tasks). example:
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();
}
}
}
I've realized that having a thread permanently "on hold" when waiting for hooks to execute other functions is just a bad way of doing what I was looking for, you should always have every thread doing something. If you're following the same path I suggest you get off of it and organize your code in a way you don't have to have these "loose ends".
Thanks everyone. Mainly #RbMm who informed me of MsgWaitForMultipleObjectsEx and guided me through it, and #nikau6 who informed about RawInput, which I'll be using in the future.
I've also finalized the class and included a function that returns when your key is either pressed or released(false when MsgWaitForMultipleObjectsEx returns anything other than WAIT_OBJECT_0), figured I'd post it here in case anyone ever needs it since most of the conversation was made in the comments and I often skip those when browsing stackoverflow.
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);
}