使用 Windows API 进行编程时,我总是立即将
HINSTANCE
中的 WinMain
设为全局变量。 如果我想制作一个“确定”按钮,我会这样做(给定全局HINSTANCE g_hInstance
):
return CreateWindow("BUTTON", "OK", WS_TABSTOP|WS_VISIBLE|WS_CHILD|BS_DEFPUSHBUTTON, 10, 10, 100, 30, exampleParentWindow, EXAMPLECHILDID, g_hInstance, NULL);
但最近我看到实例句柄无需作为参数传递或堵塞全局命名空间,而是使用对
GetModuleHandle(NULL)
* 的调用来确定。 所以,上面的例子看起来像这样:
return CreateWindow("BUTTON", "OK", WS_TABSTOP|WS_VISIBLE|WS_CHILD|BS_DEFPUSHBUTTON, 10, 10, 100, 30, exampleParentWindow, EXAMPLECHILDID, GetModuleHandle(NULL), NULL);
*如果你的编译器支持,你可以写
GetModuleHandle(nullptr)
,语句将得到相同的结果。
与显式指定实例句柄相比,调用
GetModuleHandle(NULL)
有什么优势(如果有的话)?
细则:我知道这有一个答案,但它尚未在 StackOverflow 上被表述为自己的问题。
在 EXE 中,这没有任何区别。
hInstance
和WinMain()
中的GetModuleHandle(NULL)
都指同一个HINSTANCE
(.exe文件的模块)。 但如果您在 DLL 内创建窗口,则确实会有所不同,因为您必须使用 DLL 的 hInstance
,但 GetModuleHandle(NULL)
仍将返回加载 DLL 的 EXE 的 HINSTANCE
。
HMODULE WINAPI GetModuleHandle( _In_opt_ LPCTSTR lpModuleName );
给出传递的模块名称的模块句柄。如果传递 NULL,则将获得当前正在运行的 EXE 的模块句柄。 如果您明确命名模块名称,您将获得映射到进程地址空间的该 dll 的模块句柄。 用途是,当您尝试调用 dll 导出的函数,或尝试使用该 dll 中的对话框模板时。此时,如果您使用 HMODULE 从 GetMoudleHandle(NULL) 返回的形式,您的代码将无法工作。
与直接使用 WinMain HINSTANCE 相比,使用 GetModuleHandle(NULL) 所获得的一个潜在好处更多地来自于体系结构。如果您想提供一个在 linux/windows/任何平台上运行的独立于平台的系统,您可以拥有一个执行平台相关转换的层。如果是这种情况,您不希望平台相关的对象(例如 HINSTANCE)出现在主应用程序代码中。因此,为了规避平台依赖性,我将 GetModuleHandle(NULL) 放在平台相关类的构造函数中,这与直接使用 WinMain HINSTANCE 具有相同的效果,但从主代码库本身中抽象出了特定功能。
只是为了在这些答案中添加我的两分钱。如果您需要从 DLL 中获取模块句柄(并且不想或无法通过
DllMain
调用将其保存在全局变量中),您可以使用此函数来获取它:
HMODULE getThisModuleHandle()
{
//Returns module handle where this function is running in: EXE or DLL
HMODULE hModule = NULL;
::GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS |
GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT,
(LPCTSTR)getThisModuleHandle, &hModule);
return hModule;
}
使用
GetModuleHandle(NULL)
与平台无关的函数来给出自己的路径:
const FILE *classSysFopenOwnPath() {
return fopen(classSysGetOwnPath().c_str(), "r");
}
const FilePath classSysGetOwnPath() {
#ifdef __linux__
return "/proc/self/exe";
#elif defined __win32__
char ownPathStr[MAX_PATH];
HMODULE hModule = GetModuleHandle(NULL);
if(hModule) {
GetModuleFileName(hModule, ownPathStr, sizeof(ownPathStr));
} else {
SUSUWU_ERROR("classSysGetOwnPath(): { if(!GetModuleHandle(NULL)) {/* this shouldn't happen */} }");
return FilePath(); /* return EXIT_FAILURE; */
}
return FilePath(ownPathStr);
#else
assert(NULL != classSysArgs);
assert(NULL != classSysArgs[0]);
return classSysArgs[0];
#endif
}
Windows 不是我们
build.sh
的主要目标,因此不存在 g_hInstance
或此类全局变量。
启动只是将 main()
参数存储到全局变量中:
const bool classSysInit(int argc, const char **args) {
classSysArgc = argc;
if(0 < argc) {
classSysArgs = args;
assert(NULL != args);
assert(NULL != args[0]); /* `clangtidy` off: NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic) */
return true;
}
return false;
}