EnumProcessModules 失败,内部进程 ERROR_INVALID_HANDLE

EnumProcessModules fails with ERROR_INVALID_HANDLE from inside process

我想枚举我自己的进程中的所有模块 - 本机和托管。所以我用下面的 P/Invoke 声明包装了 EnumProcessModules:

[DllImport("psapi.dll", SetLastError = true)]
private static extern bool EnumProcessModules(
        IntPtr hProcess,
        [MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.SysInt)]
        [In][Out] IntPtr[] lphModule,
        uint cb,
        [MarshalAs(UnmanagedType.U4)]
        out uint lpcbNeeded);

然后我使用

从我自己的进程(接近开始时间)调用它
var processHandle = Process.GetCurrentProcess().Handle;
uint needed;
EnumProcessModules(processHandle, new IntPtr[0], 0, out needed);
var modules = new IntPtr[needed * 2];
bool enumResult = EnumProcessModules(processHandle, modules, (uint)(needed * IntPtr.Size), out needed);

在生产环境中,大部分时间都可以,但有时第二次调用会失败,最后一个错误代码是 6,即 ERROR_INVALID_HANDLE。我通过在线阅读和查看 reference .NET implementation 了解到,如果 OS 加载程序正在接触模块信息,有时此函数会失败,但后来我认为错误应该是 ERROR_PARTIAL_COPY。我不明白当 运行 从我自己的过程中如何获得 ERROR_INVALID_HANDLE

我试图通过 运行 循环中的代码在测试中重新创建这个(希望在一些随机迭代中它会失败)但是问题没有重现。关于此的有趣(和奇怪)的事情是我设法通过以下方式重新创建它:

这些复制步骤连续工作了好几次。每次继续调试后都会出现这个错误。我还在参考代码中看到 EnumProcessModules 在循环中调用以防失败。我试图做同样的事情,但没有帮助。一旦错误发生,至少会再发生50次...

代码是 x64 机器上的 x86 运行。

好的,所以问题是从 Process 返回的 Handle 使用不安全并被收集。这是我写的一个小副本

var handle = new IntPtr(-1); // works
var handle = Process.GetCurrentProcess().Handle; // doesn't work

GC.Collect();

uint needed;
var modules = new IntPtr[1024];

var enumResult = ModulesCheck.EnumProcessModules(handle, modules, (uint)modules.Length, out needed);
Console.WriteLine(enumResult);

所以我想我应该使用 -1 或得到一个 SafeWaitHandle 并在我完成枚举之前让它保持活动状态。

P.S。为了让重现工作 - 必须 运行 它处于发布模式!