2013-01-04 56 views
8

我試圖用C#編程方式在PNG文件中創建一個高質量圖標(表示:suitable for Win Vista/7/8),以用作快捷方式圖標。由於Bitmap.GetHIcon()函數不支持這些圖標,並且我想避免外部依賴項或庫,所以我正在使用稍微修改過的ICO編寫器,我發現它的編號爲here on SO。 我有工作代碼,但我遇到了一些小故障,Windows顯示這些圖標的方式。 相關的代碼是:以編程方式創建高質量的ico文件

// 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不會。 我做了這張截圖,用正確顯示圖標一起進行比較

enter image description here

我懷疑錯誤在於ICO節省的方法,但比較它們正常顯示感染控制主任在十六進制編輯器,頭後仍得到正確書寫,但PNG圖像部分本身seeems不同。有沒有人有想法?我也歡迎更好的,不太冒險的解決方案。

+0

在閱讀它的長度之前,您是否嘗試過沖洗「FS」? –

+0

我發佈了編寫多圖像ICO文件的代碼[在此答案](http://stackoverflow.com/a/29502697/24874)。 –

回答

7

您的ico文件設置爲只保存16位精度的嵌入位圖的長度,但PNG文件太大(大於65535字節),所以長度記錄溢出。

I.e.以下行不完整:

// 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)); 

由於潔淨度和性能的問題,我會盡量避免使用那些單獨的寫操作,只是使用與寫超載字節數組參數。此外,您可以考慮使用Save-To-MemoryStream,然後使用單個寫入標頭(現在可以使用PNG的字節長度)和單個寫入來複制PNG文件,而不是使用有點棘手的Save-To-File然後查找。數據從內存流到文件。

另一點你真正應該處理的是配置IDisposable資源。即使你不需要,因爲你還沒有遇到任何問題,它會在某天將會咬你,如果你甚至有一個相當小的代碼庫,包含所有未處理的一次性代碼,你將很難找到源代碼你的泄漏和/或僵局。一般情況下:從不請致電Close,除非您真的無法避免它 - 而是將您的FileStream包裹在using區塊中。同樣,ImageBitmap是一次性的,並分配本地資源,但至少你不能得到任何鎖定問題(AFAIK - 但最好是安全的比對不起)。

+0

謝謝,解決了它。我從來沒有想過這件事。感謝您的建議,我肯定會考慮實施它們。 – Lennart

+0

不客氣:-)。 –