无法让MFC工作线程将hWnd或MDI窗口指针传递给线程

时间:2013-05-21 01:34:19

标签: multithreading mfc

由于某些原因,传递hWnd或新创建的MDI窗口的指针无法在工作线程中重新编写为其原始值。我尝试过从App类,Document类和View类创建线程。都具有相同的效果。我正在为工作线程使用全局函数。有趣的是,我在1998年使用与MFC MDI中相同的代码使用相同的代码,当时效果很好,但现在似乎没有用。

也许我只是没有看到问题。这里发生了什么?我想要做的是简单,创建一个新的View窗口,捕获它的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有效。单步执行代码并查看调试器监视器中的指针统计信息会显示一个引用CView类的helathy指针。但它并没有带来健康的回报。然后Debugger Watch表示无法评估' hWnd内存地址并说' unused = 0'。但是,通过IsWindow运行它会返回true。去搞清楚。尝试使用该句柄将CView消息发送到CView窗口时会被忽略。为什么GetAvtiveView会返回一个有效的指针,但该类中的hWnd会返回垃圾?

第3部分

经过多次挖掘后,虽然hwnd变量显示“未使用= ???”,但HWND仍然有效。在Watch窗口中。我认为它是有效的,因为线程代码中收到的hWnd与主代码中附加到pView指针的hWnd相匹配。 hWnd取自的pView指针也是有效的,因为Watch通过返回它所代表的CView类的名称将其识别为有效的CView类指针。但是仍有两个问题。一个是即使系统将有效的hWnd发送回CView窗口(pView-> m_hWnd),SendMessage(pView-> m_hWnd,ID_FILE_PRINT_PREVIEW,0,0)也拒绝工作。系统忽略它。我期望在新创建的视图窗口中运行FilePrintPreview命令。其次是FromHandle返回一个CWnd对象并使用所有这些方法将它转换为CView失败:

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部分

Probem解决了(现在)。经验:

  1. 无法将CView窗口指针传递给工作线程。请参阅上面的链接。
  2. 使用SendMessage在theads和windows之间进行通信

    //这适用于现在的工作线程

    THREADPARMS * pThreadParms =(THREADPARMS *)pParms;

    HWND hWnd = static_cast(pThreadParms-&gt; hWndView);

    LRESULT lRst = :: SendMessage(pThreadParms-&gt; hWnd,WM_GGG,0,0);

    //或者只是

    LRESULT lRst = :: SendMessage(pThreadParms-&gt; hWnd,WM_GGG,0,0);

    //与创建线程的CDoc方法一样:

    CHFTAppBView * pView =(CHFTAppBView *)pChild-&gt; GetActiveView();

    LRESULT lRst = :: SendMessage(pView-&gt; m_hWnd,WM_GGG,0,0);

    CView的留言地图......

    ON_MESSAGE(WM_GGG,kk)

  3. 将HWND传递给工作线程以识别CView窗口

  4. 无法从CWnd转移到较低级别的(或让它正常工作)。
  5. 在视图的消息映射中使用ON_MESSAGE来捕获使用SendMessage发送的命令(确保在视图的.h文件中包含方法声明为afx_msg)
  6. 一切都很直接,但对于那些寻找答案的人(面对无数可能的原因),这个总结可能有所帮助......

    我仍然没有完全理解为什么将FromHandle转换为CView在我的旧MFC应用程序中工作而不是现在。也许它与代码所在的位置有关。在旧的MFC应用程序中,它位于CView窗口中,并位于CDoc类的此代码中。 CDocument不是来自CWnd,而是CView。

    无论如何,这解决了这个问题。非常感谢所有提出建议的人 - Nik,Scott,Ulrich和Mark。你的圣人建议非常有帮助。

1 个答案:

答案 0 :(得分:5)

你可以拨打CHFTAppBView::FromHandle(hWnd),但你得到的不是指向CHFTAppBView的指针;它是指向CWnd的指针。通过转换它,你告诉编译器“相信我,这实际上是指向CHFTAppBView的指针。除非它真的不是,你不应该这样对待它或假装它是。” p>

毕竟,如果一份食谱需要橙汁,那么你就不要吃柠檬,把它涂成橙色,然后榨汁并称之为橙汁。你呢?

那该怎么办?好吧,如果您只想发送ID_FILE_PRINT消息,那么您甚至不需要CWnd。你可以这样做:

::SendMessage(hWnd, ID_FILE_PRINT, 0, 0);

当然,可能是错误的。你可能意味着做的是:

::SendMessage(hWnd, WM_COMMAND, ID_FILE_PRINT, 0);

<强>更新

同样,你不能做你想做的事。我们一步一步走吧,是吗?

//Returns a CWnd even though the handle is to a valid CView 
CHFTAppBView* pView2 = (CHFTAppBView*) CHFTAppBView::FromHandle(hWnd);

右。 CWnd::FromHandle返回指向CWnd的指针,而不是其他指针。 NOT 为您提供指向原始CHFTAppBView的指针。它会为您提供 CWnd,它附加到同一HWND无效将其强制转换为CHFTAppBView,因为新的CWnd不是CHFTAppBView

//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));

同样,CWnd::FromHandle会返回指向 new CWnd的指针,该指针对CHFTAppBView一无所知。你告诉编译器“相信我,这个指针是CHFTAppBView对象!”除了 NOT 。它是指向附加到CWnd HWND的{​​{1}}的指针。

CHFTAppBView

STILL 不会做任何事情。首先,//Doesn't appear to do anything DYNAMIC_DOWNCAST( CHFTAppBView, pView ); 返回指向DYNAMIC_DOWNCAST的指针,所以就在那里,你正在调用MFC RTTI函数,但没有对结果做任何事情。但即使你确实保存了结果,也无济于事。您可能会尝试将通用CHFTAppBView转换为不属于它的内容。

我将尝试再解释一次:当您创建视图时,MFC会创建一个CWnd对象,该对象与视图的CHFTAppBView相关联。当您致电HWND传递视图的CWnd::FromHandle时,MFC会创建一个且不同的HWND实例,该实例指向相同的CWnd - 第二个HWND CWnd,而CHFTAppBView对您的MFC类,视图,文档或其他任何内容一无所知。

您正在尝试HWND CWnd *返回并将其锤入CWnd::FromHandle。无论你怎么努力,这都行不通。你所能得到的只是一个CHFTAppBView *,而不是别的。

作为旁注,您也无法将MFC对象从一个线程传递到另一个线程,因此传递原始CWnd *将导致出现奇怪的问题,并可能导致难以跟踪错误。

更新2:

你问“为什么不能将CWnd对象强制转换为从CWnd中获取的窗口类?

让我们从CHFTAppBView *开始吧?

CWnd::FromHandle

这导致我们CWnd* PASCAL CWnd::FromHandle(HWND hWnd) { CHandleMap* pMap = afxMapHWND(TRUE); //create map if not exist ASSERT(pMap != NULL); CWnd* pWnd = (CWnd*)pMap->FromHandle(hWnd); #ifndef _AFX_NO_OCC_SUPPORT pWnd->AttachControlSite(pMap); #endif ASSERT(pWnd == NULL || pWnd->m_hWnd == hWnd); return pWnd; } 。这个代码有点复杂,在这里发布也无济于事,所以不要让事情变得混乱。但从概念上讲,这就是函数的作用:

如果在地图中找到了一个句柄,它会返回它;否则,它会创建一个新的CHandleMap::FromHandle并使CWnd指向您传入的CWnd。然后它会向您返回指向该HWND的指针。但请注意,这是一个CWnd。还有其他人。所以你看,你不能把返回CWnd的东西转换成其他东西 - 即使其他东西是从CWnd派生的 - 因为你所拥有的只是CWnd而已。

假设你有一个妻子。她非常漂亮,你非常爱她。你在钱包里随身携带她的照片。现在,当你遇到某人并且他们问你是否结婚时,你拿出钱包并自豪地向他们展示她的照片。你说话的人不认为你和照片结婚了。而且你不认为你可以将图片神奇地“转换”成你的妻子。

这里的情况有点类似。 CWnd为您提供各种各样的“图片”。这对周围的展示很有好处,但对其他很多东西都不好。你在问“为什么我不能把照片转换成我的妻子?”答案是因为这不是图片的工作原理。