`
dato0123
  • 浏览: 906623 次
文章分类
社区版块
存档分类
最新评论

在非主线程中创建窗口

 
阅读更多

//========================================================================
//TITLE:
// 在非主线程中创建窗口
//AUTHOR:
// norains
//DATE:
// Saturday 29-December-2007
//Environment:
// VS2005 + SDK-WINCE5.0-MIPSII
//========================================================================

很多朋友都会有过这样的经历,为什么在主线程中创建窗口且窗口工作很正常,但一移到非主线程(有的朋友喜欢叫它为工作线程),却无法正常工作.本文就这个问题和各位探讨,可能无法做到尽善尽美,但能抛砖引玉也算是欣慰了.

在主线程中创建一个能够正常工作的窗口,估计地球人都知道.

这是一段工作正常的代码:


#include
"windows.h"

HWNDg_hWnd
=NULL;
HINSTANCEg_hInst;


LRESULTWndProc(HWNDhWnd,UINTwMsg,WPARAMwParam,LPARAMlParam)
{
returnDefWindowProc(hWnd,wMsg,wParam,lParam);
}

voidCreateWnd(void)
{

WNDCLASSwc
={0};
wc.style
=0;
wc.lpfnWndProc
=WndProc;
wc.cbClsExtra
=0;
wc.cbWndExtra
=0;
wc.hInstance
=g_hInst;
wc.hIcon
=NULL;
wc.hCursor
=LoadCursor(NULL,IDC_ARROW);
wc.hbrBackground
=(HBRUSH)GetSysColorBrush(COLOR_WINDOW);
wc.lpszMenuName
=NULL;
wc.lpszClassName
=TEXT("SimpleWindow");

RegisterClass(
&wc);

g_hWnd
=CreateWindowEx(0,
TEXT(
"SimpleWindow"),
TEXT(
"SimpleWindow"),
WS_VISIBLE,
0,
0,
200,
200,
NULL,
NULL,
g_hInst,
0);
}



intWINAPIWinMain(HINSTANCEhInstance,
HINSTANCEhPrevInstance,
LPTSTRlpCmdLine,
intnCmdShow)
{
//TODO:Placecodehere.

g_hInst
=hInstance;

CreateWnd();

//Themessageloop
MSGmsg;
while(GetMessage(&msg,NULL,0,0))
{
TranslateMessage(
&msg);
DispatchMessage(
&msg);
}

return0;
}

如果我们创建一个线程,然后在这个线程中创建窗口,看看带给我们的是什么:

#include
"windows.h"

HWNDg_hWnd
=NULL;
HINSTANCEg_hInst;


LRESULTWndProc(HWNDhWnd,UINTwMsg,WPARAMwParam,LPARAMlParam)
{
returnDefWindowProc(hWnd,wMsg,wParam,lParam);
}

voidCreateWnd(void)
{

WNDCLASSwc
={0};
wc.style
=0;
wc.lpfnWndProc
=WndProc;
wc.cbClsExtra
=0;
wc.cbWndExtra
=0;
wc.hInstance
=g_hInst;
wc.hIcon
=NULL;
wc.hCursor
=LoadCursor(NULL,IDC_ARROW);
wc.hbrBackground
=(HBRUSH)GetSysColorBrush(COLOR_WINDOW);
wc.lpszMenuName
=NULL;
wc.lpszClassName
=TEXT("SimpleWindow");

RegisterClass(
&wc);

g_hWnd
=CreateWindowEx(0,
TEXT(
"SimpleWindow"),
TEXT(
"SimpleWindow"),
WS_VISIBLE,
0,
0,
200,
200,
NULL,
NULL,
g_hInst,
0);
}


DWORDCreateThread(PVOIDpArg)
{
CreateWnd();
return0;
}


intWINAPIWinMain(HINSTANCEhInstance,
HINSTANCEhPrevInstance,
LPTSTRlpCmdLine,
intnCmdShow)
{
//TODO:Placecodehere.

g_hInst
=hInstance;

HANDLEhThrd
=CreateThread(NULL,0,CreateThread,NULL,0,NULL);
CloseHandle(hThrd);

//Themessageloop
MSGmsg;
while(GetMessage(&msg,NULL,0,0))
{
TranslateMessage(
&msg);
DispatchMessage(
&msg);
}

return0;
}


我们似乎什么都没见到,只是窗口一闪,啥都没了.因为g_hWnd为全局变量,我们的理智告诉我们,在主线程没有退出之前,g_hWnd是不会销毁的.而用断点调试,将会发现在WndProc函数中只能接收WM_CREATE及以后一些消息,之后的再也收不到了,特别是WM_PAINT似乎就凭空消失了!那么,代码什么都没变更,只是移动到了分线程中,为何会出现这个问题呢?

一切似乎很简单,在MSDN中我们找到了答案(原文见:http://support.microsoft.com/kb/90975/en-us):

In a multithreaded application, any thread can call the CreateWindow() API to create a window. There are no restrictions on which thread(s) can create windows.

It is important to note that the message loop and window procedure for the window must be in the thread that created the window. If a different thread creates the window, the window won't get messages from DispatchMessage(), but will get messages from other sources. Therefore, the window will appear but won't show activation or repaint, cannot be moved, won't receive mouse messages, and so on.

该段话大意是:窗口在任何线程中都可以创建,但消息循环必须要和创建窗口在同一线程,否则窗口将无法从DispatchMessage()获取任何消息!

原来如此,最重要是这么一句:It is important to note that the message loop and window procedure for the window must be in the thread that created the window.

好吧,那么我们在支线程中放置消息循环代码,看看是什么结果吧:


#include
"windows.h"

HWNDg_hWnd
=NULL;
HINSTANCEg_hInst;


LRESULTWndProc(HWNDhWnd,UINTwMsg,WPARAMwParam,LPARAMlParam)
{
returnDefWindowProc(hWnd,wMsg,wParam,lParam);
}

voidCreateWnd(void)
{

WNDCLASSwc
={0};
wc.style
=0;
wc.lpfnWndProc
=WndProc;
wc.cbClsExtra
=0;
wc.cbWndExtra
=0;
wc.hInstance
=g_hInst;
wc.hIcon
=NULL;
wc.hCursor
=LoadCursor(NULL,IDC_ARROW);
wc.hbrBackground
=(HBRUSH)GetSysColorBrush(COLOR_WINDOW);
wc.lpszMenuName
=NULL;
wc.lpszClassName
=TEXT("SimpleWindow");

RegisterClass(
&wc);

g_hWnd
=CreateWindowEx(0,
TEXT(
"SimpleWindow"),
TEXT(
"SimpleWindow"),
WS_VISIBLE,
0,
0,
200,
200,
NULL,
NULL,
g_hInst,
0);
}


DWORDCreateThread(PVOIDpArg)
{
CreateWnd();

//Themessageloop
MSGmsg;
while(GetMessage(&msg,NULL,0,0))
{
TranslateMessage(
&msg);
DispatchMessage(
&msg);
}


return0;
}


intWINAPIWinMain(HINSTANCEhInstance,
HINSTANCEhPrevInstance,
LPTSTRlpCmdLine,
intnCmdShow)
{
//TODO:Placecodehere.

g_hInst
=hInstance;

HANDLEhThrd
=CreateThread(NULL,0,CreateThread,NULL,0,NULL);
CloseHandle(hThrd);


MSGmsg;
while(GetMessage(&msg,NULL,0,0))
{
TranslateMessage(
&msg);
DispatchMessage(
&msg);
}

return0;
}

一切正常,如同在主线程创建一样!

当然了,还有点需要注意的,在这个例子中,由于消息循环在主线程和分线程都分别存在,如果在WndProc()调用PostQuitMessage(),那么退出的也仅仅是分线程,而主线程还是会不停地在等待消息,从而导致程序无法正常退出.不过倒不用过分担心,和这个示例代码不同,在实际代码编写中,在主线程往往都会创建主窗口,而在这个主窗口消息处理函数调用PostQuitMessage()则完全可以让主线程正常退出.

事实告诉我们,非主线程创建窗口也能工作正常,只要我们注意一点:消息循环必须要和创建窗口在同一线程!
分享到:
评论

相关推荐

    qt 多线程 防止主线程做循环操作导致界面假死

    qt 多线程 防止主线程做循环操作导致界面假死。试过多线程的几种方法,只有这个方法可行。代码亲测可行。在子线程死循环,界面正常不死!!!

    多线程模拟汽车司机与售票员同步

    用C语言实现多线程模拟汽车司机与售票员的同步问题,操作系统实验

    将创建的线程绑定到双核CPU指定的一颗CPU中

    创建一个线程,并将该线程绑定到多核cpu中,不占用主线程的资源,这样可以在所开的线程中做一些动作,不会影响主线程中的动作。应用:客户将所有的刷新动作交给主线程完成时,可能拖动鼠标,窗口均在不断的刷新,CPU...

    多核线程绑定

    创建一个线程,并将该线程绑定到多核cpu中,不占用主线程的资源,这样可以在所开的线程中做一些动作,不会影响主线程中的动作。应用:客户将所有的刷新动作交给主线程完成时,可能拖动鼠标,窗口均在不断的刷新,CPU...

    操作系统实验——获取当前系统运行进程的信息

    主线程在循环中不断地对shared_var 进行加1操作,即每次循环shared_var 被加1;而新创建的线程则不断地对shared_var 进行减1 操作,即每次循环shared_var 被减1。观察程序运行的结果,并对你看到的现象进行解释。

    第一个duilib工程示例 博客地址:https://gudianxiaoshuo.blog.csdn.net/article

    1)在创建的项目中 wWinMain 所属的头文件中,增加自定义的主线程对象。 2)在 wWinMain 的文件中,增加主线程对象的方法实现,在这里初始化 duilib 3、创建一个窗口类 1) 头文件 2)CPP 3) 加入几个必须的...

    C# 多线程 模态 MessageBox

    在多线程中,有时候使用MessageBox.Show方法弹出对话框,弹出的Messagebox不是模态的,不能满足我的要求.所以有了这段代码.

    基于Swing的打砖块游戏的Java程序

    程序通过创建Ball、Paddle和Block类来实现游戏的运行逻辑,其中...程序的入口 main 方法通过调用 SwingUtilities.invokeLater 方法来创建一个线程并运行 BreakBlockGame 类,以避免在主线程中创建和显示 Swing 组件。

    MFC设计局域网对战五子棋游戏(源代码)

    多线程和互斥对象:因为CSokcet工作在阻塞模式,所以不能使用主线程接受和发送游戏数据,我把绘制图像的代码也放在一个独立线程中,所以需要使用互斥对象来确保主线程退出时所有子线程以释放主线程中的资源,否则会...

    【Python资源】 通过 queue 队列及时刷新 tkinter 界面显示时间的 demo 案例

    通过 queue 队列,我们可以将更新 GUI 的任务安全地传递给主线程,从而避免因为直接在子线程中更新 GUI 而导致的错误。 系统要求: Python 3.x tkinter 库(通常与 Python 标准库一起安装) queue 模块(Python ...

    操作系统实验实验进程管理

    当主线程结束时,调用ExitProcess() API函数,通知系统终止它所拥有的所有正在运行、准备运行或正在挂起的其他线程。当进程正在运行时,可以查看它的许多特性,其中少数特性也允许加以修改。 首先可查看的进程特性是...

    VC MFC类中各种类的指针的获取和应用.doc

    这个对象对应着程序的主线程。而 CWinApp 类中有一个 CWnd * m_pMainWnd 成员变量。这个成员变量记录了应用程序的主窗口。当你新建一个MFC应用程序的时候,在 InitInstance虚函数里都会出现对 m_pMainWnd 赋值的语句...

    多线程案例

    首先,我们写个简单的单线程程序,也就是只有程序自己创建的那个主线程,没有使用多线程. 创建一个新工程,向窗口添加一个label命名为label1;我们要让程序运行时label1就显示一个数字,假设为100;通常我们会直接在窗口...

    MFC创建右键弹出菜单的方法

    因为视类窗口始终覆盖在框架窗口之上,框架窗口接收不到鼠标消息,所以由视类捕获WM_RBUTTONDOWN消息。 代码如下:void CMenuView::OnRButtonDown(UINT nFlags, CPoint point) {  // TODO: 在此添加消息处理程序...

    [『辅助』] 易编远航第一期-六套大漠多线程中级进阶视频教程

    易大漠多线程中级之同步器设置主窗与多线程创建  4.易大漠多线程中级之同步器子窗口设置及获取鼠标状态 5.易大漠多线程中级之同步器同步鼠标 6.易大漠多线程中级之同步器子窗口设置及获取键盘状态 7.易大漠多...

    mse-in-workers-demo:在DedicatedWorker上下文中演示MSE使用情况(请参阅https

    工人演示中的MediaSource 在DedicatedWorker上下文中演示MSE使用情况作者: 马特·沃内兹(Matt Wolenetz)@ Google参考演示说明演示了如何通过专用工作器上下文使用Media Source Extensions API来避免在主窗口上...

    c# winform多线程的小例子

    在文本框中输入一个数字,点击开始累加按钮,程序计算从1开始累计到该数字的结果。...1:如何在工作者线程中访问主线程创建的控件; 2:如何取消比较耗时的计算; 为了便于在工作者线程中调用累加过程

    mfc常用类及其成员函数

    在MFC应用程序中有且仅有一个CWinApp派生类的对象,代表程序运行的主线程,代表应用程序本身。 CWnd类 由CCmdTarget类直接派生,是MFC中最基本的GUI对象。公共变量m_hWnd用于存放供API函数调用的窗口句柄。

    java多线程机制 -- 源码详解

    下面的例子3是一个应用程序,这个应用程序在创建窗口的同时又创建了一个新的线程,该线程负责让窗口中的一个按钮改变它的大小。 例子 3 import java.awt.*; import java.awt.event.*; public class Example3 { ...

    CreateThread创建多线程与单线程比较

    那么新创建的线程就具有和主线程一样的安全性. 如果要在线程内结束线程,可以在线程内调用 AfxEndThread. 一般直接用AfxBeginThread(ThreadProc,this); 示例: UINT myproc(LPVOID lParam){CITTDlg *pWnd = ...

Global site tag (gtag.js) - Google Analytics