http://khason.net/images/2008/12/image-32.png
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 / Invoke签名:
[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);
答案 0 :(得分:5)
基本问题是RT_BITMAP
资源不以位图文件的格式存储。如果将原始数据保存到文件,则不会得到有效的位图文件。原始数据旨在由LoadBitmap
或LoadImage
进行解释,以创建HBITMAP
。
更多详情请见here:
如果应用程序调用FindResource()(使用RT_BITMAP类型),LoadResource()和LockResource(),而不是调用LoadBitmap(),则会得到指向打包DIB的指针。压缩DIB是BITMAPINFO结构,后跟包含位图位的字节数组。
因此,如果您完全不使用LoadBitmap
或LoadImage
,那么您将不得不弄清楚如何将打包的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
);
虽然lpName
被lpType
声明为以null结尾的C字符串,但它们并不总是那种形式。它们可以使用MAKEINTRESOURCE宏形成。文件说:
返回值是低位字中的指定值,高位字中为零。
这确实是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;
然后当你调用函数传递
时(IntPtr)RT_BITMAP
作为lpType
参数。
作为替代方案,您可以坚持问题中的声明,并使用documentation中指定的替代机制:
如果字符串的第一个字符是井号(#),则其余字符表示十进制数,指定资源名称或类型的整数标识符。例如,字符串“#258”表示整数标识符258。
您似乎尝试了所有这些不同的选项。我建议您选择一个选项并坚持下去。
除此之外,你已经省略了对LockResource
的调用。您拨打FindResource
和LoadResource
。但你没有打电话给LockResource
。请注意,LoadResource
会返回HGLOBAL
。要获取指向资源数据的指针,您必须将HGLOBAL
传递给LockResource
。虽然,事实证明,在现代Windows的实现中,你可以在不执行LockResource
步骤的情况下离开,但你仍然应该遵守规则来执行它。
现在,如果你SizeofResource
返回一个非零值,那么很明显你对FindResource
的调用成功了。你必须在某种程度上误认为SizeofResource
返回的大小不正确。假设SizeofResource
这样的基础API按设计工作,必须是安全的。
我还要强调您的代码省略了错误检查。你真的应该补充一点。如果您这样做,您可能会发现您获得了有用的诊断信息。没有错误检查,你不知道你的代码可能会失败。