Elm*_*lmo 3 .net c# pinvoke resources winapi
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);
Run Code Online (Sandbox Code Playgroud)
上面的代码适用于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");
Run Code Online (Sandbox Code Playgroud)
有谁知道我在这里失踪了什么?
我不想在LoadBitmap这里使用,因为这个功能可以满足我的所有需求.
编辑:
以下返回一些正确大小*但不是正确内容的数据(如某种不同的编码):
FindResource(hMod, "#" + ID, "#2"); //RT_BITMAP = 2
FindResource(hMod, ID, 2); //RT_BITMAP = 2; changed sig
Run Code Online (Sandbox Code Playgroud)
*每次减少大约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);
Run Code Online (Sandbox Code Playgroud)
根本问题是RT_BITMAP资源不以位图文件的格式存储.如果将原始数据保存到文件,则不会得到有效的位图文件.原始数据旨在由LoadBitmap或LoadImage创建一个HBITMAP.
更多细节在这里给出:
如果应用程序调用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;
}
Run Code Online (Sandbox Code Playgroud)
然后将各种大小放入局部变量以方便:
int resSize = SizeofResource(hMod, hRes);
int headerSize = Marshal.SizeOf(typeof(BitmapFileHeader));
Run Code Online (Sandbox Code Playgroud)
然后为文件内容分配足够的空间:
byte[] Bytes = new byte[headerSize + resSize];
Run Code Online (Sandbox Code Playgroud)
现在填充标题:
BitmapFileHeader header;
header.id = 0x4D42;
header.size = Bytes.Length;
header.res1 = 0;
header.res2 = 0;
header.offset = headerSize + 40;
// magic constant, size of BITMAPINFOHEADER
Run Code Online (Sandbox Code Playgroud)
最后,用文件头填写字节数组,然后填充压缩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);
}
Run Code Online (Sandbox Code Playgroud)
然后,您可以像以前一样将字节数组保存到磁盘,您应该很高兴.
声明FindResource如下:
HRSRC WINAPI FindResource(
_In_opt_ HMODULE hModule,
_In_ LPCTSTR lpName,
_In_ LPCTSTR lpType
);
Run Code Online (Sandbox Code Playgroud)
虽然lpName被lpType宣布为空值终止的C字符串,它们并不总是这种形式的.它们可以使用MAKEINTRESOURCE宏形成.文件说:
返回值是低位字中的指定值,高位字中为零.
这确实是如何RT_BITMAP定义的.它MAKEINTRESOURCE(2)在资源类型文档中有详细说明.
所以你应该添加一些重载的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);
Run Code Online (Sandbox Code Playgroud)
然后,您可以定义RT_BITMAP如下:
public const uint RT_BITMAP = 0x00000002;
Run Code Online (Sandbox Code Playgroud)
然后当你调用函数pass时
(IntPtr)RT_BITMAP
Run Code Online (Sandbox Code Playgroud)
作为lpType参数.
作为替代方案,您可以坚持您的问题中的声明,并使用文档中指定的替代机制:
如果字符串的第一个字符是井号(#),则其余字符表示十进制数,指定资源名称或类型的整数标识符.例如,字符串"#258"表示整数标识符258.
你似乎已经尝试了所有这些不同的选项.我建议你选择一个选项并坚持下去.
最重要的是你省略了对它的调用LockResource.你叫FindResource和LoadResource.但你没有打电话LockResource.请注意,LoadResource返回一个HGLOBAL.要获取指向资源数据的指针,必须将其传递HGLOBAL给LockResource.虽然,事实证明,在现代Windows的实现中,你可以逃脱而不执行这LockResource一步,你仍然应该遵守规则来执行它.
现在,如果您SizeofResource返回非零值,那么您的呼叫显然是FindResource成功的.你必须在断言中错误地认为返回的大小SizeofResource不正确.假设这样的基础API SizeofResource按设计工作,必须是安全的.
我还要强调您的代码省略了错误检查.你真的应该补充一点.如果您这样做,您可能会发现您获得了有用的诊断信息.没有错误检查你不知道你的代码可能在哪里失败.
| 归档时间: |
|
| 查看次数: |
935 次 |
| 最近记录: |