我想枚举我自己进程中的所有模块 - 本机和托管。所以我用以下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。
答案 0 :(得分:0)
好的,问题是从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。要让repro工作 - 必须在发布模式下运行它!