由于某种原因,传递一个 hWnd 或一个新创建的 MDI 窗口的指针不能在工作线程中重新转换为它的原始值。我尝试从 App 类、Document 类和 View 类创建线程。都有相同的效果。我正在为工作线程使用全局函数。有趣的是,我使用的代码与 1998 年在 MFC MDI 中执行相同操作时使用的代码相同,当时效果很好,但现在似乎不起作用。
也许我只是没有看到问题。这里发生了什么?我想做的很简单,创建一个新的视图窗口,捕获它的 hWnd 并将该 hWnd 传递给工作线程,以便工作线程可以向窗口发送消息以打印字符串等等。我想从 Document 类启动线程。编译器是VS2010,它正在Debug中运行。
阅读此内容后:http: //msdn.microsoft.com/en-us/library/h14y172e%28v=VS.100%29.aspx,我意识到您可能无法将指向视图类的指针传递给工作人员线程。所以我专注于hWnds。在 OnTestConnect 块中,返回一个有效的 hWnd 作为指向新视图窗口的有效指针。
这是代码(来自 App 类):
struct THREADPARMS
{
HWND hWndView;
int test;
};
(注意,我尝试将结构定义为 typedef 并使用变量名,都具有相同的结果)
UINT Starter( LPVOID pParms )
{
THREADPARMS* pThreadParms = (THREADPARMS* )pParms;
//This step shouldn't be necesarry but I tried it anyway. Should
//be able to use pThreadParms->hWndView without casting.
//The hWnd value does not come across as valid. It is valid before sending.
HWND hWnd = (HWND)pThreadParms->hWndView;
//The int comes across fine
int iNum = pThreadParms->test;
CHFTAppBView* pView = (CHFTAppBView*) CHFTAppBView::FromHandle(hWnd);
//This bombs with a debug error becuase pView ptr is invalid (though it was
//valid before sending over
pView->SendMessage( ID_FILE_PRINT, 0, 0 );
return 0;
}
void CHFTAppBApp::OnTestConnect()
{
THREADPARMS* pThreadParms = new THREADPARMS;
//Create the window
AfxGetApp()->OnCmdMsg( ID_FILE_NEW, 0, NULL, NULL );
CMDIFrameWnd* pFrame = (CMDIFrameWnd*)AfxGetApp()->m_pMainWnd;
CMDIChildWnd* pChild = (CMDIChildWnd*)pFrame->GetActiveFrame();
CHFTAppBView* pView = (CHFTAppBView*)pChild->GetActiveView();
pThreadParms->hWndView = pView->m_hWnd;
pThreadParms->test = 10;
AfxBeginThread( Starter, pThreadParms );
}
第2部分
//Create the window
AfxGetApp()->OnCmdMsg( ID_FILE_NEW, 0, NULL, NULL );
CMDIFrameWnd* pFrame = (CMDIFrameWnd*)AfxGetApp()->m_pMainWnd;
CMDIChildWnd* pChild = (CMDIChildWnd*)pFrame->GetActiveFrame();
CHFTAppBView* pView = (CHFTAppBView*)pChild->GetActiveView();
HWND h = pView->m_hWnd;
SendMessage( h, ID_FILE_PRINT, 0, 0 );
我正在尝试确定 hWnd 是否有效。pView 是有效的。单步执行代码并查看 Debugger Watch 中的指针统计信息会显示一个引用 CView 类的 helathy 指针。但它不会返回健康的 hWnd。然后 Debugger Watch 说“无法评估”hWnd 内存地址并说“未使用=0”。但是,通过 IsWindow 运行它会返回 true。去搞清楚。尝试使用该句柄向 CView 窗口发送 CView 消息会被忽略。为什么 GetAvtiveView 会返回一个有效指针,但该类中的 hWnd 会返回垃圾?
第 3 部分
经过更多的挖掘,结果表明 HWND 是有效的,尽管 hwnd 变量显示 'unused=???' 在监视窗口中。我认为它是有效的,因为线程代码中收到的 hWnd 与附加到主代码中 pView 指针的 hWnd 匹配。从中获取 hWnd 的 pView 指针也是有效的,因为 Watch 通过返回它所代表的 CView 类的名称将其识别为有效的 CView 类指针。然而,仍然存在两个问题。一个是即使系统将有效的 hWnd 发送回 CView 窗口 (pView->m_hWnd),SendMessage(pView->m_hWnd, ID_FILE_PRINT_PREVIEW, 0, 0,) 也拒绝工作。系统忽略它。我希望在新创建的视图窗口中运行 FilePrintPreview 命令。
UINT ThreadTest1( LPVOID pParms )
{
THREADPARMS* pThreadParms = (THREADPARMS* )pParms;
//Returns a CWnd even though the handle is to a valid CView
CHFTAppBView* pView2 = (CHFTAppBView*) CHFTAppBView::FromHandle(hWnd);
//Doesn't seem to do anything. Still returns a hWnd that the system recognizes as
//a CWnd. That's what reports in the Watch window.
CHFTAppBView* pView = reinterpret_cast<CHFTAppBView*>(CHFTAppBView::FromHandle(hWnd));
//Doesn't appear to do anything
DYNAMIC_DOWNCAST( CHFTAppBView, pView2 );
//Confirms what watch window says -- it's a CWnd not a CView.
if ( !pView->IsKindOf( RUNTIME_CLASS(CHFTAppBView) ) )
AfxMessageBox( "Not CView" );
::SendMessage( hWnd, ID_FILE_PRINT, 0, 0 );
return 0;
}
void CHFTAppBDoc::Main()
{
AfxGetApp()->OnCmdMsg( ID_FILE_NEW, 0, NULL, NULL );
THREADPARMS* pThreadParms = new THREADPARMS;
CMDIFrameWnd* pFrame = (CMDIFrameWnd*)AfxGetApp()->m_pMainWnd;
CMDIChildWnd* pChild = (CMDIChildWnd*)pFrame->GetActiveFrame();
CHFTAppBView* pView = (CHFTAppBView*)pChild->GetActiveView();
pThreadParms->hWndView = pView->m_hWnd;
AfxBeginThread( ThreadTest1, pThreadParms );
SendMessage( pView->m_hWnd, ID_FILE_PRINT, 0, 0 );
//Or
::SendMessage( pView->m_hWnd, ID_FILE_PRINT, 0, 0 );
}
所以问题是如何在线程代码中将 hWnd 转换为正确的窗口指针?为什么系统不能将其转换为 CView?
第 4 部分
问题已解决(暂时)。教训:
- 无法将 CView 窗口指针传递给工作线程。请参阅上面的链接。
使用 SendMessage 在广告和窗口之间进行通信
//这现在从工作线程中工作
THREADPARMS* pThreadParms = (THREADPARMS* )pParms;
HWND hWnd = static_cast(pThreadParms->hWndView);
LRESULT lRst = ::SendMessage( pThreadParms->hWnd, WM_GGG, 0, 0 );
//要不就
LRESULT lRst = ::SendMessage( pThreadParms->hWnd, WM_GGG, 0, 0 );
//以及创建线程的 CDoc 方法中的这个:
CHFTAppBView* pView = (CHFTAppBView*)pChild->GetActiveView();
LRESULT lRst = ::SendMessage( pView->m_hWnd, WM_GGG, 0, 0 );
CView 的消息映射...
ON_MESSAGE(WM_GGG, kk)
将 HWND 传递给工作线程以识别 CView 窗口
- 无法从 CWnd 转换为较低的派生类(并使其正常工作)。
- 在视图的消息映射中使用 ON_MESSAGE 来捕获使用 SendMessage 发送的命令(确保在视图的 .h 文件中包含方法声明为 afx_msg)
一切都非常简单,但对于那些正在寻找答案的人(当面临无数可能的原因时),这个总结可能会有所帮助......
我仍然不完全理解为什么将 FromHandle 转换为 CView 在我的旧 MFC 应用程序中有效,而不是现在。也许它与代码所在的位置有关。在旧的 MFC 应用程序中,它位于 CView 窗口和 CDoc 类的此代码中。CDocument 不是从 CWnd 派生的,但 CView 是。
无论如何,这解决了这个问题。非常感谢所有提供建议的人——Nik、Scott、Ulrich 和 Mark。您的明智建议非常有帮助。