Is it safe to manually handle custom message sent from another thread - c++

I'm trying to post message from one thread to a window in another thread via PostThreadMessage:
auto result = PostThreadMessage(mainThreadId, UserMessage::DestroyWindowRequest, 0, 0);
The MSDN says that
Messages sent by PostThreadMessage are not associated with a window. As a general rule, messages that are not associated with a window cannot be dispatched by the DispatchMessage function.
So I cann't just receive my message. Is it safe to handle a message manually in a message loop as in code below?
INT_PTR AbstractWindow::TranslateMessageLoop()
{
MSG msg{0};
BOOL gotMessage = FALSE;
static const BOOL hasError = -1;
PostMessage(windowHandle, UserMessage::MessageLoopTranslated, 0, 0);
while ((gotMessage = GetMessage(&msg, (HWND)NULL, 0, 0)) != 0 && gotMessage != hasError) {
// Here I process a message. I assume that the message is always destined for my main window,
// so I use it's handle - windowHandle.
if (!msg.hwnd && msg.message == UserMessage::DestroyWindowRequest) {
DestroyWindow(windowHandle);
}
else {
if (PreTranslateMessage(&msg) == FALSE) {
if (IsDialogMessage(windowHandle, &msg) == FALSE) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
}
}
return msg.wParam;
}
I know, MSDN suggest to use custom hooks, but it's a little bit overkill for me now.

Related

PeekMessage always returns FALSE

I have written a small test application that inserts files (with hardcoded paths) into the currently active folder/application via delayed rendering. It works as expected. But I have a question - why does PeekMessage always return FALSE? But if you remove the PeekMessage call, Wndproc will never be called. I read a similar post, but I'm creating a window in the same thread in which I'm trying to process messages.
Code:
static LRESULT CALLBACK WindProc(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam) {
switch (Msg) {
case WM_RENDERALLFORMATS: {
OpenClipboard(hWnd);
EmptyClipboard();
}
case WM_RENDERFORMAT: {
printf("WM_RENDERFORMAT received");
<Here the file paths are copied to the clipboard>
if (Msg == WM_RENDERALLFORMATS)
CloseClipboard();
return 0;
}
case WM_DESTROYCLIPBOARD:
return 0;
}
return DefWindowProc(hWnd, Msg, wParam, lParam);
}
HWND hwnd_;
void thread_(void* ignored) {
WNDCLASSEX wcx = { 0 };
wcx.cbSize = sizeof(WNDCLASSEX);
wcx.lpfnWndProc = WindProc;
wcx.hInstance = GetModuleHandle(NULL);
wcx.lpszClassName = TEXT("my_class");
RegisterClassEx(&wcx);
hwnd_ = CreateWindowEx(0, TEXT("my_class"), TEXT(""), 0, 0, 0, 0, 0, HWND_MESSAGE, NULL, GetModuleHandle(NULL), NULL);
MSG msg;
while (true) {
if (PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE)) {
printf("PeekMessage returned TRUE\n");
TranslateMessage(&msg);
DispatchMessage(&msg);
break;
}
Sleep(1000);
}
}
void main() {
CloseHandle((HANDLE)_beginthread(thread_, 0, NULL));
// let's give some time to thread to create msg window
Sleep(100);
if (OpenClipboard(hwnd_)) {
EmptyClipboard();
SetClipboardData(CF_HDROP, NULL);
CloseClipboard();
}
while (true) {
Sleep(100);
}
}
PeekMessage() (and GetMessage()) only returns messages that are posted via PostMessage() or PostThreadMessage() to the calling thread's message queue. PeekMessage() returns FALSE when there is no posted message available at the moment it is called. GetMessage() blocks until a posted message becomes available.
However, the messages in question (WM_RENDERFORMAT and WM_RENDERALLFORMATS) are instead being sent via SendMessage...() directly to the target window. But, the messages are being sent by another thread, so PeekMessage() (or GetMessage()) is still needed, as they internally dispatch messages that are sent across thread boundaries.
This is stated in PeekMessage()'s documentation:
Dispatches incoming nonqueued messages, checks the thread message queue for a posted message, and retrieves the message (if any exist).
...
During this call, the system dispatches (DispatchMessage) pending, nonqueued messages, that is, messages sent to windows owned by the calling thread using the SendMessage, SendMessageCallback, SendMessageTimeout, or SendNotifyMessage function. Then the first queued message that matches the specified filter is retrieved. The system may also process internal events. If no filter is specified, messages are processed in the following order:
Sent messages
Posted messages
Input (hardware) messages and system internal events
Sent messages (again)
WM_PAINT messages
WM_TIMER messages
As well as the GetMessage() documentation:
Retrieves a message from the calling thread's message queue. The function dispatches incoming sent messages until a posted message is available for retrieval.
...
During this call, the system delivers pending, nonqueued messages, that is, messages sent to windows owned by the calling thread using the SendMessage, SendMessageCallback, SendMessageTimeout, or SendNotifyMessage function. Then the first queued message that matches the specified filter is retrieved. The system may also process internal events. If no filter is specified, messages are processed in the following order:
Sent messages
Posted messages
Input (hardware) messages and system internal events
Sent messages (again)
WM_PAINT messages
WM_TIMER messages
And SendMessage()'s documentation:
If the specified window was created by the calling thread, the window procedure is called immediately as a subroutine. If the specified window was created by a different thread, the system switches to that thread and calls the appropriate window procedure. Messages sent between threads are processed only when the receiving thread executes message retrieval code. The sending thread is blocked until the receiving thread processes the message. However, the sending thread will process incoming nonqueued messages while waiting for its message to be processed. To prevent this, use SendMessageTimeout with SMTO_BLOCK set. For more information on nonqueued messages, see Nonqueued Messages.
With that said, your handling of the WM_RENDERALLFORMATS message is wrong. For one thing, it must not call EmptyClipboard(), and for another, it is not checking whether your app is still the clipboard owner before rendering its data. See What is the proper handling of WM_RENDERFORMAT and WM_RENDERALLFORMATS? for why these points are important.
Also, you have a race condition in your main thread. Before it calls OpenClipboard(), it sleeps for only 100ms to wait for the window to be created first, but there is no guarantee that hwnd_ will have been assigned within that 100ms. It could take that long just for the worker thread to start running, for instance.
A better option is to have the worker thread signal an event when the window is created, and then have the main thread wait on that event (even better would be to simply move the initial SetClipboardData() call into the worker thread itself after creating the window, but you have dismissed that option).
Try something more like this instead:
static LRESULT CALLBACK WindProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
switch (uMsg) {
/*
case WM_CREATE:
if (OpenClipboard(hWnd)) {
EmptyClipboard();
SetClipboardData(CF_HDROP, NULL);
CloseClipboard();
}
return 0;
}
*/
case WM_RENDERALLFORMATS: {
if (OpenClipboard(hWnd)) {
if (GetClipboardOwner() == hWnd) {
SendMessage(hWnd, WM_RENDERFORMAT, CF_HDROP, 0);
}
CloseClipboard();
}
return 0;
}
case WM_RENDERFORMAT: {
printf("WM_RENDERFORMAT received");
if (wParam == CF_HDROP) {
// <Here the file paths are copied to the clipboard>
}
return 0;
}
case WM_DESTROYCLIPBOARD:
return 0;
}
return DefWindowProc(hWnd, Msg, wParam, lParam);
}
HWND hwnd_ = NULL;
void thread_(void* arg) {
HANDLE hEvent = (HANDLE)arg;
WNDCLASSEX wcx = { 0 };
wcx.cbSize = sizeof(WNDCLASSEX);
wcx.lpfnWndProc = WindProc;
wcx.hInstance = GetModuleHandle(NULL);
wcx.lpszClassName = TEXT("my_class");
RegisterClassEx(&wcx);
hwnd_ = CreateWindowEx(0, TEXT("my_class"), TEXT(""), 0, 0, 0, 0, 0, HWND_MESSAGE, NULL, GetModuleHandle(NULL), NULL);
SetEvent(hEvent);
if (hwnd_ == NULL) {
return 0;
}
MSG msg;
while (GetMessage(&msg, NULL, 0, 0)) {
printf("GetMessage returned a message\n");
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
int main() {
HANDLE hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
if (!hEvent)
return 1;
uintptr_t res = _beginthread(thread_, 0, hEvent);
if (res == -1L) {
CloseHandle(hEvent);
return 1;
}
WaitForSingleObject(hEvent, INFINITE);
CloseHandle(hEvent);
if (hwnd_ != NULL) {
if (OpenClipboard(hwnd_)) {
EmptyClipboard();
SetClipboardData(CF_HDROP, NULL);
CloseClipboard();
}
}
HANDLE hThread = (HANDLE)res;
WaitForSingleObject(hThread, INIFINTE);
CloseHandle(hThread);
return 0;
}
why does PeekMessage always return FALSE?
Assuming that the window handle that you pass to PeekMessage is valid, then the reason for PeekMessage returning FALSE is simply that there are no messages in the queue.
That this is the case can be discerned from the documentation which says:
If a message is available, the return value is nonzero.
If no messages are available, the return value is zero.

MFC executing thread problems

I have a CDialog that allow users to navigate, listing and showing files preview in the hard disk. In some cases there might be a lot of heavy files and these cases require a lot of time, so we moved the loading operations in a separate thread.
Now, I expect that moving disk accesses in a separate thread would have let me to use the CDialog normally, but this does not happen so I can't scroll or move the window.
Am I missing something in the process? Here's the code:
void CMyDialog::LoadFiles()
{
// …
std::thread load_file(LoadingRoutine, reinterpret_cast<void *>(&data));
load_file.detach();
// same happens if I use Afx functions
// AfxBeginThread(&CMyDialog::LoadingRoutine, reinterpret_cast<void *>(&data));
// …
}
Problem partially fixed: it seems that using threads prevents the window to consume messages regularly even if it's a non-blocking call.
My workaround consisted in giving the control back to window to let it to consume messages:
// … thread stuff
for (auto nI = 0; nI < nCount; nI++)
{
// Heavy computing
nSleepStep = 5;
nSleepTime = 200;
while (nSleepTime > nSleepStep)
{
while(PeekMessage(&msg, hWnd, 0, 0, PM_REMOVE))
{
switch( msg.message )
{
case WM_TIMER :
case WM_PAINT :
TranslateMessage( &msg );
DispatchMessage( &msg );
break;
}
}
std::this_thread::sleep_for(std::chrono::milliseconds(nSleepStep));
nSleepTime -= nSleepStep;
}
}
The correct way is to use AfxBeginThread or std::thread::detach. You will be able to use the dialog in the main UI thread normally.
Alternatively you can do this in a single thread, assuming your function can be interrupted and broken in to different parts. For example, lets say you have function that takes 3 seconds to complete:
Sleep(3000);
It can be broken in to 30 parts and simulated as
for (int i = 0; i < 30; i++)
Sleep(100);
You can update paint after each move. Note that other dialog messages must be ignored, because this is a single thread and you can only do one thing at a time. You should disable the controls to let the user know the dialog is busy. Example:
void update_paint()
{
MSG msg;
while(PeekMessage(&msg, m_hWnd, 0, 0, PM_REMOVE))
{
if(msg.message == WM_COMMAND && msg.wParam == IDCANCEL) { }//cancel reuested
if(msg.message == WM_PAINT ||
(msg.message >= WM_NCCALCSIZE && msg.message <= WM_NCACTIVATE) ||
(msg.message >= WM_NCMOUSEMOVE && msg.message <= WM_NCMBUTTONDBLCLK))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
}
void CMyDialog::single_thread()
{
MessageBox(L"start");
//disable child controls to let user know the dialog is busy
for(CWnd *p = GetWindow(GW_CHILD); p; p = p->GetWindow(GW_HWNDNEXT))
p->EnableWindow(FALSE);
for(int i = 0; i < 30; i++)
{
Sleep(100);
update_paint();
}
//enable child controls
for(CWnd *p = GetWindow(GW_CHILD); p; p = p->GetWindow(GW_HWNDNEXT))
p->EnableWindow(TRUE);
MessageBox(L"done");
}
If the function cannot be interrupted then launching a second thread is necessary.

Can't Remove WM_TIMER message

I have a problem with my code, and after several hours, I can't seem to figure it out...
Problem: I'm trying to attempt to connect to a server every ten seconds. When the timer first elapses, the callback function is called just fine, but then it is called again immediately (without waiting ten seconds), and it keeps being called again and again and again repeatedly, as if the timer message is not getting removed from the queue. Can anyone help?
The timer is set here:
SConnect::SConnect()
{
hSimConnect = NULL;
Attempt();
SetTimer(hMainWindow, reinterpret_cast<UINT_PTR>(this), 10000, (TIMERPROC)TimerProc);
}
The (only) application message loop is here:
while (true)
{
PeekMessage(&msg, NULL, 0, 0, PM_REMOVE);
if (msg.message == WM_QUIT)
break;
if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
//UPDATES
pSCObj->Update();
manager.Update();
Sleep(50);
}
And the timer callback function is here:
void CALLBACK SConnect::TimerProc(HWND hWnd, UINT uMsg, UINT_PTR idEvent, DWORD dwTime){
SConnect *pSC = reinterpret_cast<SConnect *>(idEvent);
MSG wMsg;
if (!pSC->connected)
pSC->Attempt();
else{
pSC->connected = false;
}
}
I really appreciate any help... Please let me know if you need more info...
Sincerely,
Farley
I think this is happening because you are calling PeekMessage() in your message loop instead of GetMessage().
PeekMessage() will return FALSE if there are no messages in the queue. I don't know what the implementation of PeekMessage() is, but I could see it leaving the contents of the msg parameter alone if there are no messages in the queue. This means that when PeekMessage() returns FALSE, msg will contain the previous message in the queue. msg is then blindly passed to DispatchMessage(), which then dutifully will pass that to your window's window procedure. So as long as the WM_TIMER message is the last processed message, your timer callback will be called until another message is added to the queue.
You can fix this by using a more traditional message loop:
BOOL bRet;
while( (bRet = GetMessage( &msg, nullptr, 0, 0 )) != 0)
{
if (bRet == -1)
{
// handle the error and possibly exit
}
else
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
pSCObj->Update();
manager.Update();
}
(Message loop adapted from the sample in the GetMessage() documentation.)
Since GetMessage() will block until there is a message in the queue, you don't need to call Sleep(), and your process will not use any CPU when there is nothing for it to do.
Problem is that the return value of PeekMessage() is not checked. On each loop, if there is no new message, PeekMessage leaves the contents of msg unchanged, and it is simply dispatched again.
Dispatching the messages only when PeekMessage() returns true fixes the problem:
while (true)
{
if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)){
if (msg.message == WM_QUIT)
break;
if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
//UPDATES
pSCObj->Update();
manager.Update();
Sleep(50);
}

Why application can be marked as "not responding" even if it pump messages?

Why application can be marked as "not responding" even if it pump messages?
Message loop looks like this:
bool bLoop = true;
while (bLoop)
{
int iMsgCount = 0;
MSG msg;
while ( PeekMessage(&msg, NULL, 0U, 0U, PM_REMOVE) )
{
iMsgCount++;
if(msg.message == WM_QUIT)
{
bLoop = false;
break;
{
AppPreprocessIME(msg);
TranslateMessage(&msg);
DispatchMessage(&msg);
}
Log( "Message loop (messages) %d ", iMsgCount );
Log( "ThreadId %d", GetCurrentThreadId() );
if(!bLoop)
break;
// Update/DirectX draw/etc.
}
Loop goes fine (it writes log messages), but Windows still marks it as "not responding".
Is there any other check windows do to make such decision? Or something else can lead to this behavior?
Also, window become responsive if debugger attached (started from debugger or attached during "not responding" state). I guess that debugger hooks something or alter message queue processing.

Tell windows to process all but one message

I have a function triggered by a message(WM_ONDATA defined by me) the function will execute this code :
MSG msg;
while(::PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE))
{
if( !AfxGetApp()->PumpMessage() )
{
::PostQuitMessage(0);
return 0;
}
}
return 1;
The problem is that there could be on the message queue another message that could trigger the function.
I'm wondering if I can make it process all the message but WM_ONDATA?
Recall that the third and fourth parameters to PeekMessage let you specify a range of message values. Messages outside that range won't be processed.
while (PeekMessage(&msg, NULL, 0, WM_ONDATA - 1, PM_NOREMOVE)
|| PeekMessage(&msg, NULL, WM_ONDATA + 1, 0xffff, PM_NOREMOVE))
You could get the window proc to ignore the message or to queue it's execution. If you're just looking to avoid recursion, have a reentrance lock
class MyDlg : ...
{
MyDlg(...) : m_inOnData(false), ... { .... }
...
private:
BOOL m_inOnData;
};
....
void MyDlg::OnOnData(...)
{
if (m_inOnData)
return;
m_inOnData = TRUE;
....
m_inOnData = FALSE;
}
You could get fancy with a scoped RIIA struct (so things will be exception safe and slightly less verbose)
Sure - just check the message number in msg after receipt.