Convert System.Drawing.Icon to System.Media.ImageSource

C#WpfWindowsWinforms

C# Problem Overview


I've got an IntPtr marshaled across an unmanaged/managed boundary that corresponds to an Icon Handle. Converting it to an Icon is trivial via the FromHandle() method, and this was satisfactory until recently.

Basically, I've got enough thread weirdness going on now that the MTA/STA dance I've been playing to keep a hosted WinForm from breaking the primary (WPF-tastic) UI of the application is too brittle to stick with. So the WinForm has got to go.

So, how can I get an ImageSource version of an Icon?

Note, I've tried ImageSourceConverter to no avail.

As an aside, I can get the underlying resource for some but not all of the icons involved and they generally exist outside of my application's assembly (in fact, they often exist in unmanaged dll's).

C# Solutions


Solution 1 - C#

Simple conversion method without creating any extra objects:

    public static ImageSource ToImageSource(this Icon icon)
    {
        ImageSource imageSource = Imaging.CreateBitmapSourceFromHIcon(
            icon.Handle,
            Int32Rect.Empty,
            BitmapSizeOptions.FromEmptyOptions());

        return imageSource;
    }

Solution 2 - C#

Try this:

Icon img;

Bitmap bitmap = img.ToBitmap();
IntPtr hBitmap = bitmap.GetHbitmap();

ImageSource wpfBitmap =
     Imaging.CreateBitmapSourceFromHBitmap(
          hBitmap, IntPtr.Zero, Int32Rect.Empty, 
          BitmapSizeOptions.FromEmptyOptions());

UPDATE: Incorporating Alex's suggestion and making it an extension method:

internal static class IconUtilities
{
    [DllImport("gdi32.dll", SetLastError = true)]
    private static extern bool DeleteObject(IntPtr hObject);

    public static ImageSource ToImageSource(this Icon icon)
    {            
        Bitmap bitmap = icon.ToBitmap();
        IntPtr hBitmap = bitmap.GetHbitmap();

        ImageSource wpfBitmap = Imaging.CreateBitmapSourceFromHBitmap(
            hBitmap,
            IntPtr.Zero,
            Int32Rect.Empty,
            BitmapSizeOptions.FromEmptyOptions());

        if (!DeleteObject(hBitmap))
        {
            throw new Win32Exception();
        }

        return wpfBitmap;
    }
}

Then you can do:

ImageSource wpfBitmap = img.ToImageSource();

Solution 3 - C#

MemoryStream iconStream = new MemoryStream();
myForm.Icon.Save(iconStream);
iconStream.Seek(0, SeekOrigin.Begin);
_wpfForm.Icon = System.Windows.Media.Imaging.BitmapFrame.Create(iconStream);

Solution 4 - C#

When using disposable streams it is almost always recommended to use 'using' blocks to force correct releasing of resources.

using (MemoryStream iconStream = new MemoryStream())
{
   icon.Save(iconStream);
   iconStream.Seek(0, SeekOrigin.Begin);

   this.TargetWindow.Icon = System.Windows.Media.Imaging.BitmapFrame.Create(iconStream);
}

Where icon is the source System.Drawing.Icon, and this.TargetWindow is the target System.Windows.Window.

Solution 5 - C#

Taking from some above this has created the highest quality of icons for my self. Loading the icons from a byte array. I use cache onload because if you don't you will get a disposed exception when you dispose the memory stream.

   internal static ImageSource ToImageSource(this byte[] iconBytes)
    {
        if (iconBytes == null)
            throw new ArgumentNullException(nameof(iconBytes));
        using (var ms = new MemoryStream(iconBytes))
        {
            return BitmapFrame.Create(ms, BitmapCreateOptions.PreservePixelFormat, BitmapCacheOption.OnLoad);
        }
    }

Solution 6 - C#

Somehow similar example, only tuned from developer's use cases...

    [DllImport("shell32.dll")]
    public static extern IntPtr ExtractIcon(IntPtr hInst, string file, int nIconIndex);

    [DllImport("user32.dll", SetLastError = true)]
    static extern bool DestroyIcon(IntPtr hIcon);

    /// <summary>
    /// Gets application icon from main .exe.
    /// </summary>
    /// <param name="setToObject">object to which to set up icon</param>
    /// <param name="bAsImageSource">true if get it as "ImageSource" (xaml technology), false if get it as "Icon" (winforms technology)</param>
    /// <returns>true if successful.</returns>
    public bool GetIcon(object setToObject, bool bAsImageSource)
    {
        String path = Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location);
        path = Path.Combine(path, "yourmainexecutableName.exe");
        int iIconIndex = 0;

        // If your application contains multiple icons, then
        // you could change iIconIndex here.

        object o2set = null;
        IntPtr hIcon = ExtractIcon(IntPtr.Zero, path, iIconIndex);
        if (hIcon == IntPtr.Zero)
            return false;

        Icon icon = (Icon)Icon.FromHandle(hIcon);
        if (bAsImageSource)
        {
            o2set = System.Windows.Interop.Imaging.CreateBitmapSourceFromHBitmap(
                icon.ToBitmap().GetHbitmap(), IntPtr.Zero, Int32Rect.Empty, 
                System.Windows.Media.Imaging.BitmapSizeOptions.FromEmptyOptions());
        } else {
            icon = (Icon)icon.Clone();
        }
     
        DestroyIcon(hIcon);
        setToObject.GetType().GetProperty("Icon").SetValue(setToObject, o2set);
        return true;
    } //GetIcon

Solution 7 - C#

There is a really simple solution to this problem.

Steps:

(1) add image to resources in solution explorer -> resources.resx (2) edit image properties inside "Resources" directory in solution explorer and change "Build action" to "Resource"

In xaml, add the following...

Icon="resources/name of image" (where "name of image" is the name of the image you added to resources - see point (1).

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionKevin MontroseView Question on Stackoverflow
Solution 1 - C#ByteView Answer on Stackoverflow
Solution 2 - C#Kenan E. K.View Answer on Stackoverflow
Solution 3 - C#DarrenView Answer on Stackoverflow
Solution 4 - C#Derek BartramView Answer on Stackoverflow
Solution 5 - C#Justin DavisView Answer on Stackoverflow
Solution 6 - C#TarmoPikaroView Answer on Stackoverflow
Solution 7 - C#user3442716View Answer on Stackoverflow