Understanding COM Objects and how to declare them - c#

Say I want to create the interface for IMMDeviceEnumerator.
I see examples online showing the definition:
[ComImport]
[Guid("A95664D2-9614-4F35-A746-DE8DB63617E6")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
public interface IMMDeviceEnumerator
{
}
What I understand (maybe): The [ComImport] Attribute specifies that it's from a dll. The [Guid] attribute is the interface identifier.
What I don't understand: How was this GUID value obtained? What does the [InterfaceType] attribute do? How do I fill in the rest of the functions?
I'm so lost trying to figure this stuff out, all the documentation is super opaque.

How was this GUID value obtained?
The GUID is created as part of the COM interface definition; since you're trying to call someone else's object - you need to use their GUID. You can find it in the mmdeviceapi.h the MMDevice docs point to.
Header file Mmdeviceapi.h defines the interfaces in the MMDevice API.
MIDL_INTERFACE("A95664D2-9614-4F35-A746-DE8DB63617E6")
IMMDeviceEnumerator : public IUnknown
The normal way to do this is to add a reference to the COM dll or run tlbimp.exe which will generate a COM Class Wrapper for you with all the magic goo.
If a COM type library isn't available, though - then you basically have to do it yourself by going through the IDL file.
Like p/invoke signatures, this can get pretty painful - so best to use an existing one if you can.
For the larger questions of COM interop, it basically requires learning a little bit of COM and being familiar with C#. The general docs are out there, but usually if you're just trying to use a well known COM component you're best off using a library if you can.

You create the GUID yourself. There are generators online if you don't want to assign one yourself.
All interface types should derive from IUnknown.
Update: here is a generator. https://www.guidgenerator.com/online-guid-generator.aspx
They use the same one because IMMDeviceEnumerator has already been defined with that specific GUID. If you create your own interface, you will create your own GUID.
You derive off IUnknown because
"Within that constraint, your custom interface can support almost any method or parameter, including asynchronous methods. You can also generate a type library for your custom interfaces so that clients can access information about your object's methods at run time. "

Related

Explicit interface implementation for COM interfaces in C#

Some time I ago I was working on a major refactoring of an old Win32 program implemented with COM, and there were various parts that were implemented with C# (.NET). During my work on this project, I ran across a Microsoft page on COM programming in C# that recommended C# classes explicitly implement COM interfaces, rather than implicity. I recently tried to remember why, and I couldn't. I also couldn't find the page on the MSDN site again. Can anybody please tell me why Microsoft might recommend this ?
Hmm, that makes a wee bit of sense, COM is pure interface-based programming and the actual implementation of the interfaces should be hidden. Implementing interface methods explicitly gets you that automatically because they cannot be public.
Actually doing this is quite pointless, you could (and should) simply apply the [ClassInterface(ClassInterfaceType.None)] attribute to the class. That by itself ensures that the implementation isn't exposed, only the interfaces implemented by the class are visible. Implementing the interface methods explicitly isn't actually good enough. Because you cannot hide the fact that your class inherits System.Object. Which exposes the four public methods of Object and puts a reference to mscorlib.tlb in your type library, a reference that a real COM client will never use. It will almost always work because the odds that the compiler that uses your class runs on a machine that doesn't have .NET installed are pretty small. But very yucky nonetheless, it isn't actually required. Only the machine that uses the class needs it installed.
Just don't do this. Declare the interfaces you implement, give them the [InterfaceType(ComInterfaceType.InterfaceIsDual)] attribute to allow them to be used both early and late bound. And hide the actual implementation of them with [ClassInterface(ClassInterfaceType.None)]. Only sensible way.
It's old, but from here: http://msdn.microsoft.com/en-us/library/aa288461%28v=VS.71%29.aspx they mention implementing an interface explicitly so you can implement multiple interfaces that have the same member names.
This also requires that the user of your class cast an instance of your class to the appropriate interface.
As for why this is especially important for COM: my first guess is so that COM can call one set of methods while managed code may call another. However, I'm guessing here.

What is the best way to deal with kludgy interface hierarchies (MSHTML)?

I'm using the MSHTML API from C# 4.0 and the logistics of running code are not a problem. Writing the code, however, is a pain due to the way that MSHTML and/or COM interfaces are designed. Specifically, there is no interface hierarchy when there should be one. For example, IHTMLDocument7 does not extend IHTMLDocument6, which doesn't extend IHTMLDocument5, and so on (IHTMLDocument2 does extend IHTMLDocument, though).
To further confuse matters there is an HTMLDocument interface that extends DispHTMLDocument (which has all of the methods of the IHTMLDocument* interfaces) and HTMLDocumentEvents_Event (which provides some, but not all, events). To add to the mess, HTMLDocumentClass is a coclass that implements all of the aforementioned interfaces and then some, such as IDocumentSelector and HTMLDocumentEvents4_Event.
I'd really like to be able to work with the API of HTMLDocumentClass, but trying to cast to it gave me:
System.InvalidCastException: Unable to
cast COM object of type
'mshtml.HTMLDocumentClass' to class
type 'mshtml.HTMLDocumentClass'.
Instances of types that represent COM
components cannot be cast to different
types that represent COM components;
however they can be cast to interfaces
as long as the underlying COM
component supports QueryInterface
calls for the IID of the interface.
In addition, some of the interfaces don't have an associated coclass; e.g., there are IHTMLElement* interfaces but no HTMLElement interface nor a HTMLElementClass class. Overall, I am finding it difficult to program to an interface.
Are there good techniques for wrangling with this interface train wreck, or should I give up IntelliSense and use dynamic everywhere? I considered writing wrapper classes that implemented all of the interfaces, but there are so many MSHTML interfaces and each of them has a ton of members so a practical solution has to be automated.
IHTMLDocument6 doesn't extend IHTMLDocument5
Even if it extends IHTMLDocument5, per COM rules, you are still supposed to QueryInterface to get IHTMLDocument5, not to use inheritance. I am glad that they did not let you wonder how you can QI for an interface that is already implemented by the wrapper class as a side effect of inheritance.
I suggest you to not use any of the wrapper classes and switch to backward compatible interfaces when you control the objects. The COM wrapper CLR generated for IE looks like a mshtml.HTMLDocumentClass class from a different assembly, based on the error message.
In COM programming you would see the factory pattern quite often. For the html element object, the factory method is IHTMLDocument2.createElement. Usually you can not create the object on your own if the author choose to use this pattern.
Visual Studio would automatically reference the PIA if one exists, otherwise it uses tlbexp.exe to generate interop assembly prefixed with "Interop". However most of time you would be using a handful interfaces in the PIA, so you can write your own interop types (or copy from Google Code Search) and get ride of this big assembly.

Adding an in-proc COM Server object to a C# application

I am new to COM and need to add a Server COM object to my c# application so I can call its methods and implement events. The documentation I have says this requires a COM-aware language such as Visual C++. My app is written in C# so I'm not sure how this is going to work. Any direction would be appreciated.
I am writing an app that communicates with a serial hypercom terminal. The device came with a DLL (the com server interface) that I will need to figure out how to use in my c# application.
I added a reference to the DLL to my project, which shows up as a type library. In object explorer I get interfaces, and some classes etc.
Thanks,
Matt
You can add the COM object as a reference. .NET will create an interop assembly to work with the COM object, just like it was a .NET type.
CComObjectRoot can be used as a base class for all COM objects implemented with ATL. As such you don't have to worry to implement something similar in C#, the required methods (AddRef, Release, QueryInterface) will be generated by tlbexp for classes that are tagged with ClassInterface.
STDMETHODIMP is a define which serves to declare the most common methods that can be called from COM (#define STDMETHODIMP HRESULT STDMETHODCALLTYPE). Again if your class is tagged with ClassInterface you will not have to worry about.
Such construction is required in C++ when your class implements several interfaces. I think this is not required if you tell C# compiler that your C# object implement IDispatch, IFPESOlementationEvents. The appropriate code will be written automatically by the compiler.
Probably everything will not make much sense if you are new to COM and C#, I'll suggest to take a look at the various introduction that you may find on the web, like this.

Using a COM dll from C# without a type library

I need to use a COM component (a dll) developed in Delphi ages ago. The problem is: the dll does not contain a type library... and every interop feature (eg. TlbImp) in .NET seem to rely on TLBs. The component has been used in Delphi programs here for many years without problems because "It's not much of a problem using COM objects from Delphi, because we know the interfaces" (quote Delphi developer).
Is there any way I can use this DLL from c# without a TLB? I've tried using the DLL as unmanaged, but the only method it exports are DllUnregisterServer, DllRegisterServer, DllCanUnloadNow and DllGetClassObject. I know the names of the classes and functions I'm going to use, if that can be of any help.
UPDATE:
I've tried implementing Jeff's suggestion, but I'm getting this error:
"Unable to cast COM object of type 'ComTest.ResSrvDll' to interface type 'ComTest.IResSrvDll'. This operation failed because the QueryInterface call on the COM component for the interface with IID '{75400500-939F-11D4-9E44-0050040CE72C}' failed due to the following error: No such interface supported (Exception from HRESULT: 0x80004002 (E_NOINTERFACE))."
This is what I've done:
I got this interface definition from one of the Delphi-guys:
unit ResSrvDllIf;
interface
type
IResSrvDll = interface
['{75400500-939F-11D4-9E44-0050040CE72C}']
procedure clearAll;
function ResObjOpen(const aClientID: WideString; const aClientSubID: WideString;
const aResFileName: WideString; aResShared: Integer): Integer; {safecall;}
...
end;
implementation
end.
From this I've made this interface
using System.Runtime.InteropServices;
namespace ComTest
{
[ComImport]
[Guid("75400500-939F-11D4-9E44-0050040CE72C")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
public interface IResSrvDll
{
int ResObjOpen(string aClientID, string aClientSubID, string aResFileName, int aResShared);
}
}
And this coclass (got the guid from the delphi-guys)
using System.Runtime.InteropServices;
namespace ComTest
{
[ComImport]
[Guid("75400503-939F-11D4-9E44-0050040CE72C")]
public class ResSrvDll
{
}
}
UPDATE
The solution from Jeff is the way to do it. It is worth noticing, though, that the interface definition must match the COM-components exactly! ie. same order, same names, etc.
You just need the CLS_ID and interface id. I wrote about this specific issue on my blog:
"Using Obscure Windows COM APIs in .NET"
Write a wrapper in VB.Net. VB.Net supports true late binding (no messy reflection). All you need is the progId. You should also implement IDisposable to explicitely manage the component lifecycle.
It is quite frequent that you will encounter an interface implementation that is not backed by a type library (Delphi or otherwise). Shell extensions are one example.
You basically need to make a Windows API call to create the instance through the proper COM function calls. The API will take care of managing the DLL via the exported functions you mentioned earlier.
You will need to recreate the interface definition in C# code but after that you simply create the object, cast it to the interface, and it is no different than anything else. The only real caveat here is, depending on your usage, you may have some threading issues to deal with so check the "threading model" that was used for the DLL and consider your usage based on that.
Here is a link to a tutorial on consuming the interfaces that are not TLB based.
Tutorial
If you've managed to create an instance of the object, you're over the first major hurdle!
Now try this:
myObject.GetType().InvokeMember(
"ResObjOpen", // method name goes here
BindingFlags.InvokeMethod,
null,
myObject,
new object[] {
someClientID, // arguments go here
someSubId,
somFileName,
someInt} );
The reason I think you may need to do this is if the Delphi COM object is not a "dual" object. It may only support late binding, i.e. the kind of invocation you see above.
(In C# 4.0 they're making this easier with the dynamic keyword.)
EDIT: Just noticed something very suspicious. The IID for the interface and the CLSID for the object itself appear to be the same. That's not right.
Given that you've succeeded in creating the object, it would appear to be the CLSID of the object. So it's not the right IID. You need to go back to your Delphi folks and ask them to tell you what the IID of the interface IResSrvDll is.
Edit again: You could try changing the enum member you specify from ComInterfaceType. There should be ones for IDispatch and "dual" - although as your object doesn't support IDispatch, neither of those should be the right choice. The IUnknown setting (which appears in your sample code) should work - suggesting that the IID is wrong.
Yes and no.
All C# (and any CLR language) needs in order communicate with a COM object is a compatible interface signature. Typically specifying the methods, GUID and apartment style of the interface. If you can add this definition into your code base then the TLB is not necessary.
There is a small caveat that comes with that statement. I believe you will get into trouble if you try and use a COM object across apartment boundaries and don't have a suitable TLB registered. I can't 100% remember on this point though.
You can also do late binding and then invoke methods through reflection (myObject.InvokeMember("NameOfTheMethod", options, params, etc.)).
A wrapper should, however, offer better performance and faster marshalling.
I suspect that the dynamic keyword (C# 4.0) will accomplish this. If it does, it will give results which are largely equivalent to invoking the methods, i.e. how Groo suggests.

Exposing C# via COM for C++ Client

we're considering exposing some C# types to C++ clients via COM. What problems can we expect to hit over the life of the project? E.g. how will versioning be managed?
On versioning, it would seem from reading this that we should decorate our types to be exposed with [ClassInterface(ClassInterfaceType.None)] and use an explicit interface. That way I assume we fully control the interface that will be exposed to COM clients.
Thanks in advance.
Since you are using a C++ client you should definitely use explicit interfaces for early binding. Dispatch interfaces are useful when using scripting clients such as VBS but they are rarely useful for C++ clients.
The only way to version an interface is to create a new interface (possibly inheriting from the original interface). When using explicit interfaces you have full control over this process.
This means you should create an interface for every class that you intend to expose via COM. Don't forget to mark every interface and class with the ComVisible and Guid attributes. Also all your classes must have a default constructor.
You'll have to read about the GUID attribute (including this) to maintain binary compatibility and only rebuild the clients when necessary.
Also you might be interested in the ComVisible attribute that helps reduce registry pollution.
To get full control over COM interfaces, define them in MIDL. Build a type library with those interfaces in a C++ project, then import type library to C# and implement interfaces.
This approach is useful with complex interfaces where marshaling is not trivial.
Versions should be done COM-style, changing GUIDs and adding new or inheriting interfaces.

Categories