导入 DLL 的方式不同(VS2003 到 VS2010,多线程到多线程 DLL)
Importing DLL's doesn't work the same (VS2003 to VS2010, multithreaded to multithreaded DLL)
在混合使用 VC6、VS2003 和 VS2005 进行大量代码更新期间,我 运行 遇到了一个问题,即 VS2010 的行为与 VS2003 不同。该应用程序将扫描一个 DLL 目录并尝试将它们一个一个地加载。这是在这里完成的:
CConfigPlugin::CConfigPlugin(LPCTSTR szPluginName)
{
ASSERT(szPluginName);
ASSERT(AfxIsValidString(szPluginName));
m_csFullpath = szPluginName;
m_hModule = LoadLibrary(m_csFullpath);
m_pInterface = (IConfigDllInterface *) NULL;
pInterface pPtr = pInterface(NULL);
if (m_hModule != NULL)
{
// If we loaded the DLL get the interface pointer
pPtr = pInterface(GetProcAddress(m_hModule, "GetInterface"));
}
if (pPtr != NULL)
{
pPtr(&m_pInterface);
}
else
{
::FreeLibrary(m_hModule);
m_hModule = HMODULE(NULL);
}
}
所有 DLL 显示正在加载:
...
'GenConfig.exe':已加载 'C:\src\Debug\config\GenLogonConfig.dll',已加载符号。
'GenConfig.exe':已加载 'C:\src\Debug\config\GenReportConfig.dll',已加载符号。
'GenConfig.exe':已加载 'C:\src\Debug\config\ImportConfig.dll',已加载符号。
...
每个 DLL 都有一个相同的 GetInterface 实现,如下所示:
CConfigDllInterfaceImpl<CParentDlg> gdllObj;
BOOL GetInterface(IConfigDllInterface **ppPtr)
{
*ppPtr = &gdllObj;
// Temporary edit to test if gdllObj is set to proper parent.
CString name;
name = gdllObj.GetDisplayName();
// End edit
return true;
}
模板如下:
__declspec(selectany) UINT guiAdvise;
template <class T> class CConfigDllInterfaceImpl : public IConfigDllInterface
{
public:
CConfigDllInterfaceImpl()
{
guiAdvise = RegisterWindowMessage(_T("GenConfig"));
m_pDlg = NULL;
}
virtual LPCTSTR GetDisplayName() const
{
static CString csTemp;
csTemp.LoadString(IDS_DISPLAY_NAME);
return csTemp;
}
// Can't be virtual because it uses the template T argument
BOOL DoModal(HWND hParent)
{
ASSERT(IsWindow(hParent));
if (m_pDlg == (T *) NULL)
{
m_pDlg = new T(CWnd::FromHandle(hParent));
return m_pDlg->Create();
}
else if (IsWindow(m_pDlg->GetSafeHwnd()))
{
m_pDlg->PostMessage(guiAdvise, eAdviseSwitchViews);
m_pDlg->SetActiveWindow();
}
return TRUE;
} // SNIP...
我可以看出我的模板没有正确注册到其预期的父级。 GetDisplayName 只是 returns""。我怀疑我的问题的原因是我一个月前决定将所有内容从多线程更改为多线程 DLL。这些都是 MFC 项目,这似乎是使用 _AFXDLL 并使所有内容正确编译和 link 的最简单方法。我的所有其他项目都工作正常,但我相信因为这个 DLL 的加载方式:
CConfigDllInterfaceImpl gdllObj;
不再像以前那样工作了。
那么,问题一:我的猜测是否正确?还是我完全离谱?
问题 2:如果我的怀疑是正确的,我该如何解决这个问题?此时不能返回到多线程。
提前致谢。
我终于有时间回来回答这个问题了。 Collin Dauphinee 是正确的,因为这是一个资源问题。我不知道为什么VS2003和VS2010不一样,但是解决方法很简单。
CConfigPlugin::CConfigPlugin(LPCTSTR szPluginName)
{
ASSERT(szPluginName);
ASSERT(AfxIsValidString(szPluginName));
// Save off current Afx resource handle.
HINSTANCE hCurrentAfx = AfxGetResourceHandle(); // <--- Didn't need to
// do this before.
m_csFullpath = szPluginName;
m_hModule = NULL;
m_hModule = LoadLibrary(m_csFullpath);
m_pInterface = (IConfigDllInterface *) NULL;
pInterface pPtr = pInterface(NULL);
if (m_hModule != NULL)
{
AfxSetResourceHandle(m_hModule); // <--- here is where the resources
// get properly set. This is the
// solution to the problem.
// If we loaded the DLL get the interface pointer
pPtr = pInterface(GetProcAddress(m_hModule, "GetInterface"));
}
if (pPtr != NULL)
{
pPtr(&m_pInterface);
}
else
{
::FreeLibrary(m_hModule);
m_hModule = HMODULE(NULL);
}
// Now put Afx back.
AfxSetResourceHandle(hCurrentAfx);
}
我希望这对其他人有帮助。我在这个问题上被困了好几天。
在混合使用 VC6、VS2003 和 VS2005 进行大量代码更新期间,我 运行 遇到了一个问题,即 VS2010 的行为与 VS2003 不同。该应用程序将扫描一个 DLL 目录并尝试将它们一个一个地加载。这是在这里完成的:
CConfigPlugin::CConfigPlugin(LPCTSTR szPluginName)
{
ASSERT(szPluginName);
ASSERT(AfxIsValidString(szPluginName));
m_csFullpath = szPluginName;
m_hModule = LoadLibrary(m_csFullpath);
m_pInterface = (IConfigDllInterface *) NULL;
pInterface pPtr = pInterface(NULL);
if (m_hModule != NULL)
{
// If we loaded the DLL get the interface pointer
pPtr = pInterface(GetProcAddress(m_hModule, "GetInterface"));
}
if (pPtr != NULL)
{
pPtr(&m_pInterface);
}
else
{
::FreeLibrary(m_hModule);
m_hModule = HMODULE(NULL);
}
}
所有 DLL 显示正在加载: ... 'GenConfig.exe':已加载 'C:\src\Debug\config\GenLogonConfig.dll',已加载符号。 'GenConfig.exe':已加载 'C:\src\Debug\config\GenReportConfig.dll',已加载符号。 'GenConfig.exe':已加载 'C:\src\Debug\config\ImportConfig.dll',已加载符号。 ...
每个 DLL 都有一个相同的 GetInterface 实现,如下所示:
CConfigDllInterfaceImpl<CParentDlg> gdllObj;
BOOL GetInterface(IConfigDllInterface **ppPtr)
{
*ppPtr = &gdllObj;
// Temporary edit to test if gdllObj is set to proper parent.
CString name;
name = gdllObj.GetDisplayName();
// End edit
return true;
}
模板如下:
__declspec(selectany) UINT guiAdvise;
template <class T> class CConfigDllInterfaceImpl : public IConfigDllInterface
{
public:
CConfigDllInterfaceImpl()
{
guiAdvise = RegisterWindowMessage(_T("GenConfig"));
m_pDlg = NULL;
}
virtual LPCTSTR GetDisplayName() const
{
static CString csTemp;
csTemp.LoadString(IDS_DISPLAY_NAME);
return csTemp;
}
// Can't be virtual because it uses the template T argument
BOOL DoModal(HWND hParent)
{
ASSERT(IsWindow(hParent));
if (m_pDlg == (T *) NULL)
{
m_pDlg = new T(CWnd::FromHandle(hParent));
return m_pDlg->Create();
}
else if (IsWindow(m_pDlg->GetSafeHwnd()))
{
m_pDlg->PostMessage(guiAdvise, eAdviseSwitchViews);
m_pDlg->SetActiveWindow();
}
return TRUE;
} // SNIP...
我可以看出我的模板没有正确注册到其预期的父级。 GetDisplayName 只是 returns""。我怀疑我的问题的原因是我一个月前决定将所有内容从多线程更改为多线程 DLL。这些都是 MFC 项目,这似乎是使用 _AFXDLL 并使所有内容正确编译和 link 的最简单方法。我的所有其他项目都工作正常,但我相信因为这个 DLL 的加载方式:
CConfigDllInterfaceImpl gdllObj;
不再像以前那样工作了。
那么,问题一:我的猜测是否正确?还是我完全离谱? 问题 2:如果我的怀疑是正确的,我该如何解决这个问题?此时不能返回到多线程。
提前致谢。
我终于有时间回来回答这个问题了。 Collin Dauphinee 是正确的,因为这是一个资源问题。我不知道为什么VS2003和VS2010不一样,但是解决方法很简单。
CConfigPlugin::CConfigPlugin(LPCTSTR szPluginName)
{
ASSERT(szPluginName);
ASSERT(AfxIsValidString(szPluginName));
// Save off current Afx resource handle.
HINSTANCE hCurrentAfx = AfxGetResourceHandle(); // <--- Didn't need to
// do this before.
m_csFullpath = szPluginName;
m_hModule = NULL;
m_hModule = LoadLibrary(m_csFullpath);
m_pInterface = (IConfigDllInterface *) NULL;
pInterface pPtr = pInterface(NULL);
if (m_hModule != NULL)
{
AfxSetResourceHandle(m_hModule); // <--- here is where the resources
// get properly set. This is the
// solution to the problem.
// If we loaded the DLL get the interface pointer
pPtr = pInterface(GetProcAddress(m_hModule, "GetInterface"));
}
if (pPtr != NULL)
{
pPtr(&m_pInterface);
}
else
{
::FreeLibrary(m_hModule);
m_hModule = HMODULE(NULL);
}
// Now put Afx back.
AfxSetResourceHandle(hCurrentAfx);
}
我希望这对其他人有帮助。我在这个问题上被困了好几天。