c# - System.ArgumentException HResult = 0x80070057 消息 = 参数无效。来源 = System.Drawing。为什么?
问题描述
使用 c#,我从 PE 文件中提取了光标资源并删除了 xy 热点并添加了 BITMAPFILEHEADER,但是当我尝试转换为 BITMAP 以在图片框中显示为 pbResImage.Image = Image.FromStream(bitmapStream.BaseStream) ; 我收到一个参数异常。
我的代码适用于数十个游标,并且可以使用其他程序提取有问题的游标。
我相信最简单的方法是通过代码 (bitmapBytes) 和传递给 Image.FromStream (bitmapStream) 的流来检查实际输入。我已经尝试过并没有成功。
我要求您帮助检查输入并确定有关 bitmapStream 的实际无效内容。一旦我知道 .NET 发现了什么问题,可能就会有解决方案。
这是我提取的 bitmapBytes 数组:
0000- 28 00 00 00 20 00 00 00 40 00 00 00 01 00 04 00
0010- 00 00 00 00 80 02 00 00 00 00 00 00 00 00 00 00 0020- 00 00 00 00 00 00 00
00 00 00 00 00 00 00 80 00
0030- 00 80 00 00 00 80 80 00 80 00 00 00 80 00 80 00
0040- 80 80 00 00 80 80 80 00 C0 c0 c0 00 00 00 FF 00 0 0
0050- 0 00 FF FF 00 00 00 FF 00 FF 00
0060- FF FF 00 00 FF FF FF 00 00 00 00 00 00 00 00 00
0070- 00 00 00 00 00 00 00 00 00 00 00 00 00
0 0 0 00 00 0 0 0 0 0 00 0 0 0 0 0 0 0 0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0090- 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00
0000性程00000000来00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00c0- 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00d0- 00 00 00 00 00 00 00 00 00 00 00 00 00 00 08 88
00e0- 88 80 00 00 00 00 00 00 00 00 00 00 00 00 7F FF
00f0- FF F8 00 00 00 00 00 00 00 00 00 00 00 00 7F FF
0100- FF F8 00 00 00 00 00 00 00 00 00 00 00 07 FF FF
0110- 0 00 FF 00 0 0 0 0 0 FF 80 00 00 00 00 00 7F FF FF
0120- FF FF 80 00 00 00 00 00 00 00 00 00 07 FF FF FF
0130- FF FF F8 00 00 00 00 00 00 00 00 00 07 FF
F 80 FF 0140- FF 00 00 00 00 00 00 00 00 7F FF FF FF
0150- FF FF F8 00 00 00 00 00 00 00 00 00 7F FF FF FF
0160-
FF FF FF 80 00 00 00 00 00 00 0017 07 FF FF FF
FF FF FF 80 00 00 00 00 00 00 00 07 FF 88 FF FF
0180- FF FF FF 80 00 00 00 00 00 00 00 7F F8 00 FF FF
0190- FF FF FF 80 00 00 00 00 00 00 00 7F 80 07 FF FF
01a0- FF FF 7F 80 00 00 00 F 8 00 0 00 07 FF 7F
01b0- F7 F8 0F 80 00 00 00 00 00 00 07 70 00 07 F8 0F
01c0- 80 F8 0F 80 00 00 00 00 00 00 00 00 00 07 F8 0F
01d00- 00 F8 0 0700- 80 F8 0 00 00 00 00 00 07 F8 0F
01e0- 80 F8 00 00 00 00 00 00 00 00 00 00 00 07 F8 0F
01f0- 80 77 00 00 00 00 00 00 00 00 00 00 00 07 F8 0 0 0 00 07 F8
0 00 00 00 00 00 00 00 00 00 07 F8 00
0210- 00 00 00 00 00 00 00 00 00 00 00 00 00 07 F8 00
0220-
00 00 00 00 00 00 00 0 00 00 0 0 0 0 0 0 0 0 0 08 0
00 00 00 00 00 00 00 00 00 00 00 00 00 07 F8 00
0340- 00 00 00 00 00 00 00 00 00 00 00 00 00 00 77 00
0250- 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0260- 00 0 0 FF 0 0 0 0 0 0 0 0 FF 0 0 0 0 FF FF FF FF
0270- FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF
0280- FF FF FF FF F8 1F FF FF F0 0F FF FF F0 0F FF
0290- FF E0 03 FF FF c0 03 FF FF 80 01 FF FF 80 01 FF
02a0- FF 00 01 FF FF 00 00 FF FE 00 00 FF FE 00 00 FF
02b0- Fc 00 00 FF Fc 20 00 FF F8 60 00 FF F8 E0 00 FF
02c0- FF E0 01 FF FF E0 07 FF FF E0 0F FF FF E0 7F FF
02d0- FF E1 FF FF FF E1 FF FF FF E1 FF FF FF E1 FF FF
02e0- FF F3 FF FF FF FF FF FF
这是作为“无效”参数的位图流:
0000- 42 4D F6 02 00 00 00 00 00 00 76 00 00 00 28 00
0010- 00 00 20 00 00 00 40 00 00 00 01 00 04 00 00 00
0020- 00 00 80 02 00 00 00 00 00 00 00 00 00 00 00 00
0030- 00 00 00 00 00 00 00 00 00 00 00 00 80 00 00 80
0040- 00 00 00 80 80 00 80 00 00 00 80 00 80 00 0 0 0 80 0 8 0 C 0 0 0 80
0080- 0 C C0 00 00 00 FF 00 00 FF
0060- 00 00 00 FF FF 00 FF 00 00 00 FF 00 FF 00 FF FF
0070- 00 00 FF FF FF 00 00 00 00 00 00 00 00 00 00 00
0 0080- 0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0090- 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00
0000性程00000000来00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00c0- 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00d0- 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00e0- 00 00 00 00 00 00 00 00 00 00 00 00 08 88 88 80
00f0- 00 00 00 00 00 00 00 00 00 00 00 00 7F FF FF F8
0100- 00 00 00 00 00 00 00 00 00 00 00 00 7F FF FF F8
0 010- 00 0 0 0 0 010- 00 00 0 0 00 00 00 07 FF FF FF FF
0120- 80 00 00 00 00 00 00 00 00 00 00 7F FF FF FF
0130- 80 00 00 00 00 00 00 00 00 00 07 FF FF FF 0 0 FF
0140- F 00 00 00 00 00 00 07 FF FF FF FF FF
0150- F8 00 00 00 00 00 00 00 00 00 7F FF FF FF FF FF
0160- F8 00 00 00 00 00 00 00 00 00 7F
017 FF FF FF FF FF FF 80 00 00 00 00 00 00 00 07 FF FF FF FF FF FF
0180- FF 80 00 00 00 00 00 00 00 07 FF 88 FF FF FF FF
0190- FF 80 00 00 00 00 00 00 00 7F F8 00 FF FF FF
01a0- FF 80 00 00 00 7 00 00 0 70 F 08 FF FF FF FF
01b0- 7F 80 00 00 00 00 00 00 07 F8 00 07 FF 7F F7 F8
01c0- 0F 80 00 00 00 00 00 00 07 70 00 07 F8 0F 80 F8
01d0- 0000 80 0 00 0 0F 80 00 0 0 00 00 00 07 F8 0F 80 F8
01e0- 07 70 00 00 00 00 00 00 00 00 00 07 F8 0F 80 F8
01f0- 00 00 00 00 00 00 00 00 00 00 00 07 0 0 0 0F 00
0 7 - 00 00 00 00 00 00 00 07 F8 07 70 00
0210- 00 00 00 00 00 00 00 00 00 00 00 07 F8 00 00 00
0220- 00 00 00 00 00 00 00 0 00 00 0 0 00 0 0 0 02 3 00
000 08 00 00 00 00 00 00 00 00 00 00 00 07 F8 00 00 00
0340- 00 00 00 00 00 00 00 00 00 00 00 07 F8 00 00 00
0250- 00 00 00 00 00 00 00 00 00 00 00 00 77 00 00 00
0260- 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0270- 00 00 00 00 00 00 FF FF FF FF FF FF FF FF FF FF
0280- FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF
0290- FF FF FF F8 1F FF FF F0 0F FF FF F0 0F FF FF E0
02a0- 03 FF FF C0 03 FF FF 80 01 FF FF 80 01 FF FF 00
02b0- 01 FF FF 00 00 FF FE 00 00 FF FE 00 00 FF FC 00
02c0- 00 FF FC 20 00 FF F8 60 00 FF F8 E0 00 FF FF E0
02d0- 01 FF FF E0 07 FF FF E0 0F FF FF E0 7F FF FF E1
02e0- FF FF FF E1 FF FF FF E1 FF FF FF E1 FF FF FF F3
02f0- FF FF FF FF FF FF
以下是相关代码:
public void DisplayPECursor(TreeNode node)
{
Byte[] nodeBytes = (byte[])node.Tag;
Byte[] bitmapBytes = new byte[nodeBytes.Length - 4];
// Remove hotspot-x and hotspot-y values from the cursor resource data
Buffer.BlockCopy(nodeBytes, 4, bitmapBytes, 0, nodeBytes.Count() - 4);
/////////////////////////////////// temp for debug
using (Stream file = File.OpenWrite(@"G:\___ProblemFiles\bitmapBytes.dat"))
{
file.Write(bitmapBytes, 0, bitmapBytes.Length);
}
///////////////////////////////////
(int sizeBITMAPINFOHEADER, int sizeColorTable) = GetBmHeaderInfo(bitmapBytes);
int bitmapFileLen = bitmapBytes.Length + sizeBITMAPFILEHEADER;
using (BinaryWriter bitmapStream = new BinaryWriter(new MemoryStream(bitmapFileLen)))
{
// Write the missing BITMAPFILEHEADER.
const Int16 zero = 0;
bitmapStream.Write(bmSignature);
bitmapStream.Write(bitmapFileLen);
bitmapStream.Write(zero);
bitmapStream.Write(zero);
bitmapStream.Write(sizeBITMAPFILEHEADER + sizeBITMAPINFOHEADER + sizeColorTable);
bitmapStream.Seek(sizeBITMAPFILEHEADER, SeekOrigin.Begin);
bitmapStream.Write(bitmapBytes, 0, bitmapBytes.Length);
/////////////////////////////////// temp for debug
//Save file for examination with hex editor
SaveStreamToFile(bitmapStream, @"G:\___ProblemFiles\My125.bmp");
///////////////////////////////////
pbResImage.Image = Image.FromStream(bitmapStream.BaseStream);
}
/////////////////////////////////////////////////////////////////// temp for debug
//Internal method to save file for examination with hex editor
void SaveStreamToFile(BinaryWriter streamName, string destFilename)
{
//The bmp file has been built in memory now save it to file
StreamReader reader = new StreamReader(streamName.BaseStream);
int bufferLen = 4096;
byte[] buffer = new byte[bufferLen];
FileStream saveStream = new FileStream(destFilename, FileMode.Create, FileAccess.ReadWrite);
streamName.Seek(0, SeekOrigin.Begin); // stream has been previously written so reposition to the beginning
while (true)
{
int read = streamName.BaseStream.Read(buffer, 0, bufferLen);
if (read > 0)
{
saveStream.Write(buffer, 0, read);
}
else
break;
}
saveStream.Close();
}
//////////////////////////////////////////////////////////////////////////
return;
}
_____________________________________________________________________________________________
public (int sizeBITMAPINFOHEADER, int sizeColorTable) GetBmHeaderInfo(byte[] bitmapBytes)
{
//IntPtr offsetSizeInfoHeader =
// Marshal.OffsetOf(typeof(NativeMethods.BITMAPINFOHEADER), "biSize");
//IntPtr offsetBitCount =
// Marshal.OffsetOf(typeof(NativeMethods.BITMAPINFOHEADER), "biBitCount");
//IntPtr offsetClrUsed =
// Marshal.OffsetOf(typeof(NativeMethods.BITMAPINFOHEADER), "biClrUsed");
//IntPtr offsetCompression =
// Marshal.OffsetOf(typeof(NativeMethods.BITMAPINFOHEADER), "biCompression");
//int sizeBITMAPFILEHEADER =
// System.Runtime.InteropServices.Marshal.SizeOf(typeof(NativeMethods.BITMAPFILEHEADER));
//int sizeRGBQUAD =
// System.Runtime.InteropServices.Marshal.SizeOf(typeof(NativeMethods.RGBQUAD));
//UInt16 bmSignature;
// the above variables were refactored to be class level; left here for now for clarity
// for the Stackoverflow community.
var headerSize = new byte[2];
var bitCount = new byte[2];
var compression = new byte[4];
var clrUsed = new byte[4];
Array.Copy(bitmapBytes, (int)offsetSizeInfoHeader, headerSize, 0, 2);
Array.Copy(bitmapBytes, (int)offsetBitCount, bitCount, 0, 2);
Array.Copy(bitmapBytes, (int)offsetCompression, compression, 0, 4);
Array.Copy(bitmapBytes, (int)offsetClrUsed, clrUsed, 0, 4);
int sizeBITMAPINFOHEADER = BitConverter.ToInt16(headerSize, 0);
int count = BitConverter.ToInt16(bitCount, 0);
int compress = BitConverter.ToInt32(compression, 0);
int used = BitConverter.ToInt32(clrUsed, 0);
if (used == 0) used = (int)Math.Pow(2, count); // Assume the maximum if used = 0
// Determine the size of the color table
int sizeColorTable = 0;
if (compress == (int)NativeMethods.biCompression.BI_BITFIELDS)
{
sizeColorTable = 12;
}
else if (compress == (int)NativeMethods.biCompression.BI_ALPHABITFIELDS)
{
sizeColorTable = 16;
}
else if (count < 16 && used > 0)
{
sizeColorTable = sizeRGBQUAD * used;
}
return (sizeBITMAPINFOHEADER, sizeColorTable);
}
解决方案
有一种特殊类型的 DIB 图像用于索引光标和图标,它在标题中设置了双倍高度,但实际上在其后面包含一个 1 位 AND 掩码,用于指示哪些像素是透明的。
如果您分析标头,您那里的数据似乎是一个 4 位、32x64 DIB 图像,但它实际上是一个 4 位 32x32 图像的组合,然后是一个 1 位 32x32 掩码。
这可以通过这样考虑来准确地检测到;如果高度可被 2 整除,则取一半高度,然后将给定像素格式的该半高图像的预期尺寸加上每像素 1 位的该半高图像的预期尺寸(请记住,步幅始终是 4 个字节的倍数)。如果这与您获得的图像数据的大小相匹配,则您正在处理透明图标格式。
要检查的这个大小值是 中的biSizeImage
值BITMAPINFOHEADER
,这是您当前代码似乎根本没有读取的字段。它是紧跟在压缩类型字段之后的 32 位整数。
Boolean isIcon = false;
Int32 maskSize = 0;
Int32 stride = GetClassicStride(biWidth, biBitCount);
Int32 actualHeight = biHeight;
if (biHeight % 2 == 0)
{
Int32 halfHeight = biHeight / 2;
Int32 maskStride = GetClassicStride(biWidth, 1);
Int32 maskSizeCheck = maskStride * halfHeight;
if (biSizeImage = stride * halfHeight + maskSizeCheck)
{
isIcon = true;
actualHeight = halfHeight;
maskSize = maskSizeCheck;
}
}
经典步幅公式:
public static Int32 GetClassicStride(Int32 width, Int32 bitsLength)
{
return (((((bitsLength * width) + 7) / 8) + 3) / 4) * 4;
}
结果图像:
这种格式仅适用于图标文件,不适用于经典位图,这就是为什么您的 bmp 文件头不起作用的原因。因此,要么查看特定的图标/光标文件格式,要么将其转换为 32 位图像并手动将蒙版应用于图像数据(就像我在生成上面的图像时所做的那样)。
从技术上讲,这种格式也可以用于 32 位图标,方法是使用 24bpp 基本图像和 8bpp alpha 掩码。在这种情况下,标题中的高度也将加倍。但到目前为止我还没有真正遇到过这种格式。
关于 Windows ICO 的维基百科文章:
https://en.wikipedia.org/wiki/ICO_(file_format)
ico 格式的规格:
https://web.archive.org/web/20160531004250/https://msdn.microsoft.com/en-us/library/ms997538.aspx
推荐阅读
- c++ - 在 Ubuntu 18.04 上安装 CudaSift
- flutter - 颤振中的 dio 包是否使用隔离来解码 json?
- r - R中两个文件的交集
- mongodb - 从 js 数组中找到对应的对象,并在 mongodb 聚合中使用它的字段
- workbox - 实现 Workbox 的不同方法?
- unity3d - Unity 无法启动 - 无法建立与 Unity 包管理器本地服务器进程的连接
- python - 如何使用 python 根据 pdf 文本的标题将我的文本字符串拆分为多个部分?
- javascript - 如何将空对象添加到本机构造函数对象
- ios - Swift - 在“应用”按钮单击时保存带有选中按钮的表格视图单元
- python - 如何在matplotlib(python)中组合组图