You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

C#获取当前代码所在DLL模块句柄的实现问题排查

获取当前执行代码所属DLL模块句柄的C#实现方案

问题根因

你在C++中实现的逻辑是正确的,但迁移到C#时存在3个核心问题,导致调用返回空句柄、错误码126(找不到指定模块):

  • 最核心的问题:Marshal.GetFunctionPointerForDelegate返回的不是托管方法本身的地址,而是CLR动态生成的非托管调用存根(thunk)的内存地址,这块内存属于CLR私有内存/进程堆,不在任何DLL的文件映射内存范围内,GetModuleHandleExW自然无法匹配到对应模块。
  • 托管方法的实际执行地址由CLR在JIT编译时动态分配,位于JIT堆中,同样不属于DLL的PE映射段,就算绕过委托直接取托管方法地址,也无法被GetModuleHandleExW识别。
  • P/Invoke声明存在小瑕疵:GetModuleHandleExW原生返回值是BOOL类型,你声明为Int32虽然不会直接导致调用失败,但不够严谨,容易出现返回值判断错误。

正确实现方式

方式1:调用Win32 API实现(兼容所有.NET版本)

GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS标志不仅支持传入代码段地址,也支持传入DLL映射内存范围内的任意地址(包括数据段的静态变量地址)。你可以在目标DLL内定义一个静态变量作为锚点,取其地址传入API即可,代码如下:

using System;
using System.Runtime.InteropServices;

public static class CurrentModuleResolver
{
    private const uint GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS = 0x00000004;
    // 该静态字段必须定义在你需要获取句柄的目标DLL内部
    private static IntPtr _anchorVar;

    [DllImport("kernel32.dll", SetLastError = true)]
    private static extern bool GetModuleHandleExW(
        uint dwFlags,
        IntPtr lpAddress,
        out IntPtr phModule);

    public static IntPtr GetCurrentModuleHandle()
    {
        // 获取当前DLL内静态变量的内存地址
        GCHandle handle = GCHandle.Alloc(_anchorVar, GCHandleType.Pinned);
        try
        {
            IntPtr varAddr = handle.AddrOfPinnedObject();
            if (!GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, varAddr, out IntPtr hModule))
            {
                throw new System.ComponentModel.Win32Exception(Marshal.GetLastWin32Error());
            }
            return hModule;
        }
        finally
        {
            if (handle.IsAllocated) handle.Free();
        }
    }
}

注意:_anchorVar字段必须写在目标DLL的代码中,不能放在公共类库跨DLL调用,否则会返回公共类库所在DLL的句柄。

方式2:使用.NET内置API(代码更简洁)

.NET Framework 4.0+、.NET Core/.NET 5+ 已经提供了内置方法直接获取模块句柄,不需要手动调用Win32 API,在目标DLL内直接执行以下代码即可:

using System.Reflection;
using System.Runtime.InteropServices;

IntPtr hModule = Marshal.GetHINSTANCE(Assembly.GetExecutingAssembly().GetModules()[0]);

该方法返回的句柄和Win32 API拿到的HMODULE完全等价,可以直接传入需要模块句柄的Win32 API使用。

补充说明

C版本的代码能正常工作,是因为原生C函数会被直接编译到DLL的代码段中,函数地址就在DLL的PE映射内存范围内。而C#作为托管语言,方法执行地址由CLR动态管理,不能直接套用C++取函数地址的逻辑。


内容的提问来源于stack exchange,提问作者yanpengl

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.29 23:30:39