How to make Windows Hot Keys work in Direct X environments? - c++

I am programming a really simple MFC C++ Application which is working with HotKeys. To set a HotKey I use the following method from the WinAPI for my application:
BOOL RegisterHotKey(
HWND hWnd, // window to receive hot-key notification
int id, // identifier of hot key
UINT fsModifiers, // key-modifier flags
UINT vk // virtual-key code
);
To catch any HotKey Message I use: ON_MESSAGE(WM_HOTKEY,OnHotKey) in the message map and this Callback Method to test it's functionality:
LRESULT OnHotKey(WPARAM wParam, LPARAM lParam)
{
if (wParam == MY_HOTKEY_KEY_CODE)
{
MessageBox(L"HotKey was pressed!");
return TRUE;
}
return FALSE;
}
When a HotKey is pressed it enters the OnHotKey Method and does not process the key normally. For example if I write some text in Notepad and press "O" as HotKey, the "O" wont append to my text but the Message "HotKey was pressed!" appears, which is nice.
But when I am in any Direct X Game and press my HotKey, it is not sent to my application. Also when typing something in a Direct X environment the HotKey just works as normal key.
Is Direct X binding all key inputs somehow? Is there a way to make Windows HotKeys work with Direct X environments?

Related

SetWindowsHookEx ignoring PostMessage from message loop

I am trying to mod an older game, more specifically I want to emulate keyboard input via Xbox controller input. I already have the gamepad input working, but the game ignores my fake input I create with PostMessage (I tried PostThreadMessage as well with worse outcome.)
So here is the core piece of my code, a detour of PeekMessage :
BOOL WINAPI MyPeekMessage(LPMSG lpMsg, HWND hWnd, UINT wMsgFilterMin, UINT wMsgFilterMax, UINT wRemoveMsg)
{
BOOL ret = RealPeekMessage(lpMsg, hWnd, wMsgFilterMin, wMsgFilterMax, wRemoveMsg);
if (lpMsg->message == WM_KEYDOWN)
{
cout << "Key press";
}
if (!ret)
{
if (joypadButtonDown)
{
LPARAM lparam = 0x00000001 | (LPARAM)(EnterScanCode << 16); // Scan code, repeat=1
BOOL res = PostMessage(mainHwnd, WM_KEYDOWN, VK_RETURN, lparam);
}
}
return ret;
}
Now the problem is that the game doesn't use its message loop to read keyboard input, but instead uses SetWindowsHookEx with WH_KEYBOARD on its own main window thread.
So on a real key press what happens is :
The game's main loop calls my detour which calls the real PeekMessage which calls the hook procedure. But if I send my fake message (with identical parameters) the game again calls my detour but the real PeekMessage doesn't call the hook procedure and therefore misses the input.
Some additional Info :
- I've checked that everything happens on the same thread (Main window creation, setting the hook and the main loop)
- I tried sending PostMessage directly from IDirectInputDevice8->GetDeviceState with the same result
- Invoking the hook procedure directly causes a crash (which makes sense).
I found a somewhat dirty workaround. I call the game's SetWindowsHookEx KeyboardProc callback directly and avoid the crash I mentioned in the OP by detouring CallNextHookEx to ignore my fake hook calls.

Windows Journal playback hook (WH_JOURNALPLAYBACK) ignores EVENTMSG HWND parameter

I am working a program to simulate keyboard and mouse clicks programmatically. It need to send the clicks to a target window handle (ex: notepad edit control). I am getting the handle of notepad window and generating generating WM_KEYDOWN, WM_SYSKEYDOWN, WM_KEYUP, WM_SYSKEYUP messages for that window. The events are stored in queue, and later on played using a WH_JOURNALPLAYBACK hook.
For the code snippet below, the target hwnd in the playback proc though set correctly, messages never reach to the target handle. If I bring the notepad to foreground, it does receive the messages.
I am not sure why WH_JOURNALPLAYBACK ignores the handle parameter. I would have liked to generate a series of automation messages for various handles and played it back so that even without bringing the window into focus we can send keyboard and mouse events.
Please let me know
if messages to various target handles can be sent using a journal
playback hook
why in code below hwnd is ignored
..
#include <queue>
#include <iostream>
#include <windows.h>
using std::cout;
using std::endl;
using std::error;
struct Event
{
UINT msg;
UINT wparam;
UINT lparam;
HWND hwnd;
Event(UINT m, UINT wp, UINT lp, HWND h)
:msg(m),
wparam(wp),
lparam(lp),
hwnd(h)
{}
};
HHOOK jhook= NULL;
std::queue<Event> events;
bool gotoNextMsg = false;
LRESULT CALLBACK JournalPlaybackProc(int code, WPARAM wParam, LPARAM lParam)
{
switch( code )
{
case HC_SKIP:
cout<<"skip: "<<endl;
if(!events.empty())
{
events.pop();
}
break;
case HC_GETNEXT:
{
cout<<"next: "<<events.size()<<endl;
gotoNextMsg = true;
EVENTMSG * evm = (EVENTMSG*) lParam;
Event e = events.front();
switch(e.msg)
{
case WM_KEYDOWN:
cout<<"WM_KEYDOWN"<<endl;
break;
case WM_KEYUP:
cout<<"WM_KEYUP"<<endl;
break;
case WM_SYSKEYDOWN:
cout<<"WM_SYSKEYDOWN"<<endl;
break;
case WM_SYSKEYUP:
cout<<"WM_SYSKEYUP"<<endl;
break;
}
cout<<"handle: "<<e.hwnd<<endl;
cout<<"handle1:"<<evm->hwnd<<endl;
evm->message = e.msg;
evm->paramL = e.wparam;
evm->paramH = e.lparam;
evm->hwnd = e.hwnd;
evm->time = ::GetTickCount();
}
break;
default:
if( code < 0 )
::CallNextHookEx(jhook, code, wParam, lParam);
break;
}
if(events.empty())
{
cout<<"uinstalled"<<endl;
::UnhookWindowsHookEx(jhook);
::PostMessage(NULL, WM_USER+100, 0, 0);
}
return 0;
}
A journal hook injects events into the system message queue. For keyboard and mouse messages, the system dispatches them to the current focused window, same as if the user had input them manually. The HWND you specify in the event is not used, it gets replaced during dispatching.
And if you consider that a recorded journal can be played multiple times, and its data can persist across application instances and even reboots, and that HWNDs can be reused for different things over time, it should make sense why a journal playback cannot make use of an event's HWND even if the system message queue were not involved.
So, you cannot use WH_JOURNALPLAYBACK to target a specific window that is not in the foreground. You would have to send the recorded messages yourself. But be aware of some caveats that Raymond Chen has blogged about:
You can't simulate keyboard input with PostMessage
Simulating input via WM_CHAR messages may fake out the recipient but it won't fake out the input system

C++: Trying to hook a message box and change its position

I recently started coding in C++ and I am very new to it. (I code in Javascript, PHP, Java and Obj-C more often)
I'm practicing how to hook a message box and change its position. This is what I have in my .cpp file (after reading this SO post).
#include <iostream>
#pragma comment(lib,"User32.lib")
#include <windows.h>
HHOOK hhookCBTProc = 0;
LRESULT CALLBACK pfnCBTMsgBoxHook(int nCode, WPARAM wParam, LPARAM lParam){
if (nCode == HCBT_CREATEWND)
{
CREATESTRUCT *pcs = ((CBT_CREATEWND *)lParam)->lpcs;
if ((pcs->style & WS_DLGFRAME) || (pcs->style & WS_POPUP))
{
HWND hwnd = (HWND)wParam;
SetWindowPos(hwnd, HWND_TOP,130,122, 0, 0,SWP_NOSIZE);
}
}
return (CallNextHookEx(hhookCBTProc, nCode, wParam, lParam));
}
int main(void)
{
hhookCBTProc = SetWindowsHookEx(WH_CBT,pfnCBTMsgBoxHook,
0, GetCurrentThreadId());
int sResult = MessageBox ( NULL, "Hooked!", "oh my", MB_OK );
UnhookWindowsHookEx(hhookCBTProc);
return 0;
}
For some reason the position of the message box isn't changing. Where did it go wrong?
(I know I can create a customized window or dialog. But I am doing it this way because I want to learn how to hook a message box and where I did wrong.)
Firstly you should check in the debugger that your hook is actually being called, if you haven't already.
Secondly, at the time the HCBT_CREATEWND hook event is triggered, the window has only just been created - the system has yet to size and position it. It will do this with the values in the CREATESTRUCT after the hook returns - overriding your SetWindowPos call.
See the docs from MSDN on the lParam value for this particular hook event:
Specifies a long pointer to a CBT_CREATEWND structure containing
initialization parameters for the window. The parameters include the
coordinates and dimensions of the window. By changing these
parameters, a CBTProc hook procedure can set the initial size and
position of the window.
Therefore, the correct way to use this hook to change a window's position is to modify the values in the CREATESTRUCT directly.
Also note that it's quite possible that the dialog manager sizes and positions the window after creation, so if you find that this still isn't working for you, you may need to try watching for the HCBT_MOVESIZE event instead.
From the docs
At the time of the HCBT_CREATEWND notification, the window has been
created, but its final size and position may not have been determined
and its parent window may not have been established.
Maybe try hooking into CBT_ACTIVATE instead.

get mouse coordinates when user press Ctrl and click

I want to get mouse click coordinates X and Y when user press Ctrl and click at same time.
User may click anywhere on the screen or programs. I want my program to catch the event and get coordinates when Ctrl key is down pressed and mouse click occurs at same time. I want to get system coordinates X and Y, not the window coordinates of other programs.
I'm using C++ .
How to do that ?
Windows OS, WIN API code
I'm doing next which is not working:
HHOOK MouseHook;
LRESULT CALLBACK MouseHookProc(int nCode, WPARAM wParam, LPARAM lParam){
PKBDLLHOOKSTRUCT k = (PKBDLLHOOKSTRUCT)(lParam);
POINT p;
if(wParam == WM_RBUTTONDOWN)
{
// right button clicked
GetCursorPos(&p);
//p.x
//p.y
//my program is never getting here, why ?
}
}
MouseHook = SetWindowsHookEx(WH_MOUSE_LL,(HOOKPROC)MouseHookProc,0,0);
if I change the above line to: MouseHook = SetWindowsHookEx(WH_MOUSE,(HOOKPROC)MouseHookProc,GetModuleHandle(NULL),0);
then it will work only for my own program window, but not hooking clicks outside of my program
Have you read this article? link
First of all, if you want to catch system entire mouse + keyboard event, your hooking function must be placed on a dll.
1)If nCode is less than zero, the hook procedure must return the value returned by CallNextHookEx.
Like below,
if(nCode < 0)
{
CallNextHookEx(hook, nCode, wParam, lParam);
return 0;
}
If CallNextHookEx is not called, there is a chance that other processes that have installed hook may not get the events correctly.
2)And then, check nCode again, whether it is HC_ACTION.
switch (nCode)
{
case HC_ACTION:
...
3)Finally, you can check WPARAM for WM_RBUTTONDOWN, and LPARAM for MSLLHOOKSTRUCT
The way I would approach is I would create a global variable say
int ctrl_pressed = 0
/*
0 -- ctrl nt pressed
1 -- Crtl Pressed
*/
Step 1: Now I would handle both WM_KEYDOWN and WM_KEYUP for ctrl
Step 2: In the callback of WM_KEYDOWN (if lparam is ctrl ) I would set ctrl_pressed to 1.
And in case of WM_KEYUP I would set ctrl_pressed to 0.
Step 3: Now Finally ill handle WM_MOUSECLICKED
and then check if ctrl_pressed(global) is 1 , which if true I can just get the coordinates.
This is just an approach may be not the most efficient solution.
Wait for more experienced WIN32 Developers to give their input on this.

global low level keyboard hook being called when SendInput is made. how to prevent it?

I have a win 32 application written in c++ which sets the low level keyboard hook. now i want to sendInput to any app like word / notepad. how do i do this?
i have already done enough of using findwindow / sendmessage. for all these, i need to know edit controls. finding the edit control is very difficult.
since SendInput works for any windows application, i want to use it. the problem is i get a call to my callback function with the pressed key.
for e.g i pressed A and i want to send U+0BAF unicode character to the active applciation windows. in this case, assume it is notepad.
the problem is i get two characters U+0BAF and A in the notepad.
A is being sent because i am calling CallNextHookEx( NULL, nCode, wParam, lParam);
if i return 1 after sendInput, then nothing is sent to notepad.
any suggestion?
If I understood your problem correctly, you should ignore "injected" key events in your hook procedure, like this:
LRESULT CALLBACK
hook_proc( int code, WPARAM wParam, LPARAM lParam )
{
KBDLLHOOKSTRUCT* kbd = (KBDLLHOOKSTRUCT*)lParam;
// Ignore injected events
if (code < 0 || (kbd->flags & LLKHF_INJECTED)) {
return CallNextHookEx(kbdhook, code, wParam, lParam);
}
...
Update: additionally, you have to eat characters and notify some other routine for a character press through Windows messages.
Example:
...
// Pseudocode
if (kbd->vkCode is character) {
if (WM_KEYDOWN == wParam) {
PostMessage(mainwnd, WM_MY_KEYDOWN, kbd->vkCode, 0);
return 1; // eat the char, ie 'a'
}
}
return CallNextHookEx(kbdhook, code, wParam, lParam);
And, in some other module, you handle WM_MY_KEYDOWN:
ie, #define WM_MY_KEYDOWN (WM_USER + 1)
and call the appropriate routine that will generate new key events.