将 System.Drawing.Icon 转换为 System.Media.ImageSource

Posted

技术标签:

【中文标题】将 System.Drawing.Icon 转换为 System.Media.ImageSource【英文标题】:Convert System.Drawing.Icon to System.Media.ImageSource 【发布时间】:2010-11-10 18:56:48 【问题描述】:

我有一个跨非托管/托管边界封送的 IntPtr,该边界对应于一个图标句柄。通过 FromHandle() 方法将其转换为图标很简单,直到最近才令人满意。

基本上,我已经有足够多的线程奇怪了,因为我一直在玩 MTA/STA 舞蹈以防止托管的 WinForm 破坏应用程序的主要(WPF-tastic)UI 太脆弱而无法坚持.所以 WinForm 必须离开。

那么,我怎样才能获得 ImageSource 版本的 Icon?

注意,我试过 ImageSourceConverter 无济于事。

顺便说一句,我可以获得一些的底层资源,但不是所有涉及的图标,它们通常存在于我的应用程序程序集之外(实际上,它们通常存在于非​​托管 dll 中)。

【问题讨论】:

【参考方案1】:

简单的转换方法,无需创建任何额外的对象:

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

        return imageSource;
    

【讨论】:

这个解决方案似乎比使用 Imaging.CreateBitmapSourceFromHBitmap 的解决方案更优雅。当 Imaging 可以直接从 Icon 句柄创建 BitmapSource 时,无需创建非托管位图(然后必须记住处理它)。 您正在将过时的System.Drawing 拖入 WPF...如果您是 WPF 纯粹主义者,那么这是不行的。 CreateBitmapSourceFromHIcon 位于 System.Windows.Interop 命名空间中,因此请务必添加您的 Using 或 Import 语句。【参考方案2】:

试试这个:

Icon img;

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

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

更新:采纳 Alex 的建议并使其成为扩展方法:

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;
    

那么你可以这样做:

ImageSource wpfBitmap = img.ToImageSource();

【讨论】:

完成此转换后,您应该在 hBitmap 上的 gdi32.dll 中使用 DeleteObject( IntPtr hObject ) 调用以避免内存泄漏。 在某些特定情况下,即使更新的解决方案也可能导致“参数无效”问题blog.lavablast.com/post/2007/11/…,但我需要进行更多调查。使用 Darren 的解决方案可能更安全 我在互联网上浏览了很长一段时间,试图找到这个解决方案的答案。大多数人建议在他们的项目中使用“资源”而不是“嵌入式资源”。问题在于,如果您不想在项目中分发和隐藏资源信息,则需要嵌入它们。值得注意的是要返回的资源的 。例如,在 WPF 中,要在窗口上设置一个图标,它需要一个 类型。访问“Properties.Resource.”具有原生 GDI 类型,需要转换为 Windows.Media。 如果您对主应用程序使用图标,它无论如何都会将其置于项目的本地。在这种情况下,如果您希望与 MainWindow 图标共享此图标,只需在 XAML 中使用 Icon="pack://application:,,,/MyApp;component/image.ico"> 即可将其嵌入到程序集中一样。【参考方案3】:

在使用一次性流时,几乎总是建议使用“使用”块来强制正确释放资源。

using (MemoryStream iconStream = new MemoryStream())

   icon.Save(iconStream);
   iconStream.Seek(0, SeekOrigin.Begin);

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

在哪里 icon 是源 System.Drawing.Icon,this.TargetWindow 是目标 System.Windows.Window。

【讨论】:

用这种方法得到非常低质量的图像 MemoryStream.Dispose 并没有释放任何东西,referencesource.microsoft.com/#mscorlib/system/io/… 这会导致我的 WPF 应用程序崩溃并处理异常。因此,Windows 稍后在显示图标时需要该内存流。您需要在窗口的整个生命周期中保持该内存流活跃。并在窗口关闭时将其丢弃。 我修复了它...使用这种格式,您可以像上面的示例一样处理内存流:BitmapFrame.Create(iconStream, BitmapCreateOptions.PreservePixelFormat, BitmapCacheOption.OnLoad)【参考方案4】:
MemoryStream iconStream = new MemoryStream();
myForm.Icon.Save(iconStream);
iconStream.Seek(0, SeekOrigin.Begin);
_wpfForm.Icon = System.Windows.Media.Imaging.BitmapFrame.Create(iconStream);

【讨论】:

您需要处理 MemoryStream 吗? 应该在流上使用 Dispose 并且由于某种原因这给了我非常低质量的黑白图像。其他人有这个问题吗? @Patrick,我在使用 MemoryStream 时也遇到了问题,并且输出质量非常低。我按字节(从下方)使用this solution。 您不需要释放 MemoryStream,它除了将其标记为已释放之外什么都不做:referencesource.microsoft.com/#mscorlib/system/io/…。但是它会稍微提高 GC 的性能,因为 Stream.Dispose 会将其从终结队列中移除。【参考方案5】:

从上面的一些内容为我自己创造了最高质量的图标。从字节数组加载图标。我使用缓存加载,因为如果你不这样做,当你释放内存流时你会得到一个释放的异常。

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

【讨论】:

【参考方案6】:

有点类似的例子,只是根据开发者的用例调整...

    [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

【讨论】:

【参考方案7】:

这个问题有一个非常简单的解决方案。

步骤:

(1) 在解决方案资源管理器中添加图片到资源 -> resources.resx (2) 在解决方案资源管理器中编辑“资源”目录中的图像属性并将“构建操作”更改为“资源”

在 xaml 中,添加以下...

Icon="resources/name of image"(其中“name of image”是您添加到资源中的图像的名称 - 请参见第 (1) 点。

【讨论】:

以上是关于将 System.Drawing.Icon 转换为 System.Media.ImageSource的主要内容,如果未能解决你的问题,请参考以下文章

3dmax提示应用组件中发生了未经处理的异常,如何解决

我用VS2010写的一个C#窗体程序打包后安装,启动后有一个界面无法启动,报错啥异常文本,怎么回事啊

将 A 转换为 1 B 转换为 2 ... Z 转换为 26,然后将 AA 转换为 27 AB 转换为 28(Excel 中列引用的列索引)

怎么用ABBYY将PDF转换为JPEG图像

将rowdatapacket转换为数组,如何将mysql node.js api rowdatapacket转换为数组,将字符串转换为数组

怎么用ABBYY将PDF转换为JPEG图像