我正在使用MFC对话框DLL,在按下按钮时在我的主项目中调用。在这个DLL中,我使用GDI +来显示和调整图像大小,在以下函数中:
#include "atlimage.h"
void CPhysicsDialogDlg::displayImage()
{
CImage img1;
img1.Load(m_pathText);
m_imgSize.x = img1.GetWidth();
m_imgSize.y = img1.GetHeight();
CDC *screenDC = GetDC();
CDC mDC;
mDC.CreateCompatibleDC(screenDC);
CBitmap b;
b.CreateCompatibleBitmap(screenDC, IMAGE_DISPLAY_WIDTH, IMAGE_DISPLAY_HEIGHT);
CBitmap *pob = mDC.SelectObject(&b);
mDC.SetStretchBltMode(HALFTONE);
img1.StretchBlt(mDC.m_hDC, 0, 0, IMAGE_DISPLAY_WIDTH, IMAGE_DISPLAY_HEIGHT, 0, 0,
img1.GetWidth(), img1.GetHeight(), SRCCOPY);
mDC.SelectObject(pob);
m_picture.SetBitmap((HBITMAP)b.Detach());
ReleaseDC(screenDC);
}
然而,当调用此函数时,整个程序完全冻结。在调试模式下,当我尝试暂停并运行它时,Visual Studio将显示以下消息:
该过程似乎已死锁(或未运行任何用户模式 码)。所有线程都已停止
调试时,我发现它来自ReleaseGDIPlus
文件中的atlimage.h
函数,在CImage
析构函数中调用:
inline void CImage::CInitGDIPlus::ReleaseGDIPlus() throw()
{
EnterCriticalSection(&m_sect);
if( m_dwToken != 0 )
{
Gdiplus::GdiplusShutdown( m_dwToken ); // this line freezes everything
}
m_dwToken = 0;
LeaveCriticalSection(&m_sect);
}
有趣的是,当我将这个单一对话框作为EXE运行而不是将其作为DLL调用时,每一个都可以正常工作。
我试图在函数中明确地执行GDI +启动和关闭,同样的问题:
void CPhysicsDialogDlg::displayImage()
{
GdiplusStartupInput gdiplusStartupInput;
ULONG_PTR gdiplusToken;
GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);
//function code
GdiplusShutdown(gdiplusToken);
}
我正在使用Visual Studio 2005和Windows 7。
所以问题是:为什么ReleaseGDIPlus
功能会阻塞,我该如何解决?
答案 0 :(得分:2)
原因如下:
答案 1 :(得分:2)
如果您在MFC应用程序中使用DLL,您应该:
在InitInstance
中初始化它在ExitInstance中取消初始化。
无论如何,它应该只在主线程中完成一次。
答案 2 :(得分:1)
当卸载完全不相关的常规MFC DLL时,我们现在也遇到了问题。不知何故,这混淆了关机代码。我认为MS在这里通过在全局对象(CInitGDIPlus)中放置GDI + init和关闭来犯错,从而增加了GDI +在DllMain中关闭的风险。这完全违背了他们自己的一套规则。
有关于此的kb文章(Q322909)。问题仍存在于VS2013中。
进一步研究发现,常规DLL(具有自己的CWinApp实例)与MFC结合作为共享DLL会混淆MFC的全局管理。 AfxGlobalsAddRef / AfxGlobalsRelease使用全局引用计数。当引用计数变为零时,MFC释放其资源。这些资源中有GDI +资源(例如使用CImage的CPngImage)。可以在此过程中调用GDI +关闭代码。由于当常规MFC DLL被卸载时,引用计数现在仅降至零,因此在DllMain时间内有效地调用GDI +关闭代码,从而导致挂起。好像是MFC的一个bug。
解决方案可能是过早地释放DLL。如果它们是COM组件,则可以使用“CoFreeUnusedLibrariesEx”。如果你隐式链接,我猜是没有工作。
答案 3 :(得分:1)
如果您将MFC用作共享DLL,请确保在对__super::InitInstance
的覆盖中调用CWinApp::InitInstance
(您应该有一个从CWinApp
派生并覆盖的类InitInstance
和ExitInstance
)。这将告诉MFC,在调用__super::ExitInstance
之前,您需要全局变量,以确保它们没有被其他DLL释放!