6
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

C# - ちょー雑な方法でFormのアイコンを生成する (Windows)

Last updated at Posted at 2019-11-27

画面キャプチャ

image.png

サンプルコード - 修正版


using System;
using System.Drawing;
using System.Runtime.InteropServices;
using System.Windows.Forms;

class IconMakeTest:Form
{
    class NativeMethods
    {
        [DllImport("user32.dll", CharSet = CharSet.Auto)]
        public extern static bool DestroyIcon(IntPtr handle);
    } 

    IconMakeTest()
    {
        HandleCreated+=(sender,e)=>{GetHiconExample();};
    }
    
    static string[] iconDot = new string[]{
        "................",
        ".###...##...##..",
        "..#...#..#.#..#.",
        "..#...#....#..#.",
        "..#...#....#..#.",
        "..#...#....#..#.",
        "..#...#....#..#.",
        "..#...#....#..#.",
        "..#...#....#..#.",
        "..#...#....#..#.",
        "..#...#....#..#.",
        "..#...#....#..#.",
        "..#...#....#..#.",
        "..#...#..#.#..#.",
        ".###...##...##..",
        "................",
    };

    void GetHiconExample()
    {
        Bitmap bmp = new Bitmap(16,16);
        using ( Graphics g = Graphics.FromImage(bmp) ) {
            g.Clear(Color.White);
        }
        for(int y=0;y<16;y++){
            for(int x=0;x<16;x++){
                if (iconDot[y][x]=='#') {
                    bmp.SetPixel(x,y,Color.Black);
                }
            }
        }

        IntPtr Hicon = bmp.GetHicon();
        Icon newIcon;
        newIcon = Icon.FromHandle(Hicon);

        this.Icon = newIcon;
        NativeMethods.DestroyIcon(newIcon.Handle);
    }

    [STAThread]
    static void Main(string[] args)
    {
        Application.Run(new IconMakeTest());
    }
}

説明

string配列でドット絵を表現していて、BitmapへSetPixelで描画し、GetHiconメソッドでIconへのポインタを取得し、Icon.FromHandleメソッドで取得したハンドルをFormのIconプロパティにセットしている。

HandleCreatedイベントで呼び出している理由

HandleCreatedイベントが発生したときにGetHiconExampleを呼んでいるのは、以下の理由のため。

最初は、FormのコンストラクタでGetHiconExampleを呼ぶコードを書いていた。それで実行すると、下記の例外が発生した。


System.ObjectDisposedException: 破棄されたオブジェクトにアクセスできません。
オブジェクト名 'Icon' です。

どうやらDestroyIconしたあとにIconが生成されているようである。

ILSpyで色々調べてみたところ、FormIconプロパティに代入(setアクセス)したときに、内部でUpdateWindowIconが呼ばれるのだが、IsHandleCreatedtrueでないと、このときにIconが生成(WM_SETICON)されないようになっている。

Formの内部コード抜粋

private void UpdateWindowIcon(bool redrawFrame)
{
    if (!base.IsHandleCreated)
    {
        return;
    }
    :
    SendMessage(128, 1, icon.Handle); // 128 = WM_SETICON

なので、以下の順になっていないといけない。

  1. HandleCreatedイベント発生
  2. Form.Iconへのハンドルのset
  3. DestroyIconの実行

参考サイト

  1. https://docs.microsoft.com/en-us/dotnet/api/system.drawing.icon.fromhandle?view=netframework-4.8
  2. https://dobon.net/vb/dotnet/graphics/saveimage.html

おまけ - 参考サイト 1 のサンプルコードの完全版

参考サイト 1 のサンプルコードは一部しかかかれておらず、そのままでは実行できないため、
GetHicon_ExampleメソッドとDestroyIcon以外を付け足した。


using System;
using System.Drawing;
using System.Runtime.InteropServices;
using System.Windows.Forms;

class IconTest : Form
{
    [System.Runtime.InteropServices.DllImport("user32.dll", CharSet = CharSet.Auto)]
    extern static bool DestroyIcon(IntPtr handle);

    IconTest()
    {
        Paint += (sender,e)=>{GetHicon_Example(e);};
    }

    private void GetHicon_Example(PaintEventArgs e)
    {

        // Create a Bitmap object from an image file.
        Bitmap myBitmap = new Bitmap(@"c:\FakePhoto.jpg");

        // Draw myBitmap to the screen.
        e.Graphics.DrawImage(myBitmap, 0, 0);

        // Get an Hicon for myBitmap.
        IntPtr Hicon = myBitmap.GetHicon();

        // Create a new icon from the handle. 
        Icon newIcon = Icon.FromHandle(Hicon);

        // Set the form Icon attribute to the new icon.
        this.Icon = newIcon;

        // You can now destroy the icon, since the form creates
        // its own copy of the icon accessible through the Form.Icon property.
        DestroyIcon(newIcon.Handle);

    }

    [STAThread]
    static void Main(string[] args)
    {
        Application.Run(new IconTest());
    }
}

DestroyIconのWindowsAPIの説明を見ても.Netでの使用を考慮していない説明になっており、このコードのDestroyIconを呼び出しているとこのコメントが参考になった。

6
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?