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
。
我试图通过 运行 循环中的代码在测试中重新创建这个(希望在一些随机迭代中它会失败)但是问题没有重现。关于此的有趣(和奇怪)的事情是我设法通过以下方式重新创建它:
- 在代码某处添加断点
- 调试测试
- 命中断点
- 移除断点
- 继续调试 (F5)
- 然后出现错误
这些复制步骤连续工作了好几次。每次继续调试后都会出现这个错误。我还在参考代码中看到 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。为了让重现工作 - 必须 运行 它处于发布模式!
我想枚举我自己的进程中的所有模块 - 本机和托管。所以我用下面的 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
。
我试图通过 运行 循环中的代码在测试中重新创建这个(希望在一些随机迭代中它会失败)但是问题没有重现。关于此的有趣(和奇怪)的事情是我设法通过以下方式重新创建它:
- 在代码某处添加断点
- 调试测试
- 命中断点
- 移除断点
- 继续调试 (F5)
- 然后出现错误
这些复制步骤连续工作了好几次。每次继续调试后都会出现这个错误。我还在参考代码中看到 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。为了让重现工作 - 必须 运行 它处于发布模式!