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."
Related
I've created a dialogbox with progress bar and a cancel button using CreateDialogParam to show status while copying several files (using CopyFileEx).
How do I cancel the process using CopyFileEx correctly, starting from pressing the cancel button in dialogbox? Is there anyway I can do it without using global variable? And How do I correctly handle the returned PROGRESS_CANCEL? I have provided questions in the code below to make clearer what help do I need.
//copy function
BOOL copy(HWND &hWnd, std::vector <FILECONSOLIDATEPARAMS> &vec)
{
//pass vector as lparam to dialogbox proc
LPARAM lp = reinterpret_cast<LPARAM>(&vec);
HWND hCopy = CreateDialogParam(GetModuleHandle(NULL),
MAKEINTRESOURCE(IDD_DIALOG1),
hwndmain, (DLGPROC)dlgboxcopyproc, lp);
static HWND hIDC_STATIC, hIDC_STATIC4;
hIDC_STATIC = GetDlgItem(hCopy, IDC_STATIC);
hIDC_STATIC4 = GetDlgItem(hCopy, IDC_STATIC4);
LPBOOL pbCancel = FALSE;
size_t s;
for (s = 0; s != vec.size(); s++)
{
SendMessage(hIDC_STATIC, WM_SETTEXT, 0, (LPARAM)vec[s].filename);
SendMessage(hIDC_STATIC4, WM_SETTEXT, 0,(LPARAM)vec[s].destination);
BOOL b = CopyFileEx(vec[s].filename, vec[s].destination,
&CopyProgressRoutine,(LPVOID)hCopy,pbCancel, NULL);
//how to catch and process PROGRESS_CANCEL?
if (!b)
{
DWORD dw = GetLastError();
ShowErrMsg(dw);
}
}
PostMessage(hCopy, WM_DESTROY, 0, 0);
return TRUE;
}
//dialogbox procedure
INT_PTR CALLBACK dlgboxcopyproc(HWND hWndDlg,UINT Msg,WPARAM wParam,LPARAM
lParam)
{
//translate passed lparam back to vector
std::vector<FILECONSOLIDATEPARAMS>& vect =
*reinterpret_cast<std::vector<FILECONSOLIDATEPARAMS>*>(lParam);
INITCOMMONCONTROLSEX _icex;
_icex.dwSize = sizeof(INITCOMMONCONTROLSEX);
_icex.dwICC = ICC_PROGRESS_CLASS;
InitCommonControlsEx(&_icex);
static HWND hParent;
static HWND hIDCancel;
static HWND hIDC_PROGRESS1;
static HWND hIDC_STATIC;
hParent = GetParent(hWndDlg);
hIDCancel = GetDlgItem(hWndDlg, IDCANCEL);
hIDC_PROGRESS1 = GetDlgItem(hWndDlg, IDC_PROGRESS1);
switch (Msg)
{
case WM_INITDIALOG:
{
SendMessage(hIDC_PROGRESS1, PBM_SETRANGE, 0, MAKELPARAM(0, 100));
}
return (INT_PTR)TRUE;
case WM_COMMAND:
{
switch (LOWORD(wParam))
{
case IDCANCEL:
EndDialog(hWndDlg, FALSE); //how to make pbCancel = TRUE?
return (INT_PTR)TRUE;
}
}
break;
case WM_DESTROY:
{
DestroyWindow(hWndDlg);
}
}
return FALSE;
}
//copyprogressroutine callback function
DWORD CALLBACK CopyProgressRoutine(LARGE_INTEGER TotalFileSize,
LARGE_INTEGER TotalBytesTransferred, LARGE_INTEGER StreamSize,
LARGE_INTEGER StreamBytesTransferred, DWORD dwStreamNumber, DWORD
dwCallbackReason, HANDLE hSourceFile, HANDLE hDestinationFile, LPVOID
lpData)
{
HWND hWndDlg = (HWND)lpData;
static HWND hwndIDC_PROGRESS1;
hwndIDC_PROGRESS1 = GetDlgItem(hWndDlg, IDC_PROGRESS1);
DOUBLE Percentage = ((DOUBLE)TotalBytesTransferred.QuadPart /
(DOUBLE)TotalFileSize.QuadPart) * 100;
switch (dwCallbackReason)
{
case CALLBACK_CHUNK_FINISHED:
SendMessage(hwndIDC_PROGRESS1, PBM_SETPOS, (WPARAM) Percentage, 0);
break;
case CALLBACK_STREAM_SWITCH:
Percentage = 0;
break;
}
return PROGRESS_CONTINUE; //how to make conditional return PROGRESS_CANCEL?
}
code like
LPBOOL pbCancel = FALSE;
CopyFileEx(, pbCancel, )
senseless. really we simply pass 0 in place pbCancel and have no ability cancel operation.
we need alocate some variable (let name it bCancel) of type BOOL and pass pointer of this variable to CopyFileEx
something like this:
BOOL bCancel = FALSE;
CopyFileEx(, &bCancel, )
the CopyFileEx will be periodically query value of this variable by passed pointer and if it became true - break operation and return ERROR_REQUEST_ABORTED error.
the next - CopyFileEx not return until operation is complete - this is synchronous api - as result it can not be called from GUI thread (or it simply block GUI). need call it from separate thread.
so basic solution - allocate some data structure, here place BOOL bCancel, and other data, which we will be use during copy. we will access this data structure from 2 threads - gui thread (from dialog procedure) and from separate thread, which will call CopyFileEx. for correct implement this - need use reference counting on structure. the lpProgressRoutine need post messages to gui thread for notify it about progress (gui thread can move progress bars on this notification). dialog can containing for example Cancel button. when user click it - GUI thread simply set bCancel = TRUE, as result CopyFileEx abort operation on next chunk. for start copy - need start new thread, which call CopyFileEx. based on required logic - can do this from WM_INITDIALOG or say when user press some button in dialog
basic code:
struct CopyDlg
{
enum {
WM_COPYRESULT = WM_APP, WM_PROGRESS
};
struct ProgressData {
LARGE_INTEGER TotalFileSize;
LARGE_INTEGER TotalBytesTransferred;
LARGE_INTEGER StreamSize;
LARGE_INTEGER StreamBytesTransferred;
DWORD dwStreamNumber;
};
PCWSTR m_lpExistingFileName, m_lpNewFileName;
HWND m_hwnd, m_hwndFile, m_hwndStream;
ULONG m_shift;
LONG m_dwRefCount;
LONG m_hwndLock;
BOOL m_bCancel;
CopyDlg()
{
m_dwRefCount = 1;
}
void AddRef()
{
InterlockedIncrement(&m_dwRefCount);
}
void Release()
{
if (!InterlockedDecrement(&m_dwRefCount))
{
delete this;
}
}
void LockWnd()
{
InterlockedIncrement(&m_hwndLock);
}
void UnlockWnd()
{
if (!InterlockedDecrement(&m_hwndLock))
{
// want m_hwnd be valid at PostMessage(p->m_hwnd,) time
EndDialog(m_hwnd, 0);
}
}
void OnProgress(DWORD dwCallbackReason, ProgressData* p)
{
if (dwCallbackReason == CALLBACK_STREAM_SWITCH)
{
if (p->dwStreamNumber == 1)
{
m_shift = 0;
LONGLONG QuadPart = p->TotalFileSize.QuadPart;
while (QuadPart > MAXLONG)
{
m_shift++;
QuadPart >>= 1;
}
SendMessage(m_hwndFile, PBM_SETRANGE32, 0, (LPARAM)QuadPart);
SendMessage(m_hwndFile, PBM_SETPOS, 0, 0);
}
SendMessage(m_hwndStream, PBM_SETRANGE32, 0, (LPARAM)(p->StreamSize.QuadPart >> m_shift));
SendMessage(m_hwndStream, PBM_SETPOS, 0, 0);
}
else
{
SendMessage(m_hwndStream, PBM_SETPOS, (LPARAM)(p->StreamBytesTransferred.QuadPart >> m_shift), 0);
SendMessage(m_hwndFile, PBM_SETPOS, (LPARAM)(p->TotalBytesTransferred.QuadPart >> m_shift), 0);
}
}
ULONG StartCopy(PCWSTR lpExistingFileName, PCWSTR lpNewFileName)
{
m_bCancel = FALSE;
m_lpExistingFileName = lpExistingFileName;
m_lpNewFileName = lpNewFileName;
LockWnd();
AddRef();
if (HANDLE hThread = CreateThread(0, 0, reinterpret_cast<PTHREAD_START_ROUTINE>(CopyThread), this, 0, 0))
{
CloseHandle(hThread);
return NOERROR;
}
Release();
UnlockWnd();
return GetLastError();
}
static INT_PTR CALLBACK _DialogProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
if (uMsg == WM_INITDIALOG)
{
SetWindowLongPtr(hwndDlg, DWLP_USER, (LONG_PTR)lParam);
}
if (CopyDlg* p = reinterpret_cast<CopyDlg*>(GetWindowLongPtrW(hwndDlg, DWLP_USER)))
{
p->AddRef();
INT_PTR r = p->DialogProc(hwndDlg, uMsg, wParam, lParam);
p->Release();
return r;
}
return 0;
}
INT_PTR DialogProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg)
{
case WM_NCDESTROY:
Release();
break;
case WM_DESTROY:
m_bCancel = TRUE;
break;
case WM_COMMAND:
switch (wParam)
{
case MAKEWPARAM(IDCANCEL, BN_CLICKED):
m_bCancel = TRUE;
break;
}
break;
case WM_CLOSE:
m_bCancel = TRUE;
UnlockWnd();
ShowWindow(hwndDlg, SW_HIDE);//for not get wm_close twice
break;
case WM_COPYRESULT:
UnlockWnd();
// lParam == error code from CopyFileExW
DbgPrint("CopyFileExW=%u\n", (ULONG)lParam);
break;
case WM_PROGRESS:
OnProgress((DWORD)wParam, reinterpret_cast<ProgressData*>(lParam));
delete (void*)lParam;
break;
case WM_INITDIALOG:
AddRef();
m_hwnd = hwndDlg;
m_hwndStream = GetDlgItem(hwndDlg, IDC_PROGRESS1);
m_hwndFile = GetDlgItem(hwndDlg, IDC_PROGRESS2);
m_hwndLock = 1;
StartCopy(L"**", L"**");
break;
}
return 0;
}
static ULONG CALLBACK CopyThread(CopyDlg* p)
{
PostMessage(p->m_hwnd, WM_COPYRESULT, 0, CopyFileExW(
p->m_lpExistingFileName, p->m_lpNewFileName,
reinterpret_cast<LPPROGRESS_ROUTINE>(CopyProgressRoutine),
p, &p->m_bCancel, 0) ? NOERROR : GetLastError());
p->Release();
return 0;
}
static DWORD CALLBACK CopyProgressRoutine(
__in LARGE_INTEGER TotalFileSize,
__in LARGE_INTEGER TotalBytesTransferred,
__in LARGE_INTEGER StreamSize,
__in LARGE_INTEGER StreamBytesTransferred,
__in DWORD dwStreamNumber,
__in DWORD dwCallbackReason,
__in HANDLE /*hSourceFile*/,
__in HANDLE /*hDestinationFile*/,
__in_opt CopyDlg* p
)
{
switch(dwCallbackReason)
{
case CALLBACK_CHUNK_FINISHED:
case CALLBACK_STREAM_SWITCH:
if (ProgressData* data = new ProgressData)
{
data->TotalFileSize = TotalFileSize;
data->TotalBytesTransferred = TotalBytesTransferred;
data->StreamSize = StreamSize;
data->StreamBytesTransferred = StreamBytesTransferred;
data->dwStreamNumber = dwStreamNumber;
if (!PostMessage(p->m_hwnd, WM_PROGRESS, dwCallbackReason, (LPARAM)data))
{
delete data;
return PROGRESS_CANCEL;
}
}
break;
}
// for debugging
//Sleep(3000);
//MessageBoxW(0,0,0,0);
return PROGRESS_CONTINUE;
}
};
if (CopyDlg* p = new CopyDlg)
{
DialogBoxParamW((HINSTANCE)&__ImageBase, MAKEINTRESOURCE(IDD_DIALOG1), HWND_DESKTOP, CopyDlg::_DialogProc, (LPARAM)p);
p->Release();
}
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);
}
Initially I have to say that I know nothing about WinAPI. I'm learning from quite old tutorial, which seems to be a little bit outdated. I'm trying to make a dialog box where user would type in size of a next window. I've made it in Visual Studio using Resource Editor (or whatever it is called). I'm trying to retrieve data from Edit Controls, but GetWindowText doesn't work well.
So I made global LPTSTR named SizeX and SizeY (I know I could made them local and later pass them to a function that creates the second window, but I've got then problems with hInstance... nevermind).
BOOL CALLBACK SettingsProcedure(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch (msg)
{
case WM_INITDIALOG:
{
SetWindowTextA(GetDlgItem(hwnd, IDC_EDIT1), "20"); //I'm setting default input in case the user doesn't want to write anything
SetWindowTextA(GetDlgItem(hwnd, IDC_EDIT2), "20");
}
break;
case WM_COMMAND:
{
switch (LOWORD(wParam))
{
case IDC_BUTTON1:
{
GetWindowText(GetDlgItem(hwnd, IDC_EDIT1), sizeX, GetWindowTextLength(GetDlgItem(hwnd, IDC_EDIT1)) + 1);
if (sizeX == NULL)
break; //breaks every time
GetWindowText(GetDlgItem(hwnd, IDC_EDIT2), sizeY, 10);
EndDialog(hwnd, IDC_BUTTON1);
}
break;
}
}
break;
default: return FALSE;
}
return TRUE;
}
I'm sure I have a lot of basic mistakes in this code, so please don't blame me :P
I have no idea how to make it work. The fantastic tutorial I use tells nothing about Edit Controls, it even has an information that it might be too old. Unfortunately that is the only WinAPI tutorial I've found in my language, if you know any good one in English I'd be glad.
the thing that you should do is use directly GetDlgItemInt to retrieve sizeX and sizeY otherwise you should get text as a string then convert it into int:
case WM_COMMAND:
{
switch (LOWORD(wParam))
{
case IDC_BUTTON1:
{
BOOL bCheck = FALSE;
sizeX = GetDlgItemInt(hwnd, IDC_EDIT1, &bCheck, false);
sizeY = GetDlgItemInt(hwnd, IDC_EDIT2, &bCheck, false);
// or text then convert:
int textLengthX = SendDlgItemMessage(hwnd, IDC_EDIT1, WM_GETTEXTLENGTH, 0, 0);
int textLengthY = SendDlgItemMessage(hwnd, IDC_EDIT2, WM_GETTEXTLENGTH, 0, 0);
LPSTR lpTextX = (LPSTR)GlobalAlloc(GPTR, textLengthX + 1);
LPSTR lpTextY = (LPSTR)GlobalAlloc(GPTR, textLengthY + 1);
SendDlgItemMessage(hwnd, IDC_EDIT1, WM_GETTEXT, (WPARAM)textLengthX + 1, (LPARAM)lpTextX);
SendDlgItemMessage(hwnd, IDC_EDIT1, WM_GETTEXT, (WPARAM)textLengthY + 1, (LPARAM)lpTextY);
// now you have sizeX and sizeY as strings so convert them to int:
int sizeX = atoi(lpTextX);
int sizeY = atoi(lpTextY);
GlobalFree(lpTextX);
GlobalFree(lpTextY);
}
break;
}
break;
}
I'm creating taskbar icon of a mfc application and in MyView.cpp file I've written
static const UINT WMU_NOTIFY_TASKBAR_ICON = ::RegisterWindowMessage(_T("NOTIFY_TASKBAR_ICON"));
IMPLEMENT_DYNCREATE(CMyView, CView)
BEGIN_MESSAGE_MAP(CMyView, CView)
// Standard printing commands
ON_COMMAND(ID_FILE_PRINT, &CView::OnFilePrint)
ON_COMMAND(ID_FILE_PRINT_DIRECT, &CView::OnFilePrint)
ON_COMMAND(ID_FILE_PRINT_PREVIEW, &CView::OnFilePrintPreview)
// added messages by the developer
ON_REGISTERED_MESSAGE(WMU_NOTIFY_TASKBAR_ICON, OnNotifyTaskbarIcon)
END_MESSAGE_MAP()
//...
void CMyView::AddTaskbarIcon()
{
DWORD dwMessage = NIM_ADD;
NOTIFYICONDATA nid;
nid.cbSize = sizeof(NOTIFYICONDATA);
nid.hWnd = HWND(AfxGetApp()->m_pMainWnd);
nid.uID = 0;
nid.uFlags = NIF_MESSAGE | NIF_ICON | NIF_TIP;
nid.uCallbackMessage = WMU_NOTIFY_TASKBAR_ICON;
nid.hIcon = LoadIcon(GetModuleHandle(0), MAKEINTRESOURCE(IDI_SS_ICON));
nid.dwInfoFlags = NIIF_INFO;
::Shell_NotifyIconW(dwMessage, &nid);
}
LRESULT CMyView::OnNotifyTaskbarIcon( WPARAM wParam, LPARAM lParam )
{
UINT uID = (UINT)wParam;
UINT uMouseMsg = (UINT)lParam;
switch(uMouseMsg)
{
case WM_LBUTTONDOWN:
break;
case WM_LBUTTONDBLCLK:
//if(IsIconic())
{
break;
}
case WM_RBUTTONDOWN:
{
// must be implemented:
// app popup menu will be showed
CMenu* pMenu = GetMenu();
if( pMenu )
{
CMenu *pSubMenu = NULL;
pSubMenu = pMenu->GetSubMenu( 0 );
{
SetForegroundWindow(); // *** little patch here ***
POINT pointCursor;
::GetCursorPos( &pointCursor );
pSubMenu->TrackPopupMenu( TPM_RIGHTALIGN,
pointCursor.x, pointCursor.y,
this );
}
}
}
break;
case WM_RBUTTONDBLCLK:
break;
case WM_MOUSEMOVE:
break;
}
return 0L;
}
and in My.cpp
BOOL CMyApp::InitInstance()
{
//...
myViewPtr->AddTaskbarIcon();
//...
}
the app launches, the icon appears on the taskbar but it disappears on mouse hovering.
Have I done something wrong? Thanx
AfxGetApp()->m_pMainWnd points to the main frame window, not to the view. I suspect that the frame window is receiving the WMU_NOTIFY_TASKBAR_ICON message and not handling it so Windows removes the icon.
You could either handle the message in the frame window class, or pass the handle to the view instead, like this:
void CMyView::AddTaskbarIcon()
{
...
nid.hWnd = GetSafeHwnd();
...
}
I'm using rawinput with directx...i'm trying to zoom with the camera when mouse wheel is used...when I run the program with the following code, the data I get from rawinput for the usbuttondata goes to 120 when I push mouse wheel forward...then it goes out of control...up to 65000...I thought the data was supposed to be 1 or -1 or 0...what does rawinput send as the mouse wheel data?
code:
LRESULT CALLBACK WndProc(HWND hWnd, UINT Msg,
WPARAM wParam, LPARAM lParam)
{
switch(Msg)
{
case WM_CREATE:
{
RAWINPUTDEVICE Rid[2];
// Keyboard
Rid[0].usUsagePage = 1;
Rid[0].usUsage = 6;
Rid[0].dwFlags = 0;
Rid[0].hwndTarget=Inst.Wnd.hWnd;
// Mouse
Rid[1].usUsagePage = 1;
Rid[1].usUsage = 2;
Rid[1].dwFlags = 0;
Rid[1].hwndTarget=Inst.Wnd.hWnd;
if (!RegisterRawInputDevices(Rid,2,sizeof(RAWINPUTDEVICE)))
{
MessageBox(NULL, L"Failed to Register Input Devices!", L"ALERT", MB_OK);
exit(1);
}
return 0;
}
case WM_INPUT:
{
// Determine how big the buffer should be
UINT iBuffer;
GetRawInputData((HRAWINPUT)lParam, RID_INPUT, NULL, &iBuffer,
sizeof(RAWINPUTHEADER));
LPBYTE lpb = new BYTE[iBuffer];
if (lpb == NULL)
{
return 0;
}
UINT readSize = GetRawInputData( (HRAWINPUT)lParam, RID_INPUT, lpb, &iBuffer, sizeof(RAWINPUTHEADER) ) ;
if( readSize != iBuffer )
puts( "ERROR: GetRawInputData didn't return correct size!" ) ;
RAWINPUT *raw = (RAWINPUT*) lpb;
if (raw->header.dwType== RIM_TYPEMOUSE)
{
riProcessMouseMessage(&raw->data.mouse);
}
if (raw->header.dwType== RIM_TYPEKEYBOARD)
{
//riProcessKeyboardMessage(&raw->data.keyboard);
}
}
return 0;
case WM_COMMAND:
switch(LOWORD(wParam))
{
case IDM_FILE_NEW:
{
// Create the game object
pGame = new CGame(dxMgr.getD3DDevice());
// Initialize the game object
if (!pGame->init(Inst.Wnd.hWnd))
return 0;
break;
}
case IDM_FILE_OPEN:
pGame->m_animCollection->LoadXFile("oxana.x", 0);
//objects.CreateNewObject(1, L"oxana.x", NULL);
break;
case IDM_FILE_SAVE:
break;
case IDM_FILE_SAVEAS:
break;
case IDM_FILE_EXIT:
PostQuitMessage(WM_QUIT);
break;
}
return 0;
case WM_DESTROY:
PostQuitMessage(WM_QUIT);
return 0;
default:
return DefWindowProc(hWnd, Msg, wParam, lParam);
}
return TRUE;
}
void riProcessMouseMessage( const RAWMOUSE* rmouse )
{
if(pGame != NULL)
{
//MessageBox(NULL, L"Game Found", L"SUCCESS", MB_OK);
if ( MOUSE_MOVE_RELATIVE == rmouse->usFlags )
{
riMgr.mxr = &rmouse->lLastX;
riMgr.myr = &rmouse->lLastY;
}
riMgr.mzr = (RI_MOUSE_WHEEL & rmouse->usButtonFlags) ? &rmouse->usButtonData : 0;
}
}
I suspect it is the same as WM_MOUSEWHEEL:
The high-order word indicates the distance the wheel is rotated, expressed in multiples or divisions of WHEEL_DELTA, which is 120. A positive value indicates that the wheel was rotated forward, away from the user; a negative value indicates that the wheel was rotated backward, toward the user.
The low-order word indicates whether various virtual keys are down.
Therefore you need to extract the high order word. You need to take care to handle negative values correctly. You probably don't as you get large values instead.
If you want you can use the following macro for this: GET_WHEEL_DELTA_WPARAM(wParam)
Add the following in the switch statment
case WM_MOUSEWHEEL:
{
int delta = GET_WHEEL_DELTA_WPARAM(wparam);
if(delta > 0)
{
//Mouse Wheel Up
}
else
{
//Mouse Wheel Down
}
return 0;
}