Component Object Model (COM) is a Microsoft technology used for interoperability between Windows programs. D has built-in support for basic COM, and using its features, we can also automate the implementation of advanced COM features.
Here, we'll write a COM server and client with the help of a library and look at the implementation.
Download the Win32 bindings from http://dsource.org/projects/bindings and the comhelpers.d
file from http://github.com/adamdruppe/com.
COM, like shared libaries, uses a client-server model. First, we'll write a COM server and then write its corresponding COM client.
Let's write a COM server by executing the following steps:
win32.unknwn
and comhelpers
.IUnknown
and marking all methods extern(Windows)
. Attach the IID (interface's GUID) to the interface with the ComGuid
attribute. Each method should return HRESULT
.The code for the preceding steps is as follows:
import win32.basetyps; import win32.unknwn; import comhelpers; @ComGuid(GUID(0x00421140, 0, 0, [0xC0, 0, 0, 0, 0, 0, 0, 0x46])) interface IHello : IUnknown { extern (Windows) : int Print(); } // this is the class id that we can use to instantiate in the client and attach to the // class in the implementation enum GUID CLSID_Hello = GUID(0x30421140, 0, 0, [0xC0, 0, 0, 0, 0, 0, 0, 0x46]);
ComGuid
attribute.mixin ComObject!()
method in your class and implement your interface.IDispatch
when defining your class and add mixin IDispatchImpl!()
to the class body.mixin ComServerMain!(YourClass, "ProgId", "1.0")
class, where "ProgId"
is your desired Program ID (for example, "MSXML2.XmlHttp"
) and 1.0
is your version number. The program ID and version number are used by clients to locate your object.dmd –d
to enable deprecated features because the Win32 bindings are infrequently updated. The code for this is as follows:import win32.winuser; import comhelpers; import ihello; @ComGuid(CLSID_Hello) class CHello : IHello, IDispatch { mixin ComObject!(); mixin IDispatchImpl!(); extern(Windows) public override HRESULT Print() { import std.stdio; writeln("cool?"); MessageBoxA(null, "CHello.Print()", null, MB_OK); return NOERROR; } } mixin ComServerMain!(CHello, "Hello", "1.0");
yourname.def
) with the following contents:LIBRARY yourname EXETYPE NT SUBSYSTEM WINDOWS EXPORTS DllGetClassObject = _DllGetClassObject@12 DllCanUnloadNow = _DllCanUnloadNow@0 DllRegisterServer = _DllRegisterServer@0 DllUnregisterServer = _DllUnregisterServer@0
comhelpers.d
file together to generate your DLL.regsvr32 yourdll.dll
(this will require administrator access on the computer), or use a manifest file to enable registration-free COM in your client application.Let's write a COM client by executing the following steps:
comhelpers.d
and the interface file you created for the server.createObject!interface(CLSID)
. If you are using an object without the ComGuid
attribute, you can specify it manually in the compile-time argument list after the interface. Alternatively, you may call CoCreateInstance
from the Windows API directly.createObject
object of comhelper
returns a wrapper object that automatically handles calling AddRef
and Release
.The code is as follows:
import comhelpers; import ihello; void main() { auto obj = createObject!(IHello)(CLSID_Hello); obj.Print(); }
D has some built-in understanding of COM—it recognizes the IUnknown
interface. Anything derived from it is known to be a COM object. This is a bit rudimentary and D can do better with the help of a library—that's where comhelpers.d
comes in. It is not necessary, nor part of the standard library; you can always call the Windows functions yourself. It handles some boilerplate code (with mixin
templates) and binds GUIDs to interfaces and classes with a D feature called user-defined attributes to make the code easier to write.
3.14.145.82