【转】如何在工作线程中创建窗口?

vs2022创建窗口应用程序 vs2022控制台窗口程序 阅读详情

原文:http://blog.pfan.cn/sword2008/29453.html

 

在前面我们研究了使用AFX_MANAGE_STATE(AfxGetStaticModuleState())进行DLL间的资源切换,以及工作线程中创建Windows消息循环的原理,以为就可以搞定一切类似问题了…但是请看以下代码

 

DWORD CTestMFCDlg::ThreadFunc(PVOID yy)

{

CAboutDlg dlg;

dlg.DoModal();

 

return 0;

}

 

void CTestMFCDlg::OnOK()

{

::CreateThread(NULL,NULL,(LPTHREAD_START_ROUTINE)ThreadFunc,NULL,NULL,NULL);

}

 

在VC++6.0上编译运行出现以下ASSERT。

 

void CWnd::AssertValid() const

{

……

 

CHandleMap* pMap = afxMapHWND();

 

……

 

CObject* p;

ASSERT((p = pMap->LookupPermanent(m_hWnd)) != NULL ||

(p = pMap->LookupTemporary(m_hWnd)) != NULL);

ASSERT((CWnd*)p == this);   // must be us

 

MFC有一个全局的Hash表(通过afxMapHWND()获得),用于把HWND句柄与MFC的封装对象CWnd进行关联,这样就可以通过CWnd::FromHandle()等函数把CWnd对象Attach到一个已有的HWND句柄上,利用MFC的封装函数可以简化对HWND的直接操作。很显然,这里的Assert是因为CWnd对象根据自身的窗口句柄(m_hWnd)从Hash表里找到CWnd对象指针与对象的本身(this)并不相同!这说明,CWnd对象创建时注册到的Hash表与目前检索的Hash表并不是同一个。为什么会是这样的呢?

 

CHandleMap* PASCAL afxMapHWND(BOOL bCreate)

{

AFX_MODULE_THREAD_STATE* pState = AfxGetModuleThreadState();

if (pState->m_pmapHWND == NULL && bCreate)

{

…….

 

pState->m_pmapHWND = new CHandleMap(RUNTIME_CLASS(CTempWnd),offsetof(CWnd, m_hWnd));

 

……

}

 

return pState->m_pmapHWND;

 

看来这个Hash表跟AfxGetModuleThreadState()有关,继续

 

AFX_MODULE_THREAD_STATE* AFXAPI AfxGetModuleThreadState()

{

return AfxGetModuleState()->m_thread.GetData();

}

 

AFX_MODULE_STATE* AFXAPI AfxGetModuleState()

{

_AFX_THREAD_STATE* pState = _afxThreadState;

AFX_MODULE_STATE* pResult;

if (pState->m_pModuleState != NULL)

{

// thread state's module state serves as override

pResult = pState->m_pModuleState;

}

else

{

// otherwise, use global app state

pResult = _afxBaseModuleState.GetData();

}

ASSERT(pResult != NULL);

return pResult;

}

 

那么_afxThreadState是什么呢?

 

EXTERN_THREAD_LOCAL(_AFX_THREAD_STATE, _afxThreadState)

 

class _AFX_THREAD_STATE : public CNoTrackObject

{

public:

_AFX_THREAD_STATE();

virtual ~_AFX_THREAD_STATE();

 

// override for m_pModuleState in _AFX_APP_STATE

AFX_MODULE_STATE* m_pModuleState;

AFX_MODULE_STATE* m_pPrevModuleState;

 

#define THREAD_LOCAL(class_name, ident_name) /

AFX_DATADEF CThreadLocal<class_name> ident_name;

#define EXTERN_THREAD_LOCAL(class_name, ident_name) /

extern AFX_DATA THREAD_LOCAL(class_name, ident_name)

 

分析的结果是 extern CThreadLocal<_AFX_THREAD_STATE> _afxThreadState。

 

template<class TYPE>

class CThreadLocal : public CThreadLocalObject

{

AFX_INLINE TYPE* GetData()

{

TYPE* pData = (TYPE*)CThreadLocalObject::GetData(&CreateObject);

ASSERT(pData != NULL);

return pData;

}

AFX_INLINE operator TYPE*()

{ return GetData(); }

AFX_INLINE TYPE* operator->()

{ return GetData(); }

 

static CNoTrackObject* AFXAPI CreateObject()

{ return new TYPE; }

 

 

可以看出来了,_afxThreadState是一个全局的对象。通过该对象可以获得_AFX_THREAD_STATE对象,后者是线程相关的。CThreadLocalObject的代码不再分析,大概就是检查当前的线程私有数据,如果有则返回,否则创建新的对象(即_AFX_THREAD_STATE)。

 

继续看AfxGetModuleState(),大致的意思是获取与当前线程相关联的AFX_MODULE_STATE对象,如果没有则获取该进程的缺省AFX_MODULE_STATE对象。

 

PROCESS_LOCAL(_AFX_BASE_MODULE_STATE, _afxBaseModuleState)

 

class _AFX_BASE_MODULE_STATE : public AFX_MODULE_STATE

 

#define PROCESS_LOCAL(class_name, ident_name) /

AFX_DATADEF CProcessLocal<class_name> ident_name;

#define EXTERN_PROCESS_LOCAL(class_name, ident_name) /

extern AFX_DATA PROCESS_LOCAL(class_name, ident_name)

 

继续看AfxGetModuleThreadState(),在获得了AFX_MODULE_STATE对象之后,访问其m_thread成员。这又是一个线程相关的数据,可以获得AFX_MODULE_STATE在不同线程中的私有数据。

 

// AFX_MODULE_STATE (global data for a module)

class AFX_MODULE_STATE : public CNoTrackObject

{

// define thread local portions of module state

THREAD_LOCAL(AFX_MODULE_THREAD_STATE, m_thread)

 

现在简单总结一下,AfxGetModuleState()可以获得与执行线程关联的AFX_MODULE_STATE,而AfxGetModuleThreadState()可以获得与执行线程关联的AFX_MODULE_STATE与当前执行线程关联的AFX_MODULE_THREAD_STATE。看起来有点绕,再啰嗦几句。我们可以这样理解,每一个线程在执行的时候,需要以一个Module作为缺省上下文,比如查找资源的时候,默认从该Module里查找。这就是为什么我们需要在DLL的输出函数入口处进行资源切换,其实是把调用者线程的上下文设为当前代码所在的Module,从而保证资源的正确装载。另外,Module里代码需要根据不同的执行线程保存不同的全局数据(是不是为了避免访问的冲突?),于是Module还可以有自己独有的线程相关的数据。所以,AFX_MODULE_STATE可以在不同线程中使用,而AFX_MODULE_THREAD_STATE只能在特定线程中使用。

 

现在再来看afxMapHWND(),其返回的与工作线程关联的Module在工作线程下的Hash表。我们现在可以想象,如果工作线程关联Module的不同,或者相同关联Module下,而不是工作线程下,Hash表是完全不同的。

 

现在继续检查堆栈,发现是Assert来自CWnd的消息循环

 

int CWnd::RunModalLoop(DWORD dwFlags)

{

……

 

if (!AfxGetThread()->PumpMessage())

 

CWinThread* AFXAPI AfxGetThread()

{

// check for current thread in module thread state

AFX_MODULE_THREAD_STATE* pState = AfxGetModuleThreadState();

CWinThread* pThread = pState->m_pCurrentWinThread;

 

// if no CWinThread for the module, then use the global app

if (pThread == NULL)

pThread = AfxGetApp();

 

return pThread;

 

_AFXWIN_INLINE CWinApp* AFXAPI AfxGetApp()

{ return afxCurrentWinApp; }

 

#define afxCurrentWinApp

AfxGetModuleState()->m_pCurrentWinApp

 

原来是获得当前执行线程相关的Module,并获得其包含/对应的CWinThread对象,并执行其消息循环函数。现在终于明白,为什么每个支持MFC的模块都要有一个CWinApp(从CWinThread派生)的全局对象了,原来是用于消息循环的!

 

请注意,如果当前线程没有相关联的Module,则返回当前进程的缺省AFX_MODULE_STATE对象,具体请参见AfxGetModuleState()。

 

好了,让我们来看一下,在消息循环里发生了什么。

BOOL CWinThread::PumpMessage()

{

……

 

if (m_msgCur.message != WM_KICKIDLE && !PreTranslateMessage(&m_msgCur))

{

::TranslateMessage(&m_msgCur);

::DispatchMessage(&m_msgCur);

}

return TRUE;

 

BOOL CWinThread::PreTranslateMessage(MSG* pMsg)

{

……

 

// walk from target to main window

CWnd* pMainWnd = AfxGetMainWnd();

if (CWnd::WalkPreTranslateTree(pMainWnd->GetSafeHwnd(), pMsg))

return TRUE;

 

// in case of modeless dialogs, last chance route through main

//   window's accelerator table

if (pMainWnd != NULL)

{

CWnd* pWnd = CWnd::FromHandle(pMsg->hwnd);

if (pWnd->GetTopLevelParent() != pMainWnd)

return pMainWnd->PreTranslateMessage(pMsg);

 

 

原来产生Assert的CWnd对象是主窗口对象,也就是CTestMFCDlg,而不是我们后面在工作线程里创建的CAboutDlg。这个问题让我们很疑惑,为什么在CAboutDlg的消息循环里会调用CTestMFCDlg对象呢?仔细看注释,原来消息循环里试图去查找所谓的主窗口(AfxGetMainWnd),并在找到后调用它的消息预处理函数(PreTranslateMessage)。

 

我们看看主窗口在哪里?

 

_AFXWIN_INLINE CWnd* AFXAPI AfxGetMainWnd()

{ CWinThread* pThread = AfxGetThread();

return pThread != NULL ? pThread->GetMainWnd() : NULL; }

 

CWnd* CWinThread::GetMainWnd()

{

if (m_pActiveWnd != NULL)

return m_pActiveWnd;

// probably in-place active

 

// when not inplace active, just return main window

if (m_pMainWnd != NULL)

return m_pMainWnd;

 

return CWnd::GetActiveWindow();

}

 

很显然是想获取当前线程关联的Module对应的CWinThread对应的主窗口/激活窗口。

 

 

因为主线程和工作线程都没有做Module的切换工作,_afxThreadState->m_pModuleState都为空,所以两个线程执行AfxGetModuleState()的结果总是返回进程缺省的AFX_MODULE_STATE对象,即_afxBaseModuleState。所以AfxGetThread()函数返回的必然是theApp对象(CTestMFCApp),后者的m_pMainWnd成员指向的就是CTestMFCDlg对象。

 

在调用CTestMFCDlg的PreTranslateMessage函数的时候,其内部进行了如下检查

 

CFrameWnd* CWnd::GetTopLevelFrame() const

{

if (GetSafeHwnd() == NULL) // no Window attached

return NULL;

 

ASSERT_VALID(this);

 

然后进入了前面提到的CWnd::AssertValid()函数,终于导致Assert的发生。为什么呢?因为afxMapHWND()是在工作线程下运行的,而m_pMainWnd对应的CTestMFCDlg是在主线程下创建的,两者使用的Hash表是不同的。因此工作线程根据HWND查找CWnd对象时自然是找不到,于是自动创建一个新的CWnd,当然与m_pMainWnd不同啦,虽然HWND是相同的。

 

现在我们来想想,为什么MFC要做前面的ASSERT检查呢?为什么要求MFC对象只能在创建线程里使用而不允许跨线程使用呢?就像在ASSERT的位置的注释所言:

 

// Note: if either of the above asserts fire and you are

// writing a multithreaded application, it is likely that

// you have passed a C++ object from one thread to another

// and have used that object in a way that was not intended.

// (only simple inline wrapper functions should be used)

//

// In general, CWnd objects should be passed by HWND from

// one thread to another.  The receiving thread can wrap

// the HWND with a CWnd object by using CWnd::FromHandle.

//

// It is dangerous to pass C++ objects from one thread to

// another, unless the objects are designed to be used in

// such a manner.

 

是不是为了避免资源访问冲突?比如同时操作一个对象等等,单线程总是比多线程简单很多。又或者MFC本身就使用了太多线程相关的数据,跨线程操作会导致混乱?到目前为止也还没有想清楚……

 

到目前为止,我们已经大概知道问题是如何出现的了,那么如何解决呢?这或许才是大多数人想要了解的。

 

1.使用VS2005编译运行,一切正常。

 

2.使用AfxBeginThread()创建线程,而不是CreateThread()。

void CTestMFCDlg::OnOK()

{

   AfxBeginThread((AFX_THREADPROC)ThreadFunc,NULL);

}

因为AfxBeginThread内部会自动做一些处理,所以也没有问题。

 

3.另外我们还发现,如果只有一个窗口,即没有CTestMFCDlg窗口存在的情况下,CAboutDlg是可以正常显示的,因为不涉及跨线程访问MFC对象的情况。这就是为什么我们经常在非MFC的COM组件里可以多线程使用单个MFC窗口的原因。MFC并不是只能在主线程里使用,只是不能跨线程传递对象而以。

 

4.有没有可能使用CreateThread()创建线程,而还能在线程里显示窗口的?特别是有些代码是第三方开发的,你无法修改的时候。当然了,AfxBeginThread内部可能也是调用CreateThread这个Windows的API,既然它能,我们肯定也能,只是代码的多少而已。有没有可能像前面的文章对DLL输出函数的资源切换那么简单,就用AFX_MANAGE_STATE(AfxGetStaticModuleState())?

 

我们试着加上AFX_MANAGE_STATE(AfxGetStaticModuleState()),结果是

 

_AFXWIN_INLINE HINSTANCE AFXAPI AfxGetResourceHandle()

{ ASSERT(afxCurrentResourceHandle != NULL);

 

#define   afxCurrentResourceHandle   AfxGetModuleState()->m_hCurrentResourceHandle

 

原因是_afxThreadState->m_pModuleState变成了

 

AFX_MODULE_STATE* AFXAPI AfxGetStaticModuleState()

{

AFX_MODULE_STATE* pModuleState = &afxModuleState;

return pModuleState;

}

 

static _AFX_DLL_MODULE_STATE afxModuleState;

 

而原本AfxGetModuleState()返回的是_afxBaseModuleState,两者是不同的。现在连资源都装载不了,更别说下面的消息循环了!而前面我们已经能够装载资源了,所以添加的这句话简直是火上浇油,毫无益处!

 

现在我们就纳闷了,现在的情况跟以前我们遇到的显示DLL里的窗口有什么不同么?为什么之前可以这么做,而现在不可以?看起来两者完全一样嘛!仔细想一想,原来以前的情况是只有一个线程,跨越两个DLL,所以需要切换的只是ModuleState。现在是在同一个DLL/EXE里,需要跨越的是两个线程!哦,这样看来,应该要切换的是ModuleThreadState,让两个线程使用同一个ModuleThreadState。因此,需要线程的创建者把自己的ModuleThreadState传给被创建线程,并进行相关的替换--你可以查看AfxBeginThread/CWinThread的实现代码,而以下是我的简单实现。

 

DWORD CTestMFCDlg::ThreadFunc(PVOID ref_mts)

{

AFX_MODULE_THREAD_STATE* mts=AfxGetModuleThreadState();

 

AFX_MODULE_THREAD_STATE backup_mts;

memcpy(&backup_mts,mts,sizeof(AFX_MODULE_THREAD_STATE));

 

memcpy(mts,(AFX_MODULE_THREAD_STATE*)ref_mts,sizeof(AFX_MODULE_THREAD_STATE));

 

CAboutDlg dlg;

dlg.DoModal();

 

memcpy(mts,&backup_mts,sizeof(AFX_MODULE_THREAD_STATE));

 

return 0;

}

 

void CTestMFCDlg::OnOK()

{

//AfxBeginThread((AFX_THREADPROC)ThreadFunc,NULL);

::CreateThread(NULL,NULL,(LPTHREAD_START_ROUTINE)ThreadFunc,AfxGetModuleThreadState(),NULL,NULL);

}

 

因为线程结束后,线程的私有数据会被删除,所以需要首先保存原有的地址,在最后恢复再删除,否则删除的将是引用的数据。

[MFC]界面线程和工作线程 1.前言MFC提供了2中线程的实现方式,一种是界面线程,可以处理来自系统的消息,比如窗口的点击事件;一种是工作线程,用于处理繁琐的计算或者长时间的任务,例如后台打印,计算等。2.界面线程界限线程的创建需要继承父类CWinThread,并继承与重写部分父类方法。见下表所示: 方法 说明 ExitInstance 线程退出所需的工作 InitInstance 线程创建的时候需要的工 阅读详情

相关推荐

使用MessageWindows类从工作线程切换到UI线程

日前在工作中遇到这样一个问题。从窜口读取RFID扫描器的扫描结果,并用事件通知UI显示扫描结果。于是编写了一个串口读写类,使用工作线程监视窜口,当从串口读写到数据后,触发事件更新UI(关于在Windows Mobile上执行串口读写,可参考Chris Tacke的《P/Invoking Serial APIs in the Compact Framework》)。问题来了,监视窜口的是工作线程,而

kevinojt的专栏 1667

如何在工作线程中创建窗口?

在前面我们研究了使用AFX_MANAGE_STATE(AfxGetStaticModuleState())进行DLL间的资源切换,以及工作线程中创建Windows消息循环的原理,以为就可以搞定一切类似问题了…但是请看以下代码 DWORD CTestMFCDlg::ThreadFunc(PVOID yy){CAboutDlg dlg;dlg.DoModal(); retu

optman的专栏 9211

掌握JavaScript设计模式与工作线程

本文深入探讨了JavaScript中服务工作线程的使用、缓存策略、以及各种工作线程的特点。此外,还介绍了常见的创建型设计模式,包括工厂函数、模块模式和单例模式等,这些模式能有效提升代码的可读性和复用性。

weixin_30481539的博客 719

BOOL CWnd::DestroyWindow()中出现ASSERT(m_hWnd == hWndOrig)断言的原因及解决方法

当我们在Dll中创建对话框时需要使用AFX_MANAGE_STATE(AfxGetStaticModuleState())语句对资源进行初始化,如果不调用这条语句,DLL中的对话框将无法打开。如果这条语句放在对话框类构造完成后,当调用DoModal()然后退出时就会出现ASSERT(m_hWnd == hWndOrig)断言,原因应该是 if (m_hWnd != NULL) { pMap

3533

TwinCAT 3 实战:手把手教你用PLC和C++读写第三方伺服驱动器的CoE参数

本文详细介绍了在TwinCAT 3环境中通过PLC和C++两种技术路线实现对第三方伺服驱动器CoE参数的精准操控。从环境准备到具体实现,包括硬件配置、核心函数使用及性能优化策略,为工程师提供了全面的实战指南。特别针对CoE参数访问的效率和稳定性问题,给出了实用的调试技巧和异常处理方案。

weixin_33743248的博客 442

大模型驱动时序量化因子自动优化:从WorldQuant回测到实战策略

本文探讨了如何利用大模型驱动时序量化因子的自动优化,构建从创意生成到实战验证的完整流程。通过结合WorldQuant回测平台,搭建自动化流水线,详细拆解了因子生成、回测验证与迭代优化的核心模块。文章分享了从初版实现到进阶优化的实践经验,并指出提示词工程与领域知识注入是关键,旨在实现人机智能协作,提升量化策略研发效率。

wdx012345的博客 802

关于多线程中传递MFC窗口类指针时ASSERT_VALID出错的另类解决

在多线程设计中,许多人为了省事,会将对话框类或其它类的指针传给工作线程,而在工作线程中调用该类的成员函数或成员变量等等。但是在Debug版本时,在某些情况下,特别是在工作线程中调用pWnd->UpdateData(FALSE)时,会出现错误。这个错误的原因网上有许多地方讲到了,但是,令人失望的是,讲得好的没几篇,都是非要讲什么线程模块状态什么的,让人看得云里雾里(不过,说实话,也就是从这些文章中才

七七八八 4929

DDoS攻击--Syn_Ack/Ack_Flood等攻击防护详解(TCP)

前言 Syn_Ack/Ack_Flood攻击属于TCP攻击,在了解Syn_Ack/Ack_Flood攻击之前,可以先看看TCP攻击详解。 Syn_Ack Flood 攻击: syn_ack报文出现在连接建立的第2个报文,用来确认第一次握手的syn包。 当服务器收到syn_ack报文后会在系统里查询是否属于3次握手的范畴。如果属于则回复ack,并将连接设为连接状态。若没有查到相关信息,则回复...

一只IT小小鸟 1万+

Visual C++模态对话框消息处理机制的分析

摘要:消息驱动机制是Windows操作系统的根本,模态对话框消息处理又是不同于一般消息处理的特殊形式。通过分析这种消息机制的原理,可用来处理类似的程序设计要求。  在Windows操作系统中,面向用户的GUI基本上可分为对话框形式和文档/视图两种表现形式。对话框的显示方式又可分为模态对话框和非模态对话框,以适应不同的用户交互需求。由于对话框和文档/视图框架结构各有特色,能不能将文档/视图框架结构当

无烟会议室的专栏 产品|编程|软件工程|管理|游戏|电子书 1725

Unity中URP下的菲涅尔效果实现(个性化修改)

在上篇文章中,我们实现了 URP 下的菲涅尔基础效果,我们在这篇文章中,对其进行自定义化修改。

楠溪泽岸的博客 2063

避开命令行!在VMware vCenter 8.0图形化界面里搞定SSL证书续期全流程

本文详细介绍了在VMware vCenter 8.0图形化界面中完成SSL证书续期的全流程,包括计算机SSL证书、VMCA根证书和STS证书的管理。通过可视化操作,管理员无需使用命令行即可高效处理证书问题,确保vSphere环境的稳定运行。文章还提供了证书监控与预警配置的实用技巧,帮助用户提前规避证书过期风险。

weixin_27051161的博客 280

Swing-界面线程与工作线程(创建工作线程)

工作线程: *Work Thread 如果事件处理较长时间,应当创建一个线程来处理这个任务。此线程称为“工作线程” *界面线程:一直运行,处理界面事件 *工作线程:工作完成后退出 *线程的特点:独立、并发 code: ButtonThread: package my; public class ButtonThread extends Thread { @O...

给你更好的 1205

攻克+86手机号Discord验证墙:从“无效”到“成功”的实战指南

本文提供了一份针对+86手机号在Discord注册时频繁遭遇“手机号无效”问题的实战解决指南。文章系统分析了问题根源,并分步详解了从打造纯净网络环境、尝试多种手机号格式(如+86前加0),到更换iOS等客户端设备的有效方法,旨在帮助用户成功完成Discord手机号验证。

mac99的博客 1100

工作线程与界面线程、模态对话框与非模态对话框

模态对话框(model dialog box) 在程序运行的过程中,若出现了模态对话框,那么主窗口将无法发送消息,直到模态对话框退出才可以发送。 点击模态对话框中的OK按钮,模态对话框会被销毁。 创建一个模态对话框的代码: //创建一个模态对话框 UINT _cdecl CvccDlg::ThreadProc(LPVOID lpParameter) { CvccDlg *pThi...

Jeff_的博客 1356

刷机维修进阶教程-----adb禁用错了系统app导致无法开机 如何保数据无损恢复机型

在刷机维修过程中 。我们会遇到一些由于客户使用adb指令来禁用手机app而导致手机无法开机进入系统的故障机型。通常此类问题机型有好几种解决方法。但如果客户需要保数据来恢复机型。其实操作也是很简单的.还有类似误删除应用导致不开机等等如何保数据。  通过博文了解💝💝💝    1💝💝💝----adb禁用或者删除系统app导致无法开机的几种解决方法 2💝💝💝----如何保数据来无损恢复机型 3💝💝💝----其他保数据解决系统类故障无法开机的步骤解析

小马哥的专栏 1349

threadX的USBX的初始化和解初始化的分析。

tx_application_define的最后会使用tx_byte_pool_create创建usb线程的内存空间“ux_device_app_byte_pool”,再创建usb线程,也就是执行。主要就是创建了线程,也就是第3个参数app_ux_device_thread_entry,这个参数内是MX_USB_Device_Init,也就是USB底层初始化。进来的参数只有1个,是memory_ptr,这个就是创建的usb线程空间“ux_device_app_byte_pool”

qq_31885403的博客 647

在非主线程中创建窗口

很多朋友都会有过这样的经历,为什么在主线程中创建窗口且窗口工作很正常,但一移到非主线程(有的朋友喜欢叫它为工作线程),却无法正常工作.本文就这个问题和各位探讨,可能无法做到尽善尽美,但能抛砖引玉也算是欣慰了. 在主线程中创建一个能够正常工作的窗口,估计地球人都知道. 这是一段工作正常的代码: #include "windows.h" HWND g_hWnd...

峻峰飞阳的博客 blog.youkuaiyun.com/while0 347

NTscan扫描工具

ntscan是一个扫描器,可以扫描没有密码和弱密码的共享,先到配置文件中填写猜测的用户名和密码,扫描过程中右侧出现的是扫描日志。

工作线程中调用UI线程创建的窗口的UpdateData会导致Assert的问题及解决办法.

    最近写了个代码,在UI线程中创建了一个窗口,然后在工作线程中修改了这个窗口中的一些数据,然后想用UpdateData(FALSE)来更新窗口的内容,结果在Debug版本下面就出现了Assert报错,说出错地方是wincore.cpp的888行和889行,就是这两句  ASSERT((p = pMap->LookupPermanent(m_hWnd)) != NULL ||   (p

土星·北海若 2531

pscad IEEE标准系统搭建说明

pscad IEEE标准系统搭建说明,包含典型的IEEE 9节点、14节点、30节点、39节点和118节点系统。内含标准测试系统的原始数据和潮流结果。

Probability and Statistics - 4th edition

Probability and Statistics - 4th edition

MFC(8) 如何在工作线程中创建窗口?

http://blog.youkuaiyun.com/optman/article/details/1021672 在前面我们研究了使用AFX_MANAGE_STATE(AfxGetStaticModuleState())进行DLL间的资源切换,以及工作线程中创建Windows消息循环的原理,以为就可以搞定一切类似问题了…但是请看以下代码   DWORD CTestMFCDlg::Thr

luoyouren的专栏 4058

WINCC7.3破解文件

WINCC7.3硬件够破解文件,有需要的下载!WINCC7.3硬件够破解文件,有需要的下载!

MFC工作线程的创建与控件控制

为什么要用MFC? 不是专业开发,只是做个工具。 MFC的主界面是在界面线程运行的,如果在button的响应函数中写了一个用时很长的函数,那么就要考虑运用工作线程,否则MFC程序主界面就会卡住不动,直到函数运行完才会响应消息。 开启工作线程的正确姿势 那就是用MFC封装好的线程函数AfxBeginThread。 CString strFolderPath = L"C:\\"; CWinThread* pThread = NULL; pThread = AfxBeginThread((AFX_THREADP

摔不死的笨鸟的博客 878

如何在工作线程中创建窗口

http://blog.youkuaiyun.com/windows_editor/archive/2006/11/30/1421664.aspx 如何在工作线程中创建窗口? 收藏 在前面我们研究了使用AFX_MANAGE_STATE(AfxGetStaticModuleState())进行DLL间的资源切换,以及工作线程中创建Windows消息循环的原理,以为就可以搞定一切类似问题了…但是请看以下代码  ...

weixin_33989780的博客 96

工作线程中调用UI线程创建的窗口的UpdateData会导致Assert的问题及解决办法

最近写了个代码,在UI线程中创建了一个窗口,然后在工作线程中修改了这个窗口中的一些数据,然后想用UpdateData(FALSE)来更新窗口的内容,结果在Debug版本下面就出现了Assert报错,说出错地方是wincore.cpp的888行和889行,就是这两句  ASSERT((p = pMap->LookupPermanent(m_hWnd)) != NULL ||   (p = p

float的专栏 666

UI线程和工作线程

操作系统在开启一个新的进程时,并没有为其创建消息队列,而是当进程第一次调用GDI函数后,并且进程持有窗口时,才持有消息队列。在Windows下,只有那些具备窗口(GUI用户接口界面)的进程,才会有消息队列,那些不具备GUI的进程是没有消息队列的。只处理逻辑的线程,例如:启动一个线程,用来做一个复杂的计算,计算完成之后,此线程就自动退出,这种线程称为工作线程。Windows应用程序一般由窗口和控件组成,这也是应用程序与人交互的媒介,拥有窗口(控件)的线程称为UI线程。一个进程只有一个UI线程(即主线程)

就是那个党伟 721
上一篇: 【转】深度解析VC中的消息传递机制
下一篇: 【转】操作符重载手册
code09
博客等级 码龄18年 31粉丝 56原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值