The main form opens a child form that has a handful of button CONTROLs on it. I need to trap keyboard events so I subclassed one of the controls. All is good until the control loses focus of course.
Ideally, as long as this child form is open I would like to assign the focus to this control and thus trap all the keystrokes, no matter where the user clicks.
I suspect superclassing might be a better way to go but I am not as familiar with it.
Perhaps what I should do is use accelerators on the main form?
ADDED:
I should mention that the main form has a large listview control that is subclassed to recover up/down arrows and mousewheel etc.
The traditional way is to install a keyboard hook (SetWindowsHookEx), but you need to inject it into every application, and it doesn't work across 32/64 bit boundaries.
What you can do however, and quite easily at that, is to poll the keyboard with GetKeyboardState on a timer and check whether your f1-f12 keys are activated. The timer can be as slow ticking as 100ms and it will catch almost everything while using virtually no resources.
Assuming that this is within Windows and the Win32 API, one option is to look for messages in your main GetMessage, TranslateMessage, DispatchMessage loop. You can special-case any message within this loop, irrespective of which window it's aimed at.
You should probably use IsChild to check that the message is intended for a control on your main window (as opposed to some dialog box or message box that might be displayed separately). Getting the logic right can be fiddly, too. It would be best to only intercept messages when you know your control has lost the focus, and only intercept the exact messages you need to.
Years ago, I wrote a library message loop with a lot of this built in. I had a simple manager class that held pointers to instances of my own little window class. The loop knew the difference between dialogs and normal windows, gave each window class a chance to spy on its childrens messages, and so on. You won't be able to run this directly and the conventions are a bit strange, but you might find this useful...
int c_Window_List::Message_Loop (void)
{
MSG msg;
bool l_Handled;
while (GetMessage (&msg, NULL, 0, 0))
{
l_Handled = false;
c_Windows::c_Cursor l_Cursor;
bool ok;
for (ok = l_Cursor.Find_First (g_Windows); ok; ok = l_Cursor.Step_Next ())
{
if (IsChild (l_Cursor.Key (), msg.hwnd))
{
if (l_Cursor.Data ().f_Accelerators != NULL)
{
l_Handled = TranslateAccelerator (l_Cursor.Key (), l_Cursor.Data ().f_Accelerators, &msg);
if (l_Handled) break;
}
if (l_Cursor.Data ().f_Manager != 0)
{
l_Handled = l_Cursor.Data ().f_Manager->Spy_Msg (l_Cursor.Key (), msg);
}
if (l_Handled) break;
if (l_Cursor.Data ().f_Is_Dialog)
{
l_Handled = IsDialogMessage (l_Cursor.Key (), &msg);
if (l_Handled) break;
}
}
}
if (!l_Handled)
{
TranslateMessage (&msg);
DispatchMessage (&msg);
}
if (g_Windows.Size () == 0)
{
// When all windows have closed, exit
PostQuitMessage (0);
}
}
return msg.wParam;
}
The f_ prefixes mean field - I picked up the m_ convention later, but this code hasn't been revisited in a very long time. f_Manager in particular points to an instance of my c_Window_Base class. The c_Cursor class is a kind of iterator, used to step through all the windows stored in the g_Windows variable (actually a static class member rather than a global).
Related
I'm working on an application for taking screenshots on Windows, OSX and Linux in C++/Qt. Now I need to set global hotkeys, so the user can take screenshots when the application is running in the background. I tried with Qxt and UGlobalHotkey, which are both Qt libraries, but neither of them seemed to work.
I tried to implement it for OSX with Carbon (tutorial), but I need to call a class member function, which just doesn't work. Could someone provide me with an example? You can find my code here. The function i need to call is new_screenshot().
Or is there any other way to achieve something like this? I really need my application to take a screenshot from the background, otherwise it's pretty useless (yes, I should probably have implemented it at the very beginning to see if it even works). Would it maybe be better to have a separate client for every platform (Cocoa Swift for OSX, GTK for Linux, C# client for Windows)? I have often thought about this the past few days.
Do I understand correctly that you want to call new_screenshot from the hot key event handler? If so, InstallApplicationEventHandler lets you pass a pointer to user data in 4th argument. Pass a pointer to your MainWindow instance (based on code from the tutorial):
MainWindow *mainWindow = ... // get main window somehow
InstallApplicationEventHandler(&MyHotKeyHandler,1,&eventType,mainWindow,NULL);
Then you can use it in the event handler.
OSStatus MyHotKeyHandler(EventHandlerCallRef nextHandler,EventRef theEvent, void *userData)
{
//Do something once the key is pressed
static_cast<MainWindow*>(userData)->new_screenshot();
return noErr;
}
I did something in the past with MFC and WIN32 API....so it only works on Windows...but pressing ALT+F10 was able to hide/show a window...
void CWinHideDlg::OnButtonActive()
{
CString tmp;
GetDlgItemText(IDC_BUTTON_ACTIVE,tmp);
if(0 == strcmp(tmp.GetBuffer(tmp.GetLength()),"Activate"))
{
m_myAtom=GlobalAddAtom("MY_GLOBAL_HOT_HIDE_KEY");
int err=RegisterHotKey(this->GetSafeHwnd(),m_myAtom,MOD_ALT,VK_F10);
SetDlgItemText(IDC_BUTTON_ACTIVE,"Stop");
CButton *pBtn = (CButton *)GetDlgItem(IDC_BUTTON_UNHIDE);
pBtn->EnableWindow(TRUE);
SetDlgItemText(IDC_STATIC_INFO,"Set the mouse over the window \nand press ALT + F10 to hide it...");
}
else
{
UnregisterHotKey(this->GetSafeHwnd(),m_myAtom);
GlobalDeleteAtom(m_myAtom);
CButton *pBtn = (CButton *)GetDlgItem(IDC_BUTTON_UNHIDE);
pBtn->EnableWindow(FALSE);
SetDlgItemText(IDC_BUTTON_ACTIVE,"Activate");
}
}
Basically this code activates/deactivates the hot key ALT+F10, once it activates you can hide/unhide a running window on the system by setting the mouse pointer over the window and press ALT+F10...
This is from the WindowProc function:
if(message == WM_HOTKEY)
{
CString tmp;
POINT pc;
GetCursorPos(&pc);
if(GetAsyncKeyState(VK_F10))
{
HWND hwnd=::WindowFromPoint(pc);
if(hwnd)
{
tmp.Format("%08Xh",hwnd);
m_HideWins.InsertString(m_HideWins.GetCount(),tmp);
::ShowWindow(hwnd,SW_HIDE);
}
}
}
You can use the code to register your own HOT Key and use it to take a screenshot...
Hope it helps...
I have learned a bit about Win32 API, but now I want to learn MFC. In my ebook, they said that the CWinApp class manages main thread of application, but I can't find something like GetMessage, DispatchMessage functions in this class. So how it can begin the messages loop?
Someone explain this for me please. Sorry, I'm a newer in MFC and my English is bad.
And where can I find some ebooks/tutorials about MFC in Visual Studio?
This all done in the CWinApp:Run section.
After InitInstance returns true, CWinApp:Run is launched and the message-loop takes its role. This message-loop is tricky because it also handles OnIdle calls when the application has nothing to do.
Just look into the source code.
MFC has simplified message handling by using message-maps, programmer mostly need not to bother how message-loop is running, how messages are delivered, and how mapped-messages map to the user defined functions. I would advice you to fiddle around CWnd-derived classes (like frames, dialogs), and see how mapped-messages are calling your functions.
A WM_MOUSEMOVE is calling your OnMouseMove, provided you put an entry ON_WM_MOUSEMOVE - that's an interesting this you should find how it is working. Playing around with CWinApp-derived class isn't good idea.
MFC is somewhat like a wrapped up layer on Win32. The message loop is wrapped up inside a member of CWinThread called Run. And the application class is derived from CWinApp which is in turn derived from CWinThread. This method is not generally overridden. If the message loop code should be read, this method should be overridden and the code can be seen while debugging. It handles the idle message also
int CWinThread::Run()
{
....
for (;;)
{
// phase1: check to see if we can do idle work
while (bIdle &&
!::PeekMessage(&(pState->m_msgCur), NULL, NULL, NULL, PM_NOREMOVE))
{
// call OnIdle while in bIdle state
if (!OnIdle(lIdleCount++))
bIdle = FALSE; // assume "no idle" state
}
// phase2: pump messages while available
do
{
// pump message, but quit on WM_QUIT
if (!PumpMessage())
return ExitInstance();
// reset "no idle" state after pumping "normal" message
//if (IsIdleMessage(&m_msgCur))
if (IsIdleMessage(&(pState->m_msgCur)))
{
bIdle = TRUE;
lIdleCount = 0;
}
} while (::PeekMessage(&(pState->m_msgCur), NULL, NULL, NULL, PM_NOREMOVE));
}
}
I want to send keystrokes to multiple processes. For example, if I press “1”, then I want to send the “1” to 3 "Notepad windows". Frist I want to try to send a keystroke to notepad, but it fails on the HWND:
//HANDLE hWin;
HWND windowHandle = FindWindowA(NULL, "Notepad"); //Can’t find a proccess
//Send a key
if( windowHandle ) //This one fails
{
while(true)
{
if( GetAsyncKeyState(VK_F12) != 0 )
{
SendMessageA(windowHandle, WM_KEYDOWN, VK_NUMPAD1, 0);
Sleep(1000);
SendMessageA(windowHandle, WM_KEYUP, VK_NUMPAD1, 0);
}
Sleep(100);
}
}
But the "FindWindow" method is not good enough for my program. There is also no way to get 3 different processes with the same name. So how can I make 3 handles to 3 different processes with the same name? And how can I send key’s to the processes?
You can use EnumWindows for enumerating all the top level windows on the system. You then need to filter through these windows to get the ones you are interested in. Class name is probably a better choice for filtering rather than the window name though. Here is some example code (not tested) of what I have in mind:
BOOL CALLBACK BroadcastToNotepad(HWND hwnd, LPARAM lParam)
{
wchar_t lpClassName[16];
/*
* More reliable to filter by class name. We could additionally filter
* by caption name too if necessary.
*/
if(GetClassName(hwnd, lpClassName, _countof(lpClassName))) {
if(wcscmp(lpClassName, L"Notepad") == 0) {
SendMessage(hwnd, WM_KEYDOWN, (WPARAM)lParam, 0);
Sleep(1000);
SendMessage(hwnd, WM_KEYUP, (WPARAM)lParam, 0);
}
}
return TRUE;
}
// Some handler which gets invoked when your hotkey is hit.
void handlerKey1(...)
{
EnumWindows(BroadcastToNotepad, (lParam)VK_NUMPAD1)
}
Note the usage of BroadcastToNotepad and how you can have different handlers pass in a different lParam.
One final thing to note is that PostMessage/SendMessage is not a reliable way to simulate keyboard input. This is noted by Raymond Chen here. SendInput is the preferred way for injecting input. However, to use that you will need to ensure the window you want to send to has the keyboard focus.
I recall vaguely having played with something similar to what you are doing in the past. If I remember correctly, you need to send to Notepad's child window (class name = Edit). So the code above needs to be modified as so:
if(wcscmp(lpClassName, L"Notepad") == 0) {
HWND hwndChild = FindWindowEx(hwnd, NULL, L"Edit", NULL);
SendMessage(hwndChild, WM_KEYDOWN, (WPARAM)lParam, 0);
Sleep(1000);
SendMessage(hwndChild, WM_KEYUP, (WPARAM)lParam, 0);
}
Firstly install Spy++ from Visual Studio which lets you see all the HWND windows in hierarchy ( and which process owns them).
Then you'll see why your FindWindow is failing. You'll also know the exact hierarchy calls to make on FindWindow and GetWindow().
Be aware that since Vista some HWNDs are protected and you cant send to them - but notepad is probably fine.
For sending the key, you can probably just use PostMessage to fire and forget.
First of all, why is while(true) there? Wouldn't you rather want to activate your software on F12 key press than having an infinite loop? That handle is not valid forever, you know.
Second, you'd probably want to use EnumWindows to go through all the windows and find the one you're interested in. Then you'd implement a callback function that'll need to decide on some basis if it wants to act on some window or not (be it name or something else).
SendMessage/SendMessageA/SendMessageW should work just fine when you've found a proper handle for the window you want to target for (save for some special windows that are protected from this).
So I thought this would be pretty simple, but I forgot it's MFC. Instead of registering a notification listener for data model changes that would possibly require a GUI update on each individual control I figure why not register it once and then send a message to all the open dock panes and allow them to update their controls as needed on their own terms for efficiency.
My callback function for handling the notification from the server looks something like this:
void CMainFrame::ChangeCallback(uint32_t nNewVersion, const std::vector<uint32_t>& anChangedObjectTypes)
{
CObList panes;
GetDockingManager()->GetPaneList(panes); // assert failure
if (!panes.IsEmpty())
{
POSITION pos = panes.GetHeadPosition();
while (pos)
{
CDockablePane* pPane = dynamic_cast<CDockablePane*>(panes.GetNext(pos));
if (pPane)
pPane->PostMessage(DM_REFRESH, nNewVersion);
}
}
}
The error I am getting is an assertion failure on line 926 of wincore.cpp
CHandleMap* pMap = afxMapHWND();
ASSERT(pMap != NULL); // right here
There is a comment below this saying this can happen if you pass controls across threads however this is a single threaded MFC application and this is all being done from the main frame.
Does anyone know what else can cause this?
If there is another way to go about sending a message to all the open CDockablePane derived windows in MFC that works as well ...
Here's the obvious workaround that I didn't want to have to do but after hours of debugging and no response here I guess this is a viable answer:
I added std::vector<CDockPane*> m_dockList; to the members of CMainFrame
Now after each call to AddPane in various places that can create and open new dock panes I make a subsequent call to push_back and then I override CDockablePane::OnClose like so:
CMainFrame* pMainFrame = reinterpret_cast<CMainFrame*>(AfxGetMainWnd());
if (pMainFrame)
{
std::vector<CDockPane*>::const_iterator found(
std::find(pMainFrame->DockList()->begin(), pMainFrame->DockList()->end(), this));
if (found != pMainFrame->DockList()->end())
pMainFrame->DockList()->erase(found);
}
CDockablePane::OnClose();
Now this list will only contain pointers to open dock panes which allows me to handle the event notification in my callback and simply do a for loop and PostMessage to each.
My application (the bootstrap application for an installer that I'm working on needs to launch some other applications (my installer and third party installers for my installer's prerequisites) and wait for them to complete. In order to allow the GUI to do screen updates while waiting for an app to complete, I put a message pump in the wait loop using the 'MFC-compatible' example in the Visual Studio documentation on idle loop processing as a guideline. My code (which is in a member function of a CWinApp-derived class) is as follows:
if (::CreateProcess(lpAppName, szCmdLineBuffer, NULL, NULL, TRUE, 0, NULL, NULL,
&StartupInfo, &ProcessInfo))
{
::GetExitCodeProcess(ProcessInfo.hProcess, &dwExitCode);
if (bWait)
while (dwExitCode == STILL_ACTIVE)
{
// In order to allow updates of the GUI to happen while we're waiting for
// the application to finish, we must run a mini message pump here to
// allow messages to go through and get processed. This message pump
// performs much like MFC's main message pump found in CWinThread::Run().
MSG msg;
while (::PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE))
{
if (!PumpMessage())
{
// a termination message (e.g. WM_DESTROY)
// was processed, so we need to stop waiting
dwExitCode = ERROR_CANT_WAIT;
::PostQuitMessage(0);
break;
}
}
// let MFC do its idle processing
LONG nIdle = 0;
while (OnIdle(nIdle++))
;
if (dwExitCode == STILL_ACTIVE) // was a termination message processed?
{
// no; wait for .1 second to see if the application is finished
::WaitForSingleObject(ProcessInfo.hProcess, 100);
::GetExitCodeProcess(ProcessInfo.hProcess, &dwExitCode);
}
}
::CloseHandle(ProcessInfo.hProcess);
::CloseHandle(ProcessInfo.hThread);
}
else
dwExitCode = ::GetLastError();
The problem that I'm having is that, at some point, this message pump seems to free up window and menu handles on the window that I have open at the time this code is run. I did a walk through in the debugger, and at no time did it ever get into the body of the if (!PumpMessage()) statement, so I don't know what's going on here to cause the window and menu handles to go south. If I don't have the message pump, everything works fine, except that the GUI can't update itself while the wait loop is running.
Does anyone have any ideas as to how to make this work? Alternatively, I'd like to launch a worker thread to launch the second app if bWait is TRUE, but I've never done anything with threads before, so I'll need some advice on how to do it without introducing synchronization issues, etc. (Code examples would be greatly appreciated in either case.)
I've also posted this question on the Microsoft forums, and thanks to the help of one Doug Harris at Microsoft, I found out my problem with my HWND and HMENU values was, indeed due to stale CWwnd* and CMenu* pointers (obtained using GetMenu() and GetDialogItem() calls. Getting the pointers again after launching the second app solved that problem. Also, he pointed me to a web site* that showed a better way of doing my loop using MsgWaitForMultipleObjects() to control it that doesn't involve the busy work of waiting a set amount of time and polling the process for an exit code.
My loop now looks like this:
if (bWait)
{
// In order to allow updates of the GUI to happen while we're
// waiting for the application to finish, we must run a message
// pump here to allow messages to go through and get processed.
LONG nIdleCount = 0;
for (;;)
{
MSG msg;
if (::PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE))
PumpMessage();
else //if (!OnIdle(nIdleCount++))
{
nIdleCount = 0;
if (!PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE))
{
DWORD nRes = ::MsgWaitForMultipleObjects(1, &ProcessInfo.hProcess,
FALSE, INFINITE, QS_ALLEVENTS);
if (nRes == WAIT_OBJECT_0)
break;
}
}
}
}
::GetExitCodeProcess(ProcessInfo.hProcess, &dwExitCode);
*That Web site, if you're curious, is: http://members.cox.net/doug_web/threads.htm
I think your problem is in WaitForSingleObject
Looking in MSDN you see this
Use caution when calling the wait functions and code that directly or indirectly creates windows. If a thread creates any windows, it must process messages. Message broadcasts are sent to all windows in the system. A thread that uses a wait function with no time-out interval may cause the system to become deadlocked. Two examples of code that indirectly creates windows are DDE and the CoInitialize function. Therefore, if you have a thread that creates windows, use MsgWaitForMultipleObjects or MsgWaitForMultipleObjectsEx, rather than WaitForSingleObject.
In my code in the message pump use use MsgWaitForMultipleObjects (doc).
With a call this call.
MsgWaitForMultipleObjects(1, &ProcessInfo.hProcess, FALSE, 100, QS_ALLEVENTS);
This should stop your problem with the resources dissapearing.
When you say that window and menu handles seem to be being freed, do you mean that you've got actual HWND and HMENU values that no longer seem to work, or have you got MFC CWnd* and CMenu* variables that fail?
If the latter, the problem is most likely that you're getting the CWnd* pointers by calling CWnd::FromHandle() (or CMenu::FromHandle()) somewhere (or calling something that calls them), and OnIdle() is discarding them.
The underlying reason is that MFC maintains a map from window (or menu, etc.) handles to CWnd* objects in the system. When CWnd::FromHandle() is called, it looks for a match in the map: if one is found, it's returned. If not, a new, temporary CWnd is created, added to the map, and returned. The idea behind OnIdle() is that when it's called all message processing is done, so OnIdle() discards any of these temporary CWnd objects that still exist. That's why the CWnd::FromHandle() documentation warns that the returned pointer may be temporary.
The "correct" solution to this is to not hang onto the CWnd* pointers returned from CWnd::FromHandle(). Given the simplicity of your application, it might be easier to just remove the call OnIdle(): this shouldn't have any negative effects on an installer.
Of course, this is all something of a guess, but it sounds plausible...
There is a Windows function called DisableProcessWindowsGhosting (see http://msdn.microsoft.com/en-us/library/ms648415(v=vs.85).aspx) that prevents Windows from 'ghosting' your window, and continue updating the window (your animation).