gpt4 book ai didi

c# - FindResource 不适用于位图

转载 作者:行者123 更新时间:2023-11-30 14:30:20 26 4
gpt4 key购买 nike


(来源:khason.net)

var ID = 1234;
var FilePath = "C:\\file.dll";
IntPtr hMod = LoadLibraryEx(FilePath, IntPtr.Zero, 2); //LOAD_LIBRARY_AS_DATAFILE = 2
IntPtr hRes = FindResource(hMod, "#" + ID, "PNG");
byte[] Bytes = new byte[SizeofResource(hMod, hRes)];
Marshal.Copy(LoadResource(hMod, hRes), Bytes, 0, Bytes.Length);
FreeLibrary(hMod);
System.IO.File.WriteAllBytes("C:\\img.png", Bytes);

上面的代码对 PNG 和其他自定义类型工作正常,但对 BITMAP 无效,我尝试了所有可能的组合:

FindResource(hMod, "#" + ID, "RT_BITMAP");
FindResource(hMod, "#" + ID, "BITMAP");
FindResource(hMod, "#" + ID, "Bitmap");
FindResource(hMod, "#" + ID, "BMP");
FindResource(hMod, ID, "Bitmap"); //also changed P/Invoke signature
FindResource(hMod, ID, "BITMAP"); //...
FindResource(hMod, ID, "BMP");

有人知道我在这里错过了什么吗?

我不想在这里使用LoadBitmap,因为这个函数可以满足我的所有需求。

编辑:

以下返回一些大小正确*但内容不正确的数据(如某种不同的编码):

FindResource(hMod, "#" + ID, "#2"); //RT_BITMAP = 2
FindResource(hMod, ID, 2); //RT_BITMAP = 2; changed sig

*每次大约少 12-14 个字节

P/调用签名:

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

[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
static extern bool FreeLibrary(IntPtr hModule);

[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
static extern IntPtr FindResource(IntPtr hModule, string lpName, string lpType);

[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
static extern IntPtr FindResource(IntPtr hModule, int lpName, int lpType);

[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
static extern uint SizeofResource(IntPtr hModule, IntPtr hResInfo);

[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
static extern IntPtr LoadResource(IntPtr hModule, IntPtr hResInfo);

[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
static extern IntPtr LockResource(IntPtr hResData);

最佳答案

根本问题是RT_BITMAP 资源没有以位图文件的格式存储。如果将原始数据保存到文件中,则最终不会得到有效的位图文件。原始数据旨在由 LoadBitmapLoadImage 解释以创建 HBITMAP

给出了更多细节here :

If, instead of calling LoadBitmap(), the application calls FindResource() (with RT_BITMAP type), LoadResource(), and LockResource(), a pointer to a packed DIB will be the result. A packed DIB is a BITMAPINFO structure followed by an array of bytes containing the bitmap bits.

因此,如果您坚决反对使用 LoadBitmapLoadImage,那么您将不得不弄清楚如何将打包的 DIB 转换为位图文件。本质上,您需要写出适当的位图文件头,然后在其后加上打包的 DIB 数据。

本质上,代码可能如下所示。首先定义文件头类型。

[StructLayout(LayoutKind.Sequential, Pack=1)]
struct BitmapFileHeader
{
public ushort id;
public int size;
public ushort res1;
public ushort res2;
public int offset;
}

然后为了方便把各种大小放到局部变量中:

int resSize = SizeofResource(hMod, hRes);
int headerSize = Marshal.SizeOf(typeof(BitmapFileHeader));

然后为文件内容分配足够的空间:

byte[] Bytes = new byte[headerSize + resSize];

现在填充标题:

BitmapFileHeader header;
header.id = 0x4D42;
header.size = Bytes.Length;
header.res1 = 0;
header.res2 = 0;
header.offset = headerSize + 40;
// magic constant, size of BITMAPINFOHEADER

最后,用文件头填充字节数组,然后是打包的DIB:

IntPtr headerPtr = Marshal.AllocHGlobal(headerSize);
try
{
Marshal.StructureToPtr(header, headerPtr, false);
Marshal.Copy(headerPtr, Bytes, 0, headerSize);
Marshal.Copy(pRes, Bytes, headerSize, resSize);
}
finally
{
Marshal.FreeHGlobal(headerPtr);
}

然后您可以像以前一样将字节数组保存到磁盘,您应该可以开始了。


FindResource的声明如下:

HRSRC WINAPI FindResource(
_In_opt_ HMODULE hModule,
_In_ LPCTSTR lpName,
_In_ LPCTSTR lpType
);

虽然 lpNamelpType 声明为空终止的 C 字符串,但它们并不总是这种形式。它们可以使用 MAKEINTRESOURCE 形成宏。文档说:

The return value is the specified value in the low-order word and zero in the high-order word.

这确实是 RT_BITMAP 的定义方式。它是 MAKEINTRESOURCE(2),详见 resource types文档。

所以你应该添加一些重载的 p/invoke 声明:

[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
static extern IntPtr FindResource(IntPtr hModule, string lpName, string lpType);

[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
static extern IntPtr FindResource(IntPtr hModule, IntPtr lpName, string lpType);

[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
static extern IntPtr FindResource(IntPtr hModule, string lpName, IntPtr lpType);

[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
static extern IntPtr FindResource(IntPtr hModule, IntPtr lpName, IntPtr lpType);

然后您可以定义 RT_BITMAP 如下:

public const uint RT_BITMAP = 0x00000002;

然后当你调用函数pass时

(IntPtr)RT_BITMAP

作为 lpType 参数。

作为替代方案,您可以坚持问题中的声明,并使用 documentation 中指定的替代机制。 :

If the first character of the string is a pound sign (#), the remaining characters represent a decimal number that specifies the integer identifier of the resource's name or type. For example, the string "#258" represents the integer identifier 258.

您确实尝试了所有这些不同的选项。我建议您选择一个选项并坚持下去。

除此之外,您还省略了对 LockResource 的调用。您调用 FindResourceLoadResource。但是您未能调用 LockResource。请注意,LoadResource 返回一个 HGLOBAL。为了获得指向资源数据的指针,您必须将 HGLOBAL 传递给 LockResource。虽然事实证明,在现代 Windows 的实现中,您可以不执行 LockResource 步骤而逃脱,但为了遵守规则,您仍然应该执行它。

现在,如果您到达 SizeofResource 返回一个非零值,那么很明显您对 FindResource 的调用成功了。您断言 SizeofResource 返回的大小不正确的断言一定是弄错了。必须安全地假设像 SizeofResource 这样的基础 API 按设计工作。

我还想强调您的代码省略了错误检查。你真的应该补充一下。如果您这样做,您可能会发现您获得了有用的诊断信息。如果没有错误检查,您将不知道您的代码可能在哪里失败。

关于c# - FindResource 不适用于位图,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/23615822/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com