1

Currently I'm working on VS Closed Document Reopen extension which works like in Web browsers. Reopens the last closed "tab" with a hotkey: Ctrl+Shift+T

I also added a Tool Window. Where the user can see the full Closed Documents History. What I would like to add to this WPF window is the actual icon of the file type in VS. Not Icons which are registered in Windows to those extensions. But the actual icons which are shown in the Solution Explorer pane. I want to get them form C# code as Icon/Bitmap/byte[] anything. So I could transform it to WPF BitmapSource and bind it to a ListView dynamically. So I don't want to download the VS Icons form the Internet...

The interface of the DTE2 object a bit convoluted, hard to find anything. And so far I was not able to find any solution. Any idea how can I do it?

Bizhan
  • 16,157
  • 9
  • 63
  • 101
Major
  • 5,948
  • 2
  • 45
  • 60
  • If static resources are ok then MSFT publish their icons these days - https://www.microsoft.com/en-gb/download/details.aspx?id=35825 – Alex K. Jul 23 '18 at 16:02
  • @Bijan. Yes I know. But as I wrote in the question I don't want to download any Icon kit and use that. Because there are dozens of file types and hardcoding icons into the code seems bad. I have to update the code for every new file, also there are extensions which can change the Icon... I have no idea how they do that. – Major Jul 23 '18 at 17:01
  • This comment above was addressed to @AlexK. – Bizhan Jul 23 '18 at 18:22
  • Yeah, Chrome had some weird autocomplete thing. After posting I corrected it immediately. Anyway the answer is still the same, hardconding all Icons seem bad idea. – Major Jul 23 '18 at 18:33

2 Answers2

1

Thanks for the answers and comments. Unfortunately all of them suggested things, which I stated in my question, I don't want to do. Like: download icons and use static files, or use the Operation System registered Icons.

So I continued my research and Looked into a VS Extension FileIcons. What this extension does is it replaces VS Icons with its own. And the Monikers.imagemanifest and IconIsMoniker caught my attention. Searching for that I ran into the Image service and Moniker documentation. This explains in details what is Moniker, how VS uses the Images, etc. The breakthrough was IVsImageService2 and there is a code example how to use KnownMonikers. A bit adjusting that example I was able to query and retry Icons registered in Visual Studio with a very simple code you can check it on Github.

//Acquire the IVsImageService2in the Package
var imageService = (IVsImageService2)Package.GetGlobalService(typeof(SVsImageService));

//Using the IVsImageService2 the query icons.
public Icon GetIcon(IClosedDocument document)
{
    IVsUIObject uIObj = _vsImageService2.GetIconForFile(document.Name, __VSUIDATAFORMAT.VSDF_WINFORMS);
    Icon icon = (Icon)GelUtilities.GetObjectData(uIObj);

    return icon;
}
Major
  • 5,948
  • 2
  • 45
  • 60
0

You can read it using Windows API's.

I found a solution here: Source

Here is my version of it: (mainly updated for .NET 4.7)

using System;
using System.Collections.Generic;
using System.Drawing;
using System.IO;
using System.Runtime.InteropServices;
using System.Windows;
using System.Windows.Interop;
using System.Windows.Media;
using System.Windows.Media.Imaging;

namespace Pillage
{
    public static class IconManager
    {
        private static readonly Dictionary<string, ImageSource> smallIconCache = new Dictionary<string, ImageSource>();
        private static readonly Dictionary<string, ImageSource> largeIconCache = new Dictionary<string, ImageSource>();

        public static ImageSource FindIconForFilename(string fileName, bool isLarge)
        {
            var extension = Path.GetExtension(fileName);

            if (extension == null) return null;

            var cache = isLarge ? largeIconCache : smallIconCache;

            if (cache.TryGetValue(extension, out var icon)) return icon;

            icon = IconReader
                .GetFileIcon(fileName, isLarge ? IconReader.IconSize.Large : IconReader.IconSize.Small, false)
                .ToImageSource();
            cache.Add(extension, icon);
            return icon;
        }

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

        private static class IconReader
        {
            public enum IconSize
            {
                Large = 0,
                Small = 1
            }

            public static Icon GetFileIcon(string name, IconSize size, bool linkOverlay)
            {
                var shfi = new Shell32.Shfileinfo();
                var flags = Shell32.ShgfiIcon | Shell32.ShgfiUsefileattributes;
                if (linkOverlay) flags += Shell32.ShgfiLinkoverlay;

                if (IconSize.Small == size)
                    flags += Shell32.ShgfiSmallicon;
                else
                    flags += Shell32.ShgfiLargeicon;

                Shell32.SHGetFileInfo(name,
                    Shell32.FileAttributeNormal,
                    ref shfi,
                    (uint) Marshal.SizeOf(shfi),
                    flags);

                var icon = (Icon) Icon.FromHandle(shfi.hIcon).Clone();
                User32.DestroyIcon(shfi.hIcon);

                return icon;
            }
        }

        private static class Shell32
        {
            private const int MaxPath = 256;

            [StructLayout(LayoutKind.Sequential)]
            public struct Shfileinfo
            {
                private const int Namesize = 80;
                public readonly IntPtr hIcon;
                private readonly int iIcon;
                private readonly uint dwAttributes;

                [MarshalAs(UnmanagedType.ByValTStr, SizeConst = MaxPath)] private readonly string szDisplayName;
                [MarshalAs(UnmanagedType.ByValTStr, SizeConst = Namesize)] private readonly string szTypeName;
            }

            public const uint ShgfiIcon = 0x000000100;
            public const uint ShgfiLinkoverlay = 0x000008000;
            public const uint ShgfiLargeicon = 0x000000000;
            public const uint ShgfiSmallicon = 0x000000001;
            public const uint ShgfiUsefileattributes = 0x000000010;
            public const uint FileAttributeNormal = 0x00000080;

            [DllImport("Shell32.dll")]
            public static extern IntPtr SHGetFileInfo(
                string pszPath,
                uint dwFileAttributes,
                ref Shfileinfo psfi,
                uint cbFileInfo,
                uint uFlags
            );
        }

        private static class User32
        {
            [DllImport("User32.dll")]
            public static extern int DestroyIcon(IntPtr hIcon);
        }
    }
}

I've made a program with this code open source you can find it here: Pillage It's a file search program. Shows the Icons of each file it searches based on this code.

Pillage

Kelly
  • 6,992
  • 12
  • 59
  • 76
  • Thanks for your answer. But I think this is not acceptable. Because it will get you the OS registered icons. So the icons which you can see in Windows. Not Visual Studio "style" icons what you can see in VS Solution Explorer. And I stated that in my question I need the exact icons from VS Solution Explorer. Also I already have a much simpler code to extract icons from fileExtension: System.Drawing.Icon.ExtractAssociatedIcon("fullPath"); – Major Jul 24 '18 at 06:43