【问题标题】:How do I get the HMODULE for the currently executing code?如何获取当前执行代码的 HMODULE?
【发布时间】:2010-10-08 02:38:41
【问题描述】:

我有一个静态库,可以链接到.exe.dll。在运行时,我希望我的库函数之一为静态库代码链接到的任何东西获取HMODULE

我目前使用以下技巧(灵感来自this forum):

const HMODULE GetCurrentModule()
{
    MEMORY_BASIC_INFORMATION mbi = {0};
    ::VirtualQuery( GetCurrentModule, &mbi, sizeof(mbi) );

    return reinterpret_cast<HMODULE>(mbi.AllocationBase);
}

有没有更好的方法来做到这一点,看起来不那么老套?

(注意:这样做的目的是加载一些我知道我的用户将与我的静态库同时链接的 Win32 资源。)

【问题讨论】:

标签: c++ c winapi


【解决方案1】:
HMODULE GetCurrentModule()
{ // NB: XP+ solution!
  HMODULE hModule = NULL;
  GetModuleHandleEx(
    GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS,
    (LPCTSTR)GetCurrentModule,
    &hModule);

  return hModule;
}

【讨论】:

  • 酷。我现在记得,当我编写 GetCurrentModule() 函数时,我们必须支持 Windows 2000。这就是为什么我使用 VirtualQuery() hack 而不是 GetModuleHandleEx()。
  • 可能是因为不了解 Windows 如何修复符号地址。
  • 请注意,如果成功,它会增加模块上的引用计数,因此您需要调用FreeLibrary
  • @Adrian:或者使用 GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT。
  • 我相信 Annon 打算建议您通过按位 OR 将 GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT 与 GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS 结合起来,例如:GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT。这正是我实现调用以获取 C++ 单元测试模块的实例句柄的方式,而无需编写自己的 DLLMain 例程。
【解决方案2】:

__ImageBase 是链接器生成的符号,它是模块的 DOS 标头(仅限 MSVC)。从那里您可以将其地址转换为HINSTANCEHMODULE。所以它比通过 API 更方便。

所以你只需要这样做:

EXTERN_C IMAGE_DOS_HEADER __ImageBase;
#define HINST_THISCOMPONENT ((HINSTANCE)&__ImageBase)

来自https://devblogs.microsoft.com/oldnewthing/20041025-00/?p=37483

【讨论】:

  • 根据我原始问题中的链接,__ImageBase 只是首选加载地址,并不总是实际加载地址。
  • 你怀疑Raymond Chen?链接上的讨论表明了对链接器常量的根本误解,但最后的评论是正确的。
  • Err... __ImageBase 是一个符号。如果在加载 .dll 或 .exe 时没有修复它,那么任何其他符号也不会,一切都会中断。因此,使用它是有效的,因为它的地址在图像加载时是固定的。
  • Raymond Chen 博客上的讨论很好地讨论了 GetCurrentModule(不是 Ex)的可怕副作用。
  • @MSN:我猜GetCurrentModule 上的注释是错字,因为 Windows API 中没有这样的符号。您指的是什么,哪些 cmets 讨论了这些副作用(以及它们是什么)?
【解决方案3】:

我会使用GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS 标志查看GetModuleHandleEx()。看起来您可以更改您的GetCurrentModule() 以调用此例程而不是VirtualQuery(),并将GetCurrentModule() 的地址作为lpModuleName 参数传递。

预计到达时间:

const HMODULE GetCurrentModule()
{
    DWORD flags = GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS;
    HMODULE hm = 0;
    ::GetModuleHandleEx( flags, reinterpret_cast<LPCTSTR>( GetCurrentModule ), &hm );   
    return hm;
}

我没有尝试过,但我认为这会满足你的要求。

【讨论】:

    【解决方案4】:
    #if _MSC_VER >= 1300  // for VC 7.0 
    #ifndef _delayimp_h 
    extern "C" IMAGE_DOS_HEADER __ImageBase; 
    #endif
    #endif
    ... 
    HMODULE module() 
    { 
    #if _MSC_VER < 1300  // earlier than .NET compiler (VC 6.0) 
        MEMORY_BASIC_INFORMATION mbi; 
        static int address; 
        ;::VirtualQuery(&address, &mbi, sizeof(mbi)); 
        return reinterpret_cast(mbi.AllocationBase); 
    #else  // VC 7.0 
        // from ATL 7.0 sources 
      return reinterpret_cast(&__ImageBase); 
    #endif 
    } 
    

    更多https://www.apriorit.com/dev-blog/74-hmodule-hinstance-handle-from-static-library-in-c

    【讨论】:

      【解决方案5】:

      HMODULE 是 HINSTANCE 是模块的基地址。所以,我会看看它是如何工作的。但是,如果您想要的只是可执行文件的 HMODULE,为什么不枚举进程中的所有 HMODULE(EnumProcessModules)。其中之一将链接您的 .lib。

      我看到的限制是您不知道您的 .lib 来自哪个 DLL 或 EXE。您可能希望将 HMODULE(基地址)与从 .lib 获得的 _ReturnAddress 进行比较。您的 .lib 将属于小于您的 _ReturnAddress 的最高 HMODLUE

      【讨论】:

      • 枚举模块列表效率非常低,而且不像调用 GetModuleHandleEx 那样整洁。让我们来看看;通过一个快速的 API 调用获得我需要的答案,或者循环通过可能的几十个模块,在我执行字符串比较(非常昂贵!)时。我想我会走捷径。
      猜你喜欢
      • 1970-01-01
      • 2012-08-23
      • 2010-11-18
      • 2022-01-25
      • 1970-01-01
      • 2019-03-16
      • 1970-01-01
      相关资源
      最近更新 更多