0

I have been trying to construct a class to simply grab the current icons of special folders such as "My Computer" AKA "Computer", "Libraries", etc. I'm currently trying to use the SHGetFileInfo() API to achieve this.

The original SHGetFileInfo Function on Microsoft's page...

#1 Code Block

DWORD_PTR SHGetFileInfo(
  _In_     LPCTSTR pszPath,
  DWORD dwFileAttributes,
  _Inout_  SHFILEINFO *psfi,
  UINT cbFileInfo,
  UINT uFlags
);

(above referenced from: http://msdn.microsoft.com/en-us/library/windows/desktop/bb762179%28v=vs.85%29.aspx)

On another SO question I found this as an example...

#2 Code Block

IntPtr pidl;
SHGetSpecialFolderLocation(0, CSIDL_DRIVES, pidl);
SHGetFileInfo(pidl, 0, shinfo, Marshal.SizeOf(shinfo), (SHGFI_PIDL | SHGFI_DISPLAYNAME | SHGFI_ICON | SHGFI_SMALLICON));

(above referenced from: https://stackoverflow.com/a/14293350/1039753)

That describes to use SHGetFileInfo in a way that requires the first parameter to be an IntPtr. I was under the impression that the first parameter needed to be a string based off this...

#3 Code Block

[DllImport("shell32.dll", CharSet=CharSet.Auto)]
public static extern IntPtr SHGetFileInfo(string pszPath, uint dwFileAttributes, ref SHFILEINFO psfi, uint cbFileInfo, uint uFlags);

(above referenced from: http://pinvoke.net/default.aspx/shell32.SHGetFileInfo)

Furthermore, looking around SO I also found this answer...

#4 Code Block

[DllImport("Pdh.dll")]
static extern int PdhOpenQueryW(
    [MarshalAs(UnmanagedType.LPWStr)] string szDataSource, 
    UIntPtr dwUserData,
    out IntPtr phQuery);

(above referenced from: https://stackoverflow.com/a/3598417/1039753)

In the above answer the user tells the original poster to use...

#5 Code Block

[MarshalAs(UnmanagedType.LPWStr)] string

to map to the

#6 Code Block

"_In_     LPCTSTR pszPath,"

param. Found in the MSDN c++ constructor at the top. That makes more sense to me than any of it. But what I can't wrap my head around is the answer (#2 Code Block) above that tells the original poster to use an IntPtr when the pinvoke.net site says define it as a string.

Any idea how I can make SHGetFileInfo() work to get me that icon? Thanks!


UPDATE

Here is an example of how I tried to use it...

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

SHGetFileInfo((IntPtr)oTarget, 0, ref shfi, (uint)Marshal.SizeOf(shfi), flags);

Then I get two errors...

Error   1   The best overloaded method match for 'SHGetFileInfo(string, uint, ref SHFILEINFO, uint, uint)' has some invalid arguments   C:\FakePath\ImageWorker.cs  67  25  ImageWorker

Error   2   Argument 1: cannot convert from 'System.IntPtr' to 'string' C:\FakePath\ImageTool.cs    67  47  ImageWorker
Community
  • 1
  • 1
Arvo Bowen
  • 4,524
  • 6
  • 51
  • 109
  • 1
    `SHGetFileInfo` takes a string or a PIDL depending on whether the `SHGFI_PIDL` flag is set. – Jonathan Potter Jan 17 '15 at 20:21
  • The documentation is pretty clear. I can't see any evidence of you actually attempting to call the function. – David Heffernan Jan 17 '15 at 20:25
  • @JonathanPotter Even if I set that flag the first param of the function requires a string (the way I have it defined). So if I set the flag and then try to call the function I get the errors referenced in the example I just updated (above). Should I define the DllImport differently? If so please advice. Thanks! – Arvo Bowen Jan 17 '15 at 20:52
  • @DavidHeffernan : I don't really understand where you are really going with your comment but just for giggles I updated my question with an example where I tried to use it... – Arvo Bowen Jan 17 '15 at 20:53
  • So, what is `oTarget`? – David Heffernan Jan 17 '15 at 20:54
  • It is currently an object type that I pass into my function as either a string or a IntPtr... But I think I might have just figured it out after REreading the MSDN page for the 100th time... I'm I correct in assuming that I can pass the pointer as a string as long as I use the SHGFI_PIDL flag? – Arvo Bowen Jan 17 '15 at 20:58
  • If you set the `SHGFI_PIDL` flag then you need to pass a PIDL. – David Heffernan Jan 17 '15 at 21:08
  • OK for some reason this seems like it is turning into an Abbott and Costello routine... Please clear up a few things for me... 1) Knowing what I want to do, will my current DllImport listed above work and is it correct? 2) When you and @JonathanPotter say a PIDL are you talking about a variable that is of type IntPtr or String? - Thanks – Arvo Bowen Jan 17 '15 at 21:13
  • Your problem is not that we are being awkward, it's that you don't yet know what a PIDL is. It's not a string. It's a pointer. So you'd use `IntPtr`. But then you need to know how to make one. Which is a little tricky. You could start here: http://msdn.microsoft.com/en-us/library/windows/desktop/cc144089(v=vs.85).aspx Also remember that it's going to be hard for us to comment on your code when you present lots of code that is not used, and omit the code that is used. We cannot see how you initialised `shfi` or indeed what it is. – David Heffernan Jan 17 '15 at 21:17
  • OK, First I know EXACTLY what a PIDL is, I have one being passed into the function I created... The same function that I talk about passing a STRING or INTPTR into using oTarget. You have STILL not answered a question I have been asking since the start of this whole conversation... IS MY DLLIMPORT CORRECT? If you were trying to use a PIDL with the SHGetFileInfo() function would you use the same DllImport or a different one? That answer might have just solved it from the start. – Arvo Bowen Jan 17 '15 at 21:23
  • A PIDL is a pointer, so use `IntPtr`. So your pinvoke is wrong. Change the first arg to `IntPtr`. – David Heffernan Jan 17 '15 at 21:32
  • Thanks! :) What about passing a regular string into it? Should I leave that like it is to pass a string into it? – Arvo Bowen Jan 17 '15 at 21:36
  • I've added an answer. You want two overloaded p/invoke declarations. One for PIDL and the other for string. – David Heffernan Jan 17 '15 at 21:38

2 Answers2

3

How to get an icon example:

 [DllImport("shell32.dll", CharSet = CharSet.Auto)]
 public static extern IntPtr SHGetFileInfo(string pszPath, uint dwFileAttributes, ref SHFILEINFO psfi, uint cbFileInfo, uint uFlags);

 [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
 public struct SHFILEINFO
 {
     public IntPtr hIcon;
     public int iIcon;
     public uint dwAttributes;
     public string szDisplayName;
     public string szTypeName;
 };

SHFILEINFO shinfo= new SHFILEINFO();
SHGetFileInfo(@"...", 0, ref shinfo, (uint)Marshal.SizeOf(shinfo), 0x000000100); //SHGFI_ICON

Icon icon = (Icon)Icon.FromHandle(shinfo.hIcon);

//use the icon and finally destroy it with:

DestroyIcon(shinfo.hIcon); 
  • I was going to accept this as the answer simply for the example. I had a hard time deciding what the answer should be. But +1 for the example, just was not using the correct DLLImport as I asked for. ;) – Arvo Bowen Jan 18 '15 at 16:09
3

SHGetFileInfo is rather funky. The first parameter is either a pointer to null-terminated array of characters, or a PIDL. In unmanaged code the parameter is declared as a null-terminated array of characters. If you want to pass a PIDL then you are expected to perform a cast.

In a p/invoke call, you cannot use a cast. So instead you need to declare two overloaded p/invoke imports. One with string as the first parameter, and the other with IntPtr.

David Heffernan
  • 601,492
  • 42
  • 1,072
  • 1,490
  • I though you wanted to pass a PIDL – David Heffernan Jan 18 '15 at 16:09
  • I did, and you are 100% right with your answer! :/ I had a hard time deciding what the answer should be. I was just going to suggest an edit on the other answer to add the other DllImport as well... Can you add an example on your answer with both DllImports? ;) – Arvo Bowen Jan 18 '15 at 16:12
  • so you -1 my question for my mistake huh? :P – Arvo Bowen Jan 18 '15 at 16:15
  • Er, no, not me. I'm not sure we need yet another p/invoke declaration. You are obviously quite proficient and I don't think you need more than the high level. – David Heffernan Jan 18 '15 at 16:28