从 DLL 导出 MFC 对话框
7 月 21 日:更新,见底部
在 VC++ 2005 中,我有 2 个项目。首先,一个 MFC DLL 项目(不是扩展 DLL),它有一个简单的对话框:
TestDlg.h
#pragma once
#include "afxwin.h"
#include "resource.h"
// CTestDlg dialog
namespace Dialogs
{
class __declspec(dllexport) CTestDlg : public CDialog
{
DECLARE_DYNAMIC(CTestDlg )
public:
CTestDlg (CWnd* pParent = NULL); // standard constructor
virtual ~CTestDlg ();
// Dialog Data
enum { IDD = IDD_TEST_DLG };
}
}
然后我有一个带有 MFC 库的 Win32 控制台应用程序:
TestApp.cpp
#include "stdafx.h"
#include "TestApp.h"
#include <TestDlg.h>
#ifdef _DEBUG
#define new DEBUG_NEW
#endif
CWinApp theApp;
using namespace std;
int _tmain(int argc, TCHAR* argv[], TCHAR* envp[])
{
int nRetCode = 0;
// initialize MFC and print and error on failure
if (!AfxWinInit(::GetModuleHandle(NULL), NULL, ::GetCommandLine(), 0))
{
// TODO: change error code to suit your needs
_tprintf(_T("Fatal Error: MFC initialization failed\n"));
nRetCode = 1;
}
else
{
Dialogs::CTestDlg dlg;
dlg.DoModal();
}
return nRetCode;
}
它构建并运行,但没有出现对话框。进入 DoModal()...
dlgcore.cpp
INT_PTR CDialog::DoModal()
{
// can be constructed with a resource template or InitModalIndirect
ASSERT(m_lpszTemplateName != NULL || m_hDialogTemplate != NULL ||
m_lpDialogTemplate != NULL);
// load resource as necessary
LPCDLGTEMPLATE lpDialogTemplate = m_lpDialogTemplate;
HGLOBAL hDialogTemplate = m_hDialogTemplate;
HINSTANCE hInst = AfxGetResourceHandle();
if (m_lpszTemplateName != NULL)
{
hInst = AfxFindResourceHandle(m_lpszTemplateName, RT_DIALOG);
HRSRC hResource = ::FindResource(hInst, m_lpszTemplateName, RT_DIALOG);
hDialogTemplate = LoadResource(hInst, hResource);
}
if (hDialogTemplate != NULL)
lpDialogTemplate = (LPCDLGTEMPLATE)LockResource(hDialogTemplate);
// return -1 in case of failure to load the dialog template resource
if (lpDialogTemplate == NULL)
return -1;
... more stuff
无论出于何种原因,它似乎无法加载资源,在复制部分的末尾返回 -1。我看过 CodeGuru 等上的一些文章,但没有看到任何明显的内容。我的课程没有被导出/导入吗?还是资源问题?或者是我尝试从控制台(MFC)应用程序显示它的问题?
7 月 21 日更新 我创建了一个重写的 DoModal,如下所示:
INT_PTR CTestDlg::DoModal()
{
AFX_MANAGE_STATE(AfxGetStaticModuleState( ));
return CDialog::DoModal();
}
这似乎有效,尽管我应该重写不同的方法以使功能更通用吗?
21st July: Updated, see bottom
In VC++ 2005 I have 2 projects. Firstly, a MFC DLL project (not an extension DLL) which has a simple dialog:
TestDlg.h
#pragma once
#include "afxwin.h"
#include "resource.h"
// CTestDlg dialog
namespace Dialogs
{
class __declspec(dllexport) CTestDlg : public CDialog
{
DECLARE_DYNAMIC(CTestDlg )
public:
CTestDlg (CWnd* pParent = NULL); // standard constructor
virtual ~CTestDlg ();
// Dialog Data
enum { IDD = IDD_TEST_DLG };
}
}
Then I have a Win32 console app, with MFC libraries, that does:
TestApp.cpp
#include "stdafx.h"
#include "TestApp.h"
#include <TestDlg.h>
#ifdef _DEBUG
#define new DEBUG_NEW
#endif
CWinApp theApp;
using namespace std;
int _tmain(int argc, TCHAR* argv[], TCHAR* envp[])
{
int nRetCode = 0;
// initialize MFC and print and error on failure
if (!AfxWinInit(::GetModuleHandle(NULL), NULL, ::GetCommandLine(), 0))
{
// TODO: change error code to suit your needs
_tprintf(_T("Fatal Error: MFC initialization failed\n"));
nRetCode = 1;
}
else
{
Dialogs::CTestDlg dlg;
dlg.DoModal();
}
return nRetCode;
}
It builds and runs up, but no dialog appears. Stepping into DoModal()...
dlgcore.cpp
INT_PTR CDialog::DoModal()
{
// can be constructed with a resource template or InitModalIndirect
ASSERT(m_lpszTemplateName != NULL || m_hDialogTemplate != NULL ||
m_lpDialogTemplate != NULL);
// load resource as necessary
LPCDLGTEMPLATE lpDialogTemplate = m_lpDialogTemplate;
HGLOBAL hDialogTemplate = m_hDialogTemplate;
HINSTANCE hInst = AfxGetResourceHandle();
if (m_lpszTemplateName != NULL)
{
hInst = AfxFindResourceHandle(m_lpszTemplateName, RT_DIALOG);
HRSRC hResource = ::FindResource(hInst, m_lpszTemplateName, RT_DIALOG);
hDialogTemplate = LoadResource(hInst, hResource);
}
if (hDialogTemplate != NULL)
lpDialogTemplate = (LPCDLGTEMPLATE)LockResource(hDialogTemplate);
// return -1 in case of failure to load the dialog template resource
if (lpDialogTemplate == NULL)
return -1;
... more stuff
For whatever reason it seems it can't load the resource, returning -1 at the end of the copied section. I've looked at a few articles on CodeGuru, etc, and not seen anything obvious. Is my class not being exported/imported right? Or is it a resource problem? Or is the problem that I'm trying to display it from a console (MFC) app?
21st July Update
I created an overridden DoModal as so:
INT_PTR CTestDlg::DoModal()
{
AFX_MANAGE_STATE(AfxGetStaticModuleState( ));
return CDialog::DoModal();
}
This seems to work although should I be overriding a different method to get the functionality more generic?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
正如您所指出的,问题是 MFC 找不到资源,因为模块上下文设置为主 EXE,而不是包含对话框资源的 DLL。
手动调用 AFX_MANAGE_STATE 以确保建立 DLL 上下文是实现此目的的一种方法,但它并不透明。理想的方法是将 DLL 编译为扩展 DLL,以便 MFC 可以负责从扩展 DLL 列表中加载资源并管理 DLL 之间的内存。
您可以快捷地创建扩展 DLL,并简单地创建您自己的 CDynLinkLibrary 实例,这会将您的 DLL 添加到主资源列表中。我还没有尝试过这个,更喜欢采用扩展 dll _AFXDLL 路线,所以这可能有效,也可能无效。
有关 扩展 DLL 的 MSDN 文章可能会有所帮助您确定它们是否适合您的情况,以及它们带来哪些优点/缺点。
As you've noted, the problem is that MFC is not finding the resource, since the module context is set to your main EXE rather than the DLL containing the dialog resource.
Manually calling
AFX_MANAGE_STATE
to ensure the DLL context is established is one way to work this, but it's not transparent. The ideal way is to compile your DLL as an extension DLL, so that MFC can take care of loading the resource from a list of extension DLLs and managing memory between the DLLs.You may be able to short-cut creating the extension DLL and simply create your own
CDynLinkLibrary
instance, which adds your DLL to the main resource list. I have not tried this, preferring instead to take the extension dll _AFXDLL route, so this may or may not work.The MSDN article on Extension DLLs may help you determine if they are suitable in your case, and what advantages/drawbacks they bring.
显式加载您的 *.lib
然后将您的代码加载到目的地:
Explictly load you *.lib
then destination you code:
我不确定这个结构是否真的可行。如果可能,仅导出打开 DLL 内对话框的函数。
但也许使用 AFX_MANAGE_STATE 宏可以帮助您。
I'm not sure if this construction can actually work. If possible, export just a function which opens the dialog inside the DLL.
But perhaps the use of the
AFX_MANAGE_STATE
-macro can help you.AFX_MANAGE_STATE 对我不起作用。就我而言,exe 正在从另一个 dll 调用一个对话框,该 dll 正在从第三个 dll 调用另一个对话框。 AFX_MANAGE_STATE 返回第二个 dll 的上下文,而不是第三个。为了解决这个问题,我重写了 DoModel 并在那里进行上下文切换。
AFX_MANAGE_STATE did not work for me. In my case, the exe was calling a dialog from another dll which was calling another dialog from 3rd dll. AFX_MANAGE_STATE was returning context of 2nd dll instead of 3rd one. To fix this, I am overriding DoModel and doing the context switching there.