2013-01-04 14 views
8

C#でプログラムでPNGファイルから高品質のアイコン(意味:suitable for Win Vista/7/8)を作成しようとしていますが、ショートカットアイコンとして使用しています。 Bitmap.GetHIcon()関数はこれらの種類のアイコンをサポートしていないので、外部の依存関係やライブラリを避けたいのですが、現在私はhere on SOというわずかに変更されたICOライターを使用しています。 私は作業コードを持っていますが、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(); 
} 

これは、コンパイルして動作しますが、一つの問題で。ショートカットのアイコンが正しく表示されません。私はこれもプログラム的に行いますが、手動で(ファイルのプロパティ、アイコンの変更を介して)行っても発生します。問題は、アイコンが切り取られている(画像自体が正しく表示されている)ことです。それはイメージに依存しますが、実際のアイコンの約20%しか表示されません。 XNViewのような画像ビューアでファイルを開くと、完全に表示され、正しく表示されますが、MS Paintでは表示されません。 私が正しく、私はエラーがICOの保存方法にあるが、それでも、それらを比較した後、通常のHexエディタでICOSを表示するために、ヘッダ疑い比較

enter image description here

のアイコンを表示するとともに、このスクリーンショットを作りました正しく書かれますが、PNG画像部分自体は異なって見えます。誰にもアイデアはありますか?私はまた、より良い、よりハックなソリューションを歓迎します。

+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-Fileの代わりにseekを実行する代わりに、Save-To-MemoryStreamとヘッダ用の単一のWrite(PNGの長さをバイト単位で使用できる)とPNGをコピーする単一の書き込みメモリストリームからファイルへのデータ

あなたが本当に対処すべきもう一つのポイントはです。IDisposableリソースを処分しています。問題が発生していないので、まだ必要がない場合でも、いつかはあなたを噛んでしまいます。コードベースがかなり小さくて、すべての種類の未処分のディスポーザブルがあれば、ソースを見つけるのは非常に難しいでしょう。あなたのリークおよび/またはデッドロック。一般的に:Closeは実際には避けられない限り、FileStreamusingブロックにラップしてください。同様に、ImageBitmapは使い捨てであり、ネイティブリソースを割り当てますが、少なくともあなたはそれらのロック問題を解決することはできません(AFAIK - しかし、申し訳ありません)。

+0

ありがとう、それはそれを解決しました。私は決してそれを考え出さなかったでしょう。そして提案のおかげで、私はそれらを実装することに間違いなく目を向けます。 – Lennart

+0

歓迎します:-) –

関連する問題