C# 以编程方式创建高质量ico文件

C# 以编程方式创建高质量ico文件,c#,.net-4.0,C#,.net 4.0,我正试图用C#编程从PNG文件创建一个高质量的图标(意思是:),用作快捷图标。由于Bitmap.GetHIcon()函数不支持此类图标,并且我希望避免外部依赖项或库,因此我目前使用的是我找到的一个稍加修改的ICO编写器。 我有工作代码,但我在Windows显示这些图标的方式上遇到了一些问题。 有关守则如下: // ImageFile contains the path to PNG file public static String IcoFromImageFile(String ImageFi

我正试图用C#编程从PNG文件创建一个高质量的图标(意思是:),用作快捷图标。由于Bitmap.GetHIcon()函数不支持此类图标,并且我希望避免外部依赖项或库,因此我目前使用的是我找到的一个稍加修改的ICO编写器。 我有工作代码,但我在Windows显示这些图标的方式上遇到了一些问题。 有关守则如下:

// ImageFile contains the path to PNG file
public static String IcoFromImageFile(String ImageFile) {
    //...       
    Image iconfile = Image.FromFile(ImageFile);

    //Returns a correctly resized Bitmap        
    Bitmap bm = ResizeImage(256,256,iconfile);                
    SaveAsIcon(bm, NewIconFile);

    return NewIconFile;

}        

// From: https://stackoverflow.com/a/11448060/368354
public static void SaveAsIcon(Bitmap SourceBitmap, string FilePath) {
    FileStream FS = new FileStream(FilePath, FileMode.Create);
    // ICO header
    FS.WriteByte(0); FS.WriteByte(0);
    FS.WriteByte(1); FS.WriteByte(0);
    FS.WriteByte(1); FS.WriteByte(0);

    // Image size
    // Set to 0 for 256 px width/height
    FS.WriteByte(0);
    FS.WriteByte(0);
    // Palette
    FS.WriteByte(0);
    // Reserved
    FS.WriteByte(0);
    // Number of color planes
    FS.WriteByte(1); FS.WriteByte(0);
    // Bits per pixel
    FS.WriteByte(32); FS.WriteByte(0);

    // Data size, will be written after the data
    FS.WriteByte(0);
    FS.WriteByte(0);
    FS.WriteByte(0);
    FS.WriteByte(0);

    // Offset to image data, fixed at 22
    FS.WriteByte(22);
    FS.WriteByte(0);
    FS.WriteByte(0);
    FS.WriteByte(0);

    // Writing actual data
    SourceBitmap.Save(FS, System.Drawing.Imaging.ImageFormat.Png);

    // Getting data length (file length minus header)
    long Len = FS.Length - 22;

    // Write it in the correct place
    FS.Seek(14, SeekOrigin.Begin);
    FS.WriteByte((byte)Len);
    FS.WriteByte((byte)(Len >> 8));

    FS.Close();
}
这可以编译并工作,但有一个问题。Windows在快捷方式上显示的图标不正确。我也是通过编程实现的,但即使我手动(通过文件属性、更改图标)也会实现。问题是图标被切断(图像本身显示正确)。它取决于图像,但通常仅显示实际图标的20%左右。如果我在XNView这样的图像查看器中打开该文件,它会显示完全正确,但MS Paint不会。 我制作了这个屏幕截图,以及一个正确显示的图标以供比较


我怀疑错误在于ICO保存方法,但即使在将它们与十六进制编辑器中正常显示的ICO进行比较后,标题也会正确写入,但PNG图像部分本身似乎有所不同。有人有主意吗?我也欢迎更好、更少的黑客解决方案。

您的ico文件设置为仅以16位精度保存嵌入位图的长度,但PNG文件太大(大于65535字节),因此长度记录溢出

即,以下行不完整:

// Write it in the correct place
FS.Seek(14, SeekOrigin.Begin);
FS.WriteByte((byte)Len);
FS.WriteByte((byte)(Len >> 8));
您可以添加以下行:

FS.WriteByte((byte)(Len >> 16));
FS.WriteByte((byte)(Len >> 24));
考虑到清洁度和性能,我通常会避免所有这些单独的写操作,而只使用字节数组参数的写重载。此外,您可以考虑一个保存到内存流,然后为头文件(现在可以使用PNG的字节长度)和一个单独的写来将PNG数据从内存流复制到文件。
您真正应该解决的另一点是处理
IDisposable
资源。即使你还没有遇到任何问题,所以你还不需要这样做,但总有一天它会咬到你。如果你有一个相当小的代码库,里面有各种各样的未经处理的可处置文件,那么你将很难找到泄漏和/或死锁的根源。一般来说:除非你真的无法避免,否则不要调用
Close
——而是使用
块将
文件流
包装在
中。类似地,
Image
Bitmap
是一次性的,可以分配本机资源,但至少你不会遇到任何锁定问题(好吧,但最好是安全的,而不是抱歉)。

在读取
FS
的长度之前,你有没有尝试过刷新它?我发布了编写多图像ICO文件的代码。谢谢,这就解决了问题。我永远也想不到。谢谢你的建议,我肯定会考虑实施它们。