Windows消息泵以及操作系统如何发送消息 [英] Windows message pumps and how Operating system sends messages

查看:109
本文介绍了Windows消息泵以及操作系统如何发送消息的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

您好,


我的应用程序有很多插件和所有作为表单添加到一个窗口(作为选项卡式窗格的表)。问题是每个插件上都有许多组件,其中一些组件由于处理了
的Windows消息而导致耗时的工作。有一个消息泵(消息循环,如果我没有正确使用术语,请原谅我)。其中一个插件上的耗时工作导致完全应用延迟。


我知道我可以尝试摆脱处理消息时耗时的工作。这就在眼前,但我想要的是为每个插件运行不同的消息泵,这样在插件
的Windows消息泵中发生的延迟不会导致主窗口的延迟。


当我在Microsoft文档中阅读以下内容时:


https: //docs.microsoft.com/en-us/windows/desktop/LearnWin32/window-messages 


" 对于创建窗口的每个线程,操作系统创建窗口消息的队列。此队列保存在该线程上创建的所有窗口的消息。队列本身对您的程序是隐藏的。你不能直接操纵
队列。但是,您可以通过调用
GetMessage 
功能从队列中提取消息。"


我认为如果我在单独的线程中创建每个窗口,那就足够了。  我尝试过,在不同的线程中创建2个窗口,并看到他们的消息泵没有相互影响。但这两个窗口是完全分开的。


当我创建其中一个作为另一个的孩子时,虽然消息泵是分开的,如果有的话在孩子的消息循环中是一项耗时的工作,父母也是滞后的。不知何故,操作系统暂停父窗口的消息队列
。以下是我的方式。


我想要的可能吗?我可以创建一个子窗口,它具有单独的消息泵而不是滞后于父窗口。这是正确的方法吗?如果不是在父窗口和子窗口之间分隔消息泵的正确方法是什么? 


注意:我尝试使用win32 api


 #ifndef UNICODE 
#define UNICODE
#endif

#define _CRT_SECURE_NO_WARNINGS

#include< windows.h>
#include" resource.h"
#include< stdio.h>

LRESULT CALLBACK WindowProc(HWND hwnd,UINT uMsg,WPARAM wParam,LPARAM lParam);
INT_PTR CALLBACK DialogProc(HWND hWnd,UINT消息,WPARAM wParam,LPARAM lParam);

HWND _hwnd1;
HWND _hwnd2;

DWORD WINAPI threadSvc(LPVOID lpParam);
DWORD WINAPI threadSvc_1(LPVOID lpParam);
DWORD WINAPI threadSvc_2(LPVOID lpParam);

int WINAPI wWinMain(HINSTANCE hInstance,HINSTANCE,PWSTR pCmdLine,int nCmdShow)
{



/ *启动一个主题试用* /
#if 1
DWORD dwThreadId1 = NULL;
HANDLE myThread1 = CreateThread(NULL,//默认安全属性
0,//使用默认堆栈大小
threadSvc_1,//线程函数名称
/ * pDataArray [i] * /& hInstance,//线程函数的参数
0,//使用默认创建标志
& dwThreadId1); //返回线程标识符

if(myThread1 == NULL)
OutputDebugStringA(" thread 1 failed as startn \ n);;
else
OutputDebugStringA(" thread 1 started\\\
");
#endif
Sleep(2000);

#if 1
DWORD dwThreadId2 = NULL;
HANDLE myThread2 = CreateThread(NULL,//默认安全属性
0,//使用默认堆栈大小
threadSvc_2,//线程函数名称
/ * pDataArray [i] * /& hInstance,//线程函数的参数
0,//使用默认创建标志
& dwThreadId2); //返回线程标识符
if(myThread2 == NULL)
OutputDebugStringA("thread 2无法启动\ n");
else
OutputDebugStringA(" thread 2 started\\\
");
#endif

//等待
而(1)睡眠(1000);



//运行消息循环。 - 不需要消息循环,因为它没有窗口。
// MSG msg = {};
// while(GetMessage(& msg,NULL,0,0))
// {
// TranslateMessage(& msg);
// DispatchMessage(& msg);
//}

返回0;
} // wWinMain返回

LRESULT CALLBACK WindowProc1(HWND hwnd,UINT uMsg,WPARAM wParam,LPARAM lParam)
{
DWORD threadId = GetCurrentThreadId();
char buf [512] = {};
sprintf(& buf [0]," barman:WindowProc1 [threadId =%08ul] [msgId =%04u] \ n",threadId,uMsg);
OutputDebugStringA(buf);

switch(uMsg)
{
case WM_DESTROY:
PostQuitMessage(0);
返回0;

case WM_SETCURSOR:
OutputDebugStringA(" WM_SETCURSOR \ n");
返回0;

#if 1
case WM_PAINT:
{
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hwnd,& ps);
FillRect(hdc,& ps.rcPaint,(HBRUSH)(COLOR_WINDOW + 1));
EndPaint(hwnd,& ps);
}
返回0;
#endif
}
//默认窗口处理器
返回DefWindowProc(hwnd,uMsg,wParam,lParam);
}

LRESULT CALLBACK WindowProc2(HWND hwnd,UINT uMsg,WPARAM wParam,LPARAM lParam)
{
DWORD threadId = GetCurrentThreadId();
char buf [512] = {};
sprintf(& buf [0]," barman:WindowProc2 [threadId =%08ul] [msgId =%04u] \ n",threadId,uMsg);
OutputDebugStringA(buf);

switch(uMsg)
{
case WM_DESTROY:
PostQuitMessage(0);
返回0;

case WM_PAINT:
{
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hwnd,& ps);
FillRect(hdc,& ps.rcPaint,(HBRUSH)(COLOR_WINDOW + 5));
EndPaint(hwnd,& ps);
}
返回0;
#if 0
case WM_NCHITTEST:
{
PostMessage(_hwnd1,WM_NCHITTEST,wParam,lParam);
}
返回DefWindowProc(hwnd,uMsg,wParam,lParam);
#endif
case WM_LBUTTONDOWN:
{

sprintf(& buf [0]," barman:MOUSE CLICKED ... [threadId =%ul] \ n",threadId);
OutputDebugStringA(buf);
#if 1
/ *在msg队列中创建延迟100百万* /
for(long i = 0; i< 100000000; i ++)
{
printf("de");
}
#endif
}
返回0;

} //切换案例结束
//到默认窗口处理器
返回DefWindowProc(hwnd,uMsg,wParam,lParam);
}

/ *无用线程* /
DWORD WINAPI threadSvc(LPVOID lpParam)
{
while(1)
{
OutputDebugStringA("thread running\\\
");
睡眠(500);
}

返回0;
}

//启动一个窗口并注册它然后处理它的消息队列
DWORD WINAPI threadSvc_1(LPVOID lpParam)
{

WNDCLASS wc1 = {};
const wchar_t CLASS_NAME_1 [] = L" First sample window class" ;;
wc1.lpfnWndProc = WindowProc1; // win proc for message pump
wc1.hInstance = *((HINSTANCE *)lpParam);
wc1.lpszClassName = CLASS_NAME_1;

RegisterClass(& wc1);

_hwnd1 = CreateWindowEx(0,// ex style id DWORD
CLASS_NAME_1,// class name
L" Learn to Program Windows 1",// window name
WS_OVERLAPPEDWINDOW // style
| WS_CLIPCHILDREN,//父母不要重绘子窗口。
CW_USEDEFAULT,//大小和位置
CW_USEDEFAULT,//大小和位置
CW_USEDEFAULT,//大小和位置
CW_USEDEFAULT,//大小和位置
NULL,//父窗口
NULL,// ?? hMenu
*((HINSTANCE *)lpParam ),// hInstance
NULL); // lpParam - 类型LPVOID->类型为void

的长指针if(_hwnd1 == NULL)
{
返回0;
}

ShowWindow(_hwnd1,5);
MSG msg = {};
#if 1

char buf [512] = {};

BOOL getMessageResult = false;

while(getMessageResult = GetMessage(& msg,NULL,0,0)!= 0)
{
if(getMessageResult == -1)
{
sprintf(& buf [0],"任务1消息循环获取消息返回错误[threadID:%04ul] \ n",GetCurrentThreadId());
OutputDebugStringA(buf);
返回0;
}
其他
{
sprintf(& buf [0],发送消息之前的Thread-1 [threadId:%04ul] [WHND:%d] \ n",GetCurrentThreadId(),msg.hwnd);
OutputDebugStringA(buf);
TranslateMessage(& msg);
DispatchMessage(& msg);
}


sprintf(& buf [0],发送消息后的Thread-1 [threadId:%04ul] [WHND:%d] \ n" ,GetCurrentThreadId(),msg.hwnd);
OutputDebugStringA(buf);
}
#endif


#if 0
// PeekMessage循环示例
while(WM_QUIT!= msg.message)
{
while(PeekMessage(& msg,NULL,0,0,PM_REMOVE)> 0)//或者使用if语句
{
TranslateMessage(& msg);
DispatchMessage(& msg);
}
}
#endif
返回0;
}

DWORD WINAPI threadSvc_2(LPVOID lpParam)
{
WNDCLASS wc2 = {};
const wchar_t CLASS_NAME_2 [] = L" Second sample window class" ;;
wc2.lpfnWndProc = WindowProc2;
wc2.hInstance = *((HINSTANCE *)lpParam);
wc2.lpszClassName = CLASS_NAME_2;

RegisterClass(& wc2);

_hwnd2 = CreateWindowEx(0,
CLASS_NAME_2,
L" Learn to program Windows 2",
WS_CHILD //使用的样式。
| WS_VISIBLE
// | WS_CAPTION
// | WS_SYSMENU
// | WS_THICKFRAME
// | WS_MINIMIZEBOX
// | WS_MAXIMIZEBOX
/ * WS_OVERLAPPEDWINDOW * /,
//大小和位置
CW_USEDEFAULT,CW_USEDEFAULT,300,300,
_hwnd1 / * NULL * /,//父窗口
NULL,
*( (HINSTANCE *)lpParam),
NULL);

if(_hwnd2 == NULL)
{
return 0;
}

ShowWindow(_hwnd2,5);
MSG msg = {};

#if 1
while(GetMessage(& msg,NULL,0,0))
{
if(msg.hwnd == _hwnd2)
{
TranslateMessage(& msg);
OutputDebugString(L" Thread-2 before Dispatch Message \ n");
DispatchMessage(& msg);
OutputDebugString(L" Dispatch Message之后的Thread-2;');
}
else
{
OutputDebugString(L" Thread-2忽略了msg \ n");
}


}
#endif


#if 0
// PeekMessage循环示例
while(WM_QUIT!= msg.message)
{
while(PeekMessage(& msg,NULL,0,0,PM_REMOVE)> 0)//或使用if语句
{
TranslateMessage(& msg);
DispatchMessage(& msg);
}
}
#endif

返回0;
}

解决方案

您好,


每个线程可以有一个消息循环/泵。据我所知,子窗口与其父窗口位于同一个线程中。因此,当孩子忙碌时,父母会等待。


您必须将孩子与父母分开。例如,关于对话框,请使用非模态对话框。其他一切都必须在不同的主题中。


问候,Guido


编辑:例如,对于MFC,请阅读  https://docs.microsoft.com/en-us/previous-versions/975t8ks0(v = vs.140


https ://docs.microsoft.com/en-us/windows/desktop/ProcThread/using-processes-and-threads


Hi,

I have an application that has many plugins and all added as forms into one window (as sheets of a tabbed pane). The thing is there are many components on each of plug ins and some of these components cause time consuming jobs due to processing of windows messages. There is one message pump (message loop, excuse me if I do not use the terminology correctly). Time consuming jobs on one of the plugins cause complete application to lag.

I am aware that I can try to get rid of time consuming jobs in processing messages. That is at hand, but what I want is different message pumps to run for each plug in and in this way a lag occurring in windows message pump of a plugin will not cause lag in main window.

When I read following in Microsoft documentation:

https://docs.microsoft.com/en-us/windows/desktop/LearnWin32/window-messages 

"For each thread that creates a window, the operating system creates a queue for window messages. This queue holds messages for all the windows that are created on that thread. The queue itself is hidden from your program. You cannot manipulate the queue directly. However, you can pull a message from the queue by calling the GetMessage function."

I thought that if I create each window in separate thread, it will be enough.  I tried, create 2 windows in separate threads and saw that their message pumps are not effecting one another. But these 2 windows are completely separated.

When I created one of them as child of the other, although the message pumps are separate, if there is a time consuming job in message loop of child, the parent is also lagged. Somehow the operating system is pausing the message queue of parent window. Below is the way I do it.

Is it possible what I want? Can I create a child window that has a separate message pump and not lagging the parent. And is it the correct way? If not what is the correct way to separate message pumps between parent and child windows? 

Note: I try to use win32 api

#ifndef UNICODE
#define UNICODE
#endif 

#define _CRT_SECURE_NO_WARNINGS

#include <windows.h>
#include "resource.h"
#include <stdio.h>

LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
INT_PTR CALLBACK DialogProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);

HWND _hwnd1;
HWND _hwnd2;

DWORD WINAPI threadSvc(LPVOID lpParam);
DWORD WINAPI threadSvc_1(LPVOID lpParam);
DWORD WINAPI threadSvc_2(LPVOID lpParam);

int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE, PWSTR pCmdLine, int nCmdShow)
{



	/*Start a thread for trial*/
#if 1
	DWORD   dwThreadId1 = NULL;
	HANDLE myThread1 = CreateThread(NULL,                   	// default security attributes
									0,                      	// use default stack size  
									threadSvc_1,			    // thread function name
									/*pDataArray[i]*/&hInstance,// argument to thread function 
									0,                      	// use default creation flags 
									&dwThreadId1);   			// returns the thread identifier 

	if (myThread1 == NULL)
		OutputDebugStringA("thread 1 failed to start\n");
	else 
		OutputDebugStringA("thread 1 started\n");
#endif
	Sleep(2000);
	
#if 1
	DWORD   dwThreadId2 = NULL;
	HANDLE myThread2 = CreateThread(NULL,                   	// default security attributes
									0,                      	// use default stack size  
									threadSvc_2,			    // thread function name
									/*pDataArray[i]*/&hInstance,// argument to thread function 
									0,                      	// use default creation flags 
									&dwThreadId2);   			// returns the thread identifier 
	if (myThread2 == NULL)
		OutputDebugStringA("thread 2 failed to start\n");
	else
		OutputDebugStringA("thread 2 started\n");
#endif

	// wait
	while (1) Sleep(1000);



	// Run the message loop. - no need for a message loop  as it is does not have a window.
//	MSG msg = { };
//	while (GetMessage(&msg, NULL, 0, 0))
//	{
//		TranslateMessage(&msg);
//		DispatchMessage(&msg);
//	}

	return 0;
} //wWinMain returns

LRESULT CALLBACK WindowProc1(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
	DWORD threadId = GetCurrentThreadId();
	char buf[512] = {};
	sprintf(&buf[0], "barman: WindowProc1 [threadId=%08ul] [msgId=%04u]\n", threadId, uMsg);
	OutputDebugStringA(buf);

	switch (uMsg)
	{
	case WM_DESTROY:
		PostQuitMessage(0);
		return 0;

	case WM_SETCURSOR:
		OutputDebugStringA("WM_SETCURSOR \n");
		return 0;
		
#if 1
	case WM_PAINT:
	{
		PAINTSTRUCT ps;
		HDC hdc = BeginPaint(hwnd, &ps);
		FillRect(hdc, &ps.rcPaint, (HBRUSH)(COLOR_WINDOW + 1));
		EndPaint(hwnd, &ps);
	}
	return 0;
#endif
	}
	// to default window processor
	return DefWindowProc(hwnd, uMsg, wParam, lParam);
}

LRESULT CALLBACK WindowProc2(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
	DWORD threadId = GetCurrentThreadId();
	char buf[512] = {};
	sprintf(&buf[0], "barman: WindowProc2 [threadId=%08ul] [msgId=%04u]\n", threadId, uMsg);
	OutputDebugStringA(buf);
	
	switch (uMsg)
	{
		case WM_DESTROY:
			PostQuitMessage(0);
			return 0;

		case WM_PAINT:
		{
			PAINTSTRUCT ps;
			HDC hdc = BeginPaint(hwnd, &ps);
			FillRect(hdc, &ps.rcPaint, (HBRUSH)(COLOR_WINDOW + 5));
			EndPaint(hwnd, &ps);
		}
		return 0;
#if 0
		case WM_NCHITTEST:
		{
			PostMessage(_hwnd1, WM_NCHITTEST, wParam, lParam);
		}
		return DefWindowProc(hwnd, uMsg, wParam, lParam);
#endif
		case WM_LBUTTONDOWN:
		{
			
			sprintf(&buf[0], "barman: MOUSE CLICKED... [threadId=%ul]\n", threadId);
			OutputDebugStringA(buf);
#if 1
			/*To create a lag in msg queue 100 millions*/
			for (long i = 0; i < 100000000; i++)
			{
				printf("de");
			}
#endif
		}
		return 0;

	} // switch case ends
	// to default window processor
	return DefWindowProc(hwnd, uMsg, wParam, lParam);
}

/*useless thread*/
DWORD WINAPI threadSvc(LPVOID lpParam)
{
	while (1)
	{
		OutputDebugStringA("thread running\n");
		Sleep(500);
	}
	
	return 0;
}

// start a window and register it then process its message queue
DWORD WINAPI threadSvc_1(LPVOID lpParam)
{

	WNDCLASS wc1 = { };
	const wchar_t CLASS_NAME_1[] = L"First sample window class ";
	wc1.lpfnWndProc = WindowProc1; // win proc for the message pump
	wc1.hInstance = *((HINSTANCE*)lpParam);
	wc1.lpszClassName = CLASS_NAME_1;

	RegisterClass(&wc1);

	_hwnd1 = CreateWindowEx(0,								// ex style id DWORD
							CLASS_NAME_1,					// class name
							L"Learn to Program Windows 1 ", // window name
							WS_OVERLAPPEDWINDOW				// style
							| WS_CLIPCHILDREN,				// for parent not to overpaint the child window. 
							CW_USEDEFAULT,					// Size and position
							CW_USEDEFAULT,					// Size and position
							CW_USEDEFAULT,					// Size and position
							CW_USEDEFAULT,					// Size and position
							NULL,							// parent window
							NULL,							// ?? hMenu
							*((HINSTANCE*)lpParam),			// hInstance
							NULL);							// lpParam - type LPVOID->long pointer of type void

	if (_hwnd1 == NULL)
	{
		return 0;
	}

	ShowWindow(_hwnd1, 5); 
	MSG msg = { };
#if 1

	char buf[512] = {};
	
	BOOL getMessageResult = false; 
	
	while (getMessageResult = GetMessage(&msg, NULL, 0, 0) != 0)
	{
		if (getMessageResult == -1)
		{
			sprintf(&buf[0], "task 1 message loop get message returned error [threadID:%04ul]\n", GetCurrentThreadId());
			OutputDebugStringA(buf);
			return 0;
		}
		else
		{
			sprintf(&buf[0], "Thread-1 before Dispatch Message [threadId:%04ul] [WHND:%d]\n", GetCurrentThreadId(), msg.hwnd);
			OutputDebugStringA(buf);
			TranslateMessage(&msg);
			DispatchMessage(&msg);
		}

		
		sprintf(&buf[0], "Thread-1 after Dispatch Message [threadId:%04ul] [WHND:%d]\n", GetCurrentThreadId(), msg.hwnd);
		OutputDebugStringA(buf);
	}
#endif


#if 0
	//PeekMessage loop example
	while (WM_QUIT != msg.message)
	{
		while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE) > 0) //Or use an if statement
		{
			TranslateMessage(&msg);
			DispatchMessage(&msg);
		}
	}
#endif
	return 0;
}

DWORD WINAPI threadSvc_2(LPVOID lpParam)
{
	WNDCLASS wc2 = { };
	const wchar_t CLASS_NAME_2[] = L"Second sample window class";
	wc2.lpfnWndProc = WindowProc2;
	wc2.hInstance = *((HINSTANCE*)lpParam);
	wc2.lpszClassName = CLASS_NAME_2;

	RegisterClass(&wc2);

	_hwnd2 = CreateWindowEx(0,
							CLASS_NAME_2,
							L"Learn to Program Windows 2 ",
							WS_CHILD					// Styles that are used. 
							| WS_VISIBLE  
							//| WS_CAPTION
							//| WS_SYSMENU
							//| WS_THICKFRAME
							//| WS_MINIMIZEBOX
							//| WS_MAXIMIZEBOX
							/* WS_OVERLAPPEDWINDOW*/,
							// Size and position
							CW_USEDEFAULT, CW_USEDEFAULT, 300, 300,
							_hwnd1 /*NULL*/, // parent window
							NULL,
							*((HINSTANCE*)lpParam),
							NULL);

	if (_hwnd2 == NULL)
	{
		return 0;
	}

	ShowWindow(_hwnd2, 5);
	MSG msg = { };
	
#if 1
	while (GetMessage(&msg, NULL, 0, 0))
	{
		if (msg.hwnd == _hwnd2)
		{
			TranslateMessage(&msg);
			OutputDebugString(L"Thread-2 before Dispatch Message\n");
			DispatchMessage(&msg);
			OutputDebugString(L"Thread-2 after Dispatch Message\n");
		}
		else
		{
			OutputDebugString(L"Thread-2 ignored a msg\n");
		}

		
	}
#endif


#if 0
	//PeekMessage loop example
	while (WM_QUIT != msg.message)
	{
		while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE) > 0) //Or use an if statement
		{
			TranslateMessage(&msg);
			DispatchMessage(&msg);
		}
	}
#endif

	return 0;
}

解决方案

Hello,

you can have one message loop/pump per thread. As far as I know, a child window is in the same thread as its parent window. So when the child is busy, the parent waits.

You must separate the child from the parent. For example regarding dialogs, use a nonmodal dialog. Everything else must be in separate threads.

Regards, Guido

Edit: for MFC for example, read https://docs.microsoft.com/en-us/previous-versions/975t8ks0(v=vs.140)

https://docs.microsoft.com/en-us/windows/desktop/ProcThread/using-processes-and-threads


这篇关于Windows消息泵以及操作系统如何发送消息的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆