DllImport vs ComImport
Asked Answered
P

2

5

I'm trying to wrap my head around the concepts of Platform Invocation Services, Component Object Model etc, but I find it hard to understand what is what and where different responsibilities lie. I've been studying code which contains the following:

[DllImport("shell32.dll", CharSet = CharSet.Unicode, PreserveSig = false)]
[return: MarshalAs(UnmanagedType.Interface)]
internal static extern object SHCreateItemFromParsingName([MarshalAs(UnmanagedType.LPWStr)] string pszPath, IBindCtx pbc, ref Guid riid);

[ComImport]
[Guid("43826D1E-E718-42EE-BC55-A1E261C37BFE")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
internal interface IShellItem

As I understand it, the former imports a function which creates shell items and the latter imports the type (interface) needed to create them.

What I don't understand is why one uses DllImport and the other ComImport. There is nothing in their respective documentation which states which method to use nor does the documentation provide the GUID for COM objects. My only guess is that the differentiating factor is that the former is a function and the latter an interface.

Preciado answered 15/10, 2017 at 12:6 Comment(1)
ComImport is optional in this case. You can remove it. It's really useful on COM coclasses (.NET class that you want to call C#'s new on it)Sulphide
C
6

IShellItem is a COM interface. COM in general does not compare that well to the [DllImport] way of calling external code. COM is much fancier, an interface declares an entire set of methods you can call, much as you'd do in the C# language with the interface keyword.

In practice you always need a concrete class that implements the interface, both in C# and in COM. Called a coclass in COM lingo, such classes are completely hidden from view. Often written in C++, a language that normally supports interop very poorly, but COM provides the protocol to make that C++ code callable from just about any language. Hiding the class implementation is the crucial ingredient, completely hiding the nasty little C++ details. Like constructors, memory management, inheritance, object layout, exceptions, features that port so poorly from one language to another.

You always need to a use a factory function to create the class object. A universal way is the one you are talking about, the CoCreateInstance() helper function is very commonly used. All you need to supply is the CLSID, the guid of the coclass. The COM runtime then takes care of finding the EXE or DLL that implements the coclass, loading it and obtaining the IClassFactory interface to get the object created. Having to register the COM server is crucial, keys in the registry tell it what executable file takes care of that job.

And then there is the secondary way, a factory function that is explicitly exported by the library, like SHCreateItemFromParsingName(). It avoids all the goo that I mentioned in the previous paragraph. Not as common, but not unusual, DirectX is another good example of a library that uses factory functions, like D3D11CreateDevice(). It is not very obvious exactly when Microsoft prefers a factory function over a coclass, other than perhaps discouraging using such a library from a scripting language.

If the interface would have been implemented by a coclass then you could have simply used new IShellItem() to create the object. Note the wonkiness of creating an instance of an interface, something that never makes sense in C#. But intentional for COM client code. The C# compiler generates the code under the hood to get the object factory plumbing going.

But since the SHCreateItemFromParsingName factory function is a normal exported function from a DLL, like any other, you now need to use [DllImport] to declare it.

Corticosteroid answered 15/10, 2017 at 14:26 Comment(2)
How does one know whether something is COM (IShellItem) or not (SHCreateItemFromParsingName) and how does one know when to use CoCreateInstance() and when to use a factory function? The documentation I pointed to does not indicate anything about how they should be implemented nor the GUID of the COM interface.Preciado
The words "interface" and "function" are good hints. COM programming is getting obscure so those hints are not necessarily good leads. There is an entire new generation of programmers that never knowingly wrote any COM client code or have any idea what it is all about. It is still hot and heavy however, UWP is entirely based on COM. But wrapped heavily, the language projection makes it invisible. Wrapping the scary bits in library with a nice C# style interface is pretty universal. Makes COM the assembly language programming skill of modern code.Corticosteroid
C
2

DllImport has nothing to do with ComImport.

DllImport defines essentially a function prototype in .NET in order to later call a function in a native DLL. This type of method invokation is generally called P/Invoke (short for Platform Invoke).

ComImport doesn't really import anything. It's a manual way for defining COM interfaces in c# and is typically used for custom / IUnknown interfaces or Automation-incompatible types.

My only guess is that the differentiating factor is that the former is a function and the latter an interface.

Well no, ComImport can be applied to classes too.

EDIT: I think I see what you are after now, see below

What's the big diff?

OK so at the end of the day we have two ways to potentially call native code (assuming for the moment our COM example is written in native code). Why have two different ways?

Well one big difference is how such methods (or functions) are exposed or advertised to the world.

Typical .dll files on Windows export their functions in the EXPORTS table in the .dll file. You can see what functions are being exported in a DLL by opening it in say Microsoft Dependency Viewer.

enter image description here

This includes things like:

  • PeekMessage in User32.dll
  • DeleteFile in Kernel32.dll and
  • our good friend SHCreateItemFromParsingName in shell32.dll

Now in order to call the above you must use [DllImport] as we have discussed.

COM is different. COM classes and methods are not listed in a EXPORTs table so it is imposible to [DllImport] / p-invoke them. COM relies heavily on the Windows Registry; COM Type Libraries or late-binding to get the job done.

COM can either be implemented in .EXEs or .DLLs (just to confuse things slighty). Opening up a COM .dll in dependency viewer only lists the COM registration/unregistration functions that all COM servers must implement.

e.g. Looking at mapishell.dll COM server (a part of MS Office) we see nothing (apart from registration) being exported even though we know there is functionality present.

enter image description here

More

If you want to know more about how COM works, take a look at my other answer here for more information.

Calida answered 15/10, 2017 at 12:38 Comment(3)
Could the SHCreateItemFromParsingName function be "imported" using ComImport as opposed to DllImport? I don't understand the difference in nature between those two things.Preciado
So if it's a function you just DllImport it and call it and if it's an abstract class (interface as far as C# is concerned) you need COM to manipulate it?Preciado
@Preciado noCalida

© 2022 - 2024 — McMap. All rights reserved.