【问题标题】:EnumProcessModules fails with ERROR_INVALID_HANDLE from inside processEnumProcessModules 失败,来自内部进程的 ERROR_INVALID_HANDLE
【发布时间】:2016-03-14 13:43:00
【问题描述】:

我想枚举我自己进程中的所有模块 - 本地和托管。所以我用以下 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 知道,如果操作系统加载程序正在接触模块信息,有时此功能会失败,但我认为错误应该是ERROR_PARTIAL_COPY。从我自己的进程运行时,我不明白如何获得ERROR_INVALID_HANDLE

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

  • 在代码某处添加断点
  • 调试测试
  • 打断点
  • 移除断点
  • 继续调试 (F5)
  • 然后出现错误

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

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

【问题讨论】:

  • 正确引用它:“有一个奇怪的错误 ERROR_PARTIAL_COPY。”。既然您知道它可能失败,那么您当然必须处理它失败的可能性。
  • 是的,当然,但是在不同的情况下不应该发生某些错误(例如,如果文件存在则无法获取 FileNotFound)。所以我不明白我怎么可能得到 InvalidHandle,以及我能做些什么来确保它不会发生。在这种情况下,操作系统加载程序只是在做一些我可以通过再次调用该函数来处理的事情。但是如果句柄无效,我不确定我能做什么。
  • 唯一能做的事情,对于任何错误,睡一会儿再试一次。
  • 我尝试与参考源相同 - Thread.Sleep(1) 并重复 50 次。但这没有帮助。我可以试着睡更长的时间。
  • 您还需要禁用反恶意软件以确保它不会搞砸。

标签: winapi pinvoke


【解决方案1】:

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

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 并让它保持活动状态,直到我完成枚举。

附:为了使重现工作 - 必须在发布模式下运行它!

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2012-08-27
    • 1970-01-01
    • 2013-06-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-11-12
    • 2015-07-05
    相关资源
    最近更新 更多