【问题标题】:EnumResourceNames issue - unknown errorEnumResourceNames 问题 - 未知错误
【发布时间】:2011-06-13 10:47:58
【问题描述】:

我最近在处理来自二级库/二进制模块的资源,遇到了一个奇怪的错误。

我有两个本地 WinAPI 引用:

[DllImport("kernel32.dll", SetLastError = true)]
public extern static bool EnumResourceNames(IntPtr hModule, int lpszType, EnumResNameProc lpEnumFunc, IntPtr lParam);

[DllImport("kernel32.dll", SetLastError=true)]
public extern static IntPtr LoadLibraryEx(string lpFileName, IntPtr hFile, int dwFlags);

当我调用 LoadLibraryEx 时,我得到了 IntPtr 实例——这正是我所需要的:

IntPtr x = WinApi.LoadLibraryEx(@"D:\Software\Reflector\Reflector.exe",IntPtr.Zero,2);
Debug.WriteLine(x.ToInt32());

但是,当我尝试枚举图标资源(由 ID = 3 定义)时:

Debug.WriteLine(WinApi.EnumResourceNames(x, 3, new EnumResNameProc(ListCallback), IntPtr.Zero));
Debug.WriteLine(Marshal.GetLastWin32Error());

我收到此错误代码(由 GetLastError 返回):

-532462766

据我所知,这通常意味着存在未知错误,但我只是好奇 - 从可执行文件中列出资源可能有什么问题?

【问题讨论】:

  • EnumResourceNames() 实际上返回 false 吗?如果它返回 true,那么 GetLastError() 的值是没有意义的。
  • 是托管还是非托管DLL?
  • 它返回 False - 显然它失败了。这是一个调用非托管 DLL (kernel32) 的托管 C# 应用程序。

标签: c# .net winapi interop kernel32


【解决方案1】:

-532462766 == 0xe0434352。最后三个十六进制对拼写为“CCR”,这是 Microsoft 程序员用来尝试提出易于识别的异常代码的常用技巧。确切的含义非常神秘,除了它通常与托管代码相关联并且在通常不会产生有意义的托管异常的子系统中看似非常低级。

这个神秘的异常有一个很好的候选原因,你的 EnumResources pinvoke 声明是错误的。第二个参数是 IntPtr,而不是 int。这有可能在 64 位操作系统上实现 kaboom。

如果您知道 CCR 是什么意思,请回帖。


using System;
using System.ComponentModel;
using System.Runtime.InteropServices;
using System.Diagnostics;

class Program {
    static void Main(string[] args) {
        try {
            IntPtr module = LoadLibraryEx(@"C:\windows\system32\user32.dll", IntPtr.Zero, 2);
            if (module == IntPtr.Zero) throw new Win32Exception();
            if (!EnumResourceNames(module, (IntPtr)3, new EnumResNameProc(ListCallback), IntPtr.Zero))
                throw new Win32Exception();
        }
        catch (Win32Exception ex) {
            Console.WriteLine(ex.Message);
        }
        Console.ReadLine();
    }

    static bool ListCallback(IntPtr hModule, IntPtr type, IntPtr name, IntPtr lp) {
        long idorname = (long)name;
        if (idorname >> 16 == 0) Console.WriteLine("#{0}", idorname);
        else Console.WriteLine(Marshal.PtrToStringAnsi(name));
        return true;
    }

    public delegate bool EnumResNameProc(IntPtr hModule, IntPtr type, IntPtr name, IntPtr lp);
    [DllImport("kernel32.dll", SetLastError = true)]
    public extern static bool EnumResourceNames(IntPtr hModule, IntPtr type, EnumResNameProc lpEnumFunc, IntPtr lParam);
    [DllImport("kernel32.dll", SetLastError = true)]
    public extern static IntPtr LoadLibraryEx(string lpFileName, IntPtr hFile, int dwFlags);
}

【讨论】:

  • 我认为 CCR 是 CLR 的原始名称之一。
  • 嗯...根据 MSDN 文档,我对上面提到的方法有正确的签名。根据 PInvoke.net (pinvoke.net/default.aspx/kernel32.enumresourcenames),我也在使用正确的签名。仍在试图弄清楚 CCR 的含义。
  • 不,肯定是错的。 pinvoke.net 典型地在一页中既错误又正确,没有编辑器可以清除错误。 SDK 声明使用 LPCTSTR,这是一个指针。托管代码中的 IntPtr。您在 C 代码中使用 MAKEINTRESOURCE 将 int 转换为指针。您在 C# 代码中使用 (IntPtr)3 来完成相同的操作。
  • 即使我改变了参数,我仍然得到同样的错误。这越来越有趣了。
  • 我不知道,出于同样的原因,回调也很棘手。您不能将第三个参数设为字符串。我用可以在我的机器上运行的代码更新了我的帖子。
【解决方案2】:

Hans Passant 说得对,但要详细说明错误消息,0xe0434352 是 .NET 异常的一般错误代码。如果您从 Visual Studio 调试器运行此程序,您将看到在 EnumResourceNames 尝试调用回调时引发了 System.ArgumentException。错误信息是:

作为字符串传入的指针不能在进程地址空间的底部 64K 中。

此异常被 EnumResourceNames 捕获并变为失败。正如 Hans 所展示的,解决方案是回调函数必须将第二个和第三个参数作为 IntPtr 而不是字符串。

【讨论】:

    猜你喜欢
    • 2016-12-11
    • 2017-05-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-09-07
    • 1970-01-01
    • 2021-04-05
    • 2014-04-16
    相关资源
    最近更新 更多