Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Newbie questions about COM

Tags:

c++

dll

com

I am quite new to COM so the question may seem naive.

Q1. About Windows DLL

Based on my understanding, a Windows DLL can export functions, types(classes) and global variables. Is this understanding all right?

Q2. About COM

My naive understanding is that: a COM DLL seems to be just a new logical way to organize the functions and types exported by a standard Windows DLL. A COM DLL exports both functions such as DllRegisterServer() and DllGetClassObject(), and also the Classes which implements the IUnknown interface. Is this understanding all right?

Q3. *.def & *.idl

*.def is used to define the functions exported by a Windows DLL in the traditional way, such as DllGetClassObject(). *.idl is used to define the interface implemented by a COM coclass.

Thanks in advance.

like image 946
smwikipedia Avatar asked Mar 27 '10 01:03

smwikipedia


3 Answers

Think of COM as a binary compatible way to share interfaces across DLL boundaries. C++ classes can't easily be exported from DLLs because of the non-standard name mangling done between various compiler versions. COM allows code in one DLL or executable, to create an implementation of an interface from another DLL or EXE as long as that implementation follows a defined interface and calling convention. This is what allows a COM class to be written in C# and invoked from C++, Python, and many other COM-aware languages.

COM interfaces are just standard C++ classes containing all pure virtual functions and derived from IUnknown. IUnknown is a pre-defined interface that all compliant COM interfaces must derive from that provides common facilities like reference counting and the ability to query whether an object implements a particular interface.

DLLs that wish to advertise the fact they can create implementations of COM interfaces do so by exporting 4 functions:

  • DllGetClassObject => Return a class factory of a requested interface
  • DllCanUnloadNow => Whether all instances handed out have since been released
  • DllRegisterServer => Register the types this DLL supplies in the registry
  • DllUnregisterServer => Unregister this DLL and its types from the registry

So to answer your questions:

Q1. About Windows DLL Based on my understanding, a Windows DLL can export functions, types(classes) and global variables. Is this understanding all right?

DLLs can export functions and classes (not sure about global variables, but you don't want to be exporting them DLLs even if you can! :-) ) However, exported classes will be name mangled and hence only usable by other DLLs or EXEs that happen to have the same name mangling (not a good way to do business). Functions with C-style calling convention are not name mangled and hence can be exported and called from elsewhere without problems.

Q2. A COM DLL exports both functions such as DllRegisterServer() and DllGetClassObject(), and also the Classes which implements the IUnknown interface. Is this understanding all right?

Halfway, there are 4 functions to export to be a full COM compliant DLL (shown above). You can search MSDN for any of those names to see the full signatures fro them. Your implementation for DllGetClassObject is going to be the main one which outside parties will use. They can use that to get an IClassFactory to an interface your DLL provides, and then use that to create an instance.

COM is a large and complicated beast, but its foundations are fairly straightforward. You can also check out this link COM Intro for some more info. Good luck!

like image 105
cpalmer Avatar answered Oct 06 '22 10:10

cpalmer


To add on to cpalmer's nice answer, there's no particular reason why you need to use the registry and the four recommended functions to export COM from your DLL.

You could use a registry-free COM, or COM-lite approach, where you simply export factory methods, such as

__declspec(dllexport) void MyDllCreateFoo(IFoo ** ppFoo);

DLL users would call your factory to create your class CMyFoo which implements IFoo. What DllRegisterServer et. al. do is allow CMyFoo and other classes to be looked up in the registry, among other things.

Q3: You are correct in fact, if not in spirit. .def files and .idl files are pretty different beasts. .def files are used by the linker only, and aren't even necessary - you can export all the functions you want using __declspec(dllexport) void foo() { }

inside your C++ code.

.idl files are used to generate C++ headers (.h files) which are included both by the DLL as well as its clients. It generates the interfaces plus some glue code that does things like parameter mashalling.

Again, you don't techincally need to use IDL to use COM, but it can make things more convenient.

The reason I'm breaking it down like this is to illustrate that COM is not a big monolithic thing, but rather a really small thing with a bunch of stuff built up around it that you could opt to use, or not, to taste.

like image 20
Drew Hoskins Avatar answered Oct 06 '22 10:10

Drew Hoskins


You have it all exactly right so far. The only bit I would clarify is "the Classes which implements the IUnknown interface." A class is represented by an object that implements the IClassFactory interface. You can get such a class factory by calling DllGetClassObject in a DLL, and then you can ask the class factory to make an object of the class. This is similar to many other object-oriented systems: there is a variety of objects that represent classes and which can be used to manufacture instances.

Other useful facts about COM:

  • There is a small but complicated library of helper functions, mostly named with the prefix Co. It is these which actually load the DLLs, locate the exported functions and call them. Client code would not normally call the DLL exports directly (although it is a fairly straightforward matter to write your own hosting system for simple in-process COM objects).
  • There is an important interface IDispatch which adds another layer of abstraction such that it is possible to dynamically identify methods to call on the object, using a string to find the method and passing an array of argument values. This allows scripting languages to call COM objects safely (i.e. in a way that tolerates mistakes by the programmer instead of crashing).
  • There is a more complex "marshalling" system for allowing objects to be called from other threads, other processes or even other computers; in its remote (and security-aware) for form it was called DCOM. It did not succeed on anything like the scale that other COM usages did.
  • The cross-process support was the basis of OLE2, a gimmick that every application rushed to support in the early 1990s, most of them implementing the (very complicated) interfaces wrongly and so crashing all over the place.
  • Far more successful were OLE Controls, which were technically a lot simpler (not using the marshalling) and which offered a way to extend Visual Basic.
  • There is at least one widely distributed COM-inspired system that is not compatible with COM but shares exactly the same concepts (at least the simple stuff), which is called XPCOM and is the basis of the Mozilla Firefox Web browser.
  • If you use COM pervasively, you will have a great way to integrate with the .NET framework, as it includes superb interoperability with COM. But you must follow the rules of COM precisely: AddRef/Release must work exactly as defined, so an object must never be destroyed while its reference count is greater than zero, it must be possible to use QueryInterface to find IUnknown from any interface, and from there to get back to the original interface, the IUnknown obtained from any interface must always have the same memory address for the lifetime of the object (so it can be used for identity comparison). So for example: don't create COM objects on the stack, and don't return separate objects from QueryInterface (that is, objects that have their own QueryInterface that returns different interfaces).
  • A major gotcha with COM is that it has at least two standard ways of representing strings. Neither uses reference counting. Why they never defined an IString interface is beyond me, but they didn't.
  • You'll go insane if you try to write COM code without smart pointers to hold references, or without a base class to help you implement interfaces, e.g. class MyClass : COM::Object<IThis, IThat> {...)
like image 34
Daniel Earwicker Avatar answered Oct 06 '22 11:10

Daniel Earwicker