[.net] [VB] Returning 16x16 icon for a file as a Bitmap

Started by
4 comments, last by benryves 18 years, 7 months ago
I am developing a sort of Z80 IDE - screenshot. Currently, those icons in the project explorer to the right are hard coded (if extension is .inc, use this icon, if extension is .z80, use that icon...) Ideally, though, I'd get the Windows icon for the files. I'd need to get them in the form of an Image or Bitmap, based solely on the file extension. How would one go about doing this?

[Website] [+++ Divide By Cucumber Error. Please Reinstall Universe And Reboot +++]

Advertisement
SHGetFileInfo will get the shell icon associated with a file, then perhaps use DrawIconEx for displaying it.
I need to get it as an image so I can add it to the image list control - so maybe if I create a new bitmap and draw onto that? I shall have a fiddle. Thanks!

[Website] [+++ Divide By Cucumber Error. Please Reinstall Universe And Reboot +++]

So, I have this function:
Module mdlIcons    Private Declare Function ExtractIconEx Lib "shell32.dll" Alias "ExtractIconExA" (ByVal lpszFile As String, ByVal nIconIndex As Int32, ByRef phiconLarge As Int32, ByRef phiconSmall As Int32, ByVal nIcons As Int32) As Int32    Private Declare Function DestroyIcon Lib "user32.dll" (ByVal hIcon As Int32) As Int32    Function getIconFromFilename(ByVal filename As String, ByVal largeIcon As Boolean) As Bitmap        ' Grab the extension:        Dim getExtension() As String = filename.Split(".")        ' Look up the file type:        Dim searchKey As Microsoft.Win32.RegistryKey = Microsoft.Win32.Registry.ClassesRoot.OpenSubKey("." & getExtension(UBound(getExtension)), False)        ' Was anything found?        If searchKey Is Nothing Then Return Nothing        ' Go through until you hit the end:        Dim getDefaultIcon As Microsoft.Win32.RegistryKey        Do            getDefaultIcon = searchKey.OpenSubKey("DefaultIcon", False)            If Not (getDefaultIcon Is Nothing) Then Exit Do            searchKey = Microsoft.Win32.Registry.ClassesRoot.OpenSubKey(searchKey.GetValue(""))            If searchKey Is Nothing Then Return Nothing        Loop        ' Get the details:        Dim fileDescription As String = searchKey.GetValue("")        Dim iconPath As String = getDefaultIcon.GetValue("")        ' Close the registry keys:        getDefaultIcon.Close()        searchKey.Close()        ' Now we have that data, we need to convert a "xxxx,0" path into a "xxxx" and a "0"        Dim getPlainIconDetails() As String = iconPath.Replace("""", "").Split(",")        Dim iconIndex As Integer = 0        Dim plainIconName As String = getPlainIconDetails(0)        For i As Integer = 1 To UBound(getPlainIconDetails) - 1            plainIconName &= "," & getPlainIconDetails(i)        Next        If iconPath.Replace("""", "").ToUpper.EndsWith(".ICO") Then            If UBound(getPlainIconDetails) <> 0 Then plainIconName &= getPlainIconDetails(UBound(getPlainIconDetails))        Else            iconIndex = Val(getPlainIconDetails(UBound(getPlainIconDetails)))        End If        ' Now we have all that info, let's grab the icon:        Dim iconLarge As Int32        Dim iconSmall As Int32        If (ExtractIconEx(plainIconName, iconIndex, iconLarge, iconSmall, 1) > 0) Then            Dim iconPtr As IntPtr            If largeIcon = True Then                iconPtr = New IntPtr(iconLarge)            Else                iconPtr = New IntPtr(iconSmall)            End If            Dim iconRes As Icon = Icon.FromHandle(iconPtr)            Dim returnBitmap As Bitmap = iconRes.ToBitmap            iconRes.Dispose()            If iconLarge <> 0 Then DestroyIcon(iconLarge)            If iconSmall <> 0 Then DestroyIcon(iconSmall)            Return returnBitmap        Else            Return Nothing        End If    End FunctionEnd Module

...which works fine, but makes the alpha in XP icons a horrible strong black:

Any ideas?

[Website] [+++ Divide By Cucumber Error. Please Reinstall Universe And Reboot +++]

I think this does what you want.

I haven't tested that code, but I had a C# code snippet that did just that, so I know it's possible and works well.

Edit: just found the C# snippet, in case anyone's interested:

using System;using System.Drawing;using System.Drawing.Imaging;using System.Runtime.InteropServices;	public sealed class Tools {		public struct ICONINFO {		public bool fIcon;		public int xHotspot;		public int yHotspot;		public IntPtr hbmMask;		public IntPtr hbmColor;	}	[DllImport("gdi32.dll", SetLastError = true)]	static public extern bool DeleteObject(IntPtr hObject);			[DllImport("user32.dll")]	public static extern bool GetIconInfo(IntPtr hIcon, out ICONINFO piconinfo);			public static Bitmap IconToAlphaBitmap(Icon ico) {		if (ico == null) return null;		ICONINFO ii = new ICONINFO();		GetIconInfo(ico.Handle, out ii);		Bitmap bmp = Bitmap.FromHbitmap(ii.hbmColor);		DeleteObject(ii.hbmColor);		DeleteObject(ii.hbmMask);		if (Bitmap.GetPixelFormatSize(bmp.PixelFormat) < 32) {			bmp.Dispose();			return ico.ToBitmap();		}		Rectangle bmBounds = new Rectangle(0, 0, bmp.Width, bmp.Height);		BitmapData bmData = bmp.LockBits(bmBounds, ImageLockMode.ReadOnly, bmp.PixelFormat);            		Bitmap dstBitmap = new Bitmap(bmData.Width, bmData.Height, bmData.Stride, PixelFormat.Format32bppArgb, bmData.Scan0);		bool hasAlpha = false;		for (int y=0; y <= bmData.Height-1; y++) {			for (int x=0; x <= bmData.Width-1; x++) {				Color PixelColor = Color.FromArgb(Marshal.ReadInt32(bmData.Scan0, (bmData.Stride * y) + (4 * x)));				if (PixelColor.A > 0 & PixelColor.A < 255) {					hasAlpha = true;					break;				}			}			if (hasAlpha) break;		}		bmp.UnlockBits(bmData);		if (hasAlpha)			return new Bitmap(dstBitmap);		dstBitmap.Dispose();		return new Bitmap(ico.ToBitmap());	}}


[Edited by - itachi on September 13, 2005 10:01:51 AM]
Thanks a lot, itachi. I'm a long way from my dev computer, so I'll have to wait until tonight to check it out. It looks like that VB code handles that special case with the XP icons fine, and failing that I'll take a look at the (rather better formatted!) C# solution.

[Website] [+++ Divide By Cucumber Error. Please Reinstall Universe And Reboot +++]

This topic is closed to new replies.

Advertisement