P/Invoke with Shell32, bypass Interop.Shell32.dll generation
Asked Answered
C

2

3

So I'm using the following code to dump every extended file attribute to the debug console...

ShellClass shellClass = new ShellClass();
Folder dir = shellClass.NameSpace(Path.GetDirectoryName(sFilePath));
FolderItem item = dir.ParseName(Path.GetFileName(sFilePath));
for (int i = 0; i < 267; i++)
{
   System.Debug.WriteLine(dir.GetDetailsOf(item, i));
}

As Shell32 is a non-managed library, I can't embed the DLL resource in the program directly, and it has to generate an interop helper DLL.

Anyone know of the DLLImport command lines for these functions in Shell32 to get around the whole generation of the interop DLL? I've checked pinvoke.net and under the Shell32 entry they either don't have what I'm looking for, or it's not named what I'm expecting.

If it's not possible to use a DLLImport to accomplish this, does anyone know of a way to pull all of the extended attributes about a file using a managed DLL, or at least a way to do it where I don't have to generate a helper DLL and require an installer with an application I'm developing?

Thanks a bunch!

Cockneyism answered 30/4, 2012 at 20:38 Comment(2)
Are you aware of the embedded interop feature in .NET 4+? msdn.microsoft.com/en-us/library/dd997297.aspxRosettarosette
Unfortunately, I'm stuck using 3.5, but that's a cool feature, gotta bookmark that for later.Cockneyism
S
4

Most of the Windows Shell API features have P/Invoke entry points that return COM interfaces, so you should rarely need to explicitly create a ShellClass CoClass instance. (The objects you are using, Folder and FolderItem, are mostly meant for use by late-bound clients such as script languages; with C# you can do much better.)

To avoid having to generate the interop assembly, you just need to declare the appropriate types in your C# code that match the P/Invoke functions and COM interfaces that the shell already implements. In your case, what you're looking for is the IShellFolder2 interface (which is essentially identical to the Folder object).

Typically, to do what you're asking, you would:

  1. Call SHGetDesktopFolder to return an IShellFolder interface pointing to the desktop folder.
  2. Call IShellFolder::ParseDisplayName on the folder you want, which gives you a "pointer to an Identifier list" (a PIDL) representing that folder's internal shell identifier.
  3. Cast your IShellFolder to an IShellFolder2 (using as, like any other interfaces).
  4. Call IShellFolder2::GetDetailsOf(), passing the PIDL from step 2 and the column you want.

You will need to translate the two COM interfaces, plus one structure and one P/Invoke function, and put the C# code in your project. You can get away with IntPtr for a lot of this (like the PIDL) since you only need to pass them around between COM methods. (The MSDN articles will tell you that you are responsible for freeing memory after you are done; the CLR's interop code will take care of that stuff for you in almost all cases.)

pinvoke.net will help you out here, as will this fascinatingly well written series of blog posts on doing COM interop translations from the MSDN articles.

Stelle answered 30/4, 2012 at 21:17 Comment(8)
Yikes! I'm gonna go ahead and mark that as the answer, as it is an appropriate solution, but I'm thinking the introduction of an extra 1k+ lines of code to implement all that is far more work than packaging up the DLL in an installer.Cockneyism
Late bound COM is all wrong from C#. Really, don't do that. And the extra code already exists. You just need to use your favourite search engine to find it.Babbitt
it's not that much code; maybe a hundred lines of copy/paste from p/invoke.net gives you the interfaces (though I'm not really sure I like how IShellFolder got translated, it'll certainly work). The actual code to get the properties is almost a line-for-line translation from what you have. Gimme some time and I will try to do a working sample.Stelle
I fiddled with it a bit last night and I couldn't get anything overly elegant, the handful of interfaces I was looking at to implement were ~100 lines a piece, and the properties code itself was a hideous mashup of object conversions that itself was 100+ lines. I'll be the first to admit I'm fairly inexperienced in this area, so I'll be overjoyed at any and all assistance you guys can give me! And as a side edit, I can't find out what's particularly evil about late bound COM, is there something specifically bad about using it?Cockneyism
@LordSkitch Late bound COM? No type safety. Poor documentation in comparison to early bound. Slower to call (often irrelevant). Often harder to call.Babbitt
@Michael I've just remembered the API CodePack. I'm pretty sure that's exactly what is needed here.Babbitt
that looks like it might be useful here, I've never used it; I always roll my own interop because I don't always like the options other pre-built translations choose.Stelle
And, I have to admit, I started translating IShellFolder for myself and it's rather more complex than I expected (those PIDLs are a total pain.)Stelle
B
4

In my experience, the most effective way to use the shell API from managed code is to use the Windows API Code Pack. This wraps up all the useful parts of the shell API and makes it trivially accessible from managed code. The download includes lots of examples to get you on the way.

Babbitt answered 2/5, 2012 at 0:29 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.