GetModuleHandle(NULL) 与 hInstance

问题描述 投票:0回答:5

使用 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 上被表述为自己的问题。

c++ winapi visual-c++
5个回答
45
投票

在 EXE 中,这没有任何区别。

hInstance
WinMain()
中的
GetModuleHandle(NULL)
都指同一个
HINSTANCE
(.exe文件的模块)。 但如果您在 DLL 内创建窗口,则确实会有所不同,因为您必须使用 DLL 的
hInstance
,但
GetModuleHandle(NULL)
仍将返回加载 DLL 的 EXE 的
HINSTANCE


4
投票
HMODULE WINAPI GetModuleHandle( _In_opt_  LPCTSTR lpModuleName );

给出传递的模块名称的模块句柄。如果传递 NULL,则将获得当前正在运行的 EXE 的模块句柄。 如果您明确命名模块名称,您将获得映射到进程地址空间的该 dll 的模块句柄。 用途是,当您尝试调用 dll 导出的函数,或尝试使用该 dll 中的对话框模板时。此时,如果您使用 HMODULE 从 GetMoudleHandle(NULL) 返回的形式,您的代码将无法工作。


4
投票

与直接使用 WinMain HINSTANCE 相比,使用 GetModuleHandle(NULL) 所获得的一个潜在好处更多地来自于体系结构。如果您想提供一个在 linux/windows/任何平台上运行的独立于平台的系统,您可以拥有一个执行平台相关转换的层。如果是这种情况,您不希望平台相关的对象(例如 HINSTANCE)出现在主应用程序代码中。因此,为了规避平台依赖性,我将 GetModuleHandle(NULL) 放在平台相关类的构造函数中,这与直接使用 WinMain HINSTANCE 具有相同的效果,但从主代码库本身中抽象出了特定功能。


4
投票

只是为了在这些答案中添加我的两分钱。如果您需要从 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;
}

0
投票

使用

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;
}
© www.soinside.com 2019 - 2024. All rights reserved.