In C#, some system API is needed before we can manage to manipulate with the system objects. In some cases, we are doing the technique of Interoperability to do the inter-operation manipulation outside the application.
This blog will show you one technique how to use system APIs with the Shell functionality (file system manipulation) implementation.
Shelling is the process and technique of file system manipulation. Before we can do this, we need to import some basic functions from system API. Our target is to display the system icon base on the full path or extension of the file. Let's start with the system APIs.
Shell System API (functions and variables)
[DllImport("shell32.dll", CharSet = CharSet.Auto)]
public static extern IntPtr
SHGetFileInfo(string pszPath, uint dwFileAttributes, out
SHFileInfo psfi, uint
cbFileInfo, uint uFlags);
[DllImport("user32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool
DestroyIcon(IntPtr hIcon);
public const uint SHGFI_ICON = 0x000000100;
public const uint SHGFI_USEFILEATTRIBUTES = 0x000000010;
public const uint SHGFI_OPENICON = 0x000000002;
public const uint SHGFI_SMALLICON = 0x000000001;
public const uint SHGFI_LARGEICON = 0x000000000;
public const uint FILE_ATTRIBUTE_DIRECTORY = 0x00000010;
public const uint FILE_ATTRIBUTE_FILE = 0x00000100;
The DllImport is an attribute used to import the system library into our application. For more information regarding DllImport, please visit Microsoft documentation. We used the SHGetFileInfo and DestroyIcon system function because the target of this blog is to only display the system icons (by file path and extension).
SHGetFileInfo: is the system function used to retrieve the information of file/folder from the file system (Windows operating system).
DestroyIcon: is the system function used to destroy the icon.
Note: For you to follow our implementation, please place the codes above in one static class named Interop.
After declaring the system functions/constants above, you need to create additional struct and named it SHFileInfo. Please see the code below.
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
public struct SHFileInfo
{
public IntPtr
hIcon;
public int iIcon;
public uint
dwAttributes;
[MarshalAs(UnmanagedType.ByValTStr,
SizeConst = 260)]
public string
szDisplayName;
[MarshalAs(UnmanagedType.ByValTStr,
SizeConst = 80)]
public string
szTypeName;
};
The struct above is the one who will be used by the SHGetFileInfo system function and will be the object who will handle all the information of the system file attribute.
After declaring the struct, we need to call the SHGetFileInfo system function passing the right attribute and the struct above so we can get the information of the system file. We have to create our own function to get the proper icon from the file system (with the the help of the system variables/functions above). Please see below our codes.
public class ShellManager
{
public static Icon GetIcon(string
path, ItemType type, IconSize size, ItemState
state)
{
var flags = (uint)(Interop.SHGFI_ICON | Interop.SHGFI_USEFILEATTRIBUTES);
var attribute = (uint)(object.Equals(type, ItemType.Folder)
? Interop.FILE_ATTRIBUTE_DIRECTORY : Interop.FILE_ATTRIBUTE_FILE);
if (object.Equals(type,
ItemType.Folder) && object.Equals(state, ItemState.Open))
{
flags += Interop.SHGFI_OPENICON;
}
if (object.Equals(size,
IconSize.Small))
{
flags += Interop.SHGFI_SMALLICON;
}
else
{
flags += Interop.SHGFI_LARGEICON;
}
var shfi = new SHFileInfo();
var res = Interop.SHGetFileInfo(path,
attribute, out shfi, (uint)Marshal.SizeOf(shfi), flags);
if (object.Equals(res,
IntPtr.Zero)) throw
Marshal.GetExceptionForHR(Marshal.GetHRForLastWin32Error());
try
{
Icon.FromHandle(shfi.hIcon);
return (Icon)Icon.FromHandle(shfi.hIcon).Clone();
}
catch
{
throw;
}
finally
{
Interop.DestroyIcon(shfi.hIcon);
}
}
}
What the code is doing is. We created our own class named ShellManager, inside this class we created a method named GetIcon to return the Icon object based on the file path (or extension) by using the SHGetFileInfo system function and SHFileInfo defined struct. Once you had called the system function, the hIcon property of the struct will be the one who will handle the Pointer (Handle of IntPtr object) of the system icon. From there, we can load the actual icon using the FromHandle method of Icon class.
Note: It is an Icon class of System.Drawing namespace.
Additional supportive enumeration (personalize enumerations).
public enum IconSize : short
{
Small,
Large
}
public enum ItemState : short
{
Undefined,
Open,
Close
}
Now, after placing everything in proper place. You can call the ShellManager GetIcon method to get the icon. Code below is the sample one.
var icon = ShellManager.GetIcon(Path.GetExtension(filename), ItemType.File, IconSize.Small, ItemState.Undefined);
Implementing FolderManager and FileManager class
Below are the codes we can use to manage the icon for both folder and icon.
public static class FolderManager
{
public static ImageSource GetImageSource(string directory, ItemState
folderType)
{
try
{
return FolderManager.GetImageSource(directory,
new Size(16,
16), folderType);
}
catch
{
throw;
}
}
public static ImageSource GetImageSource(string directory, Size size, ItemState folderType)
{
try
{
using (var icon = ShellManager.GetIcon(directory, ItemType.Folder, IconSize.Large,
folderType))
{
return Imaging.CreateBitmapSourceFromHIcon(icon.Handle,
System.Windows.Int32Rect.Empty, BitmapSizeOptions.FromWidthAndHeight((int)size.Width, (int)size.Height));
}
}
catch
{
throw;
}
}
}
public static class FileManager
{
public static ImageSource GetImageSource(string filename)
{
try
{
return FileManager.GetImageSource(filename,
new Size(16,
16));
}
catch
{
throw;
}
}
public static ImageSource GetImageSource(string filename, Size size)
{
try
{
using (var icon = ShellManager.GetIcon(Path.GetExtension(filename),
ItemType.File, IconSize.Small,
ItemState.Undefined))
{
return Imaging.CreateBitmapSourceFromHIcon(icon.Handle,
System.Windows.Int32Rect.Empty, BitmapSizeOptions.FromWidthAndHeight((int)size.Width, (int)size.Height));
}
}
catch
{
throw;
}
}
}
You'll notice that in the GetImageSource method, we used the ShellManager.GetIcon method and create a new ImageSource object by mimicing the existing one and do the resize. This also give us an idea how resize an image.
The code above returned the ImageSource object which you can use to the value of the Source property of the Image control.
Congratulations! You just finish the tutorial how to display system icon in your Image object.
your code sample is missing the ItemType enum def.
ReplyDeleteI assume it should look something like
public enum ItemType : short
{
Folder,
File
}
Codes Directory: Displaying System Icon In C, Wpf >>>>> Download Now
Delete>>>>> Download Full
Codes Directory: Displaying System Icon In C, Wpf >>>>> Download LINK
>>>>> Download Now
Codes Directory: Displaying System Icon In C, Wpf >>>>> Download Full
>>>>> Download LINK sV
Yes, that is the Enum type for ItemType. The Folder entry should be in 0 value ;)
ReplyDeleteI got this error in Icon Class:
ReplyDeleteThe type or namespace name 'Icon' could not be found (are you missing a using directive or an assembly reference?)
Help me to solve this error please :)
I just fixed the error by (Add the reference System.Drawing).
ReplyDeleteThanks :)
Hi Michael
ReplyDeleteI am currently learning wpf and c# I have been following your tutorial and so far I have mannaged to complete it untill the point where it uses marshal.getsizeof and so on...Marshal is underlined and say's it doesnt exist! is there anything that I am missing?
I sincerely apologize for not being active here. I have moved my blogging to Medium and Twitter. Though, I tried my best to accommodate you on here.
ReplyDeleteHere is the link to the working project:
https://github.com/mikependon/Tutorials/tree/master/WPF/TreeViewFileExplorer
Moving forward, I will place everything in my Github account.