Hello everyone new to stackoverflow.com. Not sure how would question be asked here, but doing my best. Done, quite some research on subject, but couldn't find a single thing to resolve the detection.
Example Case:
Trying to identify count of active AND disabled displays in Windows 7-10 systems.
Code GPU[Availability]:
private int MonCount;
[DllImport("User32.dll")]
private static extern bool EnumDisplayDevices(
string lpDevice, int iDevNum,
ref DISPLAY_DEVICE lpDisplayDevice, int dwFlags);
[StructLayout(LayoutKind.Sequential)]
public struct DISPLAY_DEVICE
{
public int cb;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
public string DeviceName;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 128)]
public string DeviceString;
public int StateFlags;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 128)]
public string DeviceID;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 128)]
public string DeviceKey;
public DISPLAY_DEVICE(int flags)
{
cb = 0;
StateFlags = flags;
DeviceName = new string((char)32, 32);
DeviceString = new string((char)32, 128);
DeviceID = new string((char)32, 128);
DeviceKey = new string((char)32, 128);
cb = Marshal.SizeOf(this);
}
}
public void MonitorCheck()
{
DISPLAY_DEVICE lpDisplayDevice = new DISPLAY_DEVICE(0); // OUT
DISPLAY_DEVICE monitor_name = new DISPLAY_DEVICE(0); // OUT
int devNum = 0;
while (EnumDisplayDevices(null, devNum, ref lpDisplayDevice, 0))
{
listBox1.Items.Add("\ndevNum =" + devNum);
listBox1.Items.Add("cb =" + lpDisplayDevice.cb);
listBox1.Items.Add("DeviceID =" + lpDisplayDevice.DeviceID);
listBox1.Items.Add("DeviceKey =" + lpDisplayDevice.DeviceKey);
listBox1.Items.Add("DeviceName =" + lpDisplayDevice.DeviceName.Trim());
listBox1.Items.Add("DeviceString =" + lpDisplayDevice.DeviceString.Trim());
// Show monitor name:
EnumDisplayDevices(lpDisplayDevice.DeviceName, 0, ref monitor_name, 0);
listBox1.Items.Add("Monitor name =" + monitor_name.DeviceString.Trim());
++devNum;
}
}
Source: C# how to get the Windows monitor name
Return: All attachable monitors by Graphic Adapter Information. Count is always as how many monitors can be attached in display adapter.
Code Active Displays:
Screen.AllScreens.Count();
Return: Active monitor count.
Problem:
If GPU Adapter has 3 outputs:
* Above GPU[Availability] code returns: 3
(no matter how many displays are attached to it.)
If 2 Monitors are attached (Example: Laptop Monitor + External HDMI Monitor), but display setup is set to: Laptop Monitor ONLY (External HDMI Monitor is not active) -> Active Displays code Returns: 1
Question:
How do I return 2 as there is non-active display and active display attached (Laptop Monitor + Non-active External Monitor)?
Solutions:
There's few ideas how to resolve this:
Activate all displays by extending them and simply redo: Screen.AllScreens.Count();
Idea:
Compare output of GPU[Availability] output and count all which gives any value as monitor_name.DeviceString.Trim() (However, I've tested this at desktop having NVIDIA adapter: outcome is null, but in laptop Intel HD adapter: output is always "Generic PnP-Monitor", so, this is no solution).
Stupid me... Never though to use all 3 methods to solve problem. (Seems damn hard to get code to work in code tags here at forums.
Maximum attachable displays
Currently attached displays
Currently Active displays
Solution:
Hmm, can't seem to be able to attach my code to stackoverflow reply, so:
Pastebin: Monitors counting
Related
I want to get user-friendly names of sound inputs with this code, but it can give me only first 32 chars of name, but I want it whole.
[DllImport("winmm.dll", SetLastError = true)]
static extern uint waveInGetNumDevs();
[DllImport("winmm.dll", SetLastError = true, CharSet = CharSet.Auto)]
public static extern uint waveInGetDevCaps(uint hwo, ref WAVEOUTCAPS pwoc, uint cbwoc);
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
public struct WAVEOUTCAPS
{
public ushort wMid;
public ushort wPid;
public uint vDriverVersion;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 64)]
public string szPname;
public uint dwFormats;
public ushort wChannels;
public ushort wReserved1;
public uint dwSupport;
}
public static string[] GetSoundDevices()
{
uint devices = waveInGetNumDevs();
string[] result = new string[devices];
WAVEOUTCAPS caps = new WAVEOUTCAPS();
using (StreamWriter sw = new StreamWriter("appdata/audio/name"))
{
for (uint i = 0; i < devices; i++)
{
waveInGetDevCaps(i, ref caps, (uint)Marshal.SizeOf(caps));
result[i] = caps.szPname;
sw.WriteLine(caps.szPname);
}
return result;
}
}
I need this names of sound inputs:
but this code give me only this:
Thank you guys!
You may have to use the management interface:
ManagementObjectSearcher objSearcher = new ManagementObjectSearcher(
"SELECT * FROM Win32_SoundDevice");
ManagementObjectCollection objCollection = objSearcher.Get();
foreach (ManagementObject obj in objCollection)
{
foreach (PropertyData property in obj.Properties)
{
Console.Out.WriteLine(String.Format("{0}:{1}", property.Name, property.Value));
}
}
Source: How to enumerate audio out devices in c#
You can use WASAPI to get full name of sound devices. It's only limit is that it is not available in Windows XP or older OS.
You need to use IMMDeviceEnumerator::EnumAudioEndpoints method to achieve the goal, but since the library is COM, you need to wrap it to be able to use it in C#.
A sample project is available on CodeProject.
I also couldn't find a way to get full name using WinMM library and finally end up writing a C++ wrapper around WASAPI and used that wrapper in C# through pinvoke!
Why screenshots in foreign and totally not understandable language?
There you go. English screenshot 1
You can copy it, that is what the second one means.
The number stands for the usb bus that is used.
When changing the plug keep that in mind.
I need to connect to a USB-device connected to the computer, I have looked around a bit and found some example projects, but cant get this to work. Everything runs nice, but I don't even get into the while-function.
Since SetupDiGetClassDevs is from the setupapi.dll I can't debug this function. I do have devices connected, so that is not my problem. Also hInfoSet does get size = 8.
public static HIDDevice FindDevice(int nVid, int nPid, Type oType)
{
string strPath = string.Empty;
string strSearch = string.Format("vid_{0:x4}&pid_{1:x4}", nVid, nPid); // first, build the path search string
Guid gHid;
HidD_GetHidGuid(out gHid); // next, get the GUID from Windows that it uses to represent the HID USB interface
IntPtr hInfoSet = SetupDiGetClassDevs(ref gHid, null, IntPtr.Zero, DIGCF_DEVICEINTERFACE | DIGCF_PRESENT); // this gets a list of all HID devices currently connected to the computer (InfoSet)
try
{
DeviceInterfaceData oInterface = new DeviceInterfaceData(); // build up a device interface data block
oInterface.Size = Marshal.SizeOf(oInterface);
// Now iterate through the InfoSet memory block assigned within Windows in the call to SetupDiGetClassDevs
// to get device details for each device connected
int nIndex = 0;
while (SetupDiEnumDeviceInterfaces(hInfoSet, 0, ref gHid, (uint)nIndex, ref oInterface)) // this gets the device interface information for a device at index 'nIndex' in the memory block
{
string strDevicePath = GetDevicePath(hInfoSet, ref oInterface); // get the device path (see helper method 'GetDevicePath')
if (strDevicePath.IndexOf(strSearch) >= 0) // do a string search, if we find the VID/PID string then we found our device!
{
HIDDevice oNewDevice = (HIDDevice)Activator.CreateInstance(oType); // create an instance of the class for this device
oNewDevice.Initialise(strDevicePath); // initialise it with the device path
return oNewDevice; // and return it
}
nIndex++; // if we get here, we didn't find our device. So move on to the next one.
}
}
finally
{
// Before we go, we have to free up the InfoSet memory reserved by SetupDiGetClassDevs
SetupDiDestroyDeviceInfoList(hInfoSet);
}
return null;
}
Have you tried calling Marshal.GetLastWin32Error and printing the value after the while loop if it's not entering? Make sure to enable SetLastError on SetupDiEnumDeviceInterfaces.
Afterwards, lookup your error code here:
http://msdn.microsoft.com/en-us/library/windows/desktop/ms681381%28v=vs.85%29.aspx
EDIT - After reading your comment, the structure should look something like this:
[StructLayout(LayoutKind.Sequential)]
struct GUID
{
public int a;
public short b;
public short c;
[MarshalAs(UnmanagedType.ByValArray, SizeConst=8)]
public byte[] d;
}
[StructLayout(LayoutKind.Sequential)]
struct SP_DEVICE_INTERFACE_DATA
{
public uint cbSize;
public GUID InterfaceClassGuid;
public uint Flags;
public IntPtr Reserved;
}
I am using a FileSystemWatcher to monitor a folder. But when there is some event happening in the directory, I don't know how to search who made a impact on that file. I tried to use EventLog. It just couldn't work. Is there another way to do it?
I cant remember where I found this code but its an alternative to using pInvoke which I think is a bit overkill for this task. Use the FileSystemWatcher to watch the folder and when an event fires you can work out which user made the file change using this code:
private string GetSpecificFileProperties(string file, params int[] indexes)
{
string fileName = Path.GetFileName(file);
string folderName = Path.GetDirectoryName(file);
Shell32.Shell shell = new Shell32.Shell();
Shell32.Folder objFolder;
objFolder = shell.NameSpace(folderName);
StringBuilder sb = new StringBuilder();
foreach (Shell32.FolderItem2 item in objFolder.Items())
{
if (fileName == item.Name)
{
for (int i = 0; i < indexes.Length; i++)
{
sb.Append(objFolder.GetDetailsOf(item, indexes[i]) + ",");
}
break;
}
}
string result = sb.ToString().Trim();
//Protection for no results causing an exception on the `SubString` method
if (result.Length == 0)
{
return string.Empty;
}
return result.Substring(0, result.Length - 1);
}
Shell32 is a reference to the DLL: Microsoft Shell Controls And Automation - its a COM reference
Here is some example's of how you call the method:
string Type = GetSpecificFileProperties(filePath, 2);
string ObjectKind = GetSpecificFileProperties(filePath, 11);
DateTime CreatedDate = Convert.ToDateTime(GetSpecificFileProperties(filePath, 4));
DateTime LastModifiedDate = Convert.ToDateTime(GetSpecificFileProperties(filePath, 3));
DateTime LastAccessDate = Convert.ToDateTime(GetSpecificFileProperties(filePath, 5));
string LastUser = GetSpecificFileProperties(filePath, 10);
string ComputerName = GetSpecificFileProperties(filePath, 53);
string FileSize = GetSpecificFileProperties(filePath, 1);
Or get multiple comma separated properties together:
string SizeTypeAndLastModDate = GetSpecificFileProperties(filePath, new int[] {1, 2, 3});
Note: This solution has been tested on Windows 7 and Windows 10. It wont work unless running in a STA as per Exception when using Shell32 to get File extended properties and you will see the following error:
Unable to cast COM object of type 'Shell32.ShellClass' to interface type 'Shell32.IShellDispatch6'
You need to enable auditing on the file system (and auditing is only available on NTFS). You do this by applying a group policy or local security policy. You will also have to enable auditing on the file you want to monitor. You do it the same way as you modify the permissions on the file.
Auditing events are then written to the security event log. You will have to monitor this event log for the auditing events you are interested in. One way to do this is to create a scheduled task that starts an application when the events you are interested in are logged. Starting a new process for each event is only viable if events aren't logged at a very high rate though. Otherwise you will likely experience performance problems.
Basically, you don't want to look at the contents or attributes of the file (which the shell function GetFileDetails does). Also, you don't want to use a file sharing API to get the network user that has the file open (which NetGetFileInfo does). You want to know the user of the process that last modified the file. This information is not normally recorded by Windows because it would require too many resources to do that for all file activities. Instead you can selectively enable auditing for specific users doing specifc actions on specific files (and folders).
It seems that you'll need to invoke Windows API functions to get what you want, which involves PInvoke. Some people on another forum have been looking into it and figured something out, you can find their solution here. However, it seems to work only with files on network shares (not on your local machine).
For future reference, this is the code posted by dave4dl:
[DllImport("Netapi32.dll", SetLastError = true)]
static extern int NetApiBufferFree(IntPtr Buffer);
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto, Pack = 4)]
struct FILE_INFO_3
{
public int fi3_id;
public int fi3_permission;
public int fi3_num_locks;
public string fi3_pathname;
public string fi3_username;
}
[DllImport("netapi32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
static extern int NetFileEnum(
string servername,
string basepath,
string username,
int level,
ref IntPtr bufptr,
int prefmaxlen,
out int entriesread,
out int totalentries,
IntPtr resume_handle
);
[DllImport("netapi32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
static extern int NetFileGetInfo(
string servername,
int fileid,
int level,
ref IntPtr bufptr
);
private int GetFileIdFromPath(string filePath)
{
const int MAX_PREFERRED_LENGTH = -1;
int dwReadEntries;
int dwTotalEntries;
IntPtr pBuffer = IntPtr.Zero;
FILE_INFO_3 pCurrent = new FILE_INFO_3();
int dwStatus = NetFileEnum(null, filePath, null, 3, ref pBuffer, MAX_PREFERRED_LENGTH, out dwReadEntries, out dwTotalEntries, IntPtr.Zero);
if (dwStatus == 0)
{
for (int dwIndex = 0; dwIndex < dwReadEntries; dwIndex++)
{
IntPtr iPtr = new IntPtr(pBuffer.ToInt32() + (dwIndex * Marshal.SizeOf(pCurrent)));
pCurrent = (FILE_INFO_3)Marshal.PtrToStructure(iPtr, typeof(FILE_INFO_3));
int fileId = pCurrent.fi3_id;
//because of the path filter in the NetFileEnum function call, the first (and hopefully only) entry should be the correct one
NetApiBufferFree(pBuffer);
return fileId;
}
}
NetApiBufferFree(pBuffer);
return -1; //should probably do something else here like throw an error
}
private string GetUsernameHandlingFile(int fileId)
{
string defaultValue = "[Unknown User]";
if (fileId == -1)
{
return defaultValue;
}
IntPtr pBuffer_Info = IntPtr.Zero;
int dwStatus_Info = NetFileGetInfo(null, fileId, 3, ref pBuffer_Info);
if (dwStatus_Info == 0)
{
IntPtr iPtr_Info = new IntPtr(pBuffer_Info.ToInt32());
FILE_INFO_3 pCurrent_Info = (FILE_INFO_3)Marshal.PtrToStructure(iPtr_Info, typeof(FILE_INFO_3));
NetApiBufferFree(pBuffer_Info);
return pCurrent_Info.fi3_username;
}
NetApiBufferFree(pBuffer_Info);
return defaultValue; //default if not successfull above
}
private string GetUsernameHandlingFile(string filePath)
{
int fileId = GetFileIdFromPath(filePath);
return GetUsernameHandlingFile(fileId);
}
This has been discussed many times. My answer from the same question:
You can't do this asynchronously with FileSystemWatcher, however you can do this synchronously using file system filter driver. The driver lets you get the user name of the account performing the operation.
Use code posted by dave4dl and update declare struct FILE_INFO_3 as following,
you can monitor user name of create and update file action(It is like to combination of FileSystemWatcher and OpenFiles.exe's functions of FileSharing Server)
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
public struct FILE_INFO_3
{
public int fi3_id;
public int fi3_permission;
public int fi3_num_locks;
[MarshalAs(UnmanagedType.LPWStr)]
public string fi3_pathname;
[MarshalAs(UnmanagedType.LPWStr)]
public string fi3_username;
}
here is my code :
public static string ReadListViewItem(IntPtr lstview, int item)
{
const int dwBufferSize = 1024;
int dwProcessID;
LV_ITEM lvItem;
string retval;
bool bSuccess;
IntPtr hProcess = IntPtr.Zero;
IntPtr lpRemoteBuffer = IntPtr.Zero;
IntPtr lpLocalBuffer = IntPtr.Zero;
IntPtr threadId = IntPtr.Zero;
try
{
lvItem = new LV_ITEM();
lpLocalBuffer = Marshal.AllocHGlobal(dwBufferSize);
// Get the process id owning the window
threadId = GetWindowThreadProcessId(lstview, out dwProcessID);
if ((threadId == IntPtr.Zero) || (dwProcessID == 0))
throw new ArgumentException("hWnd");
// Open the process with all access
hProcess = OpenProcess(PROCESS_ALL_ACCESS, false, dwProcessID);
if (hProcess == IntPtr.Zero)
throw new ApplicationException("Failed to access process");
// Allocate a buffer in the remote process
lpRemoteBuffer = VirtualAllocEx(hProcess, IntPtr.Zero, dwBufferSize, MEM_COMMIT,
PAGE_READWRITE);
if (lpRemoteBuffer == IntPtr.Zero)
throw new SystemException("Failed to allocate memory in remote process");
// Fill in the LVITEM struct, this is in your own process
// Set the pszText member to somewhere in the remote buffer,
// For the example I used the address imediately following the LVITEM stuct
lvItem.mask = LVIF_TEXT;
lvItem.iItem = item;
lvItem.iSubItem = 2;
lvItem.pszText = (IntPtr)(lpRemoteBuffer.ToInt32() + Marshal.SizeOf(typeof(LV_ITEM)));
lvItem.cchTextMax = 50;
// Copy the local LVITEM to the remote buffer
bSuccess = WriteProcessMemory(hProcess, lpRemoteBuffer, ref lvItem,
Marshal.SizeOf(typeof(LV_ITEM)), IntPtr.Zero);
if (!bSuccess)
throw new SystemException("Failed to write to process memory");
// Send the message to the remote window with the address of the remote buffer
SendMessage(lstview, LVM_GETITEMText, 0, lpRemoteBuffer);
// Read the struct back from the remote process into local buffer
bSuccess = ReadProcessMemory(hProcess, lpRemoteBuffer, lpLocalBuffer, dwBufferSize,IntPtr.Zero);
if (!bSuccess)
throw new SystemException("Failed to read from process memory");
// At this point the lpLocalBuffer contains the returned LV_ITEM structure
// the next line extracts the text from the buffer into a managed string
retval = Marshal.PtrToStringAnsi((IntPtr)(lpLocalBuffer +
Marshal.SizeOf(typeof(LV_ITEM))));
}
finally
{
if (lpLocalBuffer != IntPtr.Zero)
Marshal.FreeHGlobal(lpLocalBuffer);
if (lpRemoteBuffer != IntPtr.Zero)
VirtualFreeEx(hProcess, lpRemoteBuffer, 0, MEM_RELEASE);
if (hProcess != IntPtr.Zero)
CloseHandle(hProcess);
}
return retval;
}
no matter what i do retval returns empty, although lpLocalBuffer doesnt .
here is the def of ListItem :
[StructLayout(LayoutKind.Sequential)]
private struct LV_ITEM
{
public int mask;
public int iItem;
public int iSubItem;
public int state;
public int stateMask;
public IntPtr pszText;
public int cchTextMax;
public int iImage;
internal int lParam;
internal int iIndent;
}
i tried compiling for 86x , 64bit, any cpu , nothing seems to work at all !
any idea why this might be happening ?
C# + .net4 , windows 7 64bit.
Here's a different approach to doing this - use UI Automation. It does the cross-process, cross-bitness work for you, and will work against listviews, listboxes, or pretty much any other standard Windows UI. Here's a sample app that will get the HWND from the listview under the mouse pointer, and dump the items in it. It dumps just the name of each item; with Listviews, I think you can recurse into the fields in each item if you want.
// Compile using: csc ReadListView.cs /r:UIAutomationClient.dll
using System;
using System.Windows.Automation;
using System.Runtime.InteropServices;
class ReadListView
{
public static void Main()
{
Console.WriteLine("Place pointer over listview and hit return...");
Console.ReadLine();
// Get cursor position, then the window handle at that point...
POINT pt;
GetCursorPos(out pt);
IntPtr hwnd = WindowFromPoint(pt);
// Get the AutomationElement that represents the window handle...
AutomationElement el = AutomationElement.FromHandle(hwnd);
// Walk the automation element tree using content view, so we only see
// list items, not scrollbars and headers. (Use ControlViewWalker if you
// want to traverse those also.)
TreeWalker walker = TreeWalker.ContentViewWalker;
int i = 0;
for( AutomationElement child = walker.GetFirstChild(el) ;
child != null;
child = walker.GetNextSibling(child) )
{
// Print out the type of the item and its name
Console.WriteLine("item {0} is a \"{1}\" with name \"{2}\"", i++, child.Current.LocalizedControlType, child.Current.Name);
}
}
[StructLayout(LayoutKind.Sequential)]
private struct POINT
{
public int x;
public int y;
};
[DllImport("user32.dll")]
private static extern IntPtr WindowFromPoint(POINT pt);
[DllImport("user32.dll")]
private static extern int GetCursorPos(out POINT pt);
}
I know this is old, but I found it while trying to solve my problem and hopefully this will help someone else.
I used the recommendation in this question, that was in C++, and slightly modified the LV_ITEM structure to make it work with 64bit in VB.NET (I haven't tested in C# but I imagine the solution is quite similar.)
Public Structure LV_ITEM64
Public mask As Integer
Public iItem As Integer
Public iSubItem As Integer
Public state As Integer
Public stateMask As Integer
Public placeholder1 As Integer
Public pszText As Integer
Public placeholder2 As Integer
Public cchTextMax As Integer
Public iImage As Integer
End Structure
Then, when declaring the instance of the structure, I used the following code to choose between 64 bit and 32 bit structures:
Dim lvi As Object
If IntPtr.Size = 4 Then
lvi = New LV_ITEM
Else
lvi = New LV_ITEM64
End If
You have clarified that you are trying to read items from a list view control in a 32 bit process into a different 64 bit process.
I have seen many questions on this topic in various forums and not one ever seemed to achieve a successful outcome.
I think your best option is to create a 32 bit executable which will be able to read out of the other program's list view.
There is at least one obstacle to overcome if your program is 32-bit and the target program is 64-bit. Or the other way around. The LVITEM declaration will be wrong, IntPtr has the wrong number of bits. Which makes Marshal.SizeOf() return the wrong value. Alignment is okay, I think, by accident. Changing the field to either int or long can fix the problem, depending on the bitness of the target program. Which you can find out by looking at the Taskmgr.exe, Processes tab. The process name is post-fixed with "*32" if it is a 32-bit process. Or simply stay out of trouble by setting your project's Target platform setting to match the target process (x86 or AnyCPU).
Debug this by using Debug + Windows + Memory + Memory1. Put "lpLocalBuffer" in the Address box and observe what you see vs what your code reads. You should definitely be able to tell from the hex view that you got the string properly. Note that if you see zeros between the string characters then the target process uses the Unicode version of the list view. Marshal.PtrToStringUnicode is then required to read it.
Sorry my response is so late but I just came across the same issue. Here is the structure I used for VB.NET which works on both 32 and 64 bit systems.
<StructLayout(LayoutKind.Sequential, Pack:=1)> _
Public Structure LV_ITEM
Public Mask As UInteger
Public Index As Integer
Public SubIndex As Integer
Public State As Integer
Public StateMask As IntPtr
Public Text As String
Public TextLength As Integer
Public ImageIndex As Integer
Public LParam As IntPtr
End Structure
first question here. I'm developing a program in C# (.NET 3.5) that displays files in a listview. I'd like to have the "large icon" view display the icon that Windows Explorer uses for that filetype, otherwise I'll have to use some existing code like this:
private int getFileTypeIconIndex(string fileName)
{
string fileLocation = Application.StartupPath + "\\Quarantine\\" + fileName;
FileInfo fi = new FileInfo(fileLocation);
switch (fi.Extension)
{
case ".pdf":
return 1;
case ".doc": case ".docx": case ".docm": case ".dotx":case ".dotm": case ".dot":case ".wpd": case ".wps":
return 2;
default:
return 0;
}
}
The above code returns an integer that is used to select an icon from an imagelist that I populated with some common icons. It works fine but I'd need to add every extension under the sun! Is there a better way? Thanks!
You might find the use of Icon.ExtractAssociatedIcon a much simpler (an managed) approach than using SHGetFileInfo. But watch out: two files with the same extension may have different icons.
The file icons are held in the registry. It's a little convoluted but it works something like
Take the file extension and lookup
the registry entry for it, for
example .DOC Get the default value
for that registry setting,
"Word.Document.8"
Now lookup that
value in the registry.
Look at the
default value for the "Default Icon"
registry key, in this case,
C:\Windows\Installer{91120000-002E-0000-0000-0000000FF1CE}\wordicon.exe,1
Open the file and get the icon,
using any number after the comma as
the indexer.
There is some sample code at on CodeProject
I used the following solution from codeproject in one of recent my projects
Obtaining (and managing) file and folder icons using SHGetFileInfo in C#
The demo project is pretty self explanatory but basically you just have to do:
private System.Windows.Forms.ListView FileView;
private ImageList _SmallImageList = new ImageList();
private ImageList _LargeImageList = new ImageList();
private IconListManager _IconListManager;
in the constructor:
_SmallImageList.ColorDepth = ColorDepth.Depth32Bit;
_LargeImageList.ColorDepth = ColorDepth.Depth32Bit;
_SmallImageList.ImageSize = new System.Drawing.Size(16, 16);
_LargeImageList.ImageSize = new System.Drawing.Size(32, 32);
_IconListManager = new IconListManager(_SmallImageList, _LargeImageList);
FileView.SmallImageList = _SmallImageList;
FileView.LargeImageList = _LargeImageList;
and then finally when you create the ListViewItem:
ListViewItem item = new ListViewItem(file.Name, _IconListManager.AddFileIcon(file.FullName));
Worked great for me.
Edit: Here is a version without PInvoke.
[StructLayout(LayoutKind.Sequential)]
public 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;
};
public const uint SHGFI_ICON = 0x100;
public const uint SHGFI_LARGEICON = 0x0; // 'Large icon
public const uint SHGFI_SMALLICON = 0x1; // 'Small icon
[DllImport("shell32.dll")]
public static extern IntPtr SHGetFileInfo(string pszPath, uint dwFileAttributes, ref SHFILEINFO psfi, uint cbSizeFileInfo, uint uFlags);
[DllImport("User32.dll")]
public static extern int DestroyIcon(IntPtr hIcon);
public static System.Drawing.Icon GetSystemIcon(string sFilename)
{
//Use this to get the small Icon
IntPtr hImgSmall; //the handle to the system image list
//IntPtr hImgLarge; //the handle to the system image list
APIFuncs.SHFILEINFO shinfo = new APIFuncs.SHFILEINFO();
hImgSmall = APIFuncs.SHGetFileInfo(sFilename, 0, ref shinfo,
(uint)Marshal.SizeOf(shinfo), APIFuncs.SHGFI_ICON | APIFuncs.SHGFI_SMALLICON);
//Use this to get the large Icon
//hImgLarge = SHGetFileInfo(fName, 0,
// ref shinfo, (uint)Marshal.SizeOf(shinfo),
// Win32.SHGFI_ICON | Win32.SHGFI_LARGEICON);
//The icon is returned in the hIcon member of the shinfo struct
System.Drawing.Icon myIcon = (System.Drawing.Icon)System.Drawing.Icon.FromHandle(shinfo.hIcon).Clone();
DestroyIcon(shinfo.hIcon); // Cleanup
return myIcon;
}