Using system icons for TreeView in C# - c#

I'm making a file transfer app, and I need to use system icons so the icons will match different versions of Windows. They are remote files are distant so I can't just use SHFILEINFO.

So, I got it working by using this very good example from CodeProject:
public class IconReader
{
/// <summary>
/// Options to specify the size of icons to return.
/// </summary>
public enum IconSize
{
/// <summary>
/// Specify large icon - 32 pixels by 32 pixels.
/// </summary>
Large = 0,
/// <summary>
/// Specify small icon - 16 pixels by 16 pixels.
/// </summary>
Small = 1
}
/// <summary>
/// Options to specify whether folders should be in the open or closed state.
/// </summary>
public enum FolderType
{
/// <summary>
/// Specify open folder.
/// </summary>
Open = 0,
/// <summary>
/// Specify closed folder.
/// </summary>
Closed = 1
}
/// <summary>
/// Returns an icon for a given file - indicated by the name parameter.
/// </summary>
/// <param name="name">Pathname for file.</param>
/// <param name="size">Large or small</param>
/// <param name="linkOverlay">Whether to include the link icon</param>
/// <returns>System.Drawing.Icon</returns>
public static System.Drawing.Icon GetFileIcon(string name, IconSize size, bool linkOverlay)
{
Shell32.SHFILEINFO shfi = new Shell32.SHFILEINFO();
uint flags = Shell32.SHGFI_ICON | Shell32.SHGFI_USEFILEATTRIBUTES;
if (true == linkOverlay) flags += Shell32.SHGFI_LINKOVERLAY;
/* Check the size specified for return. */
if (IconSize.Small == size)
{
flags += Shell32.SHGFI_SMALLICON;
}
else
{
flags += Shell32.SHGFI_LARGEICON;
}
Shell32.SHGetFileInfo(name,
Shell32.FILE_ATTRIBUTE_NORMAL,
ref shfi,
(uint)System.Runtime.InteropServices.Marshal.SizeOf(shfi),
flags);
// Copy (clone) the returned icon to a new object, thus allowing us to clean-up properly
System.Drawing.Icon icon = (System.Drawing.Icon)System.Drawing.Icon.FromHandle(shfi.hIcon).Clone();
User32.DestroyIcon(shfi.hIcon); // Cleanup
return icon;
}
/// <summary>
/// Used to access system folder icons.
/// </summary>
/// <param name="size">Specify large or small icons.</param>
/// <param name="folderType">Specify open or closed FolderType.</param>
/// <returns>System.Drawing.Icon</returns>
public static System.Drawing.Icon GetFolderIcon(IconSize size, FolderType folderType)
{
// Need to add size check, although errors generated at present!
uint flags = Shell32.SHGFI_ICON | Shell32.SHGFI_USEFILEATTRIBUTES;
if (FolderType.Open == folderType)
{
flags += Shell32.SHGFI_OPENICON;
}
if (IconSize.Small == size)
{
flags += Shell32.SHGFI_SMALLICON;
}
else
{
flags += Shell32.SHGFI_LARGEICON;
}
// Get the folder icon
Shell32.SHFILEINFO shfi = new Shell32.SHFILEINFO();
Shell32.SHGetFileInfo(#"Folder",
Shell32.FILE_ATTRIBUTE_DIRECTORY,
ref shfi,
(uint)System.Runtime.InteropServices.Marshal.SizeOf(shfi),
flags);
System.Drawing.Icon.FromHandle(shfi.hIcon); // Load the icon from an HICON handle
// Now clone the icon, so that it can be successfully stored in an ImageList
System.Drawing.Icon icon = (System.Drawing.Icon)System.Drawing.Icon.FromHandle(shfi.hIcon).Clone();
User32.DestroyIcon(shfi.hIcon); // Cleanup
return icon;
}
public static System.Drawing.Icon GetDriveIcon(IconSize size, FolderType folderType)
{
// Need to add size check, although errors generated at present!
uint flags = Shell32.SHGFI_ICON | Shell32.SHGFI_USEFILEATTRIBUTES;
if (FolderType.Open == folderType)
{
flags += Shell32.SHGFI_OPENICON;
}
if (IconSize.Small == size)
{
flags += Shell32.SHGFI_SMALLICON;
}
else
{
flags += Shell32.SHGFI_LARGEICON;
}
// Get the folder icon
Shell32.SHFILEINFO shfi = new Shell32.SHFILEINFO();
Shell32.SHGetFileInfo(null,
Shell32.FILE_ATTRIBUTE_DIRECTORY,
ref shfi,
(uint)System.Runtime.InteropServices.Marshal.SizeOf(shfi),
flags);
System.Drawing.Icon.FromHandle(shfi.hIcon); // Load the icon from an HICON handle
// Now clone the icon, so that it can be successfully stored in an ImageList
System.Drawing.Icon icon = (System.Drawing.Icon)System.Drawing.Icon.FromHandle(shfi.hIcon).Clone();
User32.DestroyIcon(shfi.hIcon); // Cleanup
return icon;
}
}
/// <summary>
/// Wraps necessary Shell32.dll structures and functions required to retrieve Icon Handles using SHGetFileInfo. Code
/// courtesy of MSDN Cold Rooster Consulting case study.
/// </summary>
///
// This code has been left largely untouched from that in the CRC example. The main changes have been moving
// the icon reading code over to the IconReader type.
public class Shell32
{
public const int MAX_PATH = 256;
[StructLayout(LayoutKind.Sequential)]
public struct SHITEMID
{
public ushort cb;
[MarshalAs(UnmanagedType.LPArray)]
public byte[] abID;
}
[StructLayout(LayoutKind.Sequential)]
public struct ITEMIDLIST
{
public SHITEMID mkid;
}
[StructLayout(LayoutKind.Sequential)]
public struct BROWSEINFO
{
public IntPtr hwndOwner;
public IntPtr pidlRoot;
public IntPtr pszDisplayName;
[MarshalAs(UnmanagedType.LPTStr)]
public string lpszTitle;
public uint ulFlags;
public IntPtr lpfn;
public int lParam;
public IntPtr iImage;
}
// Browsing for directory.
public const uint BIF_RETURNONLYFSDIRS = 0x0001;
public const uint BIF_DONTGOBELOWDOMAIN = 0x0002;
public const uint BIF_STATUSTEXT = 0x0004;
public const uint BIF_RETURNFSANCESTORS = 0x0008;
public const uint BIF_EDITBOX = 0x0010;
public const uint BIF_VALIDATE = 0x0020;
public const uint BIF_NEWDIALOGSTYLE = 0x0040;
public const uint BIF_USENEWUI = (BIF_NEWDIALOGSTYLE | BIF_EDITBOX);
public const uint BIF_BROWSEINCLUDEURLS = 0x0080;
public const uint BIF_BROWSEFORCOMPUTER = 0x1000;
public const uint BIF_BROWSEFORPRINTER = 0x2000;
public const uint BIF_BROWSEINCLUDEFILES = 0x4000;
public const uint BIF_SHAREABLE = 0x8000;
[StructLayout(LayoutKind.Sequential)]
public struct SHFILEINFO
{
public const int NAMESIZE = 80;
public IntPtr hIcon;
public int iIcon;
public uint dwAttributes;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = MAX_PATH)]
public string szDisplayName;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = NAMESIZE)]
public string szTypeName;
};
public const uint SHGFI_ICON = 0x000000100; // get icon
public const uint SHGFI_DISPLAYNAME = 0x000000200; // get display name
public const uint SHGFI_TYPENAME = 0x000000400; // get type name
public const uint SHGFI_ATTRIBUTES = 0x000000800; // get attributes
public const uint SHGFI_ICONLOCATION = 0x000001000; // get icon location
public const uint SHGFI_EXETYPE = 0x000002000; // return exe type
public const uint SHGFI_SYSICONINDEX = 0x000004000; // get system icon index
public const uint SHGFI_LINKOVERLAY = 0x000008000; // put a link overlay on icon
public const uint SHGFI_SELECTED = 0x000010000; // show icon in selected state
public const uint SHGFI_ATTR_SPECIFIED = 0x000020000; // get only specified attributes
public const uint SHGFI_LARGEICON = 0x000000000; // get large icon
public const uint SHGFI_SMALLICON = 0x000000001; // get small icon
public const uint SHGFI_OPENICON = 0x000000002; // get open icon
public const uint SHGFI_SHELLICONSIZE = 0x000000004; // get shell size icon
public const uint SHGFI_PIDL = 0x000000008; // pszPath is a pidl
public const uint SHGFI_USEFILEATTRIBUTES = 0x000000010; // use passed dwFileAttribute
public const uint SHGFI_ADDOVERLAYS = 0x000000020; // apply the appropriate overlays
public const uint SHGFI_OVERLAYINDEX = 0x000000040; // Get the index of the overlay
public const uint FILE_ATTRIBUTE_DIRECTORY = 0x00000010;
public const uint FILE_ATTRIBUTE_NORMAL = 0x00000080;
[DllImport("Shell32.dll")]
public static extern IntPtr SHGetFileInfo(
string pszPath,
uint dwFileAttributes,
ref SHFILEINFO psfi,
uint cbFileInfo,
uint uFlags
);
}
/// <summary>
/// Wraps necessary functions imported from User32.dll. Code courtesy of MSDN Cold Rooster Consulting example.
/// </summary>
public class User32
{
/// <summary>
/// Provides access to function required to delete handle. This method is used internally
/// and is not required to be called separately.
/// </summary>
/// <param name="hIcon">Pointer to icon handle.</param>
/// <returns>N/A</returns>
[DllImport("User32.dll")]
public static extern int DestroyIcon(IntPtr hIcon);
}
But, There is still one problem: The icons look HORRIBLE ! Here is a screenshot:
Do someone know how I can solve this ?

Related

Get file icon from extension only, without existing [duplicate]

I know i can extract a file's icon using
using (System.Drawing.Icon sysicon = System.Drawing.Icon.ExtractAssociatedIcon(filePath))
{
icon = System.Windows.Interop.Imaging.CreateBitmapSourceFromHIcon(
sysicon.Handle,
System.Windows.Int32Rect.Empty,
System.Windows.Media.Imaging.BitmapSizeOptions.FromEmptyOptions());
}
But how can I, with no file, get the icon for a given extension?
Use the GetFileIcon method from this CodeProject article from Paul Ingles and pass .ext as the name parameter.
The GetFileIcon method is a wrapper around the native SHGetFileInfo and copied here for illustration:
public static System.Drawing.Icon GetFileIcon(string name, IconSize size,
bool linkOverlay)
{
Shell32.SHFILEINFO shfi = new Shell32.SHFILEINFO();
uint flags = Shell32.SHGFI_ICON | Shell32.SHGFI_USEFILEATTRIBUTES;
if (true == linkOverlay) flags += Shell32.SHGFI_LINKOVERLAY;
/* Check the size specified for return. */
if (IconSize.Small == size)
{
flags += Shell32.SHGFI_SMALLICON ; // include the small icon flag
}
else
{
flags += Shell32.SHGFI_LARGEICON ; // include the large icon flag
}
Shell32.SHGetFileInfo( name,
Shell32.FILE_ATTRIBUTE_NORMAL,
ref shfi,
(uint) System.Runtime.InteropServices.Marshal.SizeOf(shfi),
flags );
// Copy (clone) the returned icon to a new object, thus allowing us
// to call DestroyIcon immediately
System.Drawing.Icon icon = (System.Drawing.Icon)
System.Drawing.Icon.FromHandle(shfi.hIcon).Clone();
User32.DestroyIcon( shfi.hIcon ); // Cleanup
return icon;
}
Below code is also based on Paul Ingles solution, but:
Usable with WPF (ImageSource instead of Icon)
Has simple caching
Removed all stuff related to directories (I have only files in my
case)
Refactored using R# tips and wrapped with a single-class simple API
I've tested it on Windows 7 and Windows XP SP3, it works as expected with any string as a fileName.
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;
/// <summary>
/// Internals are mostly from here: http://www.codeproject.com/Articles/2532/Obtaining-and-managing-file-and-folder-icons-using
/// Caches all results.
/// </summary>
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>();
/// <summary>
/// Get an icon for a given filename
/// </summary>
/// <param name="fileName">any filename</param>
/// <param name="large">16x16 or 32x32 icon</param>
/// <returns>null if path is null, otherwise - an icon</returns>
public static ImageSource FindIconForFilename(string fileName, bool large)
{
var extension = Path.GetExtension(fileName);
if (extension == null)
return null;
var cache = large ? _largeIconCache : _smallIconCache;
ImageSource icon;
if (cache.TryGetValue(extension, out icon))
return icon;
icon = IconReader.GetFileIcon(fileName, large ? IconReader.IconSize.Large : IconReader.IconSize.Small, false).ToImageSource();
cache.Add(extension, icon);
return icon;
}
/// <summary>
/// http://stackoverflow.com/a/6580799/1943849
/// </summary>
static ImageSource ToImageSource(this Icon icon)
{
var imageSource = Imaging.CreateBitmapSourceFromHIcon(
icon.Handle,
Int32Rect.Empty,
BitmapSizeOptions.FromEmptyOptions());
return imageSource;
}
/// <summary>
/// Provides static methods to read system icons for both folders and files.
/// </summary>
/// <example>
/// <code>IconReader.GetFileIcon("c:\\general.xls");</code>
/// </example>
static class IconReader
{
/// <summary>
/// Options to specify the size of icons to return.
/// </summary>
public enum IconSize
{
/// <summary>
/// Specify large icon - 32 pixels by 32 pixels.
/// </summary>
Large = 0,
/// <summary>
/// Specify small icon - 16 pixels by 16 pixels.
/// </summary>
Small = 1
}
/// <summary>
/// Returns an icon for a given file - indicated by the name parameter.
/// </summary>
/// <param name="name">Pathname for file.</param>
/// <param name="size">Large or small</param>
/// <param name="linkOverlay">Whether to include the link icon</param>
/// <returns>System.Drawing.Icon</returns>
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;
/* Check the size specified for return. */
if (IconSize.Small == size)
flags += Shell32.ShgfiSmallicon;
else
flags += Shell32.ShgfiLargeicon;
Shell32.SHGetFileInfo(name,
Shell32.FileAttributeNormal,
ref shfi,
(uint)Marshal.SizeOf(shfi),
flags);
// Copy (clone) the returned icon to a new object, thus allowing us to clean-up properly
var icon = (Icon)Icon.FromHandle(shfi.hIcon).Clone();
User32.DestroyIcon(shfi.hIcon); // Cleanup
return icon;
}
}
/// <summary>
/// Wraps necessary Shell32.dll structures and functions required to retrieve Icon Handles using SHGetFileInfo. Code
/// courtesy of MSDN Cold Rooster Consulting case study.
/// </summary>
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; // get icon
public const uint ShgfiLinkoverlay = 0x000008000; // put a link overlay on icon
public const uint ShgfiLargeicon = 0x000000000; // get large icon
public const uint ShgfiSmallicon = 0x000000001; // get small icon
public const uint ShgfiUsefileattributes = 0x000000010; // use passed dwFileAttribute
public const uint FileAttributeNormal = 0x00000080;
[DllImport("Shell32.dll")]
public static extern IntPtr SHGetFileInfo(
string pszPath,
uint dwFileAttributes,
ref Shfileinfo psfi,
uint cbFileInfo,
uint uFlags
);
}
/// <summary>
/// Wraps necessary functions imported from User32.dll. Code courtesy of MSDN Cold Rooster Consulting example.
/// </summary>
static class User32
{
/// <summary>
/// Provides access to function required to delete handle. This method is used internally
/// and is not required to be called separately.
/// </summary>
/// <param name="hIcon">Pointer to icon handle.</param>
/// <returns>N/A</returns>
[DllImport("User32.dll")]
public static extern int DestroyIcon(IntPtr hIcon);
}
}
It can be done much simpler:
System.Drawing.Icon.ExtractAssociatedIcon("<fullPath>");
Important note: it will throw an exception if the file does not exists so worth to add validation.
Like #astef I also needed to use this for WPF, however I did require the Icons for Folders.
Since Drives and Folders don't have an extension, I utilized DirectoryInfo to determine which one is which.
For folders I always use its Closed Icon, you could however easily extend this to take Open or Closed into account.
/// <summary>
/// Internals are mostly from here: http://www.codeproject.com/Articles/2532/Obtaining-and-managing-file-and-folder-icons-using
/// Caches all results.
/// </summary>
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>();
/// <summary>
/// Get an icon for a given filename
/// </summary>
/// <param name="fileName">any filename</param>
/// <param name="large">16x16 or 32x32 icon</param>
/// <returns>null if path is null, otherwise - an icon</returns>
public static ImageSource FindIconForFilename(string fileName, bool large)
{
DirectoryInfo di = new DirectoryInfo(fileName);
string extension = di.Extension;
if (extension == null)
return null;
if (extension == string.Empty && di.Root.FullName == di.FullName) {
extension = "ROOT";
}
if (extension == string.Empty && di.Attributes.HasFlag(FileAttributes.Directory)) {
extension = "FOLDER";
} else {
if (di.Attributes.HasFlag(FileAttributes.Directory) && extension != "ROOT") {
extension = "FOLDER";
}
}
var cache = large ? _largeIconCache : _smallIconCache;
ImageSource icon;
if (cache.TryGetValue(extension, out icon))
return icon;
if (di.Attributes.HasFlag(FileAttributes.Directory) && extension != "ROOT") {
icon = IconReader.GetFolderIcon(fileName, large ? IconReader.IconSize.Large : IconReader.IconSize.Small, IconReader.FolderType.Closed).ToImageSource();
} else {
icon = IconReader.GetFileIcon(fileName, large ? IconReader.IconSize.Large : IconReader.IconSize.Small, false).ToImageSource();
}
if (extension != "ROOT") cache.Add(extension, icon);
return icon;
}
/// <summary>
/// http://stackoverflow.com/a/6580799/1943849
/// </summary>
static ImageSource ToImageSource(this System.Drawing.Icon icon)
{
var imageSource = Imaging.CreateBitmapSourceFromHIcon(
icon.Handle,
Int32Rect.Empty,
BitmapSizeOptions.FromEmptyOptions());
return imageSource;
}
/// <summary>
/// Provides static methods to read system icons for both folders and files.
/// </summary>
/// <example>
/// <code>IconReader.GetFileIcon("c:\\general.xls");</code>
/// </example>
static class IconReader
{
/// <summary>
/// Options to specify the size of icons to return.
/// </summary>
public enum IconSize
{
/// <summary>
/// Specify large icon - 32 pixels by 32 pixels.
/// </summary>
Large = 0,
/// <summary>
/// Specify small icon - 16 pixels by 16 pixels.
/// </summary>
Small = 1
}
/// <summary>
/// Returns an icon for a given file - indicated by the name parameter.
/// </summary>
/// <param name="name">Pathname for file.</param>
/// <param name="size">Large or small</param>
/// <param name="linkOverlay">Whether to include the link icon</param>
/// <returns>System.Drawing.Icon</returns>
public static System.Drawing.Icon GetFileIcon(string name, IconSize size, bool linkOverlay)
{
var shfi = new Shell32.SHFILEINFO();
var flags = Shell32.SHGFI_ICON;
if (linkOverlay) flags += Shell32.SHGFI_LINKOVERLAY;
/* Check the size specified for return. */
if (IconSize.Small == size)
flags += Shell32.SHGFI_SMALLICON;
else
flags += Shell32.SHGFI_SMALLICON;
Shell32.SHGetFileInfo(name,
Shell32.FILE_ATTRIBUTE_NORMAL,
ref shfi,
(uint)Marshal.SizeOf(shfi),
flags);
// Copy (clone) the returned icon to a new object, thus allowing us to clean-up properly
var icon = (System.Drawing.Icon)System.Drawing.Icon.FromHandle(shfi.hIcon).Clone();
User32.DestroyIcon(shfi.hIcon); // Cleanup
return icon;
}
/// <summary>
/// Options to specify whether folders should be in the open or closed state.
/// </summary>
public enum FolderType
{
/// <summary>
/// Specify open folder.
/// </summary>
Open = 0,
/// <summary>
/// Specify closed folder.
/// </summary>
Closed = 1
}
/// <summary>
/// Used to access system folder icons.
/// </summary>
/// <param name="size">Specify large or small icons.</param>
/// <param name="folderType">Specify open or closed FolderType.</param>
/// <returns>System.Drawing.Icon</returns>
public static System.Drawing.Icon GetFolderIcon(string name, IconSize size, FolderType folderType)
{
// Need to add size check, although errors generated at present!
uint flags = Shell32.SHGFI_ICON | Shell32.SHGFI_SYSICONINDEX;
if (FolderType.Open == folderType) {
flags += Shell32.SHGFI_OPENICON;
}
if (IconSize.Small == size) {
flags += Shell32.SHGFI_SMALLICON;
} else {
flags += Shell32.SHGFI_LARGEICON;
}
// Get the folder icon
Shell32.SHFILEINFO shfi = new Shell32.SHFILEINFO();
Shell32.SHGetFileInfo(name,
Shell32.FILE_ATTRIBUTE_DIRECTORY,
ref shfi,
(uint)System.Runtime.InteropServices.Marshal.SizeOf(shfi),
flags);
System.Drawing.Icon.FromHandle(shfi.hIcon); // Load the icon from an HICON handle
// Now clone the icon, so that it can be successfully stored in an ImageList
System.Drawing.Icon icon = (System.Drawing.Icon)System.Drawing.Icon.FromHandle(shfi.hIcon).Clone();
User32.DestroyIcon(shfi.hIcon); // Cleanup
return icon;
}
}
}
/// <summary>
/// Wraps necessary Shell32.dll structures and functions required to retrieve Icon Handles using SHGetFileInfo. Code
/// courtesy of MSDN Cold Rooster Consulting case study.
/// </summary>
public class Shell32
{
public const int MAX_PATH = 256;
[StructLayout(LayoutKind.Sequential)]
public struct SHITEMID
{
public ushort cb;
[MarshalAs(UnmanagedType.LPArray)]
public byte[] abID;
}
[StructLayout(LayoutKind.Sequential)]
public struct ITEMIDLIST
{
public SHITEMID mkid;
}
[StructLayout(LayoutKind.Sequential)]
public struct BROWSEINFO
{
public IntPtr hwndOwner;
public IntPtr pidlRoot;
public IntPtr pszDisplayName;
[MarshalAs(UnmanagedType.LPTStr)]
public string lpszTitle;
public uint ulFlags;
public IntPtr lpfn;
public int lParam;
public IntPtr iImage;
}
// Browsing for directory.
public const uint BIF_RETURNONLYFSDIRS = 0x0001;
public const uint BIF_DONTGOBELOWDOMAIN = 0x0002;
public const uint BIF_STATUSTEXT = 0x0004;
public const uint BIF_RETURNFSANCESTORS = 0x0008;
public const uint BIF_EDITBOX = 0x0010;
public const uint BIF_VALIDATE = 0x0020;
public const uint BIF_NEWDIALOGSTYLE = 0x0040;
public const uint BIF_USENEWUI = (BIF_NEWDIALOGSTYLE | BIF_EDITBOX);
public const uint BIF_BROWSEINCLUDEURLS = 0x0080;
public const uint BIF_BROWSEFORCOMPUTER = 0x1000;
public const uint BIF_BROWSEFORPRINTER = 0x2000;
public const uint BIF_BROWSEINCLUDEFILES = 0x4000;
public const uint BIF_SHAREABLE = 0x8000;
[StructLayout(LayoutKind.Sequential)]
public struct SHFILEINFO
{
public const int NAMESIZE = 80;
public IntPtr hIcon;
public int iIcon;
public uint dwAttributes;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = MAX_PATH)]
public string szDisplayName;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = NAMESIZE)]
public string szTypeName;
};
public const uint SHGFI_ICON = 0x000000100; // get icon
public const uint SHGFI_DISPLAYNAME = 0x000000200; // get display name
public const uint SHGFI_TYPENAME = 0x000000400; // get type name
public const uint SHGFI_ATTRIBUTES = 0x000000800; // get attributes
public const uint SHGFI_ICONLOCATION = 0x000001000; // get icon location
public const uint SHGFI_EXETYPE = 0x000002000; // return exe type
public const uint SHGFI_SYSICONINDEX = 0x000004000; // get system icon index
public const uint SHGFI_LINKOVERLAY = 0x000008000; // put a link overlay on icon
public const uint SHGFI_SELECTED = 0x000010000; // show icon in selected state
public const uint SHGFI_ATTR_SPECIFIED = 0x000020000; // get only specified attributes
public const uint SHGFI_LARGEICON = 0x000000000; // get large icon
public const uint SHGFI_SMALLICON = 0x000000001; // get small icon
public const uint SHGFI_OPENICON = 0x000000002; // get open icon
public const uint SHGFI_SHELLICONSIZE = 0x000000004; // get shell size icon
public const uint SHGFI_PIDL = 0x000000008; // pszPath is a pidl
public const uint SHGFI_USEFILEATTRIBUTES = 0x000000010; // use passed dwFileAttribute
public const uint SHGFI_ADDOVERLAYS = 0x000000020; // apply the appropriate overlays
public const uint SHGFI_OVERLAYINDEX = 0x000000040; // Get the index of the overlay
public const uint FILE_ATTRIBUTE_DIRECTORY = 0x00000010;
public const uint FILE_ATTRIBUTE_NORMAL = 0x00000080;
[DllImport("Shell32.dll")]
public static extern IntPtr SHGetFileInfo(
string pszPath,
uint dwFileAttributes,
ref SHFILEINFO psfi,
uint cbFileInfo,
uint uFlags
);
}
/// <summary>
/// Wraps necessary functions imported from User32.dll. Code courtesy of MSDN Cold Rooster Consulting example.
/// </summary>
static class User32
{
/// <summary>
/// Provides access to function required to delete handle. This method is used internally
/// and is not required to be called separately.
/// </summary>
/// <param name="hIcon">Pointer to icon handle.</param>
/// <returns>N/A</returns>
[DllImport("User32.dll")]
public static extern int DestroyIcon(IntPtr hIcon);
}
}

Getting current USB power state

I've been trying to read the current power state of an USB port (D0/D1/D2/D3). I haven't been able to find much information on how to access the actual state. Here's a description of the USB Device Power States on Microsoft docs. It has a whole section on changing, but really didn't get how to read it. I have very little experience with working on the Windows and hardware level, so excuse me if it is obvious.
I've also found this Microsoft debug application written in C called USBView. If you install it and open the USB tree, the first information displayed for individual ports is its power state.
e.g.
Device Power State: PowerDeviceD2
It has source available on GitHub, but the files are over 5000 lines long and I can't navigate C code good enough to tell how to actually read the power state.
I'm trying to implement this into a C# application, but help in any language will be appreciated!
After lots of and lots of digging in the USBView source code, I figured out you need to do the following:
Get the handle to the device info set using
IntPtr deviceInfoSet = SetupDiGetClassDevs(ref classGuid, null, IntPtr.Zero, 0x00000002 | 0x00000010)
where the classGuid for USB devices is "A5DCBF10-6530-11D2-901F-00C04FB951ED"
Get the individual device info using
SetupDiEnumDeviceInfo(deviceInfoSet, index, ref deviceInfoData)
where deviceInfoData is an instance of the SP_DEVINFO_DATA struct with the value of cbSize inicializace to 28. (The device info will be then stored in this struct.)
You start with index = 0 and then increment until the method returns false and Marshal.GetLastWin32Error() returns 259 (ERROR_NO_MORE_ITEMS) to get all the devices.
You then retrieve the power property using SetupDiGetDeviceRegistryProperty.
You can either:
A. pass in CM_POWER_DATA (then you should replace byte[] in the DllImport method signature with ref CM_POWER_DATA)
B. pass in a byte array and then parse the byte array to CM_POWER_DATA (code from this answer proved very good at it).
Here I'm showing option B - passing in byte array. The power data will be in the data variable. (Conversion skipped.)
//sizeof evaluates to 56, if you want to hardcode it
byte[] data = new byte[Marshal.SizeOf<CM_POWER_DATA>()];
SetupDiGetDeviceRegistryProperty(
deviceInfoSet,
ref deviceInfoData,
0x0000001E, //the property SPDRP_DEVICE_POWER_DATA
out uint type,
data,
data.Length,
out uint size)
);
You can then query additional information with the SetupDiGetDeviceRegistryProperty function, like the HardwareID and description.
Here is my code.
Don't forget
using System.Runtime.InteropServices;
Constants of various GUIDs and properities
public const uint SPDRP_DEVICEDESC = (0x00000000); // DeviceDesc (R/W)
public const uint SPDRP_HARDWAREID = (0x00000001); // HardwareID (R/W)
public const uint SPDRP_COMPATIBLEIDS = (0x00000002); // CompatibleIDs (R/W)
public const uint SPDRP_UNUSED0 = (0x00000003); // unused
public const uint SPDRP_SERVICE = (0x00000004); // Service (R/W)
public const uint SPDRP_UNUSED1 = (0x00000005); // unused
public const uint SPDRP_UNUSED2 = (0x00000006); // unused
public const uint SPDRP_CLASS = (0x00000007); // Class (R--tied to ClassGUID)
public const uint SPDRP_CLASSGUID = (0x00000008); // ClassGUID (R/W)
public const uint SPDRP_DRIVER = (0x00000009); // Driver (R/W)
public const uint SPDRP_CONFIGFLAGS = (0x0000000A); // ConfigFlags (R/W)
public const uint SPDRP_MFG = (0x0000000B); // Mfg (R/W)
public const uint SPDRP_FRIENDLYNAME = (0x0000000C); // FriendlyName (R/W)
public const uint SPDRP_LOCATION_INFORMATION = (0x0000000D); // LocationInformation (R/W)
public const uint SPDRP_PHYSICAL_DEVICE_OBJECT_NAME = (0x0000000E); // PhysicalDeviceObjectName (R)
public const uint SPDRP_CAPABILITIES = (0x0000000F); // Capabilities (R)
public const uint SPDRP_UI_NUMBER = (0x00000010); // UiNumber (R)
public const uint SPDRP_UPPERFILTERS = (0x00000011); // UpperFilters (R/W)
public const uint SPDRP_LOWERFILTERS = (0x00000012); // LowerFilters (R/W)
public const uint SPDRP_BUSTYPEGUID = (0x00000013); // BusTypeGUID (R)
public const uint SPDRP_LEGACYBUSTYPE = (0x00000014); // LegacyBusType (R)
public const uint SPDRP_BUSNUMBER = (0x00000015); // BusNumber (R)
public const uint SPDRP_ENUMERATOR_NAME = (0x00000016); // Enumerator Name (R)
public const uint SPDRP_SECURITY = (0x00000017); // Security (R/W, binary form)
public const uint SPDRP_SECURITY_SDS = (0x00000018); // Security (W, SDS form)
public const uint SPDRP_DEVTYPE = (0x00000019); // Device Type (R/W)
public const uint SPDRP_EXCLUSIVE = (0x0000001A); // Device is exclusive-access (R/W)
public const uint SPDRP_CHARACTERISTICS = (0x0000001B); // Device Characteristics (R/W)
public const uint SPDRP_ADDRESS = (0x0000001C); // Device Address (R)
public const uint SPDRP_UI_NUMBER_DESC_FORMAT = (0X0000001D); // UiNumberDescFormat (R/W)
public const uint SPDRP_DEVICE_POWER_DATA = (0x0000001E); // Device Power Data (R)
public const uint SPDRP_REMOVAL_POLICY = (0x0000001F); // Removal Policy (R)
public const uint SPDRP_REMOVAL_POLICY_HW_DEFAULT = (0x00000020); // Hardware Removal Policy (R)
public const uint SPDRP_REMOVAL_POLICY_OVERRIDE = (0x00000021); // Removal Policy Override (RW)
public const uint SPDRP_INSTALL_STATE = (0x00000022); // Device Install State (R)
public const uint SPDRP_LOCATION_PATHS = (0x00000023); // Device Location Paths (R)
public const uint SPDRP_BASE_CONTAINERID = (0x00000024); // Base ContainerID (R)
public const uint SPDRP_MAXIMUM_PROPERTY = (0x00000025); // Upper bound on ordinals
public const string GUID_DEVINTERFACE_USB_HUB = "f18a0e88-c30c-11d0-8815-00a0c906bed8";
public const string GUID_DEVINTERFACE_USB_DEVICE = "A5DCBF10-6530-11D2-901F-00C04FB951ED";
public const string GUID_DEVINTERFACE_USB_HOST_CONTROLLER = "3ABF6F2D-71C4-462a-8A92-1E6861E6AF27";
public const string GUID_USB_WMI_STD_DATA = "4E623B20-CB14-11D1-B331-00A0C959BBD2";
public const string GUID_USB_WMI_STD_NOTIFICATION = "4E623B20-CB14-11D1-B331-00A0C959BBD2";
public const string GUID_USB_WMI_DEVICE_PERF_INFO = "66C1AA3C-499F-49a0-A9A5-61E2359F6407";
public const string GUID_USB_WMI_NODE_INFO = "{9C179357-DC7A-4f41-B66B-323B9DDCB5B1}";
public const string GUID_USB_WMI_TRACING = "3a61881b-b4e6-4bf9-ae0f-3cd8f394e52f";
public const string GUID_USB_TRANSFER_TRACING = "{681EB8AA-403D-452c-9F8A-F0616FAC9540}";
public const string GUID_USB_PERFORMANCE_TRACING = "{D5DE77A6-6AE9-425c-B1E2-F5615FD348A9}";
public const string GUID_USB_WMI_SURPRISE_REMOVAL_NOTIFICATION = "{9BBBF831-A2F2-43B4-96D1-86944B5914B3}";
Structs and enums:
public enum DEVICE_POWER_STATE {
PowerDeviceUnspecified,
PowerDeviceD0,
PowerDeviceD1,
PowerDeviceD2,
PowerDeviceD3,
PowerDeviceMaximum
}
public enum SYSTEM_POWER_STATE {
PowerSystemUnspecified,
PowerSystemWorking,
PowerSystemSleeping1,
PowerSystemSleeping2,
PowerSystemSleeping3,
PowerSystemHibernate,
PowerSystemShutdown,
PowerSystemMaximum
}
[StructLayout(LayoutKind.Sequential, Pack = 8)]
public struct SP_DEVINFO_DATA {
public UInt32 cbSize;
public Guid ClassGuid;
public UInt32 DevInst;
public IntPtr Reserved;
}
[StructLayout(LayoutKind.Sequential, Pack = 8)]
public struct CM_POWER_DATA {
public uint PD_Size;
public DEVICE_POWER_STATE PD_MostRecentPowerState;
public uint PD_Capabilities;
public uint PD_D1Latency;
public uint PD_D2Latency;
public uint PD_D3Latency;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 7)]
public DEVICE_POWER_STATE[] PD_PowerStateMapping;
public SYSTEM_POWER_STATE PD_DeepestSystemWake;
}
DllImports
[DllImport("setupapi.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern bool SetupDiGetDeviceRegistryPropertyA(
IntPtr DeviceInfoSet,
ref SP_DEVINFO_DATA DeviceInfoData,
uint Property,
out RegistryDataType PropertyRegDataType,
byte[] PropertyBuffer,
//ref CM_POWER_DATA PropertyBuffer,
uint PropertyBufferSize,
out UInt32 RequiredSize
);
[DllImport("setupapi.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern IntPtr SetupDiGetClassDevsA(
ref Guid ClassGuid,
[MarshalAs(UnmanagedType.LPTStr)] string Enumerator,
IntPtr hwndParent,
uint Flags
);
[DllImport("setupapi.dll", SetLastError=true)]
private static extern bool SetupDiEnumDeviceInfo(
IntPtr DeviceInfoSet,
uint MemberIndex,
ref SP_DEVINFO_DATA DeviceInfoData
);
Various function for making the retrieve a bit fancier and easier to use. The main one and the one I've described above is the first one - GetInfoWithSetupDi. (You need to pass the GUID_DEVINTERFACE_USB_DEVICE to it.)
/// <summary>
/// Gets information about devices in the device class including power state.
/// </summary>
/// <param name="classGuid">The GUID of the class in which to get information about devices from.</param>
/// <returns></returns>
public static List<DeviceInfo> GetInfoWithSetupDi(Guid classGuid) {
List<DeviceInfo> deviceInfos = new List<DeviceInfo>();
IntPtr deviceInfoSet = SetupDiGetClassDevsA(ref classGuid, null, IntPtr.Zero, 0x00000002 | 0x00000010);
uint index = 0;
int error = 0;
while (error == 0) {
DeviceInfo curDevice = new DeviceInfo {
//Initializing SP_DEVINFO_DATA to be passed to SetupDiEnumDeviceInfo.
deviceInfoData = new SP_DEVINFO_DATA {
cbSize = 28
}
};
//Retrieves the information about the specified device.
bool success = SetupDiEnumDeviceInfo(deviceInfoSet, index, ref curDevice.deviceInfoData);
index++;
error = Marshal.GetLastWin32Error();
if (error == 259)
{ break; }
if (!success)
{ throw new Exception("Native method call error: " + error.ToString()); }
//Only add device after it was at least successfully retrieved.
deviceInfos.Add(curDevice);
//Retrieving individual information.
RegistryData localGetData(uint property) => GetData(deviceInfoSet, curDevice.deviceInfoData, property);
curDevice.hardwareId = (string[]) localGetData(SPDRP_HARDWAREID).parsed;
try {
curDevice.description = (string)localGetData(SPDRP_DEVICEDESC).parsed;
} catch {
curDevice.description = "Description not set.";
}
curDevice.cmPowerData = MarshallingUtils.FromBytes<CM_POWER_DATA>(
localGetData(SPDRP_DEVICE_POWER_DATA).data
);
}
return deviceInfos;
}
/// <summary>
/// Gets the required size in bytes for the given property.
/// </summary>
/// <param name="deviceInfoSet">A handle to the set of devices.</param>
/// <param name="property">The property for which the get the required size.</param>
private static uint GetRequiredSize(IntPtr deviceInfoSet, uint Property) {
SP_DEVINFO_DATA deviceInfoData = new SP_DEVINFO_DATA {
cbSize = 28
};
ThrowErrorIfNotSuccessful(SetupDiEnumDeviceInfo(deviceInfoSet, 0, ref deviceInfoData));
return GetRequiredSize(deviceInfoSet, Property, deviceInfoData);
}
/// <summary>
/// Gets the required size in bytes for the given property.
/// </summary>
/// <param name="deviceInfoSet">A handle to the set of devices.</param>
/// <param name="property">The property for which the get the required size.</param>
/// <param name="deviceInfoData">Info of a device.</param>
/// <returns></returns>
private static uint GetRequiredSize(IntPtr deviceInfoSet, uint property, SP_DEVINFO_DATA deviceInfoData) {
ThrowErrorIfNotSuccessful(
SetupDiGetDeviceRegistryPropertyA(
deviceInfoSet,
ref deviceInfoData,
property,
out RegistryDataType type,
new byte[1000],
1000,
out uint size)
);
return size;
}
/// <summary>
/// Gets the property using SetupDi of the device on the given index in the given device class.
/// </summary>
/// <param name="classGuid">The GUID of the device class.</param>
/// <param name="index">The index of the device.</param>
/// <param name="property">The property to retrieve.</param>
/// <returns></returns>
public static RegistryData GetData(Guid classGuid, uint index, uint property) {
IntPtr deviceInfoSet = SetupDiGetClassDevsA(ref classGuid, null, IntPtr.Zero, 0x00000002 | 0x00000010);
SP_DEVINFO_DATA deviceInfoData = new SP_DEVINFO_DATA {
cbSize = 28
};
ThrowErrorIfNotSuccessful(SetupDiEnumDeviceInfo(deviceInfoSet, index, ref deviceInfoData));
return GetData(deviceInfoSet, deviceInfoData, property);
}
/// <summary>
/// Gets the specified property using SetupDi of the device described in deviceInfoData in the given device info set.
/// The size is retrieved automatically.
/// </summary>
/// <param name="deviceInfoSet">A handle to the device info set.</param>
/// <param name="deviceInfoData">Description of the device.</param>
/// <param name="property">The property to retrieve.</param>
/// <returns></returns>
public static RegistryData GetData(IntPtr deviceInfoSet, SP_DEVINFO_DATA deviceInfoData, uint property) {
uint size = GetRequiredSize(deviceInfoSet, property, deviceInfoData);
return GetData(deviceInfoSet, deviceInfoData, property, size);
}
/// <summary>
/// Gets the specified property using SetupDi of the device described in deviceInfoData in the given device info set.
/// </summary>
/// <param name="deviceInfoSet">A handle to the device info set.</param>
/// <param name="deviceInfoData">Description of the device.</param>
/// <param name="property">The property to retrieve.</param>
/// <returns></returns>
public static RegistryData GetData(IntPtr deviceInfoSet, SP_DEVINFO_DATA deviceInfoData, uint property, uint size) {
byte[] data = new byte[size];
ThrowErrorIfNotSuccessful(
SetupDiGetDeviceRegistryPropertyA(
deviceInfoSet,
ref deviceInfoData,
property,
out RegistryDataType type,
data,
size,
out uint dummysize)
);
return new RegistryData(type, data);
}
/// <summary>
/// Method used to wrap native DLL calls. Throws the last system error when the call is unsuccessful.
/// </summary>
/// <param name="success">Return value of the native method. ( Usually used as ThrowErrorIfNotSuccessful(MethodCall()); )</param>
private static void ThrowErrorIfNotSuccessful(bool success) {
if (!success) {
throw new Exception("Native method call error: " + Marshal.GetLastWin32Error().ToString());
}
}
Generic version of the byte array <-> struct parser mentioned above.
public static class MarshallingUtils {
public static byte[] GetBytes<T>(T str) where T : struct {
int size = Marshal.SizeOf(str);
byte[] arr = new byte[size];
IntPtr ptr = Marshal.AllocHGlobal(size);
Marshal.StructureToPtr(str, ptr, true);
Marshal.Copy(ptr, arr, 0, size);
Marshal.FreeHGlobal(ptr);
return arr;
}
public static T FromBytes<T>(byte[] arr) where T : struct {
T str = new T();
int size = Marshal.SizeOf(str);
IntPtr ptr = Marshal.AllocHGlobal(size);
Marshal.Copy(arr, 0, ptr, size);
str = (T)Marshal.PtrToStructure(ptr, str.GetType());
Marshal.FreeHGlobal(ptr);
return str;
}
}
Helper class for parsing the retrieved registry data.
public struct RegistryData {
public readonly RegistryDataType type;
public readonly byte[] data;
public readonly object parsed;
public RegistryData(RegistryDataType type, byte[] data) {
this.type = type;
this.data = data;
this.parsed = ParseData(type, data);
}
public static string ParseString(byte[] data) {
string s = "";
foreach (byte b in data) {
if (b == 0) {
break;
}
s += (char)b;
}
return s;
}
public static string[] ParseMultiString(byte[] data) {
List<string> list = new List<string>();
string current = "";
bool terminator = false;
foreach (byte b in data) {
if (b == 0) {
if (terminator) {
break;
}
else {
terminator = true;
//Start a new string.
list.Add(current);
current = "";
}
}
else {
terminator = false;
current += (char)b;
}
}
return list.ToArray();
}
private static object ParseData(RegistryDataType type, byte[] data) {
switch (type) {
case RegistryDataType.REG_SZ:
return (object)ParseString(data);
case RegistryDataType.REG_MULTI_SZ:
return (object)ParseMultiString(data);
default: return null;
}
}
public override string ToString() {
switch (type) {
case RegistryDataType.REG_SZ: return (string)parsed;
case RegistryDataType.REG_MULTI_SZ: return String.Join(";", (List<string>)parsed);
default: return null;
}
}
}
And one last enum for the retrieved registry data type.
public enum RegistryDataType {
REG_NONE = (0), // No value type
REG_SZ = (1), // Unicode nul terminated string
REG_EXPAND_SZ = (2), // Unicode nul terminated string
// = (with environment variable references)
REG_BINARY = (3), // Free form binary
//REG_DWORD = (4), // 32-bit number
REG_DWORD_LITTLE_ENDIAN = (4), // 32-bit number = (same as REG_DWORD)
REG_DWORD_BIG_ENDIAN = (5), // 32-bit number
REG_LINK = (6), // Symbolic Link = (unicode)
REG_MULTI_SZ = (7), // Multiple Unicode strings
REG_RESOURCE_LIST = (8), // Resource list in the resource map
REG_FULL_RESOURCE_DESCRIPTOR = (9), // Resource list in the hardware description
REG_RESOURCE_REQUIREMENTS_LIST = (10),
//REG_QWORD = (11), // 64-bit number
REG_QWORD_LITTLE_ENDIAN = (11), // 64-bit number = (same as REG_QWORD)
}
And also a class to hold the info.
public class DeviceInfo {
public SP_DEVINFO_DATA deviceInfoData;
public string[] hardwareId;
public string description;
public CM_POWER_DATA cmPowerData;
}

AccessViolationException after copying a file from inside a zip archive to the clipboard and handling its data

I have an issue I am having trouble with and hope somebody here can help.
We use somewhat modified code from this OutlookDataObject project to handle files dropped from various mail clients like outlook as well as the general copy pasting of files via clipboard to process them and save them to the file system.
It works fine in most cases, however if a user opens a ZIP file with the Windows Explorer, copys a file from there and tries to add it we get an AccessViolationException at the following part of the GetData Method of the FileContentDataObject:
//marshal the unmanaged memory to to FILEGROUPDESCRIPTORW struct
object fileGroupDescriptorObject = Marshal.PtrToStructure(fileGroupDescriptorWPointer, typeof(NativeMethods.FILEGROUPDESCRIPTORW));
Here is a reduced version of the whole code in question with which you should be able to reproduce this issue:
// Starting Method
public void InsertFromClipboard()
{
FileContentDataObjectBase dataObject = GetDataObject();
if(dataObject!= null)
{
dataObject.SaveToFileSystem("C:/temp");
}
}
private FileContentDataObjectBase GetDataObject()
{
var dataObject = System.Windows.Forms.Clipboard.GetDataObject();
return new FileContentDataObject(System.Windows.Forms.Clipboard.GetDataObject());
}
public class FileContentDataObject : FileContentDataObjectBase
{
/// <summary>
/// Initializes a new instance of the <see cref="OutlookDataObject"/> class.
/// </summary>
/// <param name="underlyingDataObject">The underlying data object to wrap.</param>
public FileContentDataObject(System.Windows.Forms.IDataObject underlyingDataObject)
: base(underlyingDataObject) { }
public override void SaveToFileSystem(string path)
{
string[] filenames;
//get the names and data streams of the files dropped
if (this.GetFormats().Contains("FileGroupDescriptor"))
filenames = (string[])this.GetData("FileGroupDescriptor", true);
else if (this.GetFormats().Contains("FileGroupDescriptorW"))
filenames = (string[])this.GetData("FileGroupDescriptorW", true);
else
return;
MemoryStream[] filestreams = (MemoryStream[])this.GetData("FileContents");
SaveToFileSystem(filenames, filestreams, path);
}
public new object GetData(string format, bool autoConvert)
{
switch (format)
{
case "FileGroupDescriptorW":
//override the default handling of FileGroupDescriptorW which returns a
//MemoryStream and instead return a string array of file names
IntPtr fileGroupDescriptorWPointer = IntPtr.Zero;
try
{
//use the underlying IDataObject to get the FileGroupDescriptorW as a MemoryStream
MemoryStream fileGroupDescriptorStream = (MemoryStream)this.underlyingDataObject.GetData("FileGroupDescriptorW");
byte[] fileGroupDescriptorBytes = new byte[fileGroupDescriptorStream.Length];
fileGroupDescriptorStream.Read(fileGroupDescriptorBytes, 0, fileGroupDescriptorBytes.Length);
fileGroupDescriptorStream.Close();
//copy the file group descriptor into unmanaged memory
fileGroupDescriptorWPointer = Marshal.AllocHGlobal(fileGroupDescriptorBytes.Length);
Marshal.Copy(fileGroupDescriptorBytes, 0, fileGroupDescriptorWPointer, fileGroupDescriptorBytes.Length);
//marshal the unmanaged memory to to FILEGROUPDESCRIPTORW struct
object fileGroupDescriptorObject = Marshal.PtrToStructure(fileGroupDescriptorWPointer, typeof(NativeMethods.FILEGROUPDESCRIPTORW));
NativeMethods.FILEGROUPDESCRIPTORW fileGroupDescriptor = (NativeMethods.FILEGROUPDESCRIPTORW)fileGroupDescriptorObject;
//create a new array to store file names in of the number of items in the file group descriptor
string[] fileNames = new string[fileGroupDescriptor.cItems];
//get the pointer to the first file descriptor
IntPtr fileDescriptorPointer = (IntPtr)((int)fileGroupDescriptorWPointer + Marshal.SizeOf(fileGroupDescriptor.cItems));
//loop for the number of files acording to the file group descriptor
for (int fileDescriptorIndex = 0; fileDescriptorIndex < fileGroupDescriptor.cItems; fileDescriptorIndex++)
{
//marshal the pointer top the file descriptor as a FILEDESCRIPTORW struct and get the file name
NativeMethods.FILEDESCRIPTORW fileDescriptor = (NativeMethods.FILEDESCRIPTORW)Marshal.PtrToStructure(fileDescriptorPointer, typeof(NativeMethods.FILEDESCRIPTORW));
fileNames[fileDescriptorIndex] = fileDescriptor.cFileName;
//move the file descriptor pointer to the next file descriptor
fileDescriptorPointer = (IntPtr)((int)fileDescriptorPointer + Marshal.SizeOf(fileDescriptor));
}
//return the array of filenames
return fileNames;
}
finally
{
//free unmanaged memory pointer
Marshal.FreeHGlobal(fileGroupDescriptorWPointer);
}
}
//use underlying IDataObject to handle getting of data
return this.underlyingDataObject.GetData(format, autoConvert);
}
}
public abstract class FileContentDataObjectBase : System.Windows.Forms.IDataObject
{
#region NativeMethods
protected class NativeMethods
{
[DllImport("kernel32.dll")]
static extern IntPtr GlobalLock(IntPtr hMem);
[DllImport("ole32.dll", PreserveSig = false)]
internal static extern ILockBytes CreateILockBytesOnHGlobal(IntPtr hGlobal, bool fDeleteOnRelease);
[DllImport("OLE32.DLL", CharSet = CharSet.Auto, PreserveSig = false)]
internal static extern IntPtr GetHGlobalFromILockBytes(ILockBytes pLockBytes);
[DllImport("OLE32.DLL", CharSet = CharSet.Unicode, PreserveSig = false)]
internal static extern IStorage StgCreateDocfileOnILockBytes(ILockBytes plkbyt, uint grfMode, uint reserved);
[ComImport, InterfaceType(ComInterfaceType.InterfaceIsIUnknown), Guid("0000000B-0000-0000-C000-000000000046")]
internal interface IStorage
{
[return: MarshalAs(UnmanagedType.Interface)]
IStream CreateStream([In, MarshalAs(UnmanagedType.BStr)] string pwcsName, [In, MarshalAs(UnmanagedType.U4)] int grfMode, [In, MarshalAs(UnmanagedType.U4)] int reserved1, [In, MarshalAs(UnmanagedType.U4)] int reserved2);
[return: MarshalAs(UnmanagedType.Interface)]
IStream OpenStream([In, MarshalAs(UnmanagedType.BStr)] string pwcsName, IntPtr reserved1, [In, MarshalAs(UnmanagedType.U4)] int grfMode, [In, MarshalAs(UnmanagedType.U4)] int reserved2);
[return: MarshalAs(UnmanagedType.Interface)]
IStorage CreateStorage([In, MarshalAs(UnmanagedType.BStr)] string pwcsName, [In, MarshalAs(UnmanagedType.U4)] int grfMode, [In, MarshalAs(UnmanagedType.U4)] int reserved1, [In, MarshalAs(UnmanagedType.U4)] int reserved2);
[return: MarshalAs(UnmanagedType.Interface)]
IStorage OpenStorage([In, MarshalAs(UnmanagedType.BStr)] string pwcsName, IntPtr pstgPriority, [In, MarshalAs(UnmanagedType.U4)] int grfMode, IntPtr snbExclude, [In, MarshalAs(UnmanagedType.U4)] int reserved);
void CopyTo(int ciidExclude, [In, MarshalAs(UnmanagedType.LPArray)] Guid[] pIIDExclude, IntPtr snbExclude, [In, MarshalAs(UnmanagedType.Interface)] IStorage stgDest);
void MoveElementTo([In, MarshalAs(UnmanagedType.BStr)] string pwcsName, [In, MarshalAs(UnmanagedType.Interface)] IStorage stgDest, [In, MarshalAs(UnmanagedType.BStr)] string pwcsNewName, [In, MarshalAs(UnmanagedType.U4)] int grfFlags);
void Commit(int grfCommitFlags);
void Revert();
void EnumElements([In, MarshalAs(UnmanagedType.U4)] int reserved1, IntPtr reserved2, [In, MarshalAs(UnmanagedType.U4)] int reserved3, [MarshalAs(UnmanagedType.Interface)] out object ppVal);
void DestroyElement([In, MarshalAs(UnmanagedType.BStr)] string pwcsName);
void RenameElement([In, MarshalAs(UnmanagedType.BStr)] string pwcsOldName, [In, MarshalAs(UnmanagedType.BStr)] string pwcsNewName);
void SetElementTimes([In, MarshalAs(UnmanagedType.BStr)] string pwcsName, [In] System.Runtime.InteropServices.ComTypes.FILETIME pctime, [In] System.Runtime.InteropServices.ComTypes.FILETIME patime, [In] System.Runtime.InteropServices.ComTypes.FILETIME pmtime);
void SetClass([In] ref Guid clsid);
void SetStateBits(int grfStateBits, int grfMask);
void Stat([Out]out System.Runtime.InteropServices.ComTypes.STATSTG pStatStg, int grfStatFlag);
}
[ComImport, Guid("0000000A-0000-0000-C000-000000000046"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
internal interface ILockBytes
{
void ReadAt([In, MarshalAs(UnmanagedType.U8)] long ulOffset, [Out, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 1)] byte[] pv, [In, MarshalAs(UnmanagedType.U4)] int cb, [Out, MarshalAs(UnmanagedType.LPArray)] int[] pcbRead);
void WriteAt([In, MarshalAs(UnmanagedType.U8)] long ulOffset, IntPtr pv, [In, MarshalAs(UnmanagedType.U4)] int cb, [Out, MarshalAs(UnmanagedType.LPArray)] int[] pcbWritten);
void Flush();
void SetSize([In, MarshalAs(UnmanagedType.U8)] long cb);
void LockRegion([In, MarshalAs(UnmanagedType.U8)] long libOffset, [In, MarshalAs(UnmanagedType.U8)] long cb, [In, MarshalAs(UnmanagedType.U4)] int dwLockType);
void UnlockRegion([In, MarshalAs(UnmanagedType.U8)] long libOffset, [In, MarshalAs(UnmanagedType.U8)] long cb, [In, MarshalAs(UnmanagedType.U4)] int dwLockType);
void Stat([Out]out System.Runtime.InteropServices.ComTypes.STATSTG pstatstg, [In, MarshalAs(UnmanagedType.U4)] int grfStatFlag);
}
[StructLayout(LayoutKind.Sequential)]
internal sealed class POINTL
{
public int x;
public int y;
}
[StructLayout(LayoutKind.Sequential)]
internal sealed class SIZEL
{
public int cx;
public int cy;
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
internal sealed class FILEGROUPDESCRIPTORA
{
public uint cItems;
public FILEDESCRIPTORA[] fgd;
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
internal sealed class FILEDESCRIPTORA
{
public uint dwFlags;
public Guid clsid;
public SIZEL sizel;
public POINTL pointl;
public uint dwFileAttributes;
public System.Runtime.InteropServices.ComTypes.FILETIME ftCreationTime;
public System.Runtime.InteropServices.ComTypes.FILETIME ftLastAccessTime;
public System.Runtime.InteropServices.ComTypes.FILETIME ftLastWriteTime;
public uint nFileSizeHigh;
public uint nFileSizeLow;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)]
public string cFileName;
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
internal sealed class FILEGROUPDESCRIPTORW
{
public uint cItems;
public FILEDESCRIPTORW[] fgd;
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
internal sealed class FILEDESCRIPTORW
{
public uint dwFlags;
public Guid clsid;
public SIZEL sizel;
public POINTL pointl;
public uint dwFileAttributes;
public System.Runtime.InteropServices.ComTypes.FILETIME ftCreationTime;
public System.Runtime.InteropServices.ComTypes.FILETIME ftLastAccessTime;
public System.Runtime.InteropServices.ComTypes.FILETIME ftLastWriteTime;
public uint nFileSizeHigh;
public uint nFileSizeLow;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)]
public string cFileName;
}
}
#endregion
#region Property(s)
/// <summary>
/// Holds the <see cref="System.Windows.Forms.IDataObject"/> that this class is wrapping
/// </summary>
protected System.Windows.Forms.IDataObject underlyingDataObject;
/// <summary>
/// Holds the <see cref="System.Runtime.InteropServices.ComTypes.IDataObject"/> interface to the <see cref="System.Windows.Forms.IDataObject"/> that this class is wrapping.
/// </summary>
protected System.Runtime.InteropServices.ComTypes.IDataObject comUnderlyingDataObject;
/// <summary>
/// Holds the internal ole <see cref="System.Windows.Forms.IDataObject"/> to the <see cref="System.Windows.Forms.IDataObject"/> that this class is wrapping.
/// </summary>
protected System.Windows.Forms.IDataObject oleUnderlyingDataObject;
/// <summary>
/// Holds the <see cref="MethodInfo"/> of the "GetDataFromHGLOBLAL" method of the internal ole <see cref="System.Windows.Forms.IDataObject"/>.
/// </summary>
protected MethodInfo getDataFromHGLOBLALMethod;
#endregion
#region Constructor(s)
/// <summary>
/// Initializes a new instance of the <see cref="OutlookDataObject"/> class.
/// </summary>
/// <param name="underlyingDataObject">The underlying data object to wrap.</param>
public FileContentDataObjectBase(System.Windows.Forms.IDataObject underlyingDataObject)
{
//get the underlying dataobject and its ComType IDataObject interface to it
this.underlyingDataObject = underlyingDataObject;
this.comUnderlyingDataObject = (System.Runtime.InteropServices.ComTypes.IDataObject)this.underlyingDataObject;
//get the internal ole dataobject and its GetDataFromHGLOBLAL so it can be called later
FieldInfo innerDataField = this.underlyingDataObject.GetType().GetField("innerData", BindingFlags.NonPublic | BindingFlags.Instance);
this.oleUnderlyingDataObject = (System.Windows.Forms.IDataObject)innerDataField.GetValue(this.underlyingDataObject);
this.getDataFromHGLOBLALMethod = this.oleUnderlyingDataObject.GetType().GetMethod("GetDataFromHGLOBLAL", BindingFlags.NonPublic | BindingFlags.Instance);
}
#endregion
#region IDataObject Members
/// <summary>
/// Retrieves the data associated with the specified class type format.
/// </summary>
/// <param name="format">A <see cref="T:System.Type"></see> representing the format of the data to retrieve. See <see cref="T:System.Windows.Forms.DataFormats"></see> for predefined formats.</param>
/// <returns>
/// The data associated with the specified format, or null.
/// </returns>
public object GetData(Type format)
{
return this.GetData(format.FullName);
}
/// <summary>
/// Retrieves the data associated with the specified data format.
/// </summary>
/// <param name="format">The format of the data to retrieve. See <see cref="T:System.Windows.Forms.DataFormats"></see> for predefined formats.</param>
/// <returns>
/// The data associated with the specified format, or null.
/// </returns>
public object GetData(string format)
{
return this.GetData(format, true);
}
/// <summary>
/// Retrieves the data associated with the specified data format, using a Boolean to determine whether to convert the data to the format.
/// </summary>
/// <param name="format">The format of the data to retrieve. See <see cref="T:System.Windows.Forms.DataFormats"></see> for predefined formats.</param>
/// <param name="autoConvert">true to convert the data to the specified format; otherwise, false.</param>
/// <returns>
/// The data associated with the specified format, or null.
/// </returns>
public object GetData(string format, bool autoConvert)
{
return this.underlyingDataObject.GetData(format, autoConvert);
}
/// <summary>
/// Retrieves the data associated with the specified data format at the specified index.
/// </summary>
/// <param name="format">The format of the data to retrieve. See <see cref="T:System.Windows.Forms.DataFormats"></see> for predefined formats.</param>
/// <param name="index">The index of the data to retrieve.</param>
/// <returns>
/// A <see cref="MemoryStream"/> containing the raw data for the specified data format at the specified index.
/// </returns>
public MemoryStream GetData(string format, int index)
{
//create a FORMATETC struct to request the data with
FORMATETC formatetc = new FORMATETC();
formatetc.cfFormat = (short)DataFormats.GetFormat(format).Id;
formatetc.dwAspect = DVASPECT.DVASPECT_CONTENT;
formatetc.lindex = index;
formatetc.ptd = new IntPtr(0);
formatetc.tymed = TYMED.TYMED_ISTREAM | TYMED.TYMED_ISTORAGE | TYMED.TYMED_HGLOBAL;
//create STGMEDIUM to output request results into
STGMEDIUM medium = new STGMEDIUM();
//using the Com IDataObject interface get the data using the defined FORMATETC
this.comUnderlyingDataObject.GetData(ref formatetc, out medium);
//retrieve the data depending on the returned store type
switch (medium.tymed)
{
case TYMED.TYMED_ISTORAGE:
//to handle a IStorage it needs to be written into a second unmanaged
//memory mapped storage and then the data can be read from memory into
//a managed byte and returned as a MemoryStream
NativeMethods.IStorage iStorage = null;
NativeMethods.IStorage iStorage2 = null;
NativeMethods.ILockBytes iLockBytes = null;
System.Runtime.InteropServices.ComTypes.STATSTG iLockBytesStat;
try
{
//marshal the returned pointer to a IStorage object
iStorage = (NativeMethods.IStorage)Marshal.GetObjectForIUnknown(medium.unionmember);
Marshal.Release(medium.unionmember);
//create a ILockBytes (unmanaged byte array) and then create a IStorage using the byte array as a backing store
iLockBytes = NativeMethods.CreateILockBytesOnHGlobal(IntPtr.Zero, true);
iStorage2 = NativeMethods.StgCreateDocfileOnILockBytes(iLockBytes, 0x00001012, 0);
//copy the returned IStorage into the new IStorage
iStorage.CopyTo(0, null, IntPtr.Zero, iStorage2);
iLockBytes.Flush();
iStorage2.Commit(0);
//get the STATSTG of the ILockBytes to determine how many bytes were written to it
iLockBytesStat = new System.Runtime.InteropServices.ComTypes.STATSTG();
iLockBytes.Stat(out iLockBytesStat, 1);
int iLockBytesSize = (int)iLockBytesStat.cbSize;
//read the data from the ILockBytes (unmanaged byte array) into a managed byte array
byte[] iLockBytesContent = new byte[iLockBytesSize];
iLockBytes.ReadAt(0, iLockBytesContent, iLockBytesContent.Length, null);
//wrapped the managed byte array into a memory stream and return it
return new MemoryStream(iLockBytesContent);
}
finally
{
//release all unmanaged objects
Marshal.ReleaseComObject(iStorage2);
Marshal.ReleaseComObject(iLockBytes);
Marshal.ReleaseComObject(iStorage);
}
case TYMED.TYMED_ISTREAM:
//to handle a IStream it needs to be read into a managed byte and
//returned as a MemoryStream
IStream iStream = null;
System.Runtime.InteropServices.ComTypes.STATSTG iStreamStat;
try
{
//marshal the returned pointer to a IStream object
iStream = (IStream)Marshal.GetObjectForIUnknown(medium.unionmember);
Marshal.Release(medium.unionmember);
//get the STATSTG of the IStream to determine how many bytes are in it
iStreamStat = new System.Runtime.InteropServices.ComTypes.STATSTG();
iStream.Stat(out iStreamStat, 0);
int iStreamSize = (int)iStreamStat.cbSize;
//read the data from the IStream into a managed byte array
byte[] iStreamContent = new byte[iStreamSize];
iStream.Read(iStreamContent, iStreamContent.Length, IntPtr.Zero);
//wrapped the managed byte array into a memory stream and return it
return new MemoryStream(iStreamContent);
}
finally
{
//release all unmanaged objects
Marshal.ReleaseComObject(iStream);
}
case TYMED.TYMED_HGLOBAL:
//to handle a HGlobal the exisitng "GetDataFromHGLOBLAL" method is invoked via
//reflection
return (MemoryStream)this.getDataFromHGLOBLALMethod.Invoke(this.oleUnderlyingDataObject, new object[] { DataFormats.GetFormat((short)formatetc.cfFormat).Name, medium.unionmember });
}
return null;
}
/// <summary>
/// Returns a list of all formats that data stored in this instance is associated with or can be converted to.
/// </summary>
/// <returns>
/// An array of the names that represents a list of all formats that are supported by the data stored in this object.
/// </returns>
public string[] GetFormats()
{
return this.underlyingDataObject.GetFormats();
}
/// <summary>
/// Gets a list of all formats that data stored in this instance is associated with or can be converted to, using a Boolean value to determine whether to retrieve all formats that the data can be converted to or only native data formats.
/// </summary>
/// <param name="autoConvert">true to retrieve all formats that data stored in this instance is associated with or can be converted to; false to retrieve only native data formats.</param>
/// <returns>
/// An array of the names that represents a list of all formats that are supported by the data stored in this object.
/// </returns>
public string[] GetFormats(bool autoConvert)
{
return this.underlyingDataObject.GetFormats(autoConvert);
}
#endregion
#region methods
public static void SaveToFileSystem(string[] filenames, MemoryStream[] filestreams, string path)
{
for (int fileIndex = 0; fileIndex < filenames.Length; fileIndex++)
{
try
{
//use the fileindex to get the name and data stream
string filename = filenames[fileIndex];
MemoryStream filestream = filestreams[fileIndex];
//save the file stream using its name to the application path
FileStream outputStream = File.Create(Path.Combine(path, GetStrippedFileName(filename)));
filestream.WriteTo(outputStream);
outputStream.Close();
}
catch (IOException ex)
{
throw new Exception(ex.Message, ex);
}
catch (SecurityException ex)
{
throw new Exception(ex.Message, ex);
}
}
}
public static void SaveToFileSystem(FileContentDataObjectBase dataObject, string path)
{
dataObject.SaveToFileSystem(path);
}
public abstract void SaveToFileSystem(string path);
private static string GetStrippedFileName(string input)
{
foreach (var chr in Path.GetInvalidFileNameChars())
input = input.Replace(chr.ToString(), String.Empty);
return input;
}
#endregion
}
I would like to figure out why it happens and how I can fix the issue.
Any help or insights would be welcome.
Thanks in advance.
After searching for quite a while I finally found the problem thanks to this helpful comment.
As it turns out the FILEGROUPDESCRIPTORA and FILEGROUPDESCRIPTORW declarations in the original code we used are wrong which can cause an AccessViolation Exception in some cases.
So I did change the declarations like the commentor described from...
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public sealed class FILEGROUPDESCRIPTORA
{
public uint cItems;
public FILEDESCRIPTORA[] fgd;
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public sealed class FILEGROUPDESCRIPTORW
{
public uint cItems;
public FILEDESCRIPTORW[] fgd;
}
To...
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public sealed class FILEGROUPDESCRIPTORA
{
public uint cItems;
public FILEDESCRIPTORA fgd;
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public sealed class FILEGROUPDESCRIPTORW
{
public uint cItems;
public FILEDESCRIPTORW fgd;
}
Afterwards the AccessViolation Exceptions were gone and I only had to modify the original code I linked in my question a little more to make it work.
I had to add some additional checks during the handling of the Clipboard files 'FileContents' format to make sure the correct of the two FILEGROUPDESCRIPTORs was used each time, but now it works perfectly again.
There could still be an error in your code.
That first line below in the handler for 'FILEGROUPDESCRIPTORW' can/will fail on a Win8 machine.
replace
object fileGroupDescriptorObject = Marshal.PtrToStructure(fileGroupDescriptorWPointer, typeof(NativeMethods.FILEGROUPDESCRIPTORW));
NativeMethods.FILEGROUPDESCRIPTORW fgd = (NativeMethods.FILEGROUPDESCRIPTORW)fileGroupDescriptorObject;
files = new NativeMethods.FILEDESCRIPTOR[fgd.cItems];
pdata = (IntPtr)((int)pdata + Marshal.SizeOf(pdata));
for (int index = 0; index < fgd.cItems; index++)
with
int ITEMCOUNT = Marshal.ReadInt32(pdata);
files = new NativeMethods.FILEDESCRIPTOR[ITEMCOUNT];
// Set our pointer offset to the beginning of the FILEDESCRIPTOR* array
pdata = (IntPtr)((long)pdata + Marshal.SizeOf(pdata));
// Walk the array, converting each FILEDESCRIPTOR* to a FILEDESCRIPTOR
for (int index = 0; index < ITEMCOUNT; index++)
For more details, see:
Drag-and-Drop multiple Attached File From Outlook to C# Window Form

Viewing export table on an unmanaged dll in C#

I am currently trying to create a C# application that will allow me to view the export table from an unmanaged DLL. My problem is, once I have all the pointers I need, I have no idea how to loop through the information that the API has provided me. Here is what I have right now:
using System;
using System.ComponentModel;
using System.Runtime.InteropServices;
namespace DLLMapper
{
class DLLExportViewer
{
#region APIs
[DllImport("imagehlp.dll")]
public static extern Boolean MapAndLoad(String ImageName, String DllPath, ref LOADED_IMAGE LoadedImage, Boolean DotDll, Boolean ReadOnly);
[DllImport("imagehlp.dll")]
public static extern Boolean UnMapAndLoad(ref LOADED_IMAGE LoadedImage);
[DllImport("dbghelp.dll")]
public static extern IntPtr ImageDirectoryEntryToData(IntPtr Base, Boolean MappedAsImage, UInt16 DirectoryEntry, ref Int32 Size);
[DllImport("dbghelp.dll")]
public static extern IntPtr ImageRvaToVa(ref IMAGE_NT_HEADERS NtHeaders, IntPtr Base, UInt32 Rva, ref IMAGE_SECTION_HEADER LastRvaSection);
#endregion
#region Structures
[StructLayout(LayoutKind.Sequential, Pack = 4)]
public struct LOADED_IMAGE
{
public String ModuleName;
public IntPtr hFile;
public IntPtr MappedAddress;
public IMAGE_NT_HEADERS FileHeader;
public IMAGE_SECTION_HEADER LastRvaSection;
public Int32 NumberOfSections;
public IMAGE_SECTION_HEADER Sections;
public Int32 Characteristics;
public Boolean fSystemImage;
public Boolean fDOSImage;
public LIST_ENTRY Links;
public Int32 SizeOfImage;
}
[StructLayout(LayoutKind.Sequential, Pack = 4)]
public struct IMAGE_EXPORT_DIRECTORY
{
public UInt32 Characteristics;
public UInt32 TimeDateStamp;
public UInt16 MajorVersion;
public UInt16 MinorVersion;
public UInt32 Name;
public UInt32 Base;
public UInt32 NumberOfFunctions;
public UInt32 NumberOfNames;
public UInt32 AddressOfFunctions;
public UInt32 AddressOfNames;
public UInt32 AddressOfOrdinals;
}
[StructLayout(LayoutKind.Sequential, Pack = 4)]
public struct IMAGE_NT_HEADERS
{
public Int32 Signature;
public IMAGE_FILE_HEADER FileHeader;
public IMAGE_OPTIONAL_HEADER OptionalHeader;
}
[StructLayout(LayoutKind.Sequential, Pack = 4)]
public struct LIST_ENTRY
{
public IntPtr Flink;
public IntPtr Blink;
}
[StructLayout(LayoutKind.Sequential, Pack = 4)]
public struct IMAGE_SECTION_HEADER
{
[MarshalAs(UnmanagedType.ByValArray, SizeConst = IMAGE_SIZEOF_SHORT_NAME)]
public Byte[] Name;
public Misc Misc;
public UInt32 PhysicalAddress;
public UInt32 VirtualAddress;
public UInt32 SizeOfRawData;
public UInt32 PointerToRawData;
public UInt32 PointerToRelocations;
public UInt32 PointerToLinenumbers;
public Int16 NumberOfRelocations;
public Int16 NumberOfLinenumbers;
public UInt32 Characteristics;
}
[StructLayout(LayoutKind.Sequential, Pack = 4)]
public struct IMAGE_FILE_HEADER
{
public UInt16 Machine;
public UInt16 NumberOfSections;
public UInt32 TimeDateStamp;
public UInt32 PointerToSymbolTable;
public UInt32 NumberOfSymbols;
public UInt16 SizeOfOptionalHeader;
public UInt16 Characteristics;
}
[StructLayout(LayoutKind.Sequential, Pack = 4)]
public struct IMAGE_OPTIONAL_HEADER
{
public UInt16 Magic;
public Byte MajorLinkerVersion;
public Byte MinorLinkerVersion;
public UInt32 SizeOfCode;
public UInt32 SizeOfInitializedData;
public UInt32 SizeOfUninitializedData;
public UInt32 AddressOfEntryPoint;
public UInt32 BaseOfCode;
public UInt32 BaseOfData;
public UInt32 ImageBase;
public UInt32 SectionAlignment;
public UInt32 FileAlignment;
public UInt16 MajorOperatingSystemVersion;
public UInt16 MinorOperatingSystemVersion;
public UInt16 MajorImageVersion;
public UInt16 MinorImageVersion;
public UInt16 MajorSubsystemVersion;
public UInt16 MinorSubsystemVersion;
public UInt32 Win32VersionValue;
public UInt32 SizeOfImage;
public UInt32 SizeOfHeaders;
public UInt32 CheckSum;
public UInt16 Subsystem;
public UInt16 DllCharacteristics;
public UInt32 SizeOfStackReserve;
public UInt32 SizeOfStackCommit;
public UInt32 SizeOfHeapReserve;
public UInt32 SizeOfHeapCommit;
public UInt32 LoaderFlags;
public UInt32 NumberOfRvaAndSizes;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = IMAGE_NUMBEROF_DIRECTORY_ENTRIES)]
public IMAGE_DATA_DIRECTORY[] DataDirectory;
}
[StructLayout(LayoutKind.Sequential, Pack = 4)]
public struct IMAGE_DATA_DIRECTORY
{
public UInt32 VirtualAddress;
public UInt32 Size;
}
[StructLayout(LayoutKind.Explicit)]
public struct Misc
{
[FieldOffset(0)]
public UInt32 PhysicalAddress;
[FieldOffset(0)]
public UInt32 VirtualSize;
}
#endregion
#region Variables & Constants
public const Int32 IMAGE_SIZEOF_SHORT_NAME = 8;
public const Int32 IMAGE_NUMBEROF_DIRECTORY_ENTRIES = 16;
public const UInt16 IMAGE_DIRECTORY_ENTRY_EXPORT = 0;
/// <summary>
/// String value holding the path to the DLL file. This value is also returned by the FileName property.
/// </summary>
private String sDLLFilePath;
/// <summary>
/// Boolean value that is return by the LibraryLoaded property.
/// </summary>
private Boolean bLibraryLoaded;
/// <summary>
/// Int32 value that is returned by the FunctionCount property.
/// </summary>
private Int32 iFunctionCount;
/// <summary>
/// Int32 value that is returned by the SizeOfImage property.
/// </summary>
private Int32 iSizeOfCode;
/// <summary>
/// String array value that is returned by the ImageFunctions property.
/// </summary>
private String[] sFunctions;
#endregion
#region Properties
/// <summary>
/// Gets a boolean value indicating if the library has been loaded successfully.
/// </summary>
public Boolean LibraryLoaded { get { return bLibraryLoaded; } }
/// <summary>
/// Gets a string value indicating what file the class was initialized with.
/// </summary>
public String FileName { get { return sDLLFilePath; } }
/// <summary>
/// Gets a string array of the functions within the image.
/// </summary>
public String[] ImageFunctions { get { return sFunctions; } }
/// <summary>
/// Gets an Int32 value indicating the number of functions within the image.
/// </summary>
public Int32 FunctionCount { get { return iFunctionCount; } }
#endregion
/// <summary>
/// Initilizes the DLLExportViewer class.
/// </summary>
/// <param name="sFilePath">Path to the DLL file to initilize the class with.</param>
public DLLExportViewer(String sFilePath)
{
IMAGE_SECTION_HEADER ishSectionHeader = new IMAGE_SECTION_HEADER();
LOADED_IMAGE liLoadedImage = new LOADED_IMAGE();
IMAGE_EXPORT_DIRECTORY iedExportDirectory;
IntPtr pImageExportDirectory;
IntPtr pVirtualAddressOfNames;
Int32 iDirectoryExportSize = 0;
sDLLFilePath = sFilePath;
if (MapAndLoad(sDLLFilePath, null, ref liLoadedImage, true, true))
{
bLibraryLoaded = true;
pImageExportDirectory = ImageDirectoryEntryToData(liLoadedImage.MappedAddress, false, IMAGE_DIRECTORY_ENTRY_EXPORT, ref iDirectoryExportSize);
iedExportDirectory = (IMAGE_EXPORT_DIRECTORY)Marshal.PtrToStructure(pImageExportDirectory, typeof(IMAGE_EXPORT_DIRECTORY));
iFunctionCount = (Int32)iedExportDirectory.NumberOfFunctions;
pVirtualAddressOfNames = ImageRvaToVa(ref liLoadedImage.FileHeader, liLoadedImage.MappedAddress, iedExportDirectory.AddressOfNames, ref ishSectionHeader);
}
else
{
throw new Exception(String.Format("Failed to load library {0}\n\nError Number:{1]\nError:{2}", sDLLFilePath, Marshal.GetLastWin32Error(), new Win32Exception(Marshal.GetLastWin32Error()).Message));
}
}
}
}
My biggest concern is that I may have some of the marshalling on the structures wrong. But, all in all, I'm not sure where to go from here. Can anyone offer me some assistance?
The following shows how to do it using p/invoke to dbghelp. Note that I used a number of declarations from pinvoke.net.
using System;
using System.Text;
using System.Runtime.InteropServices;
using Microsoft.Win32.SafeHandles;
using System.ComponentModel;
internal static class NativeMethods
{
[Flags]
public enum EFileAccess : uint
{
//
// Standard Section
//
AccessSystemSecurity = 0x1000000, // AccessSystemAcl access type
MaximumAllowed = 0x2000000, // MaximumAllowed access type
Delete = 0x10000,
ReadControl = 0x20000,
WriteDAC = 0x40000,
WriteOwner = 0x80000,
Synchronize = 0x100000,
StandardRightsRequired = 0xF0000,
StandardRightsRead = ReadControl,
StandardRightsWrite = ReadControl,
StandardRightsExecute = ReadControl,
StandardRightsAll = 0x1F0000,
SpecificRightsAll = 0xFFFF,
FILE_READ_DATA = 0x0001, // file & pipe
FILE_LIST_DIRECTORY = 0x0001, // directory
FILE_WRITE_DATA = 0x0002, // file & pipe
FILE_ADD_FILE = 0x0002, // directory
FILE_APPEND_DATA = 0x0004, // file
FILE_ADD_SUBDIRECTORY = 0x0004, // directory
FILE_CREATE_PIPE_INSTANCE = 0x0004, // named pipe
FILE_READ_EA = 0x0008, // file & directory
FILE_WRITE_EA = 0x0010, // file & directory
FILE_EXECUTE = 0x0020, // file
FILE_TRAVERSE = 0x0020, // directory
FILE_DELETE_CHILD = 0x0040, // directory
FILE_READ_ATTRIBUTES = 0x0080, // all
FILE_WRITE_ATTRIBUTES = 0x0100, // all
//
// Generic Section
//
GenericRead = 0x80000000,
GenericWrite = 0x40000000,
GenericExecute = 0x20000000,
GenericAll = 0x10000000,
SPECIFIC_RIGHTS_ALL = 0x00FFFF,
FILE_ALL_ACCESS =
StandardRightsRequired |
Synchronize |
0x1FF,
FILE_GENERIC_READ =
StandardRightsRead |
FILE_READ_DATA |
FILE_READ_ATTRIBUTES |
FILE_READ_EA |
Synchronize,
FILE_GENERIC_WRITE =
StandardRightsWrite |
FILE_WRITE_DATA |
FILE_WRITE_ATTRIBUTES |
FILE_WRITE_EA |
FILE_APPEND_DATA |
Synchronize,
FILE_GENERIC_EXECUTE =
StandardRightsExecute |
FILE_READ_ATTRIBUTES |
FILE_EXECUTE |
Synchronize
}
[Flags]
public enum EFileShare : uint
{
/// <summary>
///
/// </summary>
None = 0x00000000,
/// <summary>
/// Enables subsequent open operations on an object to request read access.
/// Otherwise, other processes cannot open the object if they request read access.
/// If this flag is not specified, but the object has been opened for read access, the function fails.
/// </summary>
Read = 0x00000001,
/// <summary>
/// Enables subsequent open operations on an object to request write access.
/// Otherwise, other processes cannot open the object if they request write access.
/// If this flag is not specified, but the object has been opened for write access, the function fails.
/// </summary>
Write = 0x00000002,
/// <summary>
/// Enables subsequent open operations on an object to request delete access.
/// Otherwise, other processes cannot open the object if they request delete access.
/// If this flag is not specified, but the object has been opened for delete access, the function fails.
/// </summary>
Delete = 0x00000004
}
public enum ECreationDisposition : uint
{
/// <summary>
/// Creates a new file. The function fails if a specified file exists.
/// </summary>
New = 1,
/// <summary>
/// Creates a new file, always.
/// If a file exists, the function overwrites the file, clears the existing attributes, combines the specified file attributes,
/// and flags with FILE_ATTRIBUTE_ARCHIVE, but does not set the security descriptor that the SECURITY_ATTRIBUTES structure specifies.
/// </summary>
CreateAlways = 2,
/// <summary>
/// Opens a file. The function fails if the file does not exist.
/// </summary>
OpenExisting = 3,
/// <summary>
/// Opens a file, always.
/// If a file does not exist, the function creates a file as if dwCreationDisposition is CREATE_NEW.
/// </summary>
OpenAlways = 4,
/// <summary>
/// Opens a file and truncates it so that its size is 0 (zero) bytes. The function fails if the file does not exist.
/// The calling process must open the file with the GENERIC_WRITE access right.
/// </summary>
TruncateExisting = 5
}
[Flags]
public enum EFileAttributes : uint
{
Readonly = 0x00000001,
Hidden = 0x00000002,
System = 0x00000004,
Directory = 0x00000010,
Archive = 0x00000020,
Device = 0x00000040,
Normal = 0x00000080,
Temporary = 0x00000100,
SparseFile = 0x00000200,
ReparsePoint = 0x00000400,
Compressed = 0x00000800,
Offline = 0x00001000,
NotContentIndexed = 0x00002000,
Encrypted = 0x00004000,
Write_Through = 0x80000000,
Overlapped = 0x40000000,
NoBuffering = 0x20000000,
RandomAccess = 0x10000000,
SequentialScan = 0x08000000,
DeleteOnClose = 0x04000000,
BackupSemantics = 0x02000000,
PosixSemantics = 0x01000000,
OpenReparsePoint = 0x00200000,
OpenNoRecall = 0x00100000,
FirstPipeInstance = 0x00080000
}
[DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
public static extern SafeFileHandle CreateFile(
string lpFileName,
EFileAccess dwDesiredAccess,
EFileShare dwShareMode,
IntPtr lpSecurityAttributes,
ECreationDisposition dwCreationDisposition,
EFileAttributes dwFlagsAndAttributes,
IntPtr hTemplateFile
);
[Flags]
public enum FileMapProtection : uint
{
PageReadonly = 0x02,
PageReadWrite = 0x04,
PageWriteCopy = 0x08,
PageExecuteRead = 0x20,
PageExecuteReadWrite = 0x40,
SectionCommit = 0x8000000,
SectionImage = 0x1000000,
SectionNoCache = 0x10000000,
SectionReserve = 0x4000000,
}
[DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
public static extern SafeFileHandle CreateFileMapping(
SafeFileHandle hFile,
IntPtr lpFileMappingAttributes,
FileMapProtection flProtect,
uint dwMaximumSizeHigh,
uint dwMaximumSizeLow,
string lpName
);
[DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
public static extern SafeFileHandle CreateFileMapping(
SafeFileHandle hFile,
IntPtr lpFileMappingAttributes,
FileMapProtection flProtect,
uint dwMaximumSizeHigh,
uint dwMaximumSizeLow,
IntPtr lpName
);
[Flags]
public enum FileMapAccess : uint
{
FileMapCopy = 0x0001,
FileMapWrite = 0x0002,
FileMapRead = 0x0004,
FileMapAllAccess = 0x001f,
FileMapExecute = 0x0020,
}
[DllImport("kernel32.dll", SetLastError = true)]
public static extern IntPtr MapViewOfFile(
SafeFileHandle hFileMappingObject,
FileMapAccess dwDesiredAccess,
UInt32 dwFileOffsetHigh,
UInt32 dwFileOffsetLow,
UIntPtr dwNumberOfBytesToMap
);
[DllImport("kernel32.dll", SetLastError = true)]
public static extern bool UnmapViewOfFile(IntPtr lpBaseAddress);
[StructLayout(LayoutKind.Sequential)]
public struct IMAGE_FILE_HEADER
{
public UInt16 Machine;
public UInt16 NumberOfSections;
public UInt32 TimeDateStamp;
public UInt32 PointerToSymbolTable;
public UInt32 NumberOfSymbols;
public UInt16 SizeOfOptionalHeader;
public UInt16 Characteristics;
}
[StructLayout(LayoutKind.Sequential)]
public struct IMAGE_DATA_DIRECTORY
{
public UInt32 VirtualAddress;
public UInt32 Size;
}
[StructLayout(LayoutKind.Sequential)]
public struct IMAGE_OPTIONAL_HEADER
{
public UInt16 Magic;
public Byte MajorLinkerVersion;
public Byte MinorLinkerVersion;
public UInt32 SizeOfCode;
public UInt32 SizeOfInitializedData;
public UInt32 SizeOfUninitializedData;
public UInt32 AddressOfEntryPoint;
public UInt32 BaseOfCode;
public UInt32 BaseOfData;
public UInt32 ImageBase;
public UInt32 SectionAlignment;
public UInt32 FileAlignment;
public UInt16 MajorOperatingSystemVersion;
public UInt16 MinorOperatingSystemVersion;
public UInt16 MajorImageVersion;
public UInt16 MinorImageVersion;
public UInt16 MajorSubsystemVersion;
public UInt16 MinorSubsystemVersion;
public UInt32 Win32VersionValue;
public UInt32 SizeOfImage;
public UInt32 SizeOfHeaders;
public UInt32 CheckSum;
public UInt16 Subsystem;
public UInt16 DllCharacteristics;
public UInt32 SizeOfStackReserve;
public UInt32 SizeOfStackCommit;
public UInt32 SizeOfHeapReserve;
public UInt32 SizeOfHeapCommit;
public UInt32 LoaderFlags;
public UInt32 NumberOfRvaAndSizes;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)]
public IMAGE_DATA_DIRECTORY[] DataDirectory;
}
[StructLayout(LayoutKind.Sequential)]
public struct IMAGE_NT_HEADERS
{
public UInt32 Signature;
public IMAGE_FILE_HEADER FileHeader;
public IMAGE_OPTIONAL_HEADER OptionalHeader;
}
[DllImport("dbghelp.dll", SetLastError = true)]
public static extern IntPtr ImageNtHeader(
IntPtr ImageBase
);
[StructLayout(LayoutKind.Sequential)]
public struct IMAGE_EXPORT_DIRECTORY
{
public UInt32 Characteristics;
public UInt32 TimeDateStamp;
public UInt16 MajorVersion;
public UInt16 MinorVersion;
public UInt32 Name;
public UInt32 Base;
public UInt32 NumberOfFunctions;
public UInt32 NumberOfNames;
public UInt32 AddressOfFunctions; // RVA from base of image
public UInt32 AddressOfNames; // RVA from base of image
public UInt32 AddressOfNameOrdinals; // RVA from base of image
}
[DllImport("dbghelp.dll", SetLastError = true)]
public static extern IntPtr ImageRvaToVa(
IntPtr NtHeaders,
IntPtr Base,
UInt32 Rva,
IntPtr LastRvaSection
);
}
namespace ConsoleApplication1
{
class Program
{
private static string[] GetExports(string ModuleFileName)
{
SafeFileHandle FileHandle = NativeMethods.CreateFile(
ModuleFileName,
NativeMethods.EFileAccess.GenericRead,
NativeMethods.EFileShare.Read,
IntPtr.Zero,
NativeMethods.ECreationDisposition.OpenExisting,
NativeMethods.EFileAttributes.Normal,
IntPtr.Zero
);
if (FileHandle.IsInvalid)
throw new Win32Exception();
try
{
SafeFileHandle ImageHandle = NativeMethods.CreateFileMapping(
FileHandle,
IntPtr.Zero,
NativeMethods.FileMapProtection.PageReadonly,
0,
0,
IntPtr.Zero
);
if (ImageHandle.IsInvalid)
throw new Win32Exception();
try
{
IntPtr ImagePointer = NativeMethods.MapViewOfFile(
ImageHandle,
NativeMethods.FileMapAccess.FileMapRead,
0,
0,
UIntPtr.Zero
);
if (ImagePointer == IntPtr.Zero)
throw new Win32Exception();
try
{
IntPtr HeaderPointer = NativeMethods.ImageNtHeader(ImagePointer);
if (HeaderPointer == IntPtr.Zero)
throw new Win32Exception();
NativeMethods.IMAGE_NT_HEADERS Header = (NativeMethods.IMAGE_NT_HEADERS)Marshal.PtrToStructure(
HeaderPointer,
typeof(NativeMethods.IMAGE_NT_HEADERS)
);
if (Header.Signature != 0x00004550)// "PE\0\0" as a DWORD
throw new Exception(ModuleFileName + " is not a valid PE file");
IntPtr ExportTablePointer = NativeMethods.ImageRvaToVa(
HeaderPointer,
ImagePointer,
Header.OptionalHeader.DataDirectory[0].VirtualAddress,
IntPtr.Zero
);
if (ExportTablePointer == IntPtr.Zero)
throw new Win32Exception();
NativeMethods.IMAGE_EXPORT_DIRECTORY ExportTable = (NativeMethods.IMAGE_EXPORT_DIRECTORY)Marshal.PtrToStructure(
ExportTablePointer,
typeof(NativeMethods.IMAGE_EXPORT_DIRECTORY)
);
IntPtr NamesPointer = NativeMethods.ImageRvaToVa(
HeaderPointer,
ImagePointer,
ExportTable.AddressOfNames,
IntPtr.Zero
);
if (NamesPointer == IntPtr.Zero)
throw new Win32Exception();
NamesPointer = NativeMethods.ImageRvaToVa(
HeaderPointer,
ImagePointer,
(UInt32)Marshal.ReadInt32(NamesPointer),
IntPtr.Zero
);
if (NamesPointer == IntPtr.Zero)
throw new Win32Exception();
string[] exports = new string[ExportTable.NumberOfNames];
for (int i = 0; i < exports.Length; i++)
{
exports[i] = Marshal.PtrToStringAnsi(NamesPointer);
NamesPointer += exports[i].Length + 1;
}
return exports;
}
finally
{
if (!NativeMethods.UnmapViewOfFile(ImagePointer))
throw new Win32Exception();
}
}
finally
{
ImageHandle.Close();
}
}
finally
{
FileHandle.Close();
}
}
static void Main(string[] args)
{
foreach (string s in GetExports(#"C:\Windows\System32\kernel32.dll"))
{
Console.WriteLine(s);
}
Console.ReadLine();
}
}
}
Instead of using the API, I just wrote a class that would parse the PE Header to find the location of the export table. I used the PE file format specification from Microsoft to do this.
Microsoft Portable Executable and Common Object File Format Specification

How do I get common file type icons in C#?

As seen in This SO question on getting icons for common file types, it's quite possible for a windows program to get the icons for a registered file type using the C++ Shell API. These icons may or may not exist on disk - for example, we wanted to make our own custom file browser and want to display the system-associated icon with the file.
Is there a native C# way to get the icons for various file types (and if so, how) or must it be done through PInvoke with shell API?
And as a follow up, if there is a native .NET way of doing it, is there a cross-platform way of doing it?
One of my old open source project include an Icon class that does exactly that, feel free to rip it, seeing the age I put this file in the public domain anyway it's just PInvoke for most part.
To get an icon you use for example :
Icon zipIcon = BlackFox.Win32.Icons.IconFromExtension(".zip", SystemIconSize.Small);
Full sample :
using System;
using System.Windows.Forms;
using BlackFox.Win32;
using System.Drawing;
class Program
{
static void Main(string[] args)
{
PictureBox pict = new PictureBox();
pict.Image = Icons.IconFromExtension(".zip", Icons.SystemIconSize.Large).ToBitmap();
pict.Dock = DockStyle.Fill;
pict.SizeMode = PictureBoxSizeMode.CenterImage;
Form form = new Form();
form.Controls.Add(pict);
Application.Run(form);
}
}
The library :
using System;
using System.Drawing;
using System.Runtime.InteropServices;
using Microsoft.Win32;
using System.Reflection;
using System.Collections.Generic;
namespace BlackFox.Win32
{
public static class Icons
{
#region Custom exceptions class
public class IconNotFoundException : Exception
{
public IconNotFoundException(string fileName, int index)
: base(string.Format("Icon with Id = {0} wasn't found in file {1}", index, fileName))
{
}
}
public class UnableToExtractIconsException : Exception
{
public UnableToExtractIconsException(string fileName, int firstIconIndex, int iconCount)
: base(string.Format("Tryed to extract {2} icons starting from the one with id {1} from the \"{0}\" file but failed", fileName, firstIconIndex, iconCount))
{
}
}
#endregion
#region DllImports
/// <summary>
/// Contains information about a file object.
/// </summary>
struct SHFILEINFO
{
/// <summary>
/// Handle to the icon that represents the file. You are responsible for
/// destroying this handle with DestroyIcon when you no longer need it.
/// </summary>
public IntPtr hIcon;
/// <summary>
/// Index of the icon image within the system image list.
/// </summary>
public IntPtr iIcon;
/// <summary>
/// Array of values that indicates the attributes of the file object.
/// For information about these values, see the IShellFolder::GetAttributesOf
/// method.
/// </summary>
public uint dwAttributes;
/// <summary>
/// String that contains the name of the file as it appears in the Microsoft
/// Windows Shell, or the path and file name of the file that contains the
/// icon representing the file.
/// </summary>
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)]
public string szDisplayName;
/// <summary>
/// String that describes the type of file.
/// </summary>
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 80)]
public string szTypeName;
};
[Flags]
enum FileInfoFlags : int
{
/// <summary>
/// Retrieve the handle to the icon that represents the file and the index
/// of the icon within the system image list. The handle is copied to the
/// hIcon member of the structure specified by psfi, and the index is copied
/// to the iIcon member.
/// </summary>
SHGFI_ICON = 0x000000100,
/// <summary>
/// Indicates that the function should not attempt to access the file
/// specified by pszPath. Rather, it should act as if the file specified by
/// pszPath exists with the file attributes passed in dwFileAttributes.
/// </summary>
SHGFI_USEFILEATTRIBUTES = 0x000000010
}
/// <summary>
/// Creates an array of handles to large or small icons extracted from
/// the specified executable file, dynamic-link library (DLL), or icon
/// file.
/// </summary>
/// <param name="lpszFile">
/// Name of an executable file, DLL, or icon file from which icons will
/// be extracted.
/// </param>
/// <param name="nIconIndex">
/// <para>
/// Specifies the zero-based index of the first icon to extract. For
/// example, if this value is zero, the function extracts the first
/// icon in the specified file.
/// </para>
/// <para>
/// If this value is �1 and <paramref name="phiconLarge"/> and
/// <paramref name="phiconSmall"/> are both NULL, the function returns
/// the total number of icons in the specified file. If the file is an
/// executable file or DLL, the return value is the number of
/// RT_GROUP_ICON resources. If the file is an .ico file, the return
/// value is 1.
/// </para>
/// <para>
/// Windows 95/98/Me, Windows NT 4.0 and later: If this value is a
/// negative number and either <paramref name="phiconLarge"/> or
/// <paramref name="phiconSmall"/> is not NULL, the function begins by
/// extracting the icon whose resource identifier is equal to the
/// absolute value of <paramref name="nIconIndex"/>. For example, use -3
/// to extract the icon whose resource identifier is 3.
/// </para>
/// </param>
/// <param name="phIconLarge">
/// An array of icon handles that receives handles to the large icons
/// extracted from the file. If this parameter is NULL, no large icons
/// are extracted from the file.
/// </param>
/// <param name="phIconSmall">
/// An array of icon handles that receives handles to the small icons
/// extracted from the file. If this parameter is NULL, no small icons
/// are extracted from the file.
/// </param>
/// <param name="nIcons">
/// Specifies the number of icons to extract from the file.
/// </param>
/// <returns>
/// If the <paramref name="nIconIndex"/> parameter is -1, the
/// <paramref name="phIconLarge"/> parameter is NULL, and the
/// <paramref name="phiconSmall"/> parameter is NULL, then the return
/// value is the number of icons contained in the specified file.
/// Otherwise, the return value is the number of icons successfully
/// extracted from the file.
/// </returns>
[DllImport("Shell32", CharSet = CharSet.Auto)]
extern static int ExtractIconEx(
[MarshalAs(UnmanagedType.LPTStr)]
string lpszFile,
int nIconIndex,
IntPtr[] phIconLarge,
IntPtr[] phIconSmall,
int nIcons);
[DllImport("Shell32", CharSet = CharSet.Auto)]
extern static IntPtr SHGetFileInfo(
string pszPath,
int dwFileAttributes,
out SHFILEINFO psfi,
int cbFileInfo,
FileInfoFlags uFlags);
#endregion
/// <summary>
/// Two constants extracted from the FileInfoFlags, the only that are
/// meaningfull for the user of this class.
/// </summary>
public enum SystemIconSize : int
{
Large = 0x000000000,
Small = 0x000000001
}
/// <summary>
/// Get the number of icons in the specified file.
/// </summary>
/// <param name="fileName">Full path of the file to look for.</param>
/// <returns></returns>
static int GetIconsCountInFile(string fileName)
{
return ExtractIconEx(fileName, -1, null, null, 0);
}
#region ExtractIcon-like functions
public static void ExtractEx(string fileName, List<Icon> largeIcons,
List<Icon> smallIcons, int firstIconIndex, int iconCount)
{
/*
* Memory allocations
*/
IntPtr[] smallIconsPtrs = null;
IntPtr[] largeIconsPtrs = null;
if (smallIcons != null)
{
smallIconsPtrs = new IntPtr[iconCount];
}
if (largeIcons != null)
{
largeIconsPtrs = new IntPtr[iconCount];
}
/*
* Call to native Win32 API
*/
int apiResult = ExtractIconEx(fileName, firstIconIndex, largeIconsPtrs, smallIconsPtrs, iconCount);
if (apiResult != iconCount)
{
throw new UnableToExtractIconsException(fileName, firstIconIndex, iconCount);
}
/*
* Fill lists
*/
if (smallIcons != null)
{
smallIcons.Clear();
foreach (IntPtr actualIconPtr in smallIconsPtrs)
{
smallIcons.Add(Icon.FromHandle(actualIconPtr));
}
}
if (largeIcons != null)
{
largeIcons.Clear();
foreach (IntPtr actualIconPtr in largeIconsPtrs)
{
largeIcons.Add(Icon.FromHandle(actualIconPtr));
}
}
}
public static List<Icon> ExtractEx(string fileName, SystemIconSize size,
int firstIconIndex, int iconCount)
{
List<Icon> iconList = new List<Icon>();
switch (size)
{
case SystemIconSize.Large:
ExtractEx(fileName, iconList, null, firstIconIndex, iconCount);
break;
case SystemIconSize.Small:
ExtractEx(fileName, null, iconList, firstIconIndex, iconCount);
break;
default:
throw new ArgumentOutOfRangeException("size");
}
return iconList;
}
public static void Extract(string fileName, List<Icon> largeIcons, List<Icon> smallIcons)
{
int iconCount = GetIconsCountInFile(fileName);
ExtractEx(fileName, largeIcons, smallIcons, 0, iconCount);
}
public static List<Icon> Extract(string fileName, SystemIconSize size)
{
int iconCount = GetIconsCountInFile(fileName);
return ExtractEx(fileName, size, 0, iconCount);
}
public static Icon ExtractOne(string fileName, int index, SystemIconSize size)
{
try
{
List<Icon> iconList = ExtractEx(fileName, size, index, 1);
return iconList[0];
}
catch (UnableToExtractIconsException)
{
throw new IconNotFoundException(fileName, index);
}
}
public static void ExtractOne(string fileName, int index,
out Icon largeIcon, out Icon smallIcon)
{
List<Icon> smallIconList = new List<Icon>();
List<Icon> largeIconList = new List<Icon>();
try
{
ExtractEx(fileName, largeIconList, smallIconList, index, 1);
largeIcon = largeIconList[0];
smallIcon = smallIconList[0];
}
catch (UnableToExtractIconsException)
{
throw new IconNotFoundException(fileName, index);
}
}
#endregion
//this will look throw the registry
//to find if the Extension have an icon.
public static Icon IconFromExtension(string extension,
SystemIconSize size)
{
// Add the '.' to the extension if needed
if (extension[0] != '.') extension = '.' + extension;
//opens the registry for the wanted key.
RegistryKey Root = Registry.ClassesRoot;
RegistryKey ExtensionKey = Root.OpenSubKey(extension);
ExtensionKey.GetValueNames();
RegistryKey ApplicationKey =
Root.OpenSubKey(ExtensionKey.GetValue("").ToString());
//gets the name of the file that have the icon.
string IconLocation =
ApplicationKey.OpenSubKey("DefaultIcon").GetValue("").ToString();
string[] IconPath = IconLocation.Split(',');
if (IconPath[1] == null) IconPath[1] = "0";
IntPtr[] Large = new IntPtr[1], Small = new IntPtr[1];
//extracts the icon from the file.
ExtractIconEx(IconPath[0],
Convert.ToInt16(IconPath[1]), Large, Small, 1);
return size == SystemIconSize.Large ?
Icon.FromHandle(Large[0]) : Icon.FromHandle(Small[0]);
}
public static Icon IconFromExtensionShell(string extension, SystemIconSize size)
{
//add '.' if nessesry
if (extension[0] != '.') extension = '.' + extension;
//temp struct for getting file shell info
SHFILEINFO fileInfo = new SHFILEINFO();
SHGetFileInfo(
extension,
0,
out fileInfo,
Marshal.SizeOf(fileInfo),
FileInfoFlags.SHGFI_ICON | FileInfoFlags.SHGFI_USEFILEATTRIBUTES | (FileInfoFlags)size);
return Icon.FromHandle(fileInfo.hIcon);
}
public static Icon IconFromResource(string resourceName)
{
Assembly assembly = Assembly.GetCallingAssembly();
return new Icon(assembly.GetManifestResourceStream(resourceName));
}
/// <summary>
/// Parse strings in registry who contains the name of the icon and
/// the index of the icon an return both parts.
/// </summary>
/// <param name="regString">The full string in the form "path,index" as found in registry.</param>
/// <param name="fileName">The "path" part of the string.</param>
/// <param name="index">The "index" part of the string.</param>
public static void ExtractInformationsFromRegistryString(
string regString, out string fileName, out int index)
{
if (regString == null)
{
throw new ArgumentNullException("regString");
}
if (regString.Length == 0)
{
throw new ArgumentException("The string should not be empty.", "regString");
}
index = 0;
string[] strArr = regString.Replace("\"", "").Split(',');
fileName = strArr[0].Trim();
if (strArr.Length > 1)
{
int.TryParse(strArr[1].Trim(), out index);
}
}
public static Icon ExtractFromRegistryString(string regString, SystemIconSize size)
{
string fileName;
int index;
ExtractInformationsFromRegistryString(regString, out fileName, out index);
return ExtractOne(fileName, index, size);
}
}
}
Take a look at: http://mvolo.com/display-pretty-file-icons-in-your-aspnet-applications-with-iconhandler/
It's not the cleanest solution but it works. Otherwise, try to get your hands on a library of Icons that's based on mime type or file extension.
I'm sure you have already found a solution for your problems but for the benefit of others i have made some modifications to VirtualBlackFox's solution.
Just replace the IconFromExtension method...
public static Icon IconFromExtension(string extension,
SystemIconSize size)
{
// Add the '.' to the extension if needed
if (extension[0] != '.') extension = '.' + extension;
//opens the registry for the wanted key.
RegistryKey Root = Registry.ClassesRoot;
RegistryKey ExtensionKey = Root.OpenSubKey(extension);
ExtensionKey.GetValueNames();
RegistryKey ApplicationKey =
Root.OpenSubKey(ExtensionKey.GetValue("").ToString());
RegistryKey CurrentVer = null;
try
{
CurrentVer = Root.OpenSubKey(ApplicationKey.OpenSubKey("CurVer").GetValue("").ToString());
}
catch (Exception ex)
{
//current version not found... carry on without it?
}
if (CurrentVer != null)
ApplicationKey = CurrentVer;
//gets the name of the file that have the icon.
string IconLocation =
ApplicationKey.OpenSubKey("DefaultIcon").GetValue("").ToString();
string[] IconPath = IconLocation.Split(',');
IntPtr[] Large = null;
IntPtr[] Small = null;
int iIconPathNumber = 0;
if (IconPath.Length > 1)
iIconPathNumber = 1;
else
iIconPathNumber = 0;
if (IconPath[iIconPathNumber] == null) IconPath[iIconPathNumber] = "0";
Large = new IntPtr[1];
Small = new IntPtr[1];
//extracts the icon from the file.
if (iIconPathNumber > 0)
{
ExtractIconEx(IconPath[0],
Convert.ToInt16(IconPath[iIconPathNumber]), Large, Small, 1);
}
else
{
ExtractIconEx(IconPath[0],
Convert.ToInt16(0), Large, Small, 1);
}
return size == SystemIconSize.Large ?
Icon.FromHandle(Large[0]) : Icon.FromHandle(Small[0]);
}
Icon.ExtractAssociatedIcon()
won't do the trick. As MSDN states it only extracts icons contained in the file. So creating dummy files won't help either. To my best knowledge you have to go the p/invoke way to get to these icons. A question related to this is this. MaLio seems to have a quite complete example of how to get the icons with p/invoke.
I don't know of a platform agnostic way to do this either (and I don't think there is one).
Sorry i couldn't provide you with better news!
Instead of browsing through registry, one may use the IQueryAssociations interface. This interface can also be used to get more information about registered file types (see i.e. ASSOCSTR type). Below I attach code which replaces IconFromExtension method from VirtualBlackFox's solution (parts of his code are left untouched):
public static Icon IconFromExtension(string extension, SystemIconSize size)
{
if (extension[0] != '.') extension = '.' + extension;
object obj;
shell.AssocCreate(shell.CLSID_QueryAssociations, ref shell.IID_IQueryAssociations, out obj);
var qa = (shell.IQueryAssociations)obj;
qa.Init(shell.ASSOCF.INIT_DEFAULTTOSTAR, Convert.ToString(extension), UIntPtr.Zero, IntPtr.Zero);
var bufSize = 0;
qa.GetString(shell.ASSOCF.NOTRUNCATE, shell.ASSOCSTR.DEFAULTICON, null, null, ref bufSize);
var sb = new StringBuilder(bufSize);
qa.GetString(shell.ASSOCF.NOTRUNCATE, shell.ASSOCSTR.DEFAULTICON, null, sb, ref bufSize);
if (!String.IsNullOrEmpty(sb.ToString()))
{
var iconLocation = sb.ToString();
var iconPath = iconLocation.Split(',');
var iIconPathNumber = iconPath.Length > 1 ? 1 : 0;
if (iconPath[iIconPathNumber] == null) iconPath[iIconPathNumber] = "0";
var large = new IntPtr[1];
var small = new IntPtr[1];
//extracts the icon from the file.
ExtractIconEx(iconPath[0],
iIconPathNumber > 0 ? Convert.ToInt16(iconPath[iIconPathNumber]) : Convert.ToInt16(0),
large,
small, 1);
return size == SystemIconSize.Large
? Icon.FromHandle(large[0])
: Icon.FromHandle(small[0]);
}
return IntPtr.Zero;
}
In addition to the above code one needs "shell" class - a wrapper of Shell API (below it is limited to AssocCreate and necessary types):
using System;
using System.Runtime.InteropServices;
using System.Text;
#pragma warning disable 1591
// ReSharper disable InconsistentNaming
namespace <put_your_appropriate_namespace_here>
{
public class shell
{
[DllImport("shlwapi.dll")]
public extern static int AssocCreate(
Guid clsid,
ref Guid riid,
[MarshalAs(UnmanagedType.Interface)] out object ppv);
[Flags]
public enum ASSOCF
{
INIT_NOREMAPCLSID = 0x00000001,
INIT_BYEXENAME = 0x00000002,
OPEN_BYEXENAME = 0x00000002,
INIT_DEFAULTTOSTAR = 0x00000004,
INIT_DEFAULTTOFOLDER = 0x00000008,
NOUSERSETTINGS = 0x00000010,
NOTRUNCATE = 0x00000020,
VERIFY = 0x00000040,
REMAPRUNDLL = 0x00000080,
NOFIXUPS = 0x00000100,
IGNOREBASECLASS = 0x00000200,
INIT_IGNOREUNKNOWN = 0x00000400
}
public enum ASSOCSTR
{
COMMAND = 1,
EXECUTABLE,
FRIENDLYDOCNAME,
FRIENDLYAPPNAME,
NOOPEN,
SHELLNEWVALUE,
DDECOMMAND,
DDEIFEXEC,
DDEAPPLICATION,
DDETOPIC,
INFOTIP,
QUICKTIP,
TILEINFO,
CONTENTTYPE,
DEFAULTICON,
SHELLEXTENSION
}
public enum ASSOCKEY
{
SHELLEXECCLASS = 1,
APP,
CLASS,
BASECLASS
}
public enum ASSOCDATA
{
MSIDESCRIPTOR = 1,
NOACTIVATEHANDLER,
QUERYCLASSSTORE,
HASPERUSERASSOC,
EDITFLAGS,
VALUE
}
[Guid("c46ca590-3c3f-11d2-bee6-0000f805ca57"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
public interface IQueryAssociations
{
void Init(
[In] ASSOCF flags,
[In, MarshalAs(UnmanagedType.LPWStr)] string pszAssoc,
[In] UIntPtr hkProgid,
[In] IntPtr hwnd);
void GetString(
[In] ASSOCF flags,
[In] ASSOCSTR str,
[In, MarshalAs(UnmanagedType.LPWStr)] string pwszExtra,
[Out, MarshalAs(UnmanagedType.LPWStr)] StringBuilder pwszOut,
[In, Out] ref int pcchOut);
void GetKey(
[In] ASSOCF flags,
[In] ASSOCKEY str,
[In, MarshalAs(UnmanagedType.LPWStr)] string pwszExtra,
[Out] out UIntPtr phkeyOut);
void GetData(
[In] ASSOCF flags,
[In] ASSOCDATA data,
[In, MarshalAs(UnmanagedType.LPWStr)] string pwszExtra,
[Out, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 4)] out byte[] pvOut,
[In, Out] ref int pcbOut);
void GetEnum(); // not used actually
}
public static Guid CLSID_QueryAssociations = new Guid("a07034fd-6caa-4954-ac3f-97a27216f98a");
public static Guid IID_IQueryAssociations = new Guid("c46ca590-3c3f-11d2-bee6-0000f805ca57");
}
}
For WPF users, who need ImageSource:
Replace the return type from Icon to ImageSource and the return clause to:
var iconPtr = size == SystemIconSize.Large ? large[0] : small[0];
if (iconPtr != IntPtr.Zero)
{
return Imaging.CreateBitmapSourceFromHIcon(
iconPtr,
Int32Rect.Empty,
BitmapSizeOptions.FromEmptyOptions());
}
This class should do the job. Pass either a filename (with path) or folder name (with path).
public static class FileIcon
{
[DllImport("shell32.dll")]
private static extern IntPtr SHGetFileInfo(string pszPath, uint dwFileAttributes, ref SHFILEINFO psfi, uint cbSizeFileInfo, uint uFlags);
[StructLayout(LayoutKind.Sequential)]
private struct SHFILEINFO
{
public IntPtr hIcon;
public IntPtr iIcon;
public uint dwAttributes;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)]
public string szDisplayName;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 80)]
public string szTypeName;
};
private const uint SHGFI_ICON = 0x100;
private const uint SHGFI_LARGEICON = 0x0; // 'Large icon
private const uint SHGFI_SMALLICON = 0x1; // 'Small icon
public static System.Drawing.Icon GetLargeIcon(string file)
{
FileIcon.SHFILEINFO shinfo = new FileIcon.SHFILEINFO();
IntPtr hImgLarge = FileIcon.SHGetFileInfo(file, 0, ref shinfo, (uint)Marshal.SizeOf(shinfo), FileIcon.SHGFI_ICON | FileIcon.SHGFI_LARGEICON);
return System.Drawing.Icon.FromHandle(shinfo.hIcon);
}
public static System.Drawing.Icon GetSmallIcon(string file)
{
FileIcon.SHFILEINFO shinfo = new FileIcon.SHFILEINFO();
IntPtr hImgLarge = FileIcon.SHGetFileInfo(file, 0, ref shinfo, (uint)Marshal.SizeOf(shinfo), FileIcon.SHGFI_ICON | FileIcon.SHGFI_SMALLICON);
return System.Drawing.Icon.FromHandle(shinfo.hIcon);
}
}

Categories