win32 - .dll 内的对话框
Posted
技术标签:
【中文标题】win32 - .dll 内的对话框【英文标题】:win32 - Dialog inside .dll 【发布时间】:2015-08-26 13:55:55 【问题描述】:我想创建一个.dll,它会显示一些对话框。
在我的 .dll 中有这段代码:
HWND hDlg = CreateDialogParam(NULL, MAKEINTRESOURCE(IDD_RANKING_DIALOG), NULL, msgProc, NULL);
if (!hDlg)
ShowError(GetLastErrorAsString().c_str());
return false;
ShowError
调用消息框,GetLastErrorAsString()
只调用标准GetLastError
并转换为字符串。
我有这个输出:
在图片文件中找不到指定的资源类型。
然后我有一个标准的 win32 Window 应用程序,在那里我调用方法,它调用提到的代码。
DialogTest test;
test.showDialog(); // calls functionality from .dll
我做错了什么?我需要将资源文件链接到 .dll 吗?
我正在使用 Visual Studio 2010,并且在我的资源文件 (.rc) 中指定了 dialog
。
【问题讨论】:
您正在将NULL
中的hInstance
参数传递给CreateDialogParam()
。这意味着对话框资源将在加载了您的 DLL 的可执行文件中查找,而不是在 DLL 本身中查找。您应该传递与您的 DLL 关联的 HINSTANCE
。
我尝试过使用 GetModuleHandle(0) 而不是 NULL,但它没有帮助。那么如何获取dll的HINSTANCE。
它作为第一个参数传递给您的DllMain()
。
好的,如果我没有 DLLMain()?因为我的 .dll 是从 Java 代码调用的。他们在 java 中使用 System.LoadLibrary()。这是否会导致运行我的 DllMain() 然后我可以将 hInstance 保存到全局变量并用它创建对话框?
与资源错误无关,ShowError(GetLastErrorAsString().c_str());
是一个等待发生的错误。我假设GetLastErrorAsString
返回std::wstring
或std::string
。这是暂时的,一旦c_str()
返回,它就会超出范围。然后你将一个悬空指针传递给ShowError
。
【参考方案1】:
错误代码和消息是准确的:在您指示系统去寻找它的地方找不到资源:用于启动进程的可执行映像,而不是您的 DLL。该行为已记录在案(请参阅CreateDialogParam):
hInstance [输入,可选]
类型:HINSTANCE
包含对话框模板的模块句柄。如果此参数为 NULL,则使用当前可执行文件。
由于您的 DLL 中存储了对话框模板,因此您必须传递标识您的 DLL 的HINSTANCE
。有多种方法可以获得正确的值,但传递 NULL
或 GetModuleHandle(NULL)
将不起作用。这两者都将模块句柄返回到启动进程的可执行映像(不是您的 DLL)。
简单的解决方案:选择传递给DllMain 的 hInstance 并将其存储在全局变量中以备后用。
HINSTANCE g_hInst = NULL;
BOOL WINAPI DllMain( HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved )
switch ( fdwReason )
case DLL_PROCESS_ATTACH:
g_hInst = hinstDLL;
break;
default:
break;
return TRUE;
强大的解决方案:此解决方案可以在任何地方使用,包括 DLL、EXE 或静态 LIB。唯一的缺点:它依赖于 Microsoft 链接器的未记录功能。不过不用担心,它不会默默地失败。
EXTERN_C IMAGE_DOS_HEADER __ImageBase;
#define HINST_THISMODULE ((HINSTANCE)&__ImageBase)
HINST_THISMODULE
将始终保持正确的值,无论在何处使用。1)
同样可以使用官方接口 (GetModuleHandleEx) 来实现。以下解决方案也可以从 EXE、DLL 或静态 LIB 中使用,只要您确保将函数编译并链接到相应的模块中:
HMODULE GetCurrentModuleHandle()
HMODULE hModule = NULL;
GetModuleHandleEx( GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS,
(LPCTSTR)GetCurrentModuleHandle,
&hModule );
return hModule;
这将返回 HMODULE
,而不是 HINSTANCE
。不过,这不是问题,因为它们是同一事物2)。
1)来自Accessing the current module’s HINSTANCE from a static library
2)What is the difference between HINSTANCE and HMODULE?
【讨论】:
谢谢,所有解决方案看起来都是正确的,但我尝试了这个,它可以工作。【参考方案2】:您将 NULL 指定为 CreateDialogParam
的第一个参数。如果你想从 Win32 窗口应用程序加载对话框资源文件,你应该改用这个:
HWND hDlg = CreateDialogParam(GetModuleHandle(NULL), MAKEINTRESOURCE(IDD_RANKING_DIALOG), NULL, msgProc, NULL);
但是,如果要从 DLL 本身加载它,则应将第一个参数替换为 DLL 的 DllMain 入口点函数中的 HINSTANCE 参数。
【讨论】:
不,GetModuleHandle(NULL)
返回与当前可执行文件关联的模块,而不是 DLL。
@FrédéricHamidi 感谢您指出这一点,我相信我已经纠正了答案
可以,但不需要调用GetModuleHandle()
。如果您想从当前可执行文件的模块加载资源,您可以简单地将NULL
传递给CreateDialogParam()
。
That's historical baggage, and not necessarily good practice.
@EdwardClements 这两个陈述都是正确的。这是仍然有效的历史包袱。旋转电话的工作方式与此相同,但您可能不应该在新办公楼中安装旋转电话。以上是关于win32 - .dll 内的对话框的主要内容,如果未能解决你的问题,请参考以下文章
安装迅雷提示;损坏的图像,导致迅雷无法安装。弹出的对话框显示是:COMCTL32.dll没有被指定在windows运行
Win10下装 VC6.0单步调试报((OLE32.DLL): 0xC0000005: Access Violation)