COM is an extremely important technology on Windows. A lot of functionality rely on COM under the hood.
Also millions of ASP Classic and VB6 devcelopers used COM for their applications.
But relative few understand what COM really is.
COM is a somewhat obsolete technology - .NET provide alternative approaches for most functionality. But there are still old stuff around and cases where COM is needed for integration, so it can still be worth learning.
This article tries to explain COM from a developer perspective.
This article is heavily inspired by the book "COM and ATL 3.0" by Andrew Troelsen. The intention is not to plagiarize but to provide a shorter version with more modern examples (the book is from 2000 and the IT landscape has changed quite a bit since then).
Some familiarity with Windows and OOP in general are expected.
COM stands for Component Object Model.
COM is a technology that allows a program written in one language to easily call a component written in another language.
COM was created by Microsoft in 1993.
All COM components implements the interface IUnknown and has a factory implementing IClassFactory.
All scriptable COM components aka COM components callable from script languages implement the interface IDispatch socalled late binding.
So if we have a COM interface IXxxx, an implementation CoXxxx and a factory CoXxxFactory then class diagram looks like:
COM mostly use Windows registry to store lookup information (see Registry free for an alternative approach). Note that it requires privs to register a COM component.
Items get identified by GUID's.
The program calling the COM component is called the client and the COM component is called the server. That may sound weird at first, but note that COM is a general concept and it besides making an in-process call then it is also possible to call a COM component hosted in a separate process. And then the terms client and server suddenly makes more sense.
The COM interface can be described in an MIDL file and language specific headers can be generated.
All examples will show in-process usage.
COM components can be written in many languages. But all examples will use C++ for the COM component itself. That makes sense because most COM components are written in C++.
Clients will be shown in many languages:
(the original Microsoft Java from the late 1990's had builtin support for COM, but that is long gone - today one use a library that use JNI to call COM - and Jacob is one of the common libraries for this purpose)
Everything will be done on 64 bit Windows as 32 bit code. That is probably the most common today for COM (the platform switched from 32 to 64 bit, but the COM stuff remained at 32 bit.
The client EXE and the COM DLL of course need to be the same bitwise, so here both will be 32 bit.
Note that moving to 64 bit will only mean to change the compiler setting and change the registry keys.
The example will evolve over 12 iterations.
There will be a lot of repeated code between iterations. But the concept is that each iterations section has complete code to avoid jumping forth and back.
In this iteration we will:
We use T strings in the code, because that common practice even though probably not relevant any longer (T is Unicode on WinNT and CP-1252 on Win9x).
Note that COM use reference counting. One calls AddRef when starting usage and calls Release when done and if count goes in zero then the object gets deleted.
Test.h:
#ifndef TEST_H
#define TEST_H
#include <windows.h>
// { 2f2d7587-78e8-46ee-ba65-fc7094b37a13 }
DEFINE_GUID(IID_ITest, 0x2f2d7587, 0x78e8, 0x46ee, 0xba, 0x65, 0xfc, 0x70, 0x94, 0xb3, 0x7a, 0x13);
// { 2c7de05d-a083-40d7-b7af-90af8d432489 }
DEFINE_GUID(CLSID_CoTest, 0x2c7de05d, 0xa083, 0x40d7, 0xb7, 0xaf, 0x90, 0xaf, 0x8d, 0x43, 0x24, 0x89);
interface ITest : public IUnknown
{
virtual HRESULT __stdcall Add(long int a, long int b, long int *c) = 0;
};
class CoTest : public ITest
{
private:
LONG volatile m_refcnt;
public:
CoTest();
virtual ~CoTest();
// IUnknown
HRESULT __stdcall QueryInterface(REFIID riid, void **ppv);
ULONG __stdcall AddRef();
ULONG __stdcall Release();
// ITest
HRESULT __stdcall Add(long int a, long int b, long int *c);
};
class CoTestFactory : IClassFactory
{
private:
LONG volatile m_refcnt;
public:
CoTestFactory();
// IUnknown
HRESULT __stdcall QueryInterface(REFIID riid, void **ppv);
ULONG __stdcall AddRef();
ULONG __stdcall Release();
// IClassFactory
HRESULT __stdcall CreateInstance(LPUNKNOWN pUnk, REFIID riid, void **ppv);
HRESULT __stdcall LockServer(BOOL fLock);
};
#endif // TEST_H
Interface declare methods:
virtual HRESULT __stdcall methodname(input_1_type input_1_name, input_2_type input_1_name, ..., result_type *result_name)
Class declare methods:
HRESULT __stdcall methodname(input_1_type input_1_name, input_2_type input_1_name, ..., result_type *result_name)
Test.cpp:
#include <stdio.h>
#include <windows.h>
#include <initguid.h>
#include "Test.h"
static LONG volatile g_lckcnt = 0;
static LONG volatile g_objcnt = 0;
CoTest::CoTest()
{
#ifdef DEBUG
printf("CoTest ctor\n");
#endif
m_refcnt = 0;
InterlockedIncrement(&g_objcnt);
}
CoTest::~CoTest()
{
#ifdef DEBUG
printf("CoTest dtor\n");
#endif
InterlockedDecrement(&g_objcnt);
}
HRESULT CoTest::QueryInterface(REFIID riid, void **ppv)
{
if(riid == IID_IUnknown)
{
#ifdef DEBUG
printf("CoTest QueryInterface IUnknown\n");
#endif
*ppv = (IUnknown*)this;
((IUnknown*)(*ppv))->AddRef();
return S_OK;
}
else if(riid == IID_ITest)
{
#ifdef DEBUG
printf("CoTest QueryInterface ITest\n");
#endif
*ppv = (ITest*)this;
((ITest*)(*ppv))->AddRef();
return S_OK;
}
else
{
*ppv = NULL;
return E_NOINTERFACE;
}
}
ULONG CoTest::AddRef()
{
#ifdef DEBUG
printf("CoTest AddRef\n");
#endif
return InterlockedIncrement(&m_refcnt);
}
ULONG CoTest::Release()
{
#ifdef DEBUG
printf("CoTest Release\n");
#endif
InterlockedDecrement(&m_refcnt);
if(m_refcnt == 0)
{
delete this;
return 0;
}
else
{
return m_refcnt;
}
}
HRESULT CoTest::Add(long int a, long int b, long int *c)
{
*c = a + b;
return S_OK;
}
CoTestFactory::CoTestFactory()
{
m_refcnt = 0;
}
HRESULT CoTestFactory::QueryInterface(REFIID riid, void **ppv)
{
if(riid == IID_IUnknown)
{
*ppv = (IUnknown*)this;
((IUnknown*)(*ppv))->AddRef();
return S_OK;
}
else if(riid == IID_IClassFactory)
{
*ppv = (IClassFactory*)this;
((IClassFactory*)(*ppv))->AddRef();
return S_OK;
}
else
{
*ppv = NULL;
return E_NOINTERFACE;
}
}
ULONG CoTestFactory::AddRef()
{
return InterlockedIncrement(&m_refcnt);
}
ULONG CoTestFactory::Release()
{
InterlockedDecrement(&m_refcnt);
if(m_refcnt == 0)
{
delete this;
return 0;
}
else
{
return m_refcnt;
}
}
HRESULT __stdcall CoTestFactory::CreateInstance(LPUNKNOWN pUnk, REFIID riid, void **ppv)
{
#ifdef DEBUG
printf("CoTestFactory CreateInstance\n");
#endif
if(pUnk != NULL) return CLASS_E_NOAGGREGATION;
CoTest *pTest = new CoTest();
HRESULT res = pTest->QueryInterface(riid, ppv);
if(res != S_OK) delete pTest;
return res;
}
HRESULT __stdcall CoTestFactory::LockServer(BOOL fLock)
{
if(fLock) InterlockedIncrement(&g_lckcnt); else InterlockedDecrement(&g_lckcnt);
return S_OK;
}
HRESULT __stdcall DllGetClassObject(REFCLSID rclsid, REFIID riid, void **ppv)
{
#ifdef DEBUG
printf("DllGetClassObject\n");
#endif
if(rclsid == CLSID_CoTest)
{
CoTestFactory *pTestFact = new CoTestFactory();
HRESULT res = pTestFact->QueryInterface(riid, ppv);
if(res != S_OK) delete pTestFact;
return res;
}
else
{
return CLASS_E_CLASSNOTAVAILABLE;
}
}
HRESULT __stdcall DllCanUnloadNow()
{
if(g_lckcnt == 0 && g_objcnt == 0) return S_OK; else return S_FALSE;
}
The ITest methods are trivial. There are some complexities around the IUnknown and IClassFactory methods, but this is just boilerplate stuff.
Test.def:
LIBRARY "Test"
EXPORTS
DllGetClassObject PRIVATE
DllCanUnloadNow PRIVATE
Build:
cl /LD Test.cpp Test.def
Test.reg
Test.reg:
REGEDIT
HKEY_CLASSES_ROOT\Test.CoTest\CLSID = {2c7de05d-a083-40d7-b7af-90af8d432489}
HKEY_CLASSES_ROOT\Wow6432Node\CLSID\{2c7de05d-a083-40d7-b7af-90af8d432489} = Test.CoTest
HKEY_CLASSES_ROOT\Wow6432Node\CLSID\{2c7de05d-a083-40d7-b7af-90af8d432489}\InProcServer32 = C:\Work\com\coma\Test.dll
TestCPP1.cpp:
#include <stdio.h>
#include <stdlib.h>
#include <windows.h>
#include <initguid.h>
#include <tchar.h>
#include "Test.h"
void ReturnCheck(LPTSTR func, HRESULT res)
{
if(res != S_OK)
{
TCHAR buffer[1000];
FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, 0, res, 0, buffer, sizeof(buffer), 0);
_tprintf("%s: %s\n", func, buffer);
exit(1);
}
}
int main()
{
HRESULT res;
CoInitialize(NULL);
printf("COM/C++ with CoGetClassObject and CreateInstance:\n");
IClassFactory *pCF;
res = CoGetClassObject(CLSID_CoTest, CLSCTX_INPROC_SERVER, NULL, IID_IClassFactory, (void **)&pCF);
ReturnCheck(_T("CoGetClassObject"), res);
ITest *pTest;
res = pCF->CreateInstance(NULL, IID_ITest, (void **)&pTest);
ReturnCheck(_T("CreateInstance"), res);
long int v;
res = pTest->Add(123, 456, &v);
ReturnCheck(_T("Add"), res);
printf("123 + 345 = %d\n", v);
pTest->Release();
pCF->Release();
CoUninitialize();
return 0;
}
Build and run:
cl TestCPP1.cpp ole32.lib
TestCPP1
TestCPP2.cpp:
#include <stdio.h>
#include <stdlib.h>
#include <windows.h>
#include <initguid.h>
#include <tchar.h>
#include "Test.h"
void ReturnCheck(LPTSTR func, HRESULT res)
{
if(res != S_OK)
{
TCHAR buffer[1000];
FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, 0, res, 0, buffer, sizeof(buffer), 0);
_tprintf("%s: %s\n", func, buffer);
exit(1);
}
}
int main()
{
HRESULT res;
CoInitialize(NULL);
printf("COM/C++ with CoCreateInstance:\n");
ITest *pTest;
res = CoCreateInstance(CLSID_CoTest, NULL, CLSCTX_INPROC_SERVER, IID_ITest, (void**)&pTest);
ReturnCheck(_T("CoCreateInstance"), res);
long int v;
res = pTest->Add(123, 456, &v);
ReturnCheck(_T("Add"), res);
printf("123 + 345 = %d\n", v);
pTest->Release();
CoUninitialize();
return 0;
}
Build and run:
cl TestCPP2.cpp ole32.lib
TestCPP2
What is happening:
In this iteration we will:
COM uses BSTR for strings, so we have to use SysAllocString and SysFreeString.
Test.h:
#ifndef TEST_H
#define TEST_H
#include <windows.h>
// { 2f2d7587-78e8-46ee-ba65-fc7094b37a13 }
DEFINE_GUID(IID_ITest, 0x2f2d7587, 0x78e8, 0x46ee, 0xba, 0x65, 0xfc, 0x70, 0x94, 0xb3, 0x7a, 0x13);
// { 2c7de05d-a083-40d7-b7af-90af8d432489 }
DEFINE_GUID(CLSID_CoTest, 0x2c7de05d, 0xa083, 0x40d7, 0xb7, 0xaf, 0x90, 0xaf, 0x8d, 0x43, 0x24, 0x89);
interface ITest : public IUnknown
{
virtual HRESULT __stdcall Add(long int a, long int b, long int *c) = 0;
virtual HRESULT __stdcall Concat(BSTR a, BSTR b, BSTR *c) = 0;
};
class CoTest : public ITest
{
private:
LONG volatile m_refcnt;
public:
CoTest();
virtual ~CoTest();
// IUnknown
HRESULT __stdcall QueryInterface(REFIID riid, void **ppv);
ULONG __stdcall AddRef();
ULONG __stdcall Release();
// ITest
HRESULT __stdcall Add(long int a, long int b, long int *c);
HRESULT __stdcall Concat(BSTR a, BSTR b, BSTR *c);
};
class CoTestFactory : IClassFactory
{
private:
LONG volatile m_refcnt;
public:
CoTestFactory();
// IUnknown
HRESULT __stdcall QueryInterface(REFIID riid, void **ppv);
ULONG __stdcall AddRef();
ULONG __stdcall Release();
// IClassFactory
HRESULT __stdcall CreateInstance(LPUNKNOWN pUnk, REFIID riid, void **ppv);
HRESULT __stdcall LockServer(BOOL fLock);
};
#endif // TEST_H
Test.cpp:
#include <stdio.h>
#include <windows.h>
#include <initguid.h>
#include "Test.h"
static LONG volatile g_lckcnt = 0;
static LONG volatile g_objcnt = 0;
CoTest::CoTest()
{
#ifdef DEBUG
printf("CoTest ctor\n");
#endif
m_refcnt = 0;
InterlockedIncrement(&g_objcnt);
}
CoTest::~CoTest()
{
#ifdef DEBUG
printf("CoTest dtor\n");
#endif
InterlockedDecrement(&g_objcnt);
}
HRESULT CoTest::QueryInterface(REFIID riid, void **ppv)
{
if(riid == IID_IUnknown)
{
#ifdef DEBUG
printf("CoTest QueryInterface IUnknown\n");
#endif
*ppv = (IUnknown*)this;
((IUnknown*)(*ppv))->AddRef();
return S_OK;
}
else if(riid == IID_ITest)
{
#ifdef DEBUG
printf("CoTest QueryInterface ITest\n");
#endif
*ppv = (ITest*)this;
((ITest*)(*ppv))->AddRef();
return S_OK;
}
else
{
*ppv = NULL;
return E_NOINTERFACE;
}
}
ULONG CoTest::AddRef()
{
#ifdef DEBUG
printf("CoTest AddRef\n");
#endif
return InterlockedIncrement(&m_refcnt);
}
ULONG CoTest::Release()
{
#ifdef DEBUG
printf("CoTest Release\n");
#endif
InterlockedDecrement(&m_refcnt);
if(m_refcnt == 0)
{
delete this;
return 0;
}
else
{
return m_refcnt;
}
}
HRESULT CoTest::Add(long int a, long int b, long int *c)
{
*c = a + b;
return S_OK;
}
HRESULT CoTest::Concat(BSTR a, BSTR b, BSTR *c)
{
OLECHAR *buf = new OLECHAR[SysStringLen(a) + SysStringLen(b) + 1];
wcscpy(buf, a);
wcscat(buf, b);
*c = SysAllocString(buf);
delete[] buf;
return S_OK;
}
CoTestFactory::CoTestFactory()
{
m_refcnt = 0;
}
HRESULT CoTestFactory::QueryInterface(REFIID riid, void **ppv)
{
if(riid == IID_IUnknown)
{
*ppv = (IUnknown*)this;
((IUnknown*)(*ppv))->AddRef();
return S_OK;
}
else if(riid == IID_IClassFactory)
{
*ppv = (IClassFactory*)this;
((IClassFactory*)(*ppv))->AddRef();
return S_OK;
}
else
{
*ppv = NULL;
return E_NOINTERFACE;
}
}
ULONG CoTestFactory::AddRef()
{
return InterlockedIncrement(&m_refcnt);
}
ULONG CoTestFactory::Release()
{
InterlockedDecrement(&m_refcnt);
if(m_refcnt == 0)
{
delete this;
return 0;
}
else
{
return m_refcnt;
}
}
HRESULT __stdcall CoTestFactory::CreateInstance(LPUNKNOWN pUnk, REFIID riid, void **ppv)
{
#ifdef DEBUG
printf("CoTestFactory CreateInstance\n");
#endif
if(pUnk != NULL) return CLASS_E_NOAGGREGATION;
CoTest *pTest = new CoTest();
HRESULT res = pTest->QueryInterface(riid, ppv);
if(res != S_OK) delete pTest;
return res;
}
HRESULT __stdcall CoTestFactory::LockServer(BOOL fLock)
{
if(fLock) InterlockedIncrement(&g_lckcnt); else InterlockedDecrement(&g_lckcnt);
return S_OK;
}
HRESULT __stdcall DllGetClassObject(REFCLSID rclsid, REFIID riid, void **ppv)
{
#ifdef DEBUG
printf("DllGetClassObject\n");
#endif
if(rclsid == CLSID_CoTest)
{
CoTestFactory *pTestFact = new CoTestFactory();
HRESULT res = pTestFact->QueryInterface(riid, ppv);
if(res != S_OK) delete pTestFact;
return res;
}
else
{
return CLASS_E_CLASSNOTAVAILABLE;
}
}
HRESULT __stdcall DllCanUnloadNow()
{
if(g_lckcnt == 0 && g_objcnt == 0) return S_OK; else return S_FALSE;
}
Test.def:
LIBRARY "Test"
EXPORTS
DllGetClassObject PRIVATE
DllCanUnloadNow PRIVATE
Build:
cl /LD Test.cpp Test.def
Test.reg
Test.reg:
REGEDIT
HKEY_CLASSES_ROOT\Test.CoTest\CLSID = {2c7de05d-a083-40d7-b7af-90af8d432489}
HKEY_CLASSES_ROOT\Wow6432Node\CLSID\{2c7de05d-a083-40d7-b7af-90af8d432489} = Test.CoTest
HKEY_CLASSES_ROOT\Wow6432Node\CLSID\{2c7de05d-a083-40d7-b7af-90af8d432489}\InProcServer32 = C:\Work\com\comb\Test.dll
TestCPP1.cpp:
#include <stdio.h>
#include <stdlib.h>
#include <windows.h>
#include <initguid.h>
#include <tchar.h>
#include "Test.h"
void ReturnCheck(LPTSTR func, HRESULT res)
{
if(res != S_OK)
{
TCHAR buffer[1000];
FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, 0, res, 0, buffer, sizeof(buffer), 0);
_tprintf("%s: %s\n", func, buffer);
exit(1);
}
}
int main()
{
HRESULT res;
CoInitialize(NULL);
printf("COM/C++ with CoGetClassObject and CreateInstance:\n");
IClassFactory *pCF;
res = CoGetClassObject(CLSID_CoTest, CLSCTX_INPROC_SERVER, NULL, IID_IClassFactory, (void **)&pCF);
ReturnCheck(_T("CoGetClassObject"), res);
ITest *pTest;
res = pCF->CreateInstance(NULL, IID_ITest, (void **)&pTest);
ReturnCheck(_T("CreateInstance"), res);
long int v;
res = pTest->Add(123, 456, &v);
ReturnCheck(_T("Add"), res);
printf("123 + 345 = %d\n", v);
BSTR s1 = SysAllocString(L"ABC");
BSTR s2 = SysAllocString(L"XYZ");
BSTR s3;
res = pTest->Concat(s1, s2, &s3);
ReturnCheck(_T("Concat"), res);
printf("%d %ls + %d %ls = %d %ls\n", SysStringLen(s1), s1, SysStringLen(s2), s2, SysStringLen(s3), s3);
SysFreeString(s1);
SysFreeString(s2);
SysFreeString(s3);
pTest->Release();
pCF->Release();
CoUninitialize();
return 0;
}
Build and run:
cl TestCPP1.cpp ole32.lib
TestCPP1
TestCPP2.cpp:
#include <stdio.h>
#include <stdlib.h>
#include <windows.h>
#include <initguid.h>
#include <tchar.h>
#include "Test.h"
void ReturnCheck(LPTSTR func, HRESULT res)
{
if(res != S_OK)
{
TCHAR buffer[1000];
FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, 0, res, 0, buffer, sizeof(buffer), 0);
_tprintf("%s: %s\n", func, buffer);
exit(1);
}
}
int main()
{
HRESULT res;
CoInitialize(NULL);
printf("COM/C++ with CoCreateInstance:\n");
ITest *pTest;
res = CoCreateInstance(CLSID_CoTest, NULL, CLSCTX_INPROC_SERVER, IID_ITest, (void**)&pTest);
ReturnCheck(_T("CoCreateInstance"), res);
long int v;
res = pTest->Add(123, 456, &v);
ReturnCheck(_T("Add"), res);
printf("123 + 345 = %d\n", v);
BSTR s1 = SysAllocString(L"ABC");
BSTR s2 = SysAllocString(L"XYZ");
BSTR s3;
res = pTest->Concat(s1, s2, &s3);
ReturnCheck(_T("Concat"), res);
printf("%d %ls + %d %ls = %d %ls\n", SysStringLen(s1), s1, SysStringLen(s2), s2, SysStringLen(s3), s3);
SysFreeString(s1);
SysFreeString(s2);
SysFreeString(s3);
pTest->Release();
CoUninitialize();
return 0;
}
Build and run:
cl TestCPP2.cpp ole32.lib
TestCPP2
What is happening:
In this iteration we will:
Test.h:
#ifndef TEST_H
#define TEST_H
#include <windows.h>
// {57BDB1F4-6CD3-4097-B2DA-0301B72FF14D}
DEFINE_GUID(IID_ITest, 0x57bdb1f4, 0x6cd3, 0x4097, 0xb2, 0xda, 0x3, 0x1, 0xb7, 0x2f, 0xf1, 0x4d);
// {1894E8E6-3BD9-46f2-99F1-C6323DAF8F71}
DEFINE_GUID(CLSID_CoTest, 0x1894e8e6, 0x3bd9, 0x46f2, 0x99, 0xf1, 0xc6, 0x32, 0x3d, 0xaf, 0x8f, 0x71);
DECLARE_INTERFACE_(ITest, IUnknown)
{
STDMETHOD(Add)(long int a, long int b, long int *c) PURE;
STDMETHOD(Concat)(BSTR a, BSTR b, BSTR *c) PURE;
};
class CoTest : public ITest
{
private:
LONG volatile m_refcnt;
public:
CoTest();
virtual ~CoTest();
// IUnknown
STDMETHODIMP QueryInterface(REFIID riid, void **ppv);
STDMETHODIMP_(ULONG) AddRef();
STDMETHODIMP_(ULONG) Release();
// ITest
STDMETHODIMP Add(long int a, long int b, long int *c);
STDMETHODIMP Concat(BSTR a, BSTR b, BSTR *c);
};
class CoTestFactory : IClassFactory
{
private:
LONG volatile m_refcnt;
public:
CoTestFactory();
// IUnknown
STDMETHODIMP QueryInterface(REFIID riid, void **ppv);
STDMETHODIMP_(ULONG) AddRef();
STDMETHODIMP_(ULONG) Release();
// IClassFactory
STDMETHODIMP CreateInstance(LPUNKNOWN pUnk, REFIID riid, void **ppv);
STDMETHODIMP LockServer(BOOL fLock);
};
#endif // TEST_H
Interface declare methods:
STDMETHOD(methodname)(input_1_type input_1_name, input_2_type input_1_name, ..., result_type *result_name)
Class declare methods:
STDMETHODIMP methodname(input_1_type input_1_name, input_2_type input_1_name, ..., result_type *result_name)
Test.cpp:
#include <stdio.h>
#include <windows.h>
#include <oleauto.h>
#include <initguid.h>
#include "Test.h"
static LONG volatile g_lckcnt = 0;
static LONG volatile g_objcnt = 0;
CoTest::CoTest()
{
#ifdef DEBUG
printf("CoTest ctor\n");
#endif
m_refcnt = 0;
InterlockedIncrement(&g_objcnt);
}
CoTest::~CoTest()
{
#ifdef DEBUG
printf("CoTest dtor\n");
#endif
InterlockedDecrement(&g_objcnt);
}
STDMETHODIMP CoTest::QueryInterface(REFIID riid, void **ppv)
{
if(riid == IID_IUnknown)
{
#ifdef DEBUG
printf("CoTest QueryInterface IUnknown\n");
#endif
*ppv = (IUnknown*)this;
((IUnknown*)(*ppv))->AddRef();
return S_OK;
}
else if(riid == IID_ITest)
{
#ifdef DEBUG
printf("CoTest QueryInterface ITest\n");
#endif
*ppv = (ITest*)this;
((ITest*)(*ppv))->AddRef();
return S_OK;
}
else
{
*ppv = NULL;
return E_NOINTERFACE;
}
}
STDMETHODIMP_(ULONG) CoTest::AddRef()
{
#ifdef DEBUG
printf("CoTest AddRef\n");
#endif
return InterlockedIncrement(&m_refcnt);
}
STDMETHODIMP_(ULONG) CoTest::Release()
{
#ifdef DEBUG
printf("CoTest Release\n");
#endif
InterlockedDecrement(&m_refcnt);
if(m_refcnt == 0)
{
delete this;
return 0;
}
else
{
return m_refcnt;
}
}
STDMETHODIMP CoTest::Add(long int a, long int b, long int *c)
{
*c = a + b;
return S_OK;
}
STDMETHODIMP CoTest::Concat(BSTR a, BSTR b, BSTR *c)
{
OLECHAR *buf = new OLECHAR[SysStringLen(a) + SysStringLen(b) + 1];
wcscpy(buf, a);
wcscat(buf, b);
*c = SysAllocString(buf);
delete[] buf;
return S_OK;
}
CoTestFactory::CoTestFactory()
{
m_refcnt = 0;
}
STDMETHODIMP CoTestFactory::QueryInterface(REFIID riid, void **ppv)
{
if(riid == IID_IUnknown)
{
*ppv = (IUnknown*)this;
((IUnknown*)(*ppv))->AddRef();
return S_OK;
}
else if(riid == IID_IClassFactory)
{
*ppv = (IClassFactory*)this;
((IClassFactory*)(*ppv))->AddRef();
return S_OK;
}
else
{
*ppv = NULL;
return E_NOINTERFACE;
}
}
STDMETHODIMP_(ULONG) CoTestFactory::AddRef()
{
return InterlockedIncrement(&m_refcnt);
}
STDMETHODIMP_(ULONG) CoTestFactory::Release()
{
InterlockedDecrement(&m_refcnt);
if(m_refcnt == 0)
{
delete this;
return 0;
}
else
{
return m_refcnt;
}
}
STDMETHODIMP CoTestFactory::CreateInstance(LPUNKNOWN pUnk, REFIID riid, void **ppv)
{
#ifdef DEBUG
printf("CoTestFactory CreateInstance\n");
#endif
if(pUnk != NULL) return CLASS_E_NOAGGREGATION;
CoTest *pTest = new CoTest();
HRESULT res = pTest->QueryInterface(riid, ppv);
if(res != S_OK) delete pTest;
return res;
}
STDMETHODIMP CoTestFactory::LockServer(BOOL fLock)
{
if(fLock) InterlockedIncrement(&g_lckcnt); else InterlockedDecrement(&g_lckcnt);
return S_OK;
}
STDMETHODIMP DllGetClassObject(REFCLSID rclsid, REFIID riid, void **ppv)
{
#ifdef DEBUG
printf("DllGetClassObject\n");
#endif
if(rclsid == CLSID_CoTest)
{
CoTestFactory *pTestFact = new CoTestFactory();
HRESULT res = pTestFact->QueryInterface(riid, ppv);
if(res != S_OK) delete pTestFact;
return res;
}
else
{
return CLASS_E_CLASSNOTAVAILABLE;
}
}
STDMETHODIMP DllCanUnloadNow()
{
if(g_lckcnt == 0 && g_objcnt == 0) return S_OK; else return S_FALSE;
}
Test.def:
LIBRARY "Test"
EXPORTS
DllGetClassObject PRIVATE
DllCanUnloadNow PRIVATE
Build:
cl /LD Test.cpp Test.def
Test.reg
Test.reg:
REGEDIT
HKEY_CLASSES_ROOT\Test.CoTest\CLSID = {1894e8e6-3bd9-46f2-99f1-c6323daf8f71}
HKEY_CLASSES_ROOT\Wow6432Node\CLSID\{1894e8e6-3bd9-46f2-99f1-c6323daf8f71} = Test.CoTest
HKEY_CLASSES_ROOT\Wow6432Node\CLSID\{1894e8e6-3bd9-46f2-99f1-c6323daf8f71}\InProcServer32 = C:\Work\com\comc\Test.dll
TestCPP1.cpp:
#include <stdio.h>
#include <stdlib.h>
#include <windows.h>
#include <initguid.h>
#include <tchar.h>
#include "Test.h"
void ReturnCheck(LPTSTR func, HRESULT res)
{
if(res != S_OK)
{
TCHAR buffer[1000];
FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, 0, res, 0, buffer, sizeof(buffer), 0);
_tprintf("%s: %s\n", func, buffer);
exit(1);
}
}
int main()
{
HRESULT res;
CoInitialize(NULL);
printf("COM/C++ with CoGetClassObject and CreateInstance\n");
IClassFactory *pCF;
res = CoGetClassObject(CLSID_CoTest, CLSCTX_INPROC_SERVER, NULL, IID_IClassFactory, (void **)&pCF);
ReturnCheck(_T("CoGetClassObject"), res);
ITest *pTest;
res = pCF->CreateInstance(NULL, IID_ITest, (void **)&pTest);
ReturnCheck(_T("CreateInstance"), res);
long int v;
res = pTest->Add(123, 456, &v);
ReturnCheck(_T("Add"), res);
printf("123 + 456 = %d\n", v);
BSTR s1 = SysAllocString(L"ABC");
BSTR s2 = SysAllocString(L"XYZ");
BSTR s3;
res = pTest->Concat(s1, s2, &s3);
ReturnCheck(_T("Concat"), res);
printf("%d %ls + %d %ls = %d %ls\n", SysStringLen(s1), s1, SysStringLen(s2), s2, SysStringLen(s3), s3);
SysFreeString(s1);
SysFreeString(s2);
SysFreeString(s3);
pTest->Release();
pCF->Release();
CoUninitialize();
return 0;
}
Build and run:
cl TestCPP1.cpp ole32.lib
TestCPP1
TestCPP2.cpp:
#include <stdio.h>
#include <stdlib.h>
#include <windows.h>
#include <initguid.h>
#include <tchar.h>
#include "Test.h"
void ReturnCheck(LPTSTR func, HRESULT res)
{
if(res != S_OK)
{
TCHAR buffer[1000];
FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, 0, res, 0, buffer, sizeof(buffer), 0);
_tprintf("%s: %s\n", func, buffer);
exit(1);
}
}
int main()
{
HRESULT res;
CoInitialize(NULL);
printf("COM/C++ with CoCreateInstance\n");
ITest *pTest;
res = CoCreateInstance(CLSID_CoTest, NULL, CLSCTX_INPROC_SERVER, IID_ITest, (void**)&pTest);
ReturnCheck(_T("CoCreateInstance"), res);
long int v;
res = pTest->Add(123, 456, &v);
ReturnCheck(_T("Add"), res);
printf("123 + 456 = %d\n", v);
BSTR s1 = SysAllocString(L"ABC");
BSTR s2 = SysAllocString(L"XYZ");
BSTR s3;
res = pTest->Concat(s1, s2, &s3);
ReturnCheck(_T("Concat"), res);
printf("%d %ls + %d %ls = %d %ls\n", SysStringLen(s1), s1, SysStringLen(s2), s2, SysStringLen(s3), s3);
SysFreeString(s1);
SysFreeString(s2);
SysFreeString(s3);
pTest->Release();
CoUninitialize();
return 0;
}
Build and run:
cl TestCPP2.cpp ole32.lib
TestCPP2
What is happening:
In this iteration we will:
IDL is a special language to generate header files from.
Test.idl:
import "oaidl.idl";
// ITest
[object, uuid(CC30FE9D-8CCF-4471-BEE7-033E48D4ED06)]
interface ITest : IUnknown
{
HRESULT Add([in] long a, [in] long b, [out,retval] long *c);
HRESULT Concat([in] BSTR a, [in] BSTR b, [out,retval]BSTR *c);
}
// Test library
[uuid(F1ABF066-8F26-44e2-9D6E-C68A8DDF3446), version(1.0)]
library TestLibrary
{
importlib("stdole32.tlb");
[uuid(776C8162-CE87-45cf-8036-04E7B4ADCFF0)]
coclass CoTest
{
[default] interface ITest;
};
};
Interface declare methods:
HRESULT methodname([in] input_1_type input_1_name, [in] input_2_type input_1_name, ..., [out,retval] result_type *result_name)
TestEx.h:
#ifndef TESTEX_H
#define TESTEX_H
#include <windows.h>
#include "Test_i.c"
#include "Test.h"
class CoTest : public ITest
{
private:
LONG volatile m_refcnt;
public:
CoTest();
virtual ~CoTest();
// IUnknown
STDMETHODIMP QueryInterface(REFIID riid, void **ppv);
STDMETHODIMP_(ULONG) AddRef();
STDMETHODIMP_(ULONG) Release();
// ITest
STDMETHODIMP Add(long int a, long int b, long int *c);
STDMETHODIMP Concat(BSTR a, BSTR b, BSTR *c);
};
class CoTestFactory : IClassFactory
{
private:
LONG volatile m_refcnt;
public:
CoTestFactory();
// IUnknown
STDMETHODIMP QueryInterface(REFIID riid, void **ppv);
STDMETHODIMP_(ULONG) AddRef();
STDMETHODIMP_(ULONG) Release();
// IClassFactory
STDMETHODIMP CreateInstance(LPUNKNOWN pUnk, REFIID riid, void **ppv);
STDMETHODIMP LockServer(BOOL fLock);
};
#endif // TESTEX_H
Class declare methods:
STDMETHODIMP methodname(input_1_type input_1_name, input_2_type input_1_name, ..., result_type *result_name)
Test.cpp:
#include <stdio.h>
#include <windows.h>
#include <oleauto.h>
#include <initguid.h>
#include "TestEx.h"
static LONG volatile g_lckcnt = 0;
static LONG volatile g_objcnt = 0;
CoTest::CoTest()
{
#ifdef DEBUG
printf("CoTest ctor\n");
#endif
m_refcnt = 0;
InterlockedIncrement(&g_objcnt);
}
CoTest::~CoTest()
{
#ifdef DEBUG
printf("CoTest dtor\n");
#endif
InterlockedDecrement(&g_objcnt);
}
STDMETHODIMP CoTest::QueryInterface(REFIID riid, void **ppv)
{
if(riid == IID_IUnknown)
{
#ifdef DEBUG
printf("CoTest QueryInterface IUnknown\n");
#endif
*ppv = (IUnknown*)this;
((IUnknown*)(*ppv))->AddRef();
return S_OK;
}
else if(riid == IID_ITest)
{
#ifdef DEBUG
printf("CoTest QueryInterface ITest\n");
#endif
*ppv = (ITest*)this;
((ITest*)(*ppv))->AddRef();
return S_OK;
}
else
{
*ppv = NULL;
return E_NOINTERFACE;
}
}
STDMETHODIMP_(ULONG) CoTest::AddRef()
{
#ifdef DEBUG
printf("CoTest AddRef\n");
#endif
return InterlockedIncrement(&m_refcnt);
}
STDMETHODIMP_(ULONG) CoTest::Release()
{
#ifdef DEBUG
printf("CoTest Release\n");
#endif
InterlockedDecrement(&m_refcnt);
if(m_refcnt == 0)
{
delete this;
return 0;
}
else
{
return m_refcnt;
}
}
STDMETHODIMP CoTest::Add(long a, long b, long *c)
{
*c = a + b;
return S_OK;
}
STDMETHODIMP CoTest::Concat(BSTR a, BSTR b, BSTR *c)
{
OLECHAR *buf = new OLECHAR[SysStringLen(a) + SysStringLen(b) + 1];
wcscpy(buf, a);
wcscat(buf, b);
*c = SysAllocString(buf);
delete[] buf;
return S_OK;
}
CoTestFactory::CoTestFactory()
{
m_refcnt = 0;
}
STDMETHODIMP CoTestFactory::QueryInterface(REFIID riid, void **ppv)
{
if(riid == IID_IUnknown)
{
*ppv = (IUnknown*)this;
((IUnknown*)(*ppv))->AddRef();
return S_OK;
}
else if(riid == IID_IClassFactory)
{
*ppv = (IClassFactory*)this;
((IClassFactory*)(*ppv))->AddRef();
return S_OK;
}
else
{
*ppv = NULL;
return E_NOINTERFACE;
}
}
STDMETHODIMP_(ULONG) CoTestFactory::AddRef()
{
return InterlockedIncrement(&m_refcnt);
}
STDMETHODIMP_(ULONG) CoTestFactory::Release()
{
InterlockedDecrement(&m_refcnt);
if(m_refcnt == 0)
{
delete this;
return 0;
}
else
{
return m_refcnt;
}
}
STDMETHODIMP CoTestFactory::CreateInstance(LPUNKNOWN pUnk, REFIID riid, void **ppv)
{
#ifdef DEBUG
printf("CoTestFactory CreateInstance\n");
#endif
if(pUnk != NULL) return CLASS_E_NOAGGREGATION;
CoTest *pTest = new CoTest();
HRESULT res = pTest->QueryInterface(riid, ppv);
if(res != S_OK) delete pTest;
return res;
}
STDMETHODIMP CoTestFactory::LockServer(BOOL fLock)
{
if(fLock) InterlockedIncrement(&g_lckcnt); else InterlockedDecrement(&g_lckcnt);
return S_OK;
}
STDMETHODIMP DllGetClassObject(REFCLSID rclsid, REFIID riid, void **ppv)
{
#ifdef DEBUG
printf("DllGetClassObject\n");
#endif
if(rclsid == CLSID_CoTest)
{
CoTestFactory *pTestFact = new CoTestFactory();
HRESULT res = pTestFact->QueryInterface(riid, ppv);
if(res != S_OK) delete pTestFact;
return res;
}
else
{
return CLASS_E_CLASSNOTAVAILABLE;
}
}
STDMETHODIMP DllCanUnloadNow()
{
if(g_lckcnt == 0 && g_objcnt == 0) return S_OK; else return S_FALSE;
}
Test.def:
LIBRARY "Test"
EXPORTS
DllGetClassObject PRIVATE
DllCanUnloadNow PRIVATE
Build:
midl Test.idl
cl /LD Test.cpp Test.def oleaut32.lib
Test.reg
Test.reg:
REGEDIT
HKEY_CLASSES_ROOT\Test.CoTest\CLSID = {776c8162-ce87-45cf-8036-04e7b4adcff0}
HKEY_CLASSES_ROOT\Wow6432Node\CLSID\{776c8162-ce87-45cf-8036-04e7b4adcff0} = Test.CoTest
HKEY_CLASSES_ROOT\Wow6432Node\CLSID\{776c8162-ce87-45cf-8036-04e7b4adcff0}\InProcServer32 = C:\Work\com\comd\Test.dll
HKEY_CLASSES_ROOT\Wow6432Node\CLSID\{776c8162-ce87-45cf-8036-04e7b4adcff0}\TypeLib = {f1abf066-8f26-44e2-9d6e-c68a8ddf3446}
HKEY_CLASSES_ROOT\Wow6432Node\TypeLib\{f1abf066-8f26-44e2-9d6e-c68a8ddf3446}\1.0 = Test Library
HKEY_CLASSES_ROOT\Wow6432Node\TypeLib\{f1abf066-8f26-44e2-9d6e-c68a8ddf3446}\1.0\0\Win32 = C:\Work\com\comd\Testtype.tlb
HKEY_CLASSES_ROOT\Wow6432Node\TypeLib\{f1abf066-8f26-44e2-9d6e-c68a8ddf3446}\1.0\FLAGS = 0
HKEY_CLASSES_ROOT\Wow6432Node\TypeLib\{f1abf066-8f26-44e2-9d6e-c68a8ddf3446}\1.0\HELPDIR
TestCPP1.cpp:
#include <stdio.h>
#include <stdlib.h>
#include <windows.h>
#include <initguid.h>
#include <tchar.h>
#include "TestEx.h"
void ReturnCheck(LPTSTR func, HRESULT res)
{
if(res != S_OK)
{
TCHAR buffer[1000];
FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, 0, res, 0, buffer, sizeof(buffer), 0);
_tprintf("%s: %s\n", func, buffer);
exit(1);
}
}
int main()
{
HRESULT res;
CoInitialize(NULL);
printf("COM/C++ with CoGetClassObject and CreateInstance\n");
IClassFactory *pCF;
res = CoGetClassObject(CLSID_CoTest, CLSCTX_INPROC_SERVER, NULL, IID_IClassFactory, (void **)&pCF);
ReturnCheck(_T("CoGetClassObject"), res);
ITest *pTest;
res = pCF->CreateInstance(NULL, IID_ITest, (void **)&pTest);
ReturnCheck(_T("CreateInstance"), res);
long int v;
res = pTest->Add(123, 456, &v);
ReturnCheck(_T("Add"), res);
printf("123 + 456 = %d\n", v);
BSTR s1 = SysAllocString(L"ABC");
BSTR s2 = SysAllocString(L"XYZ");
BSTR s3;
res = pTest->Concat(s1, s2, &s3);
ReturnCheck(_T("Concat"), res);
printf("%d %ls + %d %ls = %d %ls\n", SysStringLen(s1), s1, SysStringLen(s2), s2, SysStringLen(s3), s3);
SysFreeString(s1);
SysFreeString(s2);
SysFreeString(s3);
pTest->Release();
pCF->Release();
CoUninitialize();
return 0;
}
Build and run:
cl /EHsc TestCPP1.cpp ole32.lib oleaut32.lib
TestCPP1
TestCPP2.cpp:
#include <stdio.h>
#include <stdlib.h>
#include <windows.h>
#include <initguid.h>
#include <tchar.h>
#include "TestEx.h"
void ReturnCheck(LPTSTR func, HRESULT res)
{
if(res != S_OK)
{
TCHAR buffer[1000];
FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, 0, res, 0, buffer, sizeof(buffer), 0);
_tprintf("%s: %s\n", func, buffer);
exit(1);
}
}
int main()
{
HRESULT res;
CoInitialize(NULL);
printf("COM/C++ with CoCreateInstance\n");
ITest *pTest;
res = CoCreateInstance(CLSID_CoTest, NULL, CLSCTX_INPROC_SERVER, IID_ITest, (void**)&pTest);
ReturnCheck(_T("CoCreateInstance"), res);
long int v;
res = pTest->Add(123, 456, &v);
ReturnCheck(_T("Add"), res);
printf("123 + 456 = %d\n", v);
BSTR s1 = SysAllocString(L"ABC");
BSTR s2 = SysAllocString(L"XYZ");
BSTR s3;
res = pTest->Concat(s1, s2, &s3);
ReturnCheck(_T("Concat"), res);
printf("%d %ls + %d %ls = %d %ls\n", SysStringLen(s1), s1, SysStringLen(s2), s2, SysStringLen(s3), s3);
SysFreeString(s1);
SysFreeString(s2);
SysFreeString(s3);
pTest->Release();
CoUninitialize();
return 0;
}
Build and run:
cl /EHsc TestCPP2.cpp ole32.lib oleaut32.lib
TestCPP2
TestC.c:
#include <windows.h>
#include <initguid.h>
#include <tchar.h>
#include <stdio.h>
#include <stdlib.h>
#define COBJMACROS
#include "Test.h"
void ReturnCheck(LPTSTR func, HRESULT res)
{
if(res != S_OK)
{
TCHAR buffer[1000];
FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, 0, res, 0, buffer, sizeof(buffer), 0);
_tprintf("%s: %s\n", func, buffer);
exit(1);
}
}
int main()
{
HRESULT res;
ITest *pTest;
int v;
BSTR s1, s2, s3;
CoInitialize(NULL);
printf("COM/C:\n");
res = CoCreateInstance(&CLSID_CoTest, NULL, CLSCTX_INPROC_SERVER, &IID_ITest, (void**)&pTest);
ReturnCheck(_T("CoCreateInstance"), res);
res = ITest_Add(pTest, 123, 456, &v);
ReturnCheck(_T("Add"), res);
printf("123 + 345 = %d\n", v);
s1 = SysAllocString(L"ABC");
s2 = SysAllocString(L"XYZ");
res = ITest_Concat(pTest, s1, s2, &s3);
ReturnCheck(_T("Concat"), res);
printf("%d %ls + %d %ls = %d %ls\n", SysStringLen(s1), s1, SysStringLen(s2), s2, SysStringLen(s3), s3);
SysFreeString(s1);
SysFreeString(s2);
SysFreeString(s3);
ITest_Release(pTest);
CoUninitialize();
return 0;
}
Build and run:
cl TestC.c Test_i.c ole32.lib oleaut32.lib
TestC
What is happening:
TestCPP3.cpp:
#include <stdio.h>
#include <windows.h>
#include <initguid.h>
#include <tchar.h>
#import "Test.tlb" no_namespace named_guids
void ReturnCheck(LPTSTR func, HRESULT res)
{
if(res != S_OK)
{
TCHAR buffer[1000];
FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, 0, res, 0, buffer, sizeof(buffer), 0);
_tprintf("%s: %s\n", func, buffer);
exit(1);
}
}
int main()
{
HRESULT res;
CoInitialize(NULL);
printf("COM/C++ with CreateInstance:\n");
try
{
ITestPtr spTest;
res = spTest.CreateInstance(CLSID_CoTest);
ReturnCheck(_T("CreateInstance"), res);
long v = spTest->Add(123L, 456L);
printf("123 + 345 = %d\n", v);
BSTR s1 = SysAllocString(L"ABC");
BSTR s2 = SysAllocString(L"XYZ");
BSTR s3 = spTest->Concat(s1, s2);
printf("%d %ls + %d %ls = %d %ls\n", SysStringLen(s1), s1, SysStringLen(s2), s2, SysStringLen(s3), s3);
SysFreeString(s1);
SysFreeString(s2);
SysFreeString(s3);
spTest = NULL;
}
catch (_com_error er)
{
_tprintf("%s\n", er.ErrorMessage());
}
CoUninitialize();
return 0;
}
Build and run:
cl /EHsc TestCPP3.cpp ole32.lib oleaut32.lib
TestCPP3
TestCPP4.cpp:
#include <stdio.h>
#include <windows.h>
#include <initguid.h>
#include <tchar.h>
#import "Test.tlb" no_namespace named_guids
void ReturnCheck(LPTSTR func, HRESULT res)
{
if(res != S_OK)
{
TCHAR buffer[1000];
FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, 0, res, 0, buffer, sizeof(buffer), 0);
_tprintf("%s: %s\n", func, buffer);
exit(1);
}
}
int main()
{
HRESULT res;
CoInitialize(NULL);
printf("COM/C++ with constructor:\n");
try
{
ITestPtr spTest(__uuidof(CoTest));
long v = spTest->Add(123, 456);
printf("123 + 345 = %d\n", v);
BSTR s1 = SysAllocString(L"ABC");
BSTR s2 = SysAllocString(L"XYZ");
BSTR s3 = spTest->Concat(s1, s2);
printf("%d %ls + %d %ls = %d %ls\n", SysStringLen(s1), s1, SysStringLen(s2), s2, SysStringLen(s3), s3);
SysFreeString(s1);
SysFreeString(s2);
SysFreeString(s3);
spTest = NULL;
}
catch (_com_error er)
{
_tprintf("%s\n", er.ErrorMessage());
}
CoUninitialize();
return 0;
}
Build and run:
cl /EHsc TestCPP4.cpp ole32.lib oleaut32.lib
TestCPP4
What is happening:
In this iteration we will:
Test.idl:
import "oaidl.idl";
// ITest
[object, uuid(C5E946E6-729E-4c91-94EC-493A001C335A)]
interface ITest : IUnknown
{
HRESULT Add([in] long a, [in] long b, [out,retval] long *c);
HRESULT Concat([in] BSTR a, [in] BSTR b, [out,retval]BSTR *c);
}
// Test library
[uuid(6FA71121-CA28-4fab-9311-BF6EB36F9EAE), version(1.0)]
library TestLibrary
{
importlib("stdole32.tlb");
[uuid(7644E80B-A39E-482a-A9DD-966F48C04CEE)]
coclass CoTest
{
[default] interface ITest;
};
};
TestEx.h:
#ifndef TESTEX_H
#define TESTEX_H
#include <windows.h>
#include "Test_i.c"
#include "Test.h"
class CoTest : public ITest
{
private:
LONG volatile m_refcnt;
public:
CoTest();
virtual ~CoTest();
// IUnknown
STDMETHODIMP QueryInterface(REFIID riid, void **ppv);
STDMETHODIMP_(ULONG) AddRef();
STDMETHODIMP_(ULONG) Release();
// ITest
STDMETHODIMP Add(long a, long b, long *c);
STDMETHODIMP Concat(BSTR a, BSTR b, BSTR *c);
};
class CoTestFactory : IClassFactory
{
private:
LONG volatile m_refcnt;
public:
CoTestFactory();
// IUnknown
STDMETHODIMP QueryInterface(REFIID riid, void **ppv);
STDMETHODIMP_(ULONG) AddRef();
STDMETHODIMP_(ULONG) Release();
// IClassFactory
STDMETHODIMP CreateInstance(LPUNKNOWN pUnk, REFIID riid, void **ppv);
STDMETHODIMP LockServer(BOOL fLock);
};
#endif // TESTEX_H
Test.cpp:
#include <stdio.h>
#include <windows.h>
#include <oleauto.h>
#include <initguid.h>
#include "TestEx.h"
static LONG volatile g_lckcnt = 0;
static LONG volatile g_objcnt = 0;
CoTest::CoTest()
{
#ifdef DEBUG
printf("CoTest ctor\n");
#endif
m_refcnt = 0;
InterlockedIncrement(&g_objcnt);
}
CoTest::~CoTest()
{
#ifdef DEBUG
printf("CoTest dtor\n");
#endif
InterlockedDecrement(&g_objcnt);
}
STDMETHODIMP CoTest::QueryInterface(REFIID riid, void **ppv)
{
if(riid == IID_IUnknown)
{
#ifdef DEBUG
printf("CoTest QueryInterface IUnknown\n");
#endif
*ppv = (IUnknown*)this;
((IUnknown*)(*ppv))->AddRef();
return S_OK;
}
else if(riid == IID_ITest)
{
#ifdef DEBUG
printf("CoTest QueryInterface ITest\n");
#endif
*ppv = (ITest*)this;
((ITest*)(*ppv))->AddRef();
return S_OK;
}
else
{
*ppv = NULL;
return E_NOINTERFACE;
}
}
STDMETHODIMP_(ULONG) CoTest::AddRef()
{
#ifdef DEBUG
printf("CoTest AddRef\n");
#endif
return InterlockedIncrement(&m_refcnt);
}
STDMETHODIMP_(ULONG) CoTest::Release()
{
#ifdef DEBUG
printf("CoTest Release\n");
#endif
InterlockedDecrement(&m_refcnt);
if(m_refcnt == 0)
{
delete this;
return 0;
}
else
{
return m_refcnt;
}
}
STDMETHODIMP CoTest::Add(long a, long b, long *c)
{
*c = a + b;
return S_OK;
}
STDMETHODIMP CoTest::Concat(BSTR a, BSTR b, BSTR *c)
{
OLECHAR *buf = new OLECHAR[SysStringLen(a) + SysStringLen(b) + 1];
wcscpy(buf, a);
wcscat(buf, b);
*c = SysAllocString(buf);
delete[] buf;
return S_OK;
}
CoTestFactory::CoTestFactory()
{
m_refcnt = 0;
}
STDMETHODIMP CoTestFactory::QueryInterface(REFIID riid, void **ppv)
{
if(riid == IID_IUnknown)
{
*ppv = (IUnknown*)this;
((IUnknown*)(*ppv))->AddRef();
return S_OK;
}
else if(riid == IID_IClassFactory)
{
*ppv = (IClassFactory*)this;
((IClassFactory*)(*ppv))->AddRef();
return S_OK;
}
else
{
*ppv = NULL;
return E_NOINTERFACE;
}
}
STDMETHODIMP_(ULONG) CoTestFactory::AddRef()
{
return InterlockedIncrement(&m_refcnt);
}
STDMETHODIMP_(ULONG) CoTestFactory::Release()
{
InterlockedDecrement(&m_refcnt);
if(m_refcnt == 0)
{
delete this;
return 0;
}
else
{
return m_refcnt;
}
}
STDMETHODIMP CoTestFactory::CreateInstance(LPUNKNOWN pUnk, REFIID riid, void **ppv)
{
#ifdef DEBUG
printf("CoTestFactory CreateInstance\n");
#endif
if(pUnk != NULL) return CLASS_E_NOAGGREGATION;
CoTest *pTest = new CoTest();
HRESULT res = pTest->QueryInterface(riid, ppv);
if(res != S_OK) delete pTest;
return res;
}
STDMETHODIMP CoTestFactory::LockServer(BOOL fLock)
{
if(fLock) InterlockedIncrement(&g_lckcnt); else InterlockedDecrement(&g_lckcnt);
return S_OK;
}
STDMETHODIMP DllGetClassObject(REFCLSID rclsid, REFIID riid, void **ppv)
{
#ifdef DEBUG
printf("DllGetClassObject\n");
#endif
if(rclsid == CLSID_CoTest)
{
CoTestFactory *pTestFact = new CoTestFactory();
HRESULT res = pTestFact->QueryInterface(riid, ppv);
if(res != S_OK) delete pTestFact;
return res;
}
else
{
return CLASS_E_CLASSNOTAVAILABLE;
}
}
STDMETHODIMP DllCanUnloadNow()
{
if(g_lckcnt == 0 && g_objcnt == 0) return S_OK; else return S_FALSE;
}
Test.def:
LIBRARY "Test"
EXPORTS
DllGetClassObject PRIVATE
DllCanUnloadNow PRIVATE
TestMarshal.cpp:
// dummy to get filename correct
TestMarshal.def:
LIBRARY "TestMarshal"
EXPORTS
DllGetClassObject PRIVATE
DllCanUnloadNow PRIVATE
DllRegisterServer PRIVATE
DllUnregisterServer PRIVATE
Build and registration:
midl Test.idl
cl /LD Test.cpp Test.def oleaut32.lib
cl /DWIN32 /D_WIN32_WINNT=0x0500 /DREGISTER_PROXY_DLL /LD TestMarshal.cpp dlldata.c Test_p.c Test_i.c TestMarshal.def rpcrt4.lib oleaut32.lib
Test.reg
regsvr32 TestMarshal.dll
Test.reg:
REGEDIT
HKEY_CLASSES_ROOT\Test.CoTest\CLSID = {7644e80b-a39e-482a-a9dd-966f48c04cee}
HKEY_CLASSES_ROOT\Wow6432Node\CLSID\{7644e80b-a39e-482a-a9dd-966f48c04cee} = Test.CoTest
HKEY_CLASSES_ROOT\Wow6432Node\CLSID\{7644e80b-a39e-482a-a9dd-966f48c04cee}\InProcServer32 = C:\Work\com\come\Test.dll
HKEY_CLASSES_ROOT\Wow6432Node\CLSID\{7644e80b-a39e-482a-a9dd-966f48c04cee}\TypeLib = {6fa71121-ca28-4fab-9311-bf6eb36f9eae}
HKEY_CLASSES_ROOT\Wow6432Node\TypeLib\{6fa71121-ca28-4fab-9311-bf6eb36f9eae}\1.0 = Test Library
HKEY_CLASSES_ROOT\Wow6432Node\TypeLib\{6fa71121-ca28-4fab-9311-bf6eb36f9eae}\1.0\0\Win32 = C:\Work\com\come\Test.tlb
HKEY_CLASSES_ROOT\Wow6432Node\TypeLib\{6fa71121-ca28-4fab-9311-bf6eb36f9eae}\1.0\FLAGS = 0
HKEY_CLASSES_ROOT\Wow6432Node\TypeLib\{6fa71121-ca28-4fab-9311-bf6eb36f9eae}\1.0\HELPDIR
TestCPP1.cpp:
#include <stdio.h>
#include <stdlib.h>
#include <windows.h>
#include <initguid.h>
#include <tchar.h>
#include "TestEx.h"
void ReturnCheck(LPTSTR func, HRESULT res)
{
if(res != S_OK)
{
TCHAR buffer[1000];
FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, 0, res, 0, buffer, sizeof(buffer), 0);
_tprintf("%s: %s\n", func, buffer);
exit(1);
}
}
int main()
{
HRESULT res;
CoInitialize(NULL);
printf("COM/C++ with CoGetClassObject and CreateInstance\n");
IClassFactory *pCF;
res = CoGetClassObject(CLSID_CoTest, CLSCTX_INPROC_SERVER, NULL, IID_IClassFactory, (void **)&pCF);
ReturnCheck(_T("CoGetClassObject"), res);
ITest *pTest;
res = pCF->CreateInstance(NULL, IID_ITest, (void **)&pTest);
ReturnCheck(_T("CreateInstance"), res);
long int v;
res = pTest->Add(123, 456, &v);
ReturnCheck(_T("Add"), res);
printf("123 + 456 = %d\n", v);
BSTR s1 = SysAllocString(L"ABC");
BSTR s2 = SysAllocString(L"XYZ");
BSTR s3;
res = pTest->Concat(s1, s2, &s3);
ReturnCheck(_T("Concat"), res);
printf("%d %ls + %d %ls = %d %ls\n", SysStringLen(s1), s1, SysStringLen(s2), s2, SysStringLen(s3), s3);
SysFreeString(s1);
SysFreeString(s2);
SysFreeString(s3);
pTest->Release();
pCF->Release();
CoUninitialize();
return 0;
}
Build and run:
cl /EHsc TestCPP1.cpp ole32.lib oleaut32.lib
TestCPP1
TestCPP2.cpp:
#include <stdio.h>
#include <stdlib.h>
#include <windows.h>
#include <initguid.h>
#include <tchar.h>
#include "TestEx.h"
void ReturnCheck(LPTSTR func, HRESULT res)
{
if(res != S_OK)
{
TCHAR buffer[1000];
FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, 0, res, 0, buffer, sizeof(buffer), 0);
_tprintf("%s: %s\n", func, buffer);
exit(1);
}
}
int main()
{
HRESULT res;
CoInitialize(NULL);
printf("COM/C++ with CoCreateInstance\n");
ITest *pTest;
res = CoCreateInstance(CLSID_CoTest, NULL, CLSCTX_INPROC_SERVER, IID_ITest, (void**)&pTest);
ReturnCheck(_T("CoCreateInstance"), res);
long int v;
res = pTest->Add(123, 456, &v);
ReturnCheck(_T("Add"), res);
printf("123 + 456 = %d\n", v);
BSTR s1 = SysAllocString(L"ABC");
BSTR s2 = SysAllocString(L"XYZ");
BSTR s3;
res = pTest->Concat(s1, s2, &s3);
ReturnCheck(_T("Concat"), res);
printf("%d %ls + %d %ls = %d %ls\n", SysStringLen(s1), s1, SysStringLen(s2), s2, SysStringLen(s3), s3);
SysFreeString(s1);
SysFreeString(s2);
SysFreeString(s3);
pTest->Release();
CoUninitialize();
return 0;
}
Build and run:
cl /EHsc TestCPP2.cpp ole32.lib oleaut32.lib
TestCPP2
TestC.c:
#include <stdio.h>
#include <stdlib.h>
#include <windows.h>
#include <initguid.h>
#include <tchar.h>
#define COBJMACROS
#include "Test.h"
void ReturnCheck(LPTSTR func, HRESULT res)
{
if(res != S_OK)
{
TCHAR buffer[1000];
FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, 0, res, 0, buffer, sizeof(buffer), 0);
_tprintf("%s: %s\n", func, buffer);
exit(1);
}
}
int main()
{
HRESULT res;
ITest *pTest;
int v;
BSTR s1, s2, s3;
CoInitialize(NULL);
printf("COM/C:\n");
res = CoCreateInstance(&CLSID_CoTest, NULL, CLSCTX_INPROC_SERVER, &IID_ITest, (void**)&pTest);
ReturnCheck(_T("CoCreateInstance"), res);
res = ITest_Add(pTest, 123, 456, &v);
ReturnCheck(_T("Add"), res);
printf("123 + 345 = %d\n", v);
s1 = SysAllocString(L"ABC");
s2 = SysAllocString(L"XYZ");
res = ITest_Concat(pTest, s1, s2, &s3);
ReturnCheck(_T("Concat"), res);
printf("%d %ls + %d %ls = %d %ls\n", SysStringLen(s1), s1, SysStringLen(s2), s2, SysStringLen(s3), s3);
SysFreeString(s1);
SysFreeString(s2);
SysFreeString(s3);
ITest_Release(pTest);
CoUninitialize();
return 0;
}
Build and run:
cl TestC.c Test_i.c ole32.lib oleaut32.lib
TestC
What is happening:
TestCPP3.cpp:
#include <stdio.h>
#include <windows.h>
#include <initguid.h>
#include <tchar.h>
#import "Test.tlb" no_namespace named_guids
void ReturnCheck(LPTSTR func, HRESULT res)
{
if(res != S_OK)
{
TCHAR buffer[1000];
FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, 0, res, 0, buffer, sizeof(buffer), 0);
_tprintf("%s: %s\n", func, buffer);
exit(1);
}
}
int main()
{
HRESULT res;
CoInitialize(NULL);
printf("COM/C++ with CreateInstance:\n");
try
{
ITestPtr spTest;
res = spTest.CreateInstance(CLSID_CoTest);
ReturnCheck(_T("CreateInstance"), res);
long v = spTest->Add(123L, 456L);
printf("123 + 345 = %d\n", v);
BSTR s1 = SysAllocString(L"ABC");
BSTR s2 = SysAllocString(L"XYZ");
BSTR s3 = spTest->Concat(s1, s2);
printf("%d %ls + %d %ls = %d %ls\n", SysStringLen(s1), s1, SysStringLen(s2), s2, SysStringLen(s3), s3);
SysFreeString(s1);
SysFreeString(s2);
SysFreeString(s3);
spTest = NULL;
}
catch (_com_error er)
{
_tprintf("%s\n", er.ErrorMessage());
}
CoUninitialize();
return 0;
}
Build and run:
cl /EHsc TestCPP3.cpp ole32.lib oleaut32.lib
TestCPP3
TestCPP4.cpp:
#include <stdio.h>
#include <windows.h>
#include <initguid.h>
#include <tchar.h>
#import "Test.tlb" no_namespace named_guids
void ReturnCheck(LPTSTR func, HRESULT res)
{
if(res != S_OK)
{
TCHAR buffer[1000];
FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, 0, res, 0, buffer, sizeof(buffer), 0);
_tprintf("%s: %s\n", func, buffer);
exit(1);
}
}
int main()
{
HRESULT res;
CoInitialize(NULL);
printf("COM/C++ with constructor:\n");
try
{
ITestPtr spTest(__uuidof(CoTest));
long v = spTest->Add(123, 456);
printf("123 + 345 = %d\n", v);
BSTR s1 = SysAllocString(L"ABC");
BSTR s2 = SysAllocString(L"XYZ");
BSTR s3 = spTest->Concat(s1, s2);
printf("%d %ls + %d %ls = %d %ls\n", SysStringLen(s1), s1, SysStringLen(s2), s2, SysStringLen(s3), s3);
SysFreeString(s1);
SysFreeString(s2);
SysFreeString(s3);
spTest = NULL;
}
catch (_com_error er)
{
_tprintf("%s\n", er.ErrorMessage());
}
CoUninitialize();
return 0;
}
Build and run:
cl /EHsc TestCPP4.cpp ole32.lib oleaut32.lib
TestCPP4
TestCS.cpp:
using System;
using Test;
public class MainClass
{
public static void Main(string[] args)
{
Console.WriteLine("COM/C# static");
CoTest cotest = new CoTestClass();
ITest test = (ITest)cotest;
Console.WriteLine("123 + 456 = {0}", test.Add(123, 456));
Console.WriteLine("ABC + XYZ = {0}", test.Concat("ABC", "XYZ"));
}
}
Build and run:
tlbimp /out=TestWrap.dll /namespace:Test Test.tlb
csc /platform:x86 /r:TestWrap.dll TestCS.cs
TestCS
What is happening:
In this iteration we will:
Test.idl:
import "oaidl.idl";
// ITest
[object, uuid(41AD7CCD-365F-42ff-BA57-F4E6849D418F), oleautomation]
interface ITest : IUnknown
{
HRESULT Add([in] long a, [in] long b, [out,retval] long *c);
HRESULT Concat([in] BSTR a, [in] BSTR b, [out,retval]BSTR *c);
}
// Test library
[uuid(BBA1FF63-7EA0-463f-85C6-42E1105BCA71), version(1.0)]
library TestLibrary
{
importlib("stdole32.tlb");
[uuid(F658A1B1-E5A2-4f12-8054-B6167930E9EC)]
coclass CoTest
{
[default] interface ITest;
};
};
TestEx.h:
#ifndef TESTEX_H
#define TESTEX_H
#include <windows.h>
#include "Test_i.c"
#include "Test.h"
class CoTest : public ITest
{
private:
LONG volatile m_refcnt;
public:
CoTest();
virtual ~CoTest();
// IUnknown
STDMETHODIMP QueryInterface(REFIID riid, void **ppv);
STDMETHODIMP_(ULONG) AddRef();
STDMETHODIMP_(ULONG) Release();
// ITest
STDMETHODIMP Add(long a, long b, long *c);
STDMETHODIMP Concat(BSTR a, BSTR b, BSTR *c);
};
class CoTestFactory : IClassFactory
{
private:
LONG volatile m_refcnt;
public:
CoTestFactory();
// IUnknown
STDMETHODIMP QueryInterface(REFIID riid, void **ppv);
STDMETHODIMP_(ULONG) AddRef();
STDMETHODIMP_(ULONG) Release();
// IClassFactory
STDMETHODIMP CreateInstance(LPUNKNOWN pUnk, REFIID riid, void **ppv);
STDMETHODIMP LockServer(BOOL fLock);
};
#endif // TESTEX_H
Test.cpp:
#include <stdio.h>
#include <windows.h>
#include <oleauto.h>
#include <initguid.h>
#include "TestEx.h"
static LONG volatile g_lckcnt = 0;
static LONG volatile g_objcnt = 0;
CoTest::CoTest()
{
#ifdef DEBUG
printf("CoTest ctor\n");
#endif
m_refcnt = 0;
InterlockedIncrement(&g_objcnt);
}
CoTest::~CoTest()
{
#ifdef DEBUG
printf("CoTest dtor\n");
#endif
InterlockedDecrement(&g_objcnt);
}
STDMETHODIMP CoTest::QueryInterface(REFIID riid, void **ppv)
{
if(riid == IID_IUnknown)
{
#ifdef DEBUG
printf("CoTest QueryInterface IUnknown\n");
#endif
*ppv = (IUnknown*)this;
((IUnknown*)(*ppv))->AddRef();
return S_OK;
}
else if(riid == IID_ITest)
{
#ifdef DEBUG
printf("CoTest QueryInterface ITest\n");
#endif
*ppv = (ITest*)this;
((ITest*)(*ppv))->AddRef();
return S_OK;
}
else
{
*ppv = NULL;
return E_NOINTERFACE;
}
}
STDMETHODIMP_(ULONG) CoTest::AddRef()
{
#ifdef DEBUG
printf("CoTest AddRef\n");
#endif
return InterlockedIncrement(&m_refcnt);
}
STDMETHODIMP_(ULONG) CoTest::Release()
{
#ifdef DEBUG
printf("CoTest Release\n");
#endif
InterlockedDecrement(&m_refcnt);
if(m_refcnt == 0)
{
delete this;
return 0;
}
else
{
return m_refcnt;
}
}
STDMETHODIMP CoTest::Add(long a, long b, long *c)
{
*c = a + b;
return S_OK;
}
STDMETHODIMP CoTest::Concat(BSTR a, BSTR b, BSTR *c)
{
OLECHAR *buf = new OLECHAR[SysStringLen(a) + SysStringLen(b) + 1];
wcscpy(buf, a);
wcscat(buf, b);
*c = SysAllocString(buf);
delete[] buf;
return S_OK;
}
CoTestFactory::CoTestFactory()
{
m_refcnt = 0;
}
STDMETHODIMP CoTestFactory::QueryInterface(REFIID riid, void **ppv)
{
if(riid == IID_IUnknown)
{
*ppv = (IUnknown*)this;
((IUnknown*)(*ppv))->AddRef();
return S_OK;
}
else if(riid == IID_IClassFactory)
{
*ppv = (IClassFactory*)this;
((IClassFactory*)(*ppv))->AddRef();
return S_OK;
}
else
{
*ppv = NULL;
return E_NOINTERFACE;
}
}
STDMETHODIMP_(ULONG) CoTestFactory::AddRef()
{
return InterlockedIncrement(&m_refcnt);
}
STDMETHODIMP_(ULONG) CoTestFactory::Release()
{
InterlockedDecrement(&m_refcnt);
if(m_refcnt == 0)
{
delete this;
return 0;
}
else
{
return m_refcnt;
}
}
STDMETHODIMP CoTestFactory::CreateInstance(LPUNKNOWN pUnk, REFIID riid, void **ppv)
{
#ifdef DEBUG
printf("CoTestFactory CreateInstance\n");
#endif
if(pUnk != NULL) return CLASS_E_NOAGGREGATION;
CoTest *pTest = new CoTest();
HRESULT res = pTest->QueryInterface(riid, ppv);
if(res != S_OK) delete pTest;
return res;
}
STDMETHODIMP CoTestFactory::LockServer(BOOL fLock)
{
if(fLock) InterlockedIncrement(&g_lckcnt); else InterlockedDecrement(&g_lckcnt);
return S_OK;
}
STDMETHODIMP DllGetClassObject(REFCLSID rclsid, REFIID riid, void **ppv)
{
#ifdef DEBUG
printf("DllGetClassObject\n");
#endif
if(rclsid == CLSID_CoTest)
{
CoTestFactory *pTestFact = new CoTestFactory();
HRESULT res = pTestFact->QueryInterface(riid, ppv);
if(res != S_OK) delete pTestFact;
return res;
}
else
{
return CLASS_E_CLASSNOTAVAILABLE;
}
}
STDMETHODIMP DllCanUnloadNow()
{
if(g_lckcnt == 0 && g_objcnt == 0) return S_OK; else return S_FALSE;
}
STDMETHODIMP DllRegisterServer()
{
ITypeLib *pTLib = NULL;
LoadTypeLibEx(L"Test.tlb", REGKIND_REGISTER, &pTLib);
pTLib->Release();
return S_OK;
}
STDMETHODIMP DllUnregisterServer()
{
UnRegisterTypeLib(LIBID_TestLibrary, 1, 0, LANG_NEUTRAL, SYS_WIN32);
return S_OK;
}
Test.def:
LIBRARY "Test"
EXPORTS
DllGetClassObject PRIVATE
DllCanUnloadNow PRIVATE
DllRegisterServer PRIVATE
DllUnregisterServer PRIVATE
Build and registration:
midl Test.idl
cl /LD Test.cpp Test.def oleaut32.lib
Test.reg
regsvr32 Test.dll
Test.reg:
REGEDIT
HKEY_CLASSES_ROOT\Test.CoTest\CLSID = {f658a1b1-e5a2-4f12-8054-b6167930e9ec}
HKEY_CLASSES_ROOT\Wow6432Node\CLSID\{f658a1b1-e5a2-4f12-8054-b6167930e9ec} = Test.CoTest
HKEY_CLASSES_ROOT\Wow6432Node\CLSID\{f658a1b1-e5a2-4f12-8054-b6167930e9ec}\InProcServer32 = C:\Work\com\comf\Test.dll
HKEY_CLASSES_ROOT\Wow6432Node\CLSID\{f658a1b1-e5a2-4f12-8054-b6167930e9ec}\TypeLib = {bba1ff63-7ea0-463f-85c6-42e1105bca71}
TestCPP1.cpp:
#include <stdio.h>
#include <stdlib.h>
#include <windows.h>
#include <initguid.h>
#include <tchar.h>
#include "TestEx.h"
void ReturnCheck(LPTSTR func, HRESULT res)
{
if(res != S_OK)
{
TCHAR buffer[1000];
FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, 0, res, 0, buffer, sizeof(buffer), 0);
_tprintf("%s: %s\n", func, buffer);
exit(1);
}
}
int main()
{
HRESULT res;
CoInitialize(NULL);
printf("COM/C++ with CoGetClassObject and CreateInstance\n");
IClassFactory *pCF;
res = CoGetClassObject(CLSID_CoTest, CLSCTX_INPROC_SERVER, NULL, IID_IClassFactory, (void **)&pCF);
ReturnCheck(_T("CoGetClassObject"), res);
ITest *pTest;
res = pCF->CreateInstance(NULL, IID_ITest, (void **)&pTest);
ReturnCheck(_T("CreateInstance"), res);
long int v;
res = pTest->Add(123, 456, &v);
ReturnCheck(_T("Add"), res);
printf("123 + 456 = %d\n", v);
BSTR s1 = SysAllocString(L"ABC");
BSTR s2 = SysAllocString(L"XYZ");
BSTR s3;
res = pTest->Concat(s1, s2, &s3);
ReturnCheck(_T("Concat"), res);
printf("%d %ls + %d %ls = %d %ls\n", SysStringLen(s1), s1, SysStringLen(s2), s2, SysStringLen(s3), s3);
SysFreeString(s1);
SysFreeString(s2);
SysFreeString(s3);
pTest->Release();
pCF->Release();
CoUninitialize();
return 0;
}
Build and run:
cl /EHsc TestCPP1.cpp ole32.lib oleaut32.lib
TestCPP1
TestCPP2.cpp:
#include <stdio.h>
#include <stdlib.h>
#include <windows.h>
#include <initguid.h>
#include <tchar.h>
#include "TestEx.h"
void ReturnCheck(LPTSTR func, HRESULT res)
{
if(res != S_OK)
{
TCHAR buffer[1000];
FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, 0, res, 0, buffer, sizeof(buffer), 0);
_tprintf("%s: %s\n", func, buffer);
exit(1);
}
}
int main()
{
HRESULT res;
CoInitialize(NULL);
printf("COM/C++ with CoCreateInstance\n");
ITest *pTest;
res = CoCreateInstance(CLSID_CoTest, NULL, CLSCTX_INPROC_SERVER, IID_ITest, (void**)&pTest);
ReturnCheck(_T("CoCreateInstance"), res);
long int v;
res = pTest->Add(123, 456, &v);
ReturnCheck(_T("Add"), res);
printf("123 + 456 = %d\n", v);
BSTR s1 = SysAllocString(L"ABC");
BSTR s2 = SysAllocString(L"XYZ");
BSTR s3;
res = pTest->Concat(s1, s2, &s3);
ReturnCheck(_T("Concat"), res);
printf("%d %ls + %d %ls = %d %ls\n", SysStringLen(s1), s1, SysStringLen(s2), s2, SysStringLen(s3), s3);
SysFreeString(s1);
SysFreeString(s2);
SysFreeString(s3);
pTest->Release();
CoUninitialize();
return 0;
}
Build and run:
cl /EHsc TestCPP2.cpp ole32.lib oleaut32.lib
TestCPP2
TestC.c:
#include <stdio.h>
#include <stdlib.h>
#include <windows.h>
#include <initguid.h>
#include <tchar.h>
#define COBJMACROS
#include "Test.h"
void ReturnCheck(LPTSTR func, HRESULT res)
{
if(res != S_OK)
{
TCHAR buffer[1000];
FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, 0, res, 0, buffer, sizeof(buffer), 0);
_tprintf("%s: %s\n", func, buffer);
exit(1);
}
}
int main()
{
HRESULT res;
ITest *pTest;
int v;
BSTR s1, s2, s3;
CoInitialize(NULL);
printf("COM/C:\n");
res = CoCreateInstance(&CLSID_CoTest, NULL, CLSCTX_INPROC_SERVER, &IID_ITest, (void**)&pTest);
ReturnCheck(_T("CoCreateInstance"), res);
res = ITest_Add(pTest, 123, 456, &v);
ReturnCheck(_T("Add"), res);
printf("123 + 345 = %d\n", v);
s1 = SysAllocString(L"ABC");
s2 = SysAllocString(L"XYZ");
res = ITest_Concat(pTest, s1, s2, &s3);
ReturnCheck(_T("Concat"), res);
printf("%d %ls + %d %ls = %d %ls\n", SysStringLen(s1), s1, SysStringLen(s2), s2, SysStringLen(s3), s3);
SysFreeString(s1);
SysFreeString(s2);
SysFreeString(s3);
ITest_Release(pTest);
CoUninitialize();
return 0;
}
Build and run:
cl TestC.c Test_i.c ole32.lib oleaut32.lib
TestC
What is happening:
TestCPP3.cpp:
#include <stdio.h>
#include <windows.h>
#include <initguid.h>
#include <tchar.h>
#import "Test.tlb" no_namespace named_guids
void ReturnCheck(LPTSTR func, HRESULT res)
{
if(res != S_OK)
{
TCHAR buffer[1000];
FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, 0, res, 0, buffer, sizeof(buffer), 0);
_tprintf("%s: %s\n", func, buffer);
exit(1);
}
}
int main()
{
HRESULT res;
CoInitialize(NULL);
printf("COM/C++ with CreateInstance:\n");
try
{
ITestPtr spTest;
res = spTest.CreateInstance(CLSID_CoTest);
ReturnCheck(_T("CreateInstance"), res);
long v = spTest->Add(123L, 456L);
printf("123 + 345 = %d\n", v);
BSTR s1 = SysAllocString(L"ABC");
BSTR s2 = SysAllocString(L"XYZ");
BSTR s3 = spTest->Concat(s1, s2);
printf("%d %ls + %d %ls = %d %ls\n", SysStringLen(s1), s1, SysStringLen(s2), s2, SysStringLen(s3), s3);
SysFreeString(s1);
SysFreeString(s2);
SysFreeString(s3);
spTest = NULL;
}
catch (_com_error er)
{
_tprintf("%s\n", er.ErrorMessage());
}
CoUninitialize();
return 0;
}
Build and run:
cl /EHsc TestCPP3.cpp ole32.lib oleaut32.lib
TestCPP3
TestCPP4.cpp:
#include <stdio.h>
#include <windows.h>
#include <initguid.h>
#include <tchar.h>
#import "Test.tlb" no_namespace named_guids
void ReturnCheck(LPTSTR func, HRESULT res)
{
if(res != S_OK)
{
TCHAR buffer[1000];
FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, 0, res, 0, buffer, sizeof(buffer), 0);
_tprintf("%s: %s\n", func, buffer);
exit(1);
}
}
int main()
{
HRESULT res;
CoInitialize(NULL);
printf("COM/C++ with constructor:\n");
try
{
ITestPtr spTest(__uuidof(CoTest));
long v = spTest->Add(123, 456);
printf("123 + 345 = %d\n", v);
BSTR s1 = SysAllocString(L"ABC");
BSTR s2 = SysAllocString(L"XYZ");
BSTR s3 = spTest->Concat(s1, s2);
printf("%d %ls + %d %ls = %d %ls\n", SysStringLen(s1), s1, SysStringLen(s2), s2, SysStringLen(s3), s3);
SysFreeString(s1);
SysFreeString(s2);
SysFreeString(s3);
spTest = NULL;
}
catch (_com_error er)
{
_tprintf("%s\n", er.ErrorMessage());
}
CoUninitialize();
return 0;
}
Build and run:
cl /EHsc TestCPP4.cpp ole32.lib oleaut32.lib
TestCPP4
TestCS.cpp:
using System;
using Test;
public class MainClass
{
public static void Main(string[] args)
{
Console.WriteLine("COM/C# static");
CoTest cotest = new CoTestClass();
ITest test = (ITest)cotest;
Console.WriteLine("123 + 456 = {0}", test.Add(123, 456));
Console.WriteLine("ABC + XYZ = {0}", test.Concat("ABC", "XYZ"));
}
}
Build and run:
tlbimp /out=TestWrap.dll /namespace:Test Test.tlb
csc /platform:x86 /r:TestWrap.dll TestCS.cs
TestCS
What is happening:
In this iteration we will:
Test.idl:
import "oaidl.idl";
// ITest
[object, uuid(40237A9C-9766-4097-AA67-2844AC846A6F), oleautomation, dual]
interface ITest : IDispatch
{
[id(1)] HRESULT Add([in] long a, [in] long b, [out,retval] long *c);
[id(2)] HRESULT Concat([in] BSTR a, [in] BSTR b, [out,retval]BSTR *c);
}
// Test library
[uuid(CC9EC8D0-1AAA-4e32-97A3-9E875EB7D744), version(1.0)]
library TestLibrary
{
importlib("stdole32.tlb");
[uuid(FC2AE506-88F3-4f00-9755-76768114617A)]
coclass CoTest
{
[default] interface ITest;
};
};
Interface declare methods:
[id(membernumber)] HRESULT methodname([in] input_1_type input_1_name, [in] input_2_type input_1_name, ..., [out,retval] result_type *result_name)
TestEx.h:
#ifndef TESTEX_H
#define TESTEX_H
#include <windows.h>
#include "Test_i.c"
#include "Test.h"
class CoTest : public ITest
{
private:
LONG volatile m_refcnt;
public:
CoTest();
virtual ~CoTest();
// IUnknown
STDMETHODIMP QueryInterface(REFIID riid, void **ppv);
STDMETHODIMP_(ULONG) AddRef();
STDMETHODIMP_(ULONG) Release();
// IDispatch
STDMETHODIMP GetTypeInfoCount(UINT *pctinfo);
STDMETHODIMP GetTypeInfo(UINT iTInfo, LCID lcid, ITypeInfo **ppTInfo);
STDMETHODIMP GetIDsOfNames(REFIID riid, LPOLESTR *rgszNames, UINT cNames, LCID lcid, DISPID *rgDispId);
STDMETHODIMP Invoke(DISPID dispIdMember, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS *pDispParams, VARIANT *pVarResult, EXCEPINFO *pExceptInfo, UINT *puArgErr);
// ITest
STDMETHODIMP Add(long a, long b, long *c);
STDMETHODIMP Concat(BSTR a, BSTR b, BSTR *c);
};
class CoTestFactory : IClassFactory
{
private:
LONG volatile m_refcnt;
public:
CoTestFactory();
// IUnknown
STDMETHODIMP QueryInterface(REFIID riid, void **ppv);
STDMETHODIMP_(ULONG) AddRef();
STDMETHODIMP_(ULONG) Release();
// IClassFactory
STDMETHODIMP CreateInstance(LPUNKNOWN pUnk, REFIID riid, void **ppv);
STDMETHODIMP LockServer(BOOL fLock);
};
#endif // TESTEX_H
Test.cpp:
#include <stdio.h>
#include <windows.h>
#include <oleauto.h>
#include <initguid.h>
#include "TestEx.h"
#define DISPID_ADD 1
#define DISPID_CONCAT 2
static LONG volatile g_lckcnt = 0;
static LONG volatile g_objcnt = 0;
CoTest::CoTest()
{
#ifdef DEBUG
printf("CoTest ctor\n");
#endif
m_refcnt = 0;
InterlockedIncrement(&g_objcnt);
}
CoTest::~CoTest()
{
#ifdef DEBUG
printf("CoTest dtor\n");
#endif
InterlockedDecrement(&g_objcnt);
}
STDMETHODIMP CoTest::QueryInterface(REFIID riid, void **ppv)
{
if(riid == IID_IUnknown)
{
#ifdef DEBUG
printf("CoTest QueryInterface IUnknown\n");
#endif
*ppv = (IUnknown*)this;
((IUnknown*)(*ppv))->AddRef();
return S_OK;
}
else if(riid == IID_IDispatch)
{
#ifdef DEBUG
printf("CoTest QueryInterface IDispatch\n");
#endif
*ppv = (IDispatch*)this;
((ITest*)(*ppv))->AddRef();
return S_OK;
}
else if(riid == IID_ITest)
{
#ifdef DEBUG
printf("CoTest QueryInterface ITest\n");
#endif
*ppv = (ITest*)this;
((ITest*)(*ppv))->AddRef();
return S_OK;
}
else
{
*ppv = NULL;
return E_NOINTERFACE;
}
}
STDMETHODIMP_(ULONG) CoTest::AddRef()
{
#ifdef DEBUG
printf("CoTest AddRef\n");
#endif
return InterlockedIncrement(&m_refcnt);
}
STDMETHODIMP_(ULONG) CoTest::Release()
{
#ifdef DEBUG
printf("CoTest Release\n");
#endif
InterlockedDecrement(&m_refcnt);
if(m_refcnt == 0)
{
delete this;
return 0;
}
else
{
return m_refcnt;
}
}
STDMETHODIMP CoTest::GetTypeInfoCount(UINT *pctinfo)
{
*pctinfo = 0;
return S_OK;
}
STDMETHODIMP CoTest::GetTypeInfo(UINT iTInfo, LCID lcid, ITypeInfo **ppTInfo)
{
*ppTInfo = NULL;
return E_NOTIMPL;
}
STDMETHODIMP CoTest::GetIDsOfNames(REFIID riid, LPOLESTR *rgszNames, UINT cNames, LCID lcid, DISPID *rgDispId)
{
// max. 1 name per call
if(cNames > 1) return E_INVALIDARG;
if(_wcsicmp(rgszNames[0], L"Add") == 0)
{
#ifdef DEBUG
printf("CoTest GetIDsOfNames Add\n");
#endif
*rgDispId = DISPID_ADD;
}
else if(_wcsicmp(rgszNames[0], L"Concat") == 0)
{
#ifdef DEBUG
printf("CoTest GetIDsOfNames Concat\n");
#endif
*rgDispId = DISPID_CONCAT;
}
else
{
return DISP_E_UNKNOWNNAME;
}
return S_OK;
}
STDMETHODIMP CoTest::Invoke(DISPID dispIdMember, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS *pDispParams, VARIANT *pVarResult, EXCEPINFO *pExceptInfo, UINT *puArgErr)
{
switch(dispIdMember)
{
case DISPID_ADD:
#ifdef DEBUG
printf("CoTest Invoke Add\n");
#endif
long ltmp;
Add(pDispParams->rgvarg[1].lVal, pDispParams->rgvarg[0].lVal, <mp);
pVarResult->vt = VT_I4;
pVarResult->lVal = ltmp;
return S_OK;
case DISPID_CONCAT:
#ifdef DEBUG
printf("CoTest Invoke Concat\n");
#endif
BSTR bstrtmp;
Concat(pDispParams->rgvarg[1].bstrVal, pDispParams->rgvarg[0].bstrVal, &bstrtmp);
pVarResult->vt = VT_BSTR;
pVarResult->bstrVal = bstrtmp;
return S_OK;
default:
return DISP_E_UNKNOWNINTERFACE;
}
}
STDMETHODIMP CoTest::Add(long a, long b, long *c)
{
*c = a + b;
return S_OK;
}
STDMETHODIMP CoTest::Concat(BSTR a, BSTR b, BSTR *c)
{
OLECHAR *buf = new OLECHAR[SysStringLen(a) + SysStringLen(b) + 1];
wcscpy(buf, a);
wcscat(buf, b);
*c = SysAllocString(buf);
delete[] buf;
return S_OK;
}
CoTestFactory::CoTestFactory()
{
m_refcnt = 0;
}
STDMETHODIMP CoTestFactory::QueryInterface(REFIID riid, void **ppv)
{
if(riid == IID_IUnknown)
{
*ppv = (IUnknown*)this;
((IUnknown*)(*ppv))->AddRef();
return S_OK;
}
else if(riid == IID_IClassFactory)
{
*ppv = (IClassFactory*)this;
((IClassFactory*)(*ppv))->AddRef();
return S_OK;
}
else
{
*ppv = NULL;
return E_NOINTERFACE;
}
}
STDMETHODIMP_(ULONG) CoTestFactory::AddRef()
{
return InterlockedIncrement(&m_refcnt);
}
STDMETHODIMP_(ULONG) CoTestFactory::Release()
{
InterlockedDecrement(&m_refcnt);
if(m_refcnt == 0)
{
delete this;
return 0;
}
else
{
return m_refcnt;
}
}
STDMETHODIMP CoTestFactory::CreateInstance(LPUNKNOWN pUnk, REFIID riid, void **ppv)
{
#ifdef DEBUG
printf("CoTestFactory CreateInstance\n");
#endif
if(pUnk != NULL) return CLASS_E_NOAGGREGATION;
CoTest *pTest = new CoTest();
HRESULT res = pTest->QueryInterface(riid, ppv);
if(res != S_OK) delete pTest;
return res;
}
STDMETHODIMP CoTestFactory::LockServer(BOOL fLock)
{
if(fLock) InterlockedIncrement(&g_lckcnt); else InterlockedDecrement(&g_lckcnt);
return S_OK;
}
STDMETHODIMP DllGetClassObject(REFCLSID rclsid, REFIID riid, void **ppv)
{
#ifdef DEBUG
printf("DllGetClassObject\n");
#endif
if(rclsid == CLSID_CoTest)
{
CoTestFactory *pTestFact = new CoTestFactory();
HRESULT res = pTestFact->QueryInterface(riid, ppv);
if(res != S_OK) delete pTestFact;
return res;
}
else
{
return CLASS_E_CLASSNOTAVAILABLE;
}
}
STDMETHODIMP DllCanUnloadNow()
{
if(g_lckcnt == 0 && g_objcnt == 0) return S_OK; else return S_FALSE;
}
STDMETHODIMP DllRegisterServer()
{
ITypeLib *pTLib = NULL;
LoadTypeLibEx(L"Test.tlb", REGKIND_REGISTER, &pTLib);
pTLib->Release();
return S_OK;
}
STDMETHODIMP DllUnregisterServer()
{
UnRegisterTypeLib(LIBID_TestLibrary, 1, 0, LANG_NEUTRAL, SYS_WIN32);
return S_OK;
}
Test.def:
LIBRARY "Test"
EXPORTS
DllGetClassObject PRIVATE
DllCanUnloadNow PRIVATE
DllRegisterServer PRIVATE
DllUnregisterServer PRIVATE
Build and registration:
midl Test.idl
cl /LD Test.cpp Test.def oleaut32.lib
Test.reg
regsvr32 Test.dll
Test.reg:
REGEDIT
HKEY_CLASSES_ROOT\Test.CoTest\CLSID = {fc2ae506-88f3-4f00-9755-76768114617a}
HKEY_CLASSES_ROOT\Wow6432Node\Test.CoTest\CLSID = {fc2ae506-88f3-4f00-9755-76768114617a}
HKEY_CLASSES_ROOT\Wow6432Node\CLSID\{fc2ae506-88f3-4f00-9755-76768114617a} = Test.CoTest
HKEY_CLASSES_ROOT\Wow6432Node\CLSID\{fc2ae506-88f3-4f00-9755-76768114617a}\InProcServer32 = C:\Work\com\comg\Test.dll
HKEY_CLASSES_ROOT\Wow6432Node\CLSID\{fc2ae506-88f3-4f00-9755-76768114617a}\TypeLib = {cc9ec8d0-1aaa-4e32-97a3-9e875eb7d744}
TestC.c:
#include <stdio.h>
#include <stdlib.h>
#include <windows.h>
#include <initguid.h>
#include <tchar.h>
#define COBJMACROS
#include "Test.h"
void ReturnCheck(LPTSTR func, HRESULT res)
{
if(res != S_OK)
{
TCHAR buffer[1000];
FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, 0, res, 0, buffer, sizeof(buffer), 0);
_tprintf("%s: %s\n", func, buffer);
exit(1);
}
}
int main()
{
HRESULT res;
ITest *pTest;
int v;
BSTR s1, s2, s3;
CoInitialize(NULL);
printf("COM/C:\n");
res = CoCreateInstance(&CLSID_CoTest, NULL, CLSCTX_INPROC_SERVER, &IID_ITest, (void**)&pTest);
ReturnCheck(_T("CoCreateInstance"), res);
res = ITest_Add(pTest, 123, 456, &v);
ReturnCheck(_T("Add"), res);
printf("123 + 345 = %d\n", v);
s1 = SysAllocString(L"ABC");
s2 = SysAllocString(L"XYZ");
res = ITest_Concat(pTest, s1, s2, &s3);
ReturnCheck(_T("Concat"), res);
printf("%d %ls + %d %ls = %d %ls\n", SysStringLen(s1), s1, SysStringLen(s2), s2, SysStringLen(s3), s3);
SysFreeString(s1);
SysFreeString(s2);
SysFreeString(s3);
ITest_Release(pTest);
CoUninitialize();
return 0;
}
Build and run:
cl TestC.c Test_i.c ole32.lib oleaut32.lib
TestC
What is happening:
TestCPP3.cpp:
#include <stdio.h>
#include <windows.h>
#include <initguid.h>
#include <tchar.h>
#import "Test.tlb" no_namespace named_guids
void ReturnCheck(LPTSTR func, HRESULT res)
{
if(res != S_OK)
{
TCHAR buffer[1000];
FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, 0, res, 0, buffer, sizeof(buffer), 0);
_tprintf("%s: %s\n", func, buffer);
exit(1);
}
}
int main()
{
HRESULT res;
CoInitialize(NULL);
printf("COM/C++ with CreateInstance:\n");
try
{
ITestPtr spTest;
res = spTest.CreateInstance(CLSID_CoTest);
ReturnCheck(_T("CreateInstance"), res);
long v = spTest->Add(123L, 456L);
printf("123 + 345 = %d\n", v);
BSTR s1 = SysAllocString(L"ABC");
BSTR s2 = SysAllocString(L"XYZ");
BSTR s3 = spTest->Concat(s1, s2);
printf("%d %ls + %d %ls = %d %ls\n", SysStringLen(s1), s1, SysStringLen(s2), s2, SysStringLen(s3), s3);
SysFreeString(s1);
SysFreeString(s2);
SysFreeString(s3);
spTest = NULL;
}
catch (_com_error er)
{
_tprintf("%s\n", er.ErrorMessage());
}
CoUninitialize();
return 0;
}
Build and run:
cl /EHsc TestCPP3.cpp ole32.lib oleaut32.lib
TestCPP3
TestCPP4.cpp:
#include <stdio.h>
#include <windows.h>
#include <initguid.h>
#include <tchar.h>
#import "Test.tlb" no_namespace named_guids
void ReturnCheck(LPTSTR func, HRESULT res)
{
if(res != S_OK)
{
TCHAR buffer[1000];
FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, 0, res, 0, buffer, sizeof(buffer), 0);
_tprintf("%s: %s\n", func, buffer);
exit(1);
}
}
int main()
{
HRESULT res;
CoInitialize(NULL);
printf("COM/C++ with constructor:\n");
try
{
ITestPtr spTest(__uuidof(CoTest));
long v = spTest->Add(123, 456);
printf("123 + 345 = %d\n", v);
BSTR s1 = SysAllocString(L"ABC");
BSTR s2 = SysAllocString(L"XYZ");
BSTR s3 = spTest->Concat(s1, s2);
printf("%d %ls + %d %ls = %d %ls\n", SysStringLen(s1), s1, SysStringLen(s2), s2, SysStringLen(s3), s3);
SysFreeString(s1);
SysFreeString(s2);
SysFreeString(s3);
spTest = NULL;
}
catch (_com_error er)
{
_tprintf("%s\n", er.ErrorMessage());
}
CoUninitialize();
return 0;
}
Build and run:
cl /EHsc TestCPP4.cpp ole32.lib oleaut32.lib
TestCPP4
TestCS.cpp:
using System;
using Test;
public class MainClass
{
public static void Main(string[] args)
{
Console.WriteLine("COM/C# static");
CoTest cotest = new CoTestClass();
ITest test = (ITest)cotest;
Console.WriteLine("123 + 456 = {0}", test.Add(123, 456));
Console.WriteLine("ABC + XYZ = {0}", test.Concat("ABC", "XYZ"));
}
}
Build and run:
tlbimp /out=TestWrap.dll /namespace:Test Test.tlb
csc /platform:x86 /r:TestWrap.dll TestCS.cs
TestCS
TestPas.pas:
program TestPas;
{$mode delphi}{$H+}
uses
ActiveX, TestLibrary_1_0_TLB;
var
test : ITest;
begin
writeln('COM/Pascal static:');
CoInitialize(nil);
test := CoCoTest.Create;
writeln('123 + 456 = ', test.Add(123, 456));
writeln('ABC + XYZ = ', test.Concat('ABC', 'XYZ'));
CoUninitialize;
end.
Build and run:
importtl Test.tlb
fpc TestLibrary_1_0_TLB.pas
fpc TestPas.pas
TestPas
What is happening:
TestVBS.vbs:
WScript.Echo "COM/VBS:"
Set test = CreateObject("Test.CoTest")
WScript.Echo "123 + 456 = " & CStr(test.Add(CLng(123), CLng(456)))
WScript.Echo "ABC + XYZ = " & test.Concat("ABC", "XYZ")
Set test = Nothing
Build and run:
cscript TestVBS.vbs
TestCSDyn.cs:
using System;
public class MainClass
{
public static void Main(string[] args)
{
Console.WriteLine("COM/C# dynamic");
dynamic test = Activator.CreateInstance(Type.GetTypeFromProgID("Test.CoTest"));
Console.WriteLine("123 + 456 = {0}", test.Add(123, 456));
Console.WriteLine("ABC + XYZ = {0}", test.Concat("ABC", "XYZ"));
}
}
Build and run:
csc /platform:x86 TestCSDyn.cs
TestCSDyn
TestPasDyn.pas:
program TestPasDyn;
uses
ActiveX,ComObj;
var
test : Variant;
begin
writeln('COM/Pascal dynamic:');
CoInitialize(nil);
test := CreateOLEObject('Test.CoTest');
writeln('123 + 456 = ', test.Add(123, 456));
writeln('ABC + XYZ = ', test.Concat('ABC', 'XYZ'));
CoUninitialize;
end.
Build and run:
fpc TestPasDyn.pas
TestPasDyn
TestJava.java:
import com.jacob.activeX.ActiveXComponent;
import com.jacob.com.Variant;
public class TestJava {
public static void main(String[] args) {
System.out.println("COM/Java with Jacob ActiveXComponent");
ActiveXComponent test = new ActiveXComponent("Test.CoTest");
System.out.println("123 + 456 = " + test.invoke("Add", new Variant(123), new Variant(456)).getInt());
System.out.println("ABC + XYZ = " + test.invoke("Concat", new Variant("ABC"), new Variant("XYZ")).getString());
}
}
Build and run:
javac -cp %JACOBDIR%\jacob.jar TestJava.java
java -Djava.library.path=%JACOBDIR% -cp .;%JACOBDIR%\jacob.jar TestJava
Nobody will use IDispatch with C++, but it is still relevant to see the code to understand what is happening in other languages.
TestCPP5.cpp:
#include <stdio.h>
#include <windows.h>
#include <initguid.h>
#include <tchar.h>
#import "Test.tlb" no_namespace named_guids
void ReturnCheck(LPTSTR func, HRESULT res)
{
if(res != S_OK)
{
TCHAR buffer[1000];
FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, 0, res, 0, buffer, sizeof(buffer), 0);
_tprintf("%s: %s\n", func, buffer);
exit(1);
}
}
int main()
{
HRESULT res;
CoInitialize(NULL);
printf("COM/C++ via IDispatch:\n");
CLSID clsid;
res = CLSIDFromProgID(L"Test.CoTest", &CLSID);
ReturnCheck(_T("CLSIDFromProgID"), res);
IDispatch *pDisp = NULL;
res = CoCreateInstance(clsid, NULL, CLSCTX_SERVER, IID_IDispatch, (void **)&pDisp);
ReturnCheck(_T("CoCreateInstance"), res);
DISPID dispid;
LPOLESTR func;
VARIANT args[2], retval;
DISPPARAMS params;
params.rgvarg = args;
params.rgdispidNamedArgs = 0;
params.cArgs = 2;
params.cNamedArgs = 0;
func = L"Add";
res = pDisp->GetIDsOfNames(IID_NULL, &func, 1, LOCALE_SYSTEM_DEFAULT, &dispid);
ReturnCheck(_T("IDispatch GetIDsOfNames"), res);
VariantInit(&args[0]);
args[0].vt = VT_I4;
args[0].lVal = 456;
VariantInit(&args[1]);
args[1].vt = VT_I4;
args[1].lVal = 123;
VariantInit(&retval);
res = pDisp->Invoke(dispid, IID_NULL, LOCALE_SYSTEM_DEFAULT, DISPATCH_METHOD, ¶ms, &retval, NULL, NULL);
ReturnCheck(_T("IDispatch Invoke"), res);
printf("123 + 345 = %d\n", retval.lVal);
func = L"Concat";
res = pDisp->GetIDsOfNames(IID_NULL, &func, 1, LOCALE_SYSTEM_DEFAULT, &dispid);
ReturnCheck(_T("IDispatch GetIDsOfNames"), res);
VariantInit(&args[0]);
args[0].vt = VT_BSTR;
args[0].bstrVal = SysAllocString(L"ABC");
VariantInit(&args[1]);
args[1].vt = VT_BSTR;
args[1].bstrVal = SysAllocString(L"XYZ");
VariantInit(&retval);
res = pDisp->Invoke(dispid, IID_NULL, LOCALE_SYSTEM_DEFAULT, DISPATCH_METHOD, ¶ms, &retval, NULL, NULL);
ReturnCheck(_T("IDispatch Invoke"), res);
printf("3 ABC + 3 XYZ = %d %ls\n", SysStringLen(retval.bstrVal), retval.bstrVal);
pDisp->Release();
CoUninitialize();
return 0;
}
Build and run:
cl /EHsc TestCPP5.cpp ole32.lib oleaut32.lib
TestCPP5
What is happening:
In this iteration we will:
Test.idl:
import "oaidl.idl";
// ITest
[object, uuid(A9296DFF-065A-4591-AE74-D4B0205F6F67), oleautomation, dual]
interface ITest : IDispatch
{
[id(1)] HRESULT Add([in] long a, [in] long b, [out,retval] long *c);
[id(2)] HRESULT Concat([in] BSTR a, [in] BSTR b, [out,retval]BSTR *c);
[id(3),propget] HRESULT IV([out, retval] long *iv);
[id(3),propput] HRESULT IV([in] long iv);
[id(4),propget] HRESULT SV([out, retval] BSTR *sv);
[id(4),propput] HRESULT SV([in] BSTR sv);
}
// Test library
[uuid(1A2EDF64-048D-43d0-BE7E-90C604592280), version(1.0)]
library TestLibrary
{
importlib("stdole32.tlb");
[uuid(575529A2-0360-407f-9133-4DCB664A61A1)]
coclass CoTest
{
[default] interface ITest;
};
};
Interface declare properties:
[id(membernumber,propget)] HRESULT propertyname([out,retval] type *name)
[id(membernumber,propput)] HRESULT propertyname([in] type *_name)
TestEx.h:
#ifndef TESTEX_H
#define TESTEX_H
#include <windows.h>
#include "Test_i.c"
#include "Test.h"
class CoTest : public ITest
{
private:
LONG volatile m_refcnt;
long m_iv;
BSTR m_sv;
public:
CoTest();
virtual ~CoTest();
// IUnknown
STDMETHODIMP QueryInterface(REFIID riid, void **ppv);
STDMETHODIMP_(ULONG) AddRef();
STDMETHODIMP_(ULONG) Release();
// IDispatch
STDMETHODIMP GetTypeInfoCount(UINT *pctinfo);
STDMETHODIMP GetTypeInfo(UINT iTInfo, LCID lcid, ITypeInfo **ppTInfo);
STDMETHODIMP GetIDsOfNames(REFIID riid, LPOLESTR *rgszNames, UINT cNames, LCID lcid, DISPID *rgDispId);
STDMETHODIMP Invoke(DISPID dispIdMember, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS *pDispParams, VARIANT *pVarResult, EXCEPINFO *pExceptInfo, UINT *puArgErr);
// ITest
STDMETHODIMP Add(long a, long b, long *c);
STDMETHODIMP Concat(BSTR a, BSTR b, BSTR *c);
STDMETHODIMP get_IV(long *iv);
STDMETHODIMP put_IV(long iv);
STDMETHODIMP get_SV(BSTR *sv);
STDMETHODIMP put_SV(BSTR sv);
};
class CoTestFactory : IClassFactory
{
private:
LONG volatile m_refcnt;
public:
CoTestFactory();
// IUnknown
STDMETHODIMP QueryInterface(REFIID riid, void **ppv);
STDMETHODIMP_(ULONG) AddRef();
STDMETHODIMP_(ULONG) Release();
// IClassFactory
STDMETHODIMP CreateInstance(LPUNKNOWN pUnk, REFIID riid, void **ppv);
STDMETHODIMP LockServer(BOOL fLock);
};
#endif // TESTEX_H
Test.cpp:
#include <stdio.h>
#include <windows.h>
#include <oleauto.h>
#include <initguid.h>
#include "TestEx.h"
#define DISPID_ADD 1
#define DISPID_CONCAT 2
#define DISPID_IV 3
#define DISPID_SV 4
static LONG volatile g_lckcnt = 0;
static LONG volatile g_objcnt = 0;
CoTest::CoTest()
{
#ifdef DEBUG
printf("CoTest ctor\n");
#endif
m_iv = 0;
m_sv = SysAllocString(L"");
m_refcnt = 0;
InterlockedIncrement(&g_objcnt);
}
CoTest::~CoTest()
{
#ifdef DEBUG
printf("CoTest dtor\n");
#endif
SysFreeString(m_sv);
InterlockedDecrement(&g_objcnt);
}
STDMETHODIMP CoTest::QueryInterface(REFIID riid, void **ppv)
{
if(riid == IID_IUnknown)
{
#ifdef DEBUG
printf("CoTest QueryInterface IUnknown\n");
#endif
*ppv = (IUnknown*)this;
((IUnknown*)(*ppv))->AddRef();
return S_OK;
}
else if(riid == IID_IDispatch)
{
#ifdef DEBUG
printf("CoTest QueryInterface IDispatch\n");
#endif
*ppv = (IDispatch*)this;
((ITest*)(*ppv))->AddRef();
return S_OK;
}
else if(riid == IID_ITest)
{
#ifdef DEBUG
printf("CoTest QueryInterface ITest\n");
#endif
*ppv = (ITest*)this;
((ITest*)(*ppv))->AddRef();
return S_OK;
}
else
{
*ppv = NULL;
return E_NOINTERFACE;
}
}
STDMETHODIMP_(ULONG) CoTest::AddRef()
{
#ifdef DEBUG
printf("CoTest AddRef\n");
#endif
return InterlockedIncrement(&m_refcnt);
}
STDMETHODIMP_(ULONG) CoTest::Release()
{
#ifdef DEBUG
printf("CoTest Release\n");
#endif
InterlockedDecrement(&m_refcnt);
if(m_refcnt == 0)
{
delete this;
return 0;
}
else
{
return m_refcnt;
}
}
STDMETHODIMP CoTest::GetTypeInfoCount(UINT *pctinfo)
{
*pctinfo = 0;
return S_OK;
}
STDMETHODIMP CoTest::GetTypeInfo(UINT iTInfo, LCID lcid, ITypeInfo **ppTInfo)
{
*ppTInfo = NULL;
return E_NOTIMPL;
}
STDMETHODIMP CoTest::GetIDsOfNames(REFIID riid, LPOLESTR *rgszNames, UINT cNames, LCID lcid, DISPID *rgDispId)
{
// max. 1 name per call
if(cNames > 1) return E_INVALIDARG;
if(_wcsicmp(rgszNames[0], L"Add") == 0)
{
#ifdef DEBUG
printf("CoTest GetIDsOfNames Add\n");
#endif
*rgDispId = DISPID_ADD;
}
else if(_wcsicmp(rgszNames[0], L"Concat") == 0)
{
#ifdef DEBUG
printf("CoTest GetIDsOfNames Concat\n");
#endif
*rgDispId = DISPID_CONCAT;
}
else if(_wcsicmp(rgszNames[0], L"IV") == 0)
{
#ifdef DEBUG
printf("CoTest GetIDsOfNames IV\n");
#endif
*rgDispId = DISPID_IV;
}
else if(_wcsicmp(rgszNames[0], L"SV") == 0)
{
#ifdef DEBUG
printf("CoTest GetIDsOfNames SV\n");
#endif
*rgDispId = DISPID_SV;
}
else
{
return DISP_E_UNKNOWNNAME;
}
return S_OK;
}
STDMETHODIMP CoTest::Invoke(DISPID dispIdMember, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS *pDispParams, VARIANT *pVarResult, EXCEPINFO *pExceptInfo, UINT *puArgErr)
{
switch(dispIdMember)
{
case DISPID_ADD:
#ifdef DEBUG
printf("CoTest Invoke Add\n");
#endif
long ltmp;
Add(pDispParams->rgvarg[1].lVal, pDispParams->rgvarg[0].lVal, <mp);
pVarResult->vt = VT_I4;
pVarResult->lVal = ltmp;
return S_OK;
case DISPID_CONCAT:
#ifdef DEBUG
printf("CoTest Invoke Concat\n");
#endif
BSTR bstrtmp;
Concat(pDispParams->rgvarg[1].bstrVal, pDispParams->rgvarg[0].bstrVal, &bstrtmp);
pVarResult->vt = VT_BSTR;
pVarResult->bstrVal = bstrtmp;
return S_OK;
case DISPID_IV:
if(wFlags & DISPATCH_PROPERTYGET)
{
#ifdef DEBUG
printf("CoTest Invoke Get IV\n");
#endif
long ltmp;
get_IV(<mp);
pVarResult->vt = VT_I4;
pVarResult->lVal = ltmp;
}
if(wFlags & DISPATCH_PROPERTYPUT)
{
#ifdef DEBUG
printf("CoTest Invoke Put IV\n");
#endif
if(pDispParams->rgvarg[0].vt == VT_I4)
{
put_IV(pDispParams->rgvarg[0].lVal);
}
else if(pDispParams->rgvarg[0].vt == VT_I2)
{
put_IV(pDispParams->rgvarg[0].iVal);
}
else if(pDispParams->rgvarg[0].vt == VT_I1)
{
put_IV(pDispParams->rgvarg[0].bVal);
}
}
return S_OK;
case DISPID_SV:
if(wFlags & DISPATCH_PROPERTYGET)
{
#ifdef DEBUG
printf("CoTest Invoke Get SV\n");
#endif
BSTR bstrtmp;
get_SV(&bstrtmp);
pVarResult->vt = VT_BSTR;
pVarResult->bstrVal = bstrtmp;
}
if(wFlags & DISPATCH_PROPERTYPUT)
{
#ifdef DEBUG
printf("CoTest Invoke Put SV\n");
#endif
put_SV(pDispParams->rgvarg[0].bstrVal);
}
return S_OK;
default:
return DISP_E_UNKNOWNINTERFACE;
}
}
STDMETHODIMP CoTest::Add(long a, long b, long *c)
{
*c = a + b;
return S_OK;
}
STDMETHODIMP CoTest::Concat(BSTR a, BSTR b, BSTR *c)
{
OLECHAR *buf = new OLECHAR[SysStringLen(a) + SysStringLen(b) + 1];
wcscpy(buf, a);
wcscat(buf, b);
*c = SysAllocString(buf);
delete[] buf;
return S_OK;
}
STDMETHODIMP CoTest::get_IV(long *iv)
{
*iv = m_iv;
return S_OK;
}
STDMETHODIMP CoTest::put_IV(long iv)
{
m_iv = iv;
return S_OK;
}
STDMETHODIMP CoTest::get_SV(BSTR *sv)
{
*sv = SysAllocString(m_sv);
return S_OK;
}
STDMETHODIMP CoTest::put_SV(BSTR sv)
{
SysReAllocString(&m_sv, sv);
return S_OK;
}
CoTestFactory::CoTestFactory()
{
m_refcnt = 0;
}
STDMETHODIMP CoTestFactory::QueryInterface(REFIID riid, void **ppv)
{
if(riid == IID_IUnknown)
{
*ppv = (IUnknown*)this;
((IUnknown*)(*ppv))->AddRef();
return S_OK;
}
else if(riid == IID_IClassFactory)
{
*ppv = (IClassFactory*)this;
((IClassFactory*)(*ppv))->AddRef();
return S_OK;
}
else
{
*ppv = NULL;
return E_NOINTERFACE;
}
}
STDMETHODIMP_(ULONG) CoTestFactory::AddRef()
{
return InterlockedIncrement(&m_refcnt);
}
STDMETHODIMP_(ULONG) CoTestFactory::Release()
{
InterlockedDecrement(&m_refcnt);
if(m_refcnt == 0)
{
delete this;
return 0;
}
else
{
return m_refcnt;
}
}
STDMETHODIMP CoTestFactory::CreateInstance(LPUNKNOWN pUnk, REFIID riid, void **ppv)
{
#ifdef DEBUG
printf("CoTestFactory CreateInstance\n");
#endif
if(pUnk != NULL) return CLASS_E_NOAGGREGATION;
CoTest *pTest = new CoTest();
HRESULT res = pTest->QueryInterface(riid, ppv);
if(res != S_OK) delete pTest;
return res;
}
STDMETHODIMP CoTestFactory::LockServer(BOOL fLock)
{
if(fLock) InterlockedIncrement(&g_lckcnt); else InterlockedDecrement(&g_lckcnt);
return S_OK;
}
STDMETHODIMP DllGetClassObject(REFCLSID rclsid, REFIID riid, void **ppv)
{
#ifdef DEBUG
printf("DllGetClassObject\n");
#endif
if(rclsid == CLSID_CoTest)
{
CoTestFactory *pTestFact = new CoTestFactory();
HRESULT res = pTestFact->QueryInterface(riid, ppv);
if(res != S_OK) delete pTestFact;
return res;
}
else
{
return CLASS_E_CLASSNOTAVAILABLE;
}
}
STDMETHODIMP DllCanUnloadNow()
{
if(g_lckcnt == 0 && g_objcnt == 0) return S_OK; else return S_FALSE;
}
STDMETHODIMP DllRegisterServer()
{
ITypeLib *pTLib = NULL;
LoadTypeLibEx(L"Test.tlb", REGKIND_REGISTER, &pTLib);
pTLib->Release();
return S_OK;
}
STDMETHODIMP DllUnregisterServer()
{
UnRegisterTypeLib(LIBID_TestLibrary, 1, 0, LANG_NEUTRAL, SYS_WIN32);
return S_OK;
}
Test.def:
LIBRARY "Test"
EXPORTS
DllGetClassObject PRIVATE
DllCanUnloadNow PRIVATE
DllRegisterServer PRIVATE
DllUnregisterServer PRIVATE
Build and registration:
midl Test.idl
cl /LD Test.cpp Test.def oleaut32.lib
Test.reg
regsvr32 Test.dll
Test.reg:
REGEDIT
HKEY_CLASSES_ROOT\Test.CoTest\CLSID = {575529a2-0360-407f-9133-4dcb664a61a1}
HKEY_CLASSES_ROOT\Wow6432Node\CLSID\{575529a2-0360-407f-9133-4dcb664a61a1} = Test.CoTest
HKEY_CLASSES_ROOT\Wow6432Node\CLSID\{575529a2-0360-407f-9133-4dcb664a61a1}\InProcServer32 = C:\Work\com\comh\Test.dll
HKEY_CLASSES_ROOT\CLSID\Wow6432Node\{575529a2-0360-407f-9133-4dcb664a61a1}\TypeLib = {1a2edf64-048d-43d0-be7e-90c604592280}
TestC.c:
#include <stdio.h>
#include <stdlib.h>
#include <windows.h>
#include <initguid.h>
#include <tchar.h>
#define COBJMACROS
#include "Test.h"
void ReturnCheck(LPTSTR func, HRESULT res)
{
if(res != S_OK)
{
TCHAR buffer[1000];
FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, 0, res, 0, buffer, sizeof(buffer), 0);
_tprintf("%s: %s\n", func, buffer);
exit(1);
}
}
int main()
{
HRESULT res;
ITest *pTest;
int v, iv;
BSTR s1, s2, s3, sv;
CoInitialize(NULL);
printf("COM/C:\n");
res = CoCreateInstance(&CLSID_CoTest, NULL, CLSCTX_INPROC_SERVER, &IID_ITest, (void**)&pTest);
ReturnCheck(_T("CoCreateInstance"), res);
res = ITest_Add(pTest, 123, 456, &v);
ReturnCheck(_T("Add"), res);
printf("123 + 345 = %d\n", v);
s1 = SysAllocString(L"ABC");
s2 = SysAllocString(L"XYZ");
res = ITest_Concat(pTest, s1, s2, &s3);
ReturnCheck(_T("Concat"), res);
printf("%d %ls + %d %ls = %d %ls\n", SysStringLen(s1), s1, SysStringLen(s2), s2, SysStringLen(s3), s3);
SysFreeString(s1);
SysFreeString(s2);
SysFreeString(s3);
ITest_get_IV(pTest, &iv);
ITest_get_SV(pTest, &sv);
printf("IV = %d SV = %d %ls\n", iv, SysStringLen(sv), sv);
SysFreeString(sv);
ITest_put_IV(pTest, 123);
ITest_put_SV(pTest, SysAllocString(L"ABC"));
ITest_get_IV(pTest, &iv);
ITest_get_SV(pTest, &sv);
printf("IV = %d SV = %d %ls\n", iv, SysStringLen(sv), sv);
SysFreeString(sv);
ITest_Release(pTest);
CoUninitialize();
return 0;
}
Build and run:
cl TestC.c Test_i.c ole32.lib oleaut32.lib
TestC
What is happening:
TestCPP3.cpp:
#include <stdio.h>
#include <windows.h>
#include <initguid.h>
#include <tchar.h>
#import "Test.tlb" no_namespace named_guids
void ReturnCheck(LPTSTR func, HRESULT res)
{
if(res != S_OK)
{
TCHAR buffer[1000];
FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, 0, res, 0, buffer, sizeof(buffer), 0);
_tprintf("%s: %s\n", func, buffer);
exit(1);
}
}
int main()
{
HRESULT res;
CoInitialize(NULL);
printf("COM/C++ with CreateInstance:\n");
try
{
ITestPtr spTest;
res = spTest.CreateInstance(CLSID_CoTest);
ReturnCheck(_T("CreateInstance"), res);
long v = spTest->Add(123L, 456L);
printf("123 + 345 = %d\n", v);
BSTR s1 = SysAllocString(L"ABC");
BSTR s2 = SysAllocString(L"XYZ");
BSTR s3 = spTest->Concat(s1, s2);
printf("%d %ls + %d %ls = %d %ls\n", SysStringLen(s1), s1, SysStringLen(s2), s2, SysStringLen(s3), s3);
SysFreeString(s1);
SysFreeString(s2);
SysFreeString(s3);
BSTR sv = spTest->GetSV();
printf("IV = %d SV = %d %ls\n", spTest->GetIV(), SysStringLen(sv), sv);
SysFreeString(sv);
spTest->PutIV(123);
spTest->PutSV(SysAllocString(L"ABC"));
sv = spTest->GetSV();
printf("IV = %d SV = %d %ls\n", spTest->GetIV(), SysStringLen(sv), sv);
SysFreeString(sv);
spTest = NULL;
}
catch (_com_error er)
{
_tprintf("%s\n", er.ErrorMessage());
}
CoUninitialize();
return 0;
}
Build and run:
cl /EHsc TestCPP3.cpp ole32.lib oleaut32.lib
TestCPP3
TestCPP4.cpp:
#include <stdio.h>
#include <windows.h>
#include <initguid.h>
#include <tchar.h>
#import "Test.tlb" no_namespace named_guids
void ReturnCheck(LPTSTR func, HRESULT res)
{
if(res != S_OK)
{
TCHAR buffer[1000];
FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, 0, res, 0, buffer, sizeof(buffer), 0);
_tprintf("%s: %s\n", func, buffer);
exit(1);
}
}
int main()
{
HRESULT res;
CoInitialize(NULL);
printf("COM/C++ with constructor:\n");
try
{
ITestPtr spTest(__uuidof(CoTest));
long v = spTest->Add(123, 456);
printf("123 + 345 = %d\n", v);
BSTR s1 = SysAllocString(L"ABC");
BSTR s2 = SysAllocString(L"XYZ");
BSTR s3 = spTest->Concat(s1, s2);
printf("%d %ls + %d %ls = %d %ls\n", SysStringLen(s1), s1, SysStringLen(s2), s2, SysStringLen(s3), s3);
SysFreeString(s1);
SysFreeString(s2);
SysFreeString(s3);
BSTR sv = spTest->GetSV();
printf("IV = %d SV = %d %ls\n", spTest->GetIV(), SysStringLen(sv), sv);
SysFreeString(sv);
spTest->PutIV(123);
spTest->PutSV(SysAllocString(L"ABC"));
sv = spTest->GetSV();
printf("IV = %d SV = %d %ls\n", spTest->GetIV(), SysStringLen(sv), sv);
SysFreeString(sv);
spTest = NULL;
}
catch (_com_error er)
{
_tprintf("%s\n", er.ErrorMessage());
}
CoUninitialize();
return 0;
}
Build and run:
cl /EHsc TestCPP4.cpp ole32.lib oleaut32.lib
TestCPP4
TestCS.cpp:
using System;
using Test;
public class MainClass
{
public static void Main(string[] args)
{
Console.WriteLine("COM/C# static");
CoTest cotest = new CoTestClass();
ITest test = (ITest)cotest;
Console.WriteLine("123 + 456 = {0}", test.Add(123, 456));
Console.WriteLine("ABC + XYZ = {0}", test.Concat("ABC", "XYZ"));
Console.WriteLine("IV = {0} SV = {1}", test.IV, test.SV);
test.IV = 123;
test.SV = "ABC";
Console.WriteLine("IV = {0} SV = {1}", test.IV, test.SV);
}
}
Build and run:
tlbimp /out=TestWrap.dll /namespace:Test Test.tlb
csc /platform:x86 /r:TestWrap.dll TestCS.cs
TestCS
TestPas.pas:
program TestPas;
{$mode delphi}{$H+}
uses
ActiveX, TestLibrary_1_0_TLB;
var
test : ITest;
begin
writeln('COM/Pascal static:');
CoInitialize(nil);
test := CoCoTest.Create;
writeln('123 + 456 = ', test.Add(123, 456));
writeln('ABC + XYZ = ', test.Concat('ABC', 'XYZ'));
writeln('IV = ', test.IV, ' SV = ', test.SV);
test.IV := 123;
test.SV := 'ABC';
writeln('IV = ', test.IV, ' SV = ', test.SV);
CoUninitialize;
end.
Build and run:
importtl Test.tlb
fpc TestLibrary_1_0_TLB.pas
fpc TestPas.pas
TestPas
What is happening:
TestVBS.vbs:
WScript.Echo "COM/VBS:"
Set test = CreateObject("Test.CoTest")
WScript.Echo "123 + 456 = " & CStr(test.Add(CLng(123), CLng(456)))
WScript.Echo "ABC + XYZ = " & test.Concat("ABC", "XYZ")
WScript.Echo "IV = " & CStr(test.IV) & " SV = " & test.SV
test.IV = 123
test.SV = "ABC"
WScript.Echo "IV = " & CStr(test.IV) & " SV = " & test.SV
Set test = Nothing
Build and run:
cscript TestVBS.vbs
TestCSDyn.cs:
using System;
public class MainClass
{
public static void Main(string[] args)
{
Console.WriteLine("COM/C# dynamic");
dynamic test = Activator.CreateInstance(Type.GetTypeFromProgID("Test.CoTest"));
Console.WriteLine("123 + 456 = {0}", test.Add(123, 456));
Console.WriteLine("ABC + XYZ = {0}", test.Concat("ABC", "XYZ"));
Console.WriteLine("IV = {0} SV = {1}", test.IV, test.SV);
test.IV = 123;
test.SV = "ABC";
Console.WriteLine("IV = {0} SV = {1}", test.IV, test.SV);
}
}
Build and run:
csc /platform:x86 TestCSDyn.cs
TestCSDyn
TestPasDyn.pas:
program TestPasDyn;
uses
ActiveX,ComObj;
var
test : Variant;
begin
writeln('COM/Pascal dynamic:');
CoInitialize(nil);
test := CreateOLEObject('Test.CoTest');
writeln('123 + 456 = ', test.Add(123, 456));
writeln('ABC + XYZ = ', test.Concat('ABC', 'XYZ'));
writeln('IV = ', test.IV, ' SV = ', test.SV);
test.IV := 123;
test.SV := 'ABC';
writeln('IV = ', test.IV, ' SV = ', test.SV);
CoUninitialize;
end.
Build and run:
fpc TestPasDyn.pas
TestPasDyn
TestJava.java:
import com.jacob.activeX.ActiveXComponent;
import com.jacob.com.Variant;
public class TestJava {
public static void main(String[] args) {
System.out.println("COM/Java with Jacob ActiveXComponent");
ActiveXComponent test = new ActiveXComponent("Test.CoTest");
System.out.println("123 + 456 = " + test.invoke("Add", new Variant(123), new Variant(456)).getInt());
System.out.println("ABC + XYZ = " + test.invoke("Concat", new Variant("ABC"), new Variant("XYZ")).getString());
System.out.println("IV = " + test.getPropertyAsInt("IV") + " SV = " + test.getPropertyAsString("SV"));
test.setProperty("IV", 123);
test.setProperty("SV", "ABC");
System.out.println("IV = " + test.getPropertyAsInt("IV") + " SV = " + test.getPropertyAsString("SV"));
}
}
Build and run:
javac -cp %JACOBDIR%\jacob.jar TestJava.java
java -Djava.library.path=%JACOBDIR% -cp .;%JACOBDIR%\jacob.jar TestJava
Nobody will use IDispatch with C++, but it is still relevant to see the code to understand what is happening in other languages.
TestCPP5.cpp:
#include <stdio.h>
#include <windows.h>
#include <initguid.h>
#include <tchar.h>
#import "Test.tlb" no_namespace named_guids
void ReturnCheck(LPTSTR func, HRESULT res)
{
if(res != S_OK)
{
TCHAR buffer[1000];
FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, 0, res, 0, buffer, sizeof(buffer), 0);
_tprintf("%s: %s\n", func, buffer);
exit(1);
}
}
int main()
{
HRESULT res;
CoInitialize(NULL);
printf("COM/C++ via IDispatch:\n");
CLSID clsid;
res = CLSIDFromProgID(L"Test.CoTest", &CLSid);
ReturnCheck(_T("CLSIDFromProgID"), res);
IDispatch *pDisp = NULL;
res = CoCreateInstance(clsid, NULL, CLSCTX_SERVER, IID_IDispatch, (void **)&pDisp);
ReturnCheck(_T("CoCreateInstance"), res);
DISPID dispid;
LPOLESTR func;
VARIANT args[2], retval;
DISPPARAMS params;
params.rgvarg = args;
params.rgdispidNamedArgs = 0;
params.cNamedArgs = 0;
// Add
params.cArgs = 2;
func = L"Add";
res = pDisp->GetIDsOfNames(IID_NULL, &func, 1, LOCALE_SYSTEM_DEFAULT, &dispid);
ReturnCheck(_T("IDispatch GetIDsOfNames"), res);
VariantInit(&args[0]);
args[0].vt = VT_I4;
args[0].lVal = 456;
VariantInit(&args[1]);
args[1].vt = VT_I4;
args[1].lVal = 123;
VariantInit(&retval);
res = pDisp->Invoke(dispid, IID_NULL, LOCALE_SYSTEM_DEFAULT, DISPATCH_METHOD, ¶ms, &retval, NULL, NULL);
ReturnCheck(_T("IDispatch Invoke"), res);
printf("123 + 345 = %d\n", retval.lVal);
// Concat
params.cArgs = 2;
func = L"Concat";
res = pDisp->GetIDsOfNames(IID_NULL, &func, 1, LOCALE_SYSTEM_DEFAULT, &dispid);
ReturnCheck(_T("IDispatch GetIDsOfNames"), res);
VariantInit(&args[0]);
args[0].vt = VT_BSTR;
args[0].bstrVal = SysAllocString(L"ABC");
VariantInit(&args[1]);
args[1].vt = VT_BSTR;
args[1].bstrVal = SysAllocString(L"XYZ");
VariantInit(&retval);
res = pDisp->Invoke(dispid, IID_NULL, LOCALE_SYSTEM_DEFAULT, DISPATCH_METHOD, ¶ms, &retval, NULL, NULL);
ReturnCheck(_T("IDispatch Invoke"), res);
printf("3 ABC + 3 XYZ = %d %ls\n", SysStringLen(retval.bstrVal), retval.bstrVal);
// Get
params.cArgs = 0;
func = L"IV";
res = pDisp->GetIDsOfNames(IID_NULL, &func, 1, LOCALE_SYSTEM_DEFAULT, &dispid);
ReturnCheck(_T("IDispatch GetIDsOfNames"), res);
VariantInit(&retval);
res = pDisp->Invoke(dispid, IID_NULL, LOCALE_SYSTEM_DEFAULT, DISPATCH_PROPERTYGET, ¶ms, &retval, NULL, NULL);
ReturnCheck(_T("IDispatch Invoke"), res);
printf("IV = %d", retval.lVal);
params.cArgs = 0;
func = L"SV";
res = pDisp->GetIDsOfNames(IID_NULL, &func, 1, LOCALE_SYSTEM_DEFAULT, &dispid);
ReturnCheck(_T("IDispatch GetIDsOfNames"), res);
VariantInit(&retval);
res = pDisp->Invoke(dispid, IID_NULL, LOCALE_SYSTEM_DEFAULT, DISPATCH_PROPERTYGET, ¶ms, &retval, NULL, NULL);
ReturnCheck(_T("IDispatch Invoke"), res);
printf(" SV = %d %ls\n", SysStringLen(retval.bstrVal), retval.bstrVal);
// Put
params.cArgs = 1;
func = L"IV";
res = pDisp->GetIDsOfNames(IID_NULL, &func, 1, LOCALE_SYSTEM_DEFAULT, &dispid);
ReturnCheck(_T("IDispatch GetIDsOfNames"), res);
VariantInit(&args[0]);
args[0].vt = VT_I4;
args[0].lVal = 123;
VariantInit(&retval);
res = pDisp->Invoke(dispid, IID_NULL, LOCALE_SYSTEM_DEFAULT, DISPATCH_PROPERTYPUT, ¶ms, &retval, NULL, NULL);
ReturnCheck(_T("IDispatch Invoke"), res);
params.cArgs = 1;
func = L"SV";
res = pDisp->GetIDsOfNames(IID_NULL, &func, 1, LOCALE_SYSTEM_DEFAULT, &dispid);
ReturnCheck(_T("IDispatch GetIDsOfNames"), res);
VariantInit(&args[0]);
args[0].vt = VT_BSTR;
args[0].bstrVal = SysAllocString(L"ABC");
VariantInit(&retval);
res = pDisp->Invoke(dispid, IID_NULL, LOCALE_SYSTEM_DEFAULT, DISPATCH_PROPERTYPUT, ¶ms, &retval, NULL, NULL);
ReturnCheck(_T("IDispatch Invoke"), res);
// Get
params.cArgs = 0;
func = L"IV";
res = pDisp->GetIDsOfNames(IID_NULL, &func, 1, LOCALE_SYSTEM_DEFAULT, &dispid);
ReturnCheck(_T("IDispatch GetIDsOfNames"), res);
VariantInit(&retval);
res = pDisp->Invoke(dispid, IID_NULL, LOCALE_SYSTEM_DEFAULT, DISPATCH_PROPERTYGET, ¶ms, &retval, NULL, NULL);
ReturnCheck(_T("IDispatch Invoke"), res);
printf("IV = %d", retval.lVal);
params.cArgs = 0;
func = L"SV";
res = pDisp->GetIDsOfNames(IID_NULL, &func, 1, LOCALE_SYSTEM_DEFAULT, &dispid);
ReturnCheck(_T("IDispatch GetIDsOfNames"), res);
VariantInit(&retval);
res = pDisp->Invoke(dispid, IID_NULL, LOCALE_SYSTEM_DEFAULT, DISPATCH_PROPERTYGET, ¶ms, &retval, NULL, NULL);
ReturnCheck(_T("IDispatch Invoke"), res);
printf(" SV = %d %ls\n", SysStringLen(retval.bstrVal), retval.bstrVal);
//
pDisp->Release();
CoUninitialize();
return 0;
}
Build and run:
cl /EHsc TestCPP5.cpp ole32.lib oleaut32.lib
TestCPP5
What is happening:
In this iteration we will:
Test.idl:
import "oaidl.idl";
// ITest
[object, uuid(45BE0445-DA8F-44f2-A44F-B7F041127F3A), oleautomation, dual]
interface ITest : IDispatch
{
[id(1)] HRESULT Add([in] long a, [in] long b, [out,retval] long *c);
[id(2)] HRESULT Concat([in] BSTR a, [in] BSTR b, [out,retval]BSTR *c);
[id(3),propget] HRESULT IV([out, retval] long *iv);
[id(3),propput] HRESULT IV([in] long iv);
[id(4),propget] HRESULT SV([out, retval] BSTR *sv);
[id(4),propput] HRESULT SV([in] BSTR sv);
}
// Test library
[uuid(1C187D8E-FBA1-432a-B0C3-E0506642E789), version(1.0)]
library TestLibrary
{
importlib("stdole32.tlb");
[uuid(39599C7C-2D46-4217-904C-EA10C726D59B)]
coclass CoTest
{
[default] interface ITest;
};
};
TestEx.h:
#ifndef TESTEX_H
#define TESTEX_H
#include <windows.h>
#include "Test_i.c"
#include "Test.h"
class CoTest : public ITest
{
private:
LONG volatile m_refcnt;
ITypeInfo *m_ptypeInfo;
long m_iv;
BSTR m_sv;
public:
CoTest();
virtual ~CoTest();
// IUnknown
STDMETHODIMP QueryInterface(REFIID riid, void **ppv);
STDMETHODIMP_(ULONG) AddRef();
STDMETHODIMP_(ULONG) Release();
// IDispatch
STDMETHODIMP GetTypeInfoCount(UINT *pctinfo);
STDMETHODIMP GetTypeInfo(UINT iTInfo, LCID lcid, ITypeInfo **ppTInfo);
STDMETHODIMP GetIDsOfNames(REFIID riid, LPOLESTR *rgszNames, UINT cNames, LCID lcid, DISPID *rgDispId);
STDMETHODIMP Invoke(DISPID dispIdMember, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS *pDispParams, VARIANT *pVarResult, EXCEPINFO *pExceptInfo, UINT *puArgErr);
// ITest
STDMETHODIMP Add(long a, long b, long *c);
STDMETHODIMP Concat(BSTR a, BSTR b, BSTR *c);
STDMETHODIMP get_IV(long *iv);
STDMETHODIMP put_IV(long iv);
STDMETHODIMP get_SV(BSTR *sv);
STDMETHODIMP put_SV(BSTR sv);
};
class CoTestFactory : IClassFactory
{
private:
LONG volatile m_refcnt;
public:
CoTestFactory();
// IUnknown
STDMETHODIMP QueryInterface(REFIID riid, void **ppv);
STDMETHODIMP_(ULONG) AddRef();
STDMETHODIMP_(ULONG) Release();
// IClassFactory
STDMETHODIMP CreateInstance(LPUNKNOWN pUnk, REFIID riid, void **ppv);
STDMETHODIMP LockServer(BOOL fLock);
};
#endif // TESTEX_H
Test.cpp:
#include <stdio.h>
#include <windows.h>
#include <oleauto.h>
#include <initguid.h>
#include "TestEx.h"
static LONG volatile g_lckcnt = 0;
static LONG volatile g_objcnt = 0;
CoTest::CoTest() : m_ptypeInfo(NULL)
{
#ifdef DEBUG
printf("CoTest ctor\n");
#endif
m_iv = 0;
m_sv = SysAllocString(L"");
m_refcnt = 0;
InterlockedIncrement(&g_objcnt);
ITypeLib *pTypeLibrary = NULL;
HRESULT hr = LoadRegTypeLib(LIBID_TestLibrary, 1, 0, LANG_NEUTRAL, &pTypeLibrary);
if(SUCCEEDED(hr))
{
pTypeLibrary->GetTypeInfoOfGuid(IID_ITest, &m_ptypeInfo);
pTypeLibrary->Release();
}
}
CoTest::~CoTest()
{
#ifdef DEBUG
printf("CoTest dtor\n");
#endif
SysFreeString(m_sv);
m_ptypeInfo->Release();
InterlockedDecrement(&g_objcnt);
}
STDMETHODIMP CoTest::QueryInterface(REFIID riid, void **ppv)
{
if(riid == IID_IUnknown)
{
#ifdef DEBUG
printf("CoTest QueryInterface IUnknown\n");
#endif
*ppv = (IUnknown*)this;
((IUnknown*)(*ppv))->AddRef();
return S_OK;
}
else if(riid == IID_IDispatch)
{
#ifdef DEBUG
printf("CoTest QueryInterface IDispatch\n");
#endif
*ppv = (IDispatch*)this;
((ITest*)(*ppv))->AddRef();
return S_OK;
}
else if(riid == IID_ITest)
{
#ifdef DEBUG
printf("CoTest QueryInterface ITest\n");
#endif
*ppv = (ITest*)this;
((ITest*)(*ppv))->AddRef();
return S_OK;
}
else
{
*ppv = NULL;
return E_NOINTERFACE;
}
}
STDMETHODIMP_(ULONG) CoTest::AddRef()
{
#ifdef DEBUG
printf("CoTest AddRef\n");
#endif
return InterlockedIncrement(&m_refcnt);
}
STDMETHODIMP_(ULONG) CoTest::Release()
{
#ifdef DEBUG
printf("CoTest Release\n");
#endif
InterlockedDecrement(&m_refcnt);
if(m_refcnt == 0)
{
delete this;
return 0;
}
else
{
return m_refcnt;
}
}
STDMETHODIMP CoTest::GetTypeInfoCount(UINT *pctinfo)
{
*pctinfo = 1;
return S_OK;
}
STDMETHODIMP CoTest::GetTypeInfo(UINT iTInfo, LCID lcid, ITypeInfo **ppTInfo)
{
*ppTInfo = m_ptypeInfo;
m_ptypeInfo->AddRef();
return S_OK;
}
STDMETHODIMP CoTest::GetIDsOfNames(REFIID riid, LPOLESTR *rgszNames, UINT cNames, LCID lcid, DISPID *rgDispId)
{
#ifdef DEBUG
printf("CoTest GetIDsOfNames %ls\n", rgszNames[0]);
#endif
return DispGetIDsOfNames(m_ptypeInfo, rgszNames, cNames, rgDispId);
}
STDMETHODIMP CoTest::Invoke(DISPID dispIdMember, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS *pDispParams, VARIANT *pVarResult, EXCEPINFO *pExceptInfo, UINT *puArgErr)
{
#ifdef DEBUG
printf("CoTest Invoke\n");
#endif
return DispInvoke(this, m_ptypeInfo, dispIdMember, wFlags, pDispParams, pVarResult, pExceptInfo, puArgErr);
}
STDMETHODIMP CoTest::Add(long a, long b, long *c)
{
*c = a + b;
return S_OK;
}
STDMETHODIMP CoTest::Concat(BSTR a, BSTR b, BSTR *c)
{
OLECHAR *buf = new OLECHAR[SysStringLen(a) + SysStringLen(b) + 1];
wcscpy(buf, a);
wcscat(buf, b);
*c = SysAllocString(buf);
delete[] buf;
return S_OK;
}
STDMETHODIMP CoTest::get_IV(long *iv)
{
*iv = m_iv;
return S_OK;
}
STDMETHODIMP CoTest::put_IV(long iv)
{
m_iv = iv;
return S_OK;
}
STDMETHODIMP CoTest::get_SV(BSTR *sv)
{
*sv = SysAllocString(m_sv);
return S_OK;
}
STDMETHODIMP CoTest::put_SV(BSTR sv)
{
SysReAllocString(&m_sv, sv);
return S_OK;
}
CoTestFactory::CoTestFactory()
{
m_refcnt = 0;
}
STDMETHODIMP CoTestFactory::QueryInterface(REFIID riid, void **ppv)
{
if(riid == IID_IUnknown)
{
*ppv = (IUnknown*)this;
((IUnknown*)(*ppv))->AddRef();
return S_OK;
}
else if(riid == IID_IClassFactory)
{
*ppv = (IClassFactory*)this;
((IClassFactory*)(*ppv))->AddRef();
return S_OK;
}
else
{
*ppv = NULL;
return E_NOINTERFACE;
}
}
STDMETHODIMP_(ULONG) CoTestFactory::AddRef()
{
return InterlockedIncrement(&m_refcnt);
}
STDMETHODIMP_(ULONG) CoTestFactory::Release()
{
InterlockedDecrement(&m_refcnt);
if(m_refcnt == 0)
{
delete this;
return 0;
}
else
{
return m_refcnt;
}
}
STDMETHODIMP CoTestFactory::CreateInstance(LPUNKNOWN pUnk, REFIID riid, void **ppv)
{
#ifdef DEBUG
printf("CoTestFactory CreateInstance\n");
#endif
if(pUnk != NULL) return CLASS_E_NOAGGREGATION;
CoTest *pTest = new CoTest();
HRESULT res = pTest->QueryInterface(riid, ppv);
if(res != S_OK) delete pTest;
return res;
}
STDMETHODIMP CoTestFactory::LockServer(BOOL fLock)
{
if(fLock) InterlockedIncrement(&g_lckcnt); else InterlockedDecrement(&g_lckcnt);
return S_OK;
}
STDMETHODIMP DllGetClassObject(REFCLSID rclsid, REFIID riid, void **ppv)
{
#ifdef DEBUG
printf("DllGetClassObject\n");
#endif
if(rclsid == CLSID_CoTest)
{
CoTestFactory *pTestFact = new CoTestFactory();
HRESULT res = pTestFact->QueryInterface(riid, ppv);
if(res != S_OK) delete pTestFact;
return res;
}
else
{
return CLASS_E_CLASSNOTAVAILABLE;
}
}
STDMETHODIMP DllCanUnloadNow()
{
if(g_lckcnt == 0 && g_objcnt == 0) return S_OK; else return S_FALSE;
}
STDMETHODIMP DllRegisterServer()
{
ITypeLib *pTLib = NULL;
LoadTypeLibEx(L"Test.tlb", REGKIND_REGISTER, &pTLib);
pTLib->Release();
return S_OK;
}
STDMETHODIMP DllUnregisterServer()
{
UnRegisterTypeLib(LIBID_TestLibrary, 1, 0, LANG_NEUTRAL, SYS_WIN32);
return S_OK;
}
Test.def:
LIBRARY "Test"
EXPORTS
DllGetClassObject PRIVATE
DllCanUnloadNow PRIVATE
DllRegisterServer PRIVATE
DllUnregisterServer PRIVATE
Build and registration:
midl Test.idl
cl /LD Test.cpp Test.def oleaut32.lib
Test.reg
regsvr32 Test.dll
Test.reg:
REGEDIT
HKEY_CLASSES_ROOT\Test.CoTest\CLSID = {39599c7c-2d46-4217-904c-ea10c726d59b}
HKEY_CLASSES_ROOT\Wow6432Node\CLSID\{39599c7c-2d46-4217-904c-ea10c726d59b} = Test.CoTest
HKEY_CLASSES_ROOT\Wow6432Node\CLSID\{39599c7c-2d46-4217-904c-ea10c726d59b}\InProcServer32 = C:\Work\com\comi\Test.dll
HKEY_CLASSES_ROOT\Wow6432Node\CLSID\{39599c7c-2d46-4217-904c-ea10c726d59b}\TypeLib = {1c187d8e-fba1-432a-b0c3-e0506642e789}
TestC.c:
#include <stdio.h>
#include <stdlib.h>
#include <windows.h>
#include <initguid.h>
#include <tchar.h>
#define COBJMACROS
#include "Test.h"
void ReturnCheck(LPTSTR func, HRESULT res)
{
if(res != S_OK)
{
TCHAR buffer[1000];
FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, 0, res, 0, buffer, sizeof(buffer), 0);
_tprintf("%s: %s\n", func, buffer);
exit(1);
}
}
int main()
{
HRESULT res;
ITest *pTest;
int v, iv;
BSTR s1, s2, s3, sv;
CoInitialize(NULL);
printf("COM/C:\n");
res = CoCreateInstance(&CLSID_CoTest, NULL, CLSCTX_INPROC_SERVER, &IID_ITest, (void**)&pTest);
ReturnCheck(_T("CoCreateInstance"), res);
res = ITest_Add(pTest, 123, 456, &v);
ReturnCheck(_T("Add"), res);
printf("123 + 345 = %d\n", v);
s1 = SysAllocString(L"ABC");
s2 = SysAllocString(L"XYZ");
res = ITest_Concat(pTest, s1, s2, &s3);
ReturnCheck(_T("Concat"), res);
printf("%d %ls + %d %ls = %d %ls\n", SysStringLen(s1), s1, SysStringLen(s2), s2, SysStringLen(s3), s3);
SysFreeString(s1);
SysFreeString(s2);
SysFreeString(s3);
ITest_get_IV(pTest, &iv);
ITest_get_SV(pTest, &sv);
printf("IV = %d SV = %d %ls\n", iv, SysStringLen(sv), sv);
SysFreeString(sv);
ITest_put_IV(pTest, 123);
ITest_put_SV(pTest, SysAllocString(L"ABC"));
ITest_get_IV(pTest, &iv);
ITest_get_SV(pTest, &sv);
printf("IV = %d SV = %d %ls\n", iv, SysStringLen(sv), sv);
SysFreeString(sv);
ITest_Release(pTest);
CoUninitialize();
return 0;
}
Build and run:
cl TestC.c Test_i.c ole32.lib oleaut32.lib
TestC
What is happening:
TestCPP3.cpp:
#include <stdio.h>
#include <windows.h>
#include <initguid.h>
#include <tchar.h>
#import "Test.tlb" no_namespace named_guids
void ReturnCheck(LPTSTR func, HRESULT res)
{
if(res != S_OK)
{
TCHAR buffer[1000];
FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, 0, res, 0, buffer, sizeof(buffer), 0);
_tprintf("%s: %s\n", func, buffer);
exit(1);
}
}
int main()
{
HRESULT res;
CoInitialize(NULL);
printf("COM/C++ with CreateInstance:\n");
try
{
ITestPtr spTest;
res = spTest.CreateInstance(CLSID_CoTest);
ReturnCheck(_T("CreateInstance"), res);
long v = spTest->Add(123L, 456L);
printf("123 + 345 = %d\n", v);
BSTR s1 = SysAllocString(L"ABC");
BSTR s2 = SysAllocString(L"XYZ");
BSTR s3 = spTest->Concat(s1, s2);
printf("%d %ls + %d %ls = %d %ls\n", SysStringLen(s1), s1, SysStringLen(s2), s2, SysStringLen(s3), s3);
SysFreeString(s1);
SysFreeString(s2);
SysFreeString(s3);
BSTR sv = spTest->GetSV();
printf("IV = %d SV = %d %ls\n", spTest->GetIV(), SysStringLen(sv), sv);
SysFreeString(sv);
spTest->PutIV(123);
spTest->PutSV(SysAllocString(L"ABC"));
sv = spTest->GetSV();
printf("IV = %d SV = %d %ls\n", spTest->GetIV(), SysStringLen(sv), sv);
SysFreeString(sv);
spTest = NULL;
}
catch (_com_error er)
{
_tprintf("%s\n", er.ErrorMessage());
}
CoUninitialize();
return 0;
}
Build and run:
cl /EHsc TestCPP3.cpp ole32.lib oleaut32.lib
TestCPP3
TestCPP4.cpp:
#include <stdio.h>
#include <windows.h>
#include <initguid.h>
#include <tchar.h>
#import "Test.tlb" no_namespace named_guids
void ReturnCheck(LPTSTR func, HRESULT res)
{
if(res != S_OK)
{
TCHAR buffer[1000];
FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, 0, res, 0, buffer, sizeof(buffer), 0);
_tprintf("%s: %s\n", func, buffer);
exit(1);
}
}
int main()
{
HRESULT res;
CoInitialize(NULL);
printf("COM/C++ with constructor:\n");
try
{
ITestPtr spTest(__uuidof(CoTest));
long v = spTest->Add(123, 456);
printf("123 + 345 = %d\n", v);
BSTR s1 = SysAllocString(L"ABC");
BSTR s2 = SysAllocString(L"XYZ");
BSTR s3 = spTest->Concat(s1, s2);
printf("%d %ls + %d %ls = %d %ls\n", SysStringLen(s1), s1, SysStringLen(s2), s2, SysStringLen(s3), s3);
SysFreeString(s1);
SysFreeString(s2);
SysFreeString(s3);
BSTR sv = spTest->GetSV();
printf("IV = %d SV = %d %ls\n", spTest->GetIV(), SysStringLen(sv), sv);
SysFreeString(sv);
spTest->PutIV(123);
spTest->PutSV(SysAllocString(L"ABC"));
sv = spTest->GetSV();
printf("IV = %d SV = %d %ls\n", spTest->GetIV(), SysStringLen(sv), sv);
SysFreeString(sv);
spTest = NULL;
}
catch (_com_error er)
{
_tprintf("%s\n", er.ErrorMessage());
}
CoUninitialize();
return 0;
}
Build and run:
cl /EHsc TestCPP4.cpp ole32.lib oleaut32.lib
TestCPP4
TestCS.cpp:
using System;
using Test;
public class MainClass
{
public static void Main(string[] args)
{
Console.WriteLine("COM/C# static");
CoTest cotest = new CoTestClass();
ITest test = (ITest)cotest;
Console.WriteLine("123 + 456 = {0}", test.Add(123, 456));
Console.WriteLine("ABC + XYZ = {0}", test.Concat("ABC", "XYZ"));
Console.WriteLine("IV = {0} SV = {1}", test.IV, test.SV);
test.IV = 123;
test.SV = "ABC";
Console.WriteLine("IV = {0} SV = {1}", test.IV, test.SV);
}
}
Build and run:
tlbimp /out=TestWrap.dll /namespace:Test Test.tlb
csc /platform:x86 /r:TestWrap.dll TestCS.cs
TestCS
TestPas.pas:
program TestPas;
{$mode delphi}{$H+}
uses
ActiveX, TestLibrary_1_0_TLB;
var
test : ITest;
begin
writeln('COM/Pascal static:');
CoInitialize(nil);
test := CoCoTest.Create;
writeln('123 + 456 = ', test.Add(123, 456));
writeln('ABC + XYZ = ', test.Concat('ABC', 'XYZ'));
writeln('IV = ', test.IV, ' SV = ', test.SV);
test.IV := 123;
test.SV := 'ABC';
writeln('IV = ', test.IV, ' SV = ', test.SV);
CoUninitialize;
end.
Build and run:
importtl Test.tlb
fpc TestLibrary_1_0_TLB.pas
fpc TestPas.pas
TestPas
What is happening:
TestVBS.vbs:
WScript.Echo "COM/VBS:"
Set test = CreateObject("Test.CoTest")
WScript.Echo "123 + 456 = " & CStr(test.Add(CLng(123), CLng(456)))
WScript.Echo "ABC + XYZ = " & test.Concat("ABC", "XYZ")
WScript.Echo "IV = " & CStr(test.IV) & " SV = " & test.SV
test.IV = 123
test.SV = "ABC"
WScript.Echo "IV = " & CStr(test.IV) & " SV = " & test.SV
Set test = Nothing
Build and run:
cscript TestVBS.vbs
TestCSDyn.cs:
using System;
public class MainClass
{
public static void Main(string[] args)
{
Console.WriteLine("COM/C# dynamic");
dynamic test = Activator.CreateInstance(Type.GetTypeFromProgID("Test.CoTest"));
Console.WriteLine("123 + 456 = {0}", test.Add(123, 456));
Console.WriteLine("ABC + XYZ = {0}", test.Concat("ABC", "XYZ"));
Console.WriteLine("IV = {0} SV = {1}", test.IV, test.SV);
test.IV = 123;
test.SV = "ABC";
Console.WriteLine("IV = {0} SV = {1}", test.IV, test.SV);
}
}
Build and run:
csc /platform:x86 TestCSDyn.cs
TestCSDyn
TestPasDyn.pas:
program TestPasDyn;
uses
ActiveX,ComObj;
var
test : Variant;
begin
writeln('COM/Pascal dynamic:');
CoInitialize(nil);
test := CreateOLEObject('Test.CoTest');
writeln('123 + 456 = ', test.Add(123, 456));
writeln('ABC + XYZ = ', test.Concat('ABC', 'XYZ'));
writeln('IV = ', test.IV, ' SV = ', test.SV);
test.IV := 123;
test.SV := 'ABC';
writeln('IV = ', test.IV, ' SV = ', test.SV);
CoUninitialize;
end.
Build and run:
fpc TestPasDyn.pas
TestPasDyn
TestJava.java:
import com.jacob.activeX.ActiveXComponent;
import com.jacob.com.Variant;
public class TestJava {
public static void main(String[] args) {
System.out.println("COM/Java with Jacob ActiveXComponent");
ActiveXComponent test = new ActiveXComponent("Test.CoTest");
System.out.println("123 + 456 = " + test.invoke("Add", new Variant(123), new Variant(456)).getInt());
System.out.println("ABC + XYZ = " + test.invoke("Concat", new Variant("ABC"), new Variant("XYZ")).getString());
System.out.println("IV = " + test.getPropertyAsInt("IV") + " SV = " + test.getPropertyAsString("SV"));
test.setProperty("IV", 123);
test.setProperty("SV", "ABC");
System.out.println("IV = " + test.getPropertyAsInt("IV") + " SV = " + test.getPropertyAsString("SV"));
}
}
Build and run:
javac -cp %JACOBDIR%\jacob.jar TestJava.java
java -Djava.library.path=%JACOBDIR% -cp .;%JACOBDIR%\jacob.jar TestJava
Nobody will use IDispatch with C++, but it is still relevant to see the code to understand what is happening in other languages.
TestCPP5.cpp:
#include <stdio.h>
#include <windows.h>
#include <initguid.h>
#include <tchar.h>
#import "Test.tlb" no_namespace named_guids
void ReturnCheck(LPTSTR func, HRESULT res)
{
if(res != S_OK)
{
TCHAR buffer[1000];
FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, 0, res, 0, buffer, sizeof(buffer), 0);
_tprintf("%s: %s\n", func, buffer);
exit(1);
}
}
int main()
{
HRESULT res;
CoInitialize(NULL);
printf("COM/C++ via IDispatch:\n");
CLSID clsid;
res = CLSIDFromProgID(L"Test.CoTest", &CLSid);
ReturnCheck(_T("CLSIDFromProgID"), res);
IDispatch *pDisp = NULL;
res = CoCreateInstance(clsid, NULL, CLSCTX_SERVER, IID_IDispatch, (void **)&pDisp);
ReturnCheck(_T("CoCreateInstance"), res);
DISPID dispid;
LPOLESTR func;
VARIANT args[2], retval;
DISPPARAMS params;
params.rgvarg = args;
params.rgdispidNamedArgs = 0;
params.cNamedArgs = 0;
// Add
params.cArgs = 2;
func = L"Add";
res = pDisp->GetIDsOfNames(IID_NULL, &func, 1, LOCALE_SYSTEM_DEFAULT, &dispid);
ReturnCheck(_T("IDispatch GetIDsOfNames"), res);
VariantInit(&args[0]);
args[0].vt = VT_I4;
args[0].lVal = 456;
VariantInit(&args[1]);
args[1].vt = VT_I4;
args[1].lVal = 123;
VariantInit(&retval);
res = pDisp->Invoke(dispid, IID_NULL, LOCALE_SYSTEM_DEFAULT, DISPATCH_METHOD, ¶ms, &retval, NULL, NULL);
ReturnCheck(_T("IDispatch Invoke"), res);
printf("123 + 345 = %d\n", retval.lVal);
// Concat
params.cArgs = 2;
func = L"Concat";
res = pDisp->GetIDsOfNames(IID_NULL, &func, 1, LOCALE_SYSTEM_DEFAULT, &dispid);
ReturnCheck(_T("IDispatch GetIDsOfNames"), res);
VariantInit(&args[0]);
args[0].vt = VT_BSTR;
args[0].bstrVal = SysAllocString(L"ABC");
VariantInit(&args[1]);
args[1].vt = VT_BSTR;
args[1].bstrVal = SysAllocString(L"XYZ");
VariantInit(&retval);
res = pDisp->Invoke(dispid, IID_NULL, LOCALE_SYSTEM_DEFAULT, DISPATCH_METHOD, ¶ms, &retval, NULL, NULL);
ReturnCheck(_T("IDispatch Invoke"), res);
printf("3 ABC + 3 XYZ = %d %ls\n", SysStringLen(retval.bstrVal), retval.bstrVal);
// Get
params.cArgs = 0;
func = L"IV";
res = pDisp->GetIDsOfNames(IID_NULL, &func, 1, LOCALE_SYSTEM_DEFAULT, &dispid);
ReturnCheck(_T("IDispatch GetIDsOfNames"), res);
VariantInit(&retval);
res = pDisp->Invoke(dispid, IID_NULL, LOCALE_SYSTEM_DEFAULT, DISPATCH_PROPERTYGET, ¶ms, &retval, NULL, NULL);
ReturnCheck(_T("IDispatch Invoke"), res);
printf("IV = %d", retval.lVal);
params.cArgs = 0;
func = L"SV";
res = pDisp->GetIDsOfNames(IID_NULL, &func, 1, LOCALE_SYSTEM_DEFAULT, &dispid);
ReturnCheck(_T("IDispatch GetIDsOfNames"), res);
VariantInit(&retval);
res = pDisp->Invoke(dispid, IID_NULL, LOCALE_SYSTEM_DEFAULT, DISPATCH_PROPERTYGET, ¶ms, &retval, NULL, NULL);
ReturnCheck(_T("IDispatch Invoke"), res);
printf(" SV = %d %ls\n", SysStringLen(retval.bstrVal), retval.bstrVal);
// Put
DISPID temp = DISPID_PROPERTYPUT; // these 3 lines are necessary
params.rgdispidNamedArgs = &temp;
params.cNamedArgs = 1;
//
params.cArgs = 1;
func = L"IV";
res = pDisp->GetIDsOfNames(IID_NULL, &func, 1, LOCALE_SYSTEM_DEFAULT, &dispid);
ReturnCheck(_T("IDispatch GetIDsOfNames"), res);
VariantInit(&args[0]);
args[0].vt = VT_I4;
args[0].lVal = 123;
res = pDisp->Invoke(dispid, IID_NULL, LOCALE_SYSTEM_DEFAULT, DISPATCH_PROPERTYPUT, ¶ms, NULL, NULL, NULL);
ReturnCheck(_T("IDispatch Invoke"), res);
params.cArgs = 1;
func = L"SV";
res = pDisp->GetIDsOfNames(IID_NULL, &func, 1, LOCALE_SYSTEM_DEFAULT, &dispid);
ReturnCheck(_T("IDispatch GetIDsOfNames"), res);
VariantInit(&args[0]);
args[0].vt = VT_BSTR;
args[0].bstrVal = SysAllocString(L"ABC");
res = pDisp->Invoke(dispid, IID_NULL, LOCALE_SYSTEM_DEFAULT, DISPATCH_PROPERTYPUT, ¶ms, NULL, NULL, NULL);
ReturnCheck(_T("IDispatch Invoke"), res);
//
params.rgdispidNamedArgs = 0;
params.cNamedArgs = 0;
// Get
params.cArgs = 0;
func = L"IV";
res = pDisp->GetIDsOfNames(IID_NULL, &func, 1, LOCALE_SYSTEM_DEFAULT, &dispid);
ReturnCheck(_T("IDispatch GetIDsOfNames"), res);
VariantInit(&retval);
res = pDisp->Invoke(dispid, IID_NULL, LOCALE_SYSTEM_DEFAULT, DISPATCH_PROPERTYGET, ¶ms, &retval, NULL, NULL);
ReturnCheck(_T("IDispatch Invoke"), res);
printf("IV = %d", retval.lVal);
params.cArgs = 0;
func = L"SV";
res = pDisp->GetIDsOfNames(IID_NULL, &func, 1, LOCALE_SYSTEM_DEFAULT, &dispid);
ReturnCheck(_T("IDispatch GetIDsOfNames"), res);
VariantInit(&retval);
res = pDisp->Invoke(dispid, IID_NULL, LOCALE_SYSTEM_DEFAULT, DISPATCH_PROPERTYGET, ¶ms, &retval, NULL, NULL);
ReturnCheck(_T("IDispatch Invoke"), res);
printf(" SV = %d %ls\n", SysStringLen(retval.bstrVal), retval.bstrVal);
//
pDisp->Release();
CoUninitialize();
return 0;
}
Build and run:
cl /EHsc TestCPP5.cpp ole32.lib oleaut32.lib
TestCPP5
What is happening:
In this iteration we will:
Note that this is how one should write a COM component as it requires significant less code than the previous ways. But the code is also somewhat hard to understand and the previous iterations should help understand what this code is doing.
Test.idl:
import "oaidl.idl";
// ITest
[object, uuid(1B255E86-9F4C-442c-8987-947BD9D6AC7C), oleautomation, dual, pointer_default(unique)]
interface ITest : IDispatch
{
[id(1)] HRESULT Add([in] long a, [in] long b, [out,retval] long *c);
[id(2)] HRESULT Concat([in] BSTR a, [in] BSTR b, [out,retval]BSTR *c);
[id(3),propget] HRESULT IV([out, retval] long *iv);
[id(3),propput] HRESULT IV([in] long iv);
[id(4),propget] HRESULT SV([out, retval] BSTR *sv);
[id(4),propput] HRESULT SV([in] BSTR sv);
}
// Test library
[uuid(105A7B5F-67E9-4d10-A22D-B3C989904332), version(1.0)]
library TestLibrary
{
importlib("stdole32.tlb");
[uuid(A178C19F-C64B-4a96-8D6C-83698E5B7A70)]
coclass CoTest
{
[default] interface ITest;
};
};
TestEx.h:
#ifndef TESTEX_H
#define TESTEX_H
#include <windows.h>
#include <tchar.h>
#include <atlbase.h>
#include <atlcom.h>
#include "Test_i.c"
#include "Test.h"
class ATL_NO_VTABLE CCoTest : public CComObjectRootEx<CComSingleThreadModel>, public CComCoClass<CCoTest, &CLSID_CoTest>, public IDispatchImpl<ITest, &IID_ITest, &LIBID_TestLibrary>
{
private:
long m_iv;
BSTR m_sv;
public:
CCoTest();
virtual ~CCoTest();
BEGIN_COM_MAP(CCoTest)
COM_INTERFACE_ENTRY(ITest)
COM_INTERFACE_ENTRY2(IDispatch, ITest)
END_COM_MAP()
// ITest
STDMETHODIMP Add(long a, long b, long *c);
STDMETHODIMP Concat(BSTR a, BSTR b, BSTR *c);
STDMETHODIMP get_IV(long *iv);
STDMETHODIMP put_IV(long iv);
STDMETHODIMP get_SV(BSTR *sv);
STDMETHODIMP put_SV(BSTR sv);
static HRESULT WINAPI UpdateRegistry(BOOL b)
{
return _Module.UpdateRegistryClass(CLSID_CoTest, _T("Test.CoTest.1"), _T("Test.CoTest"), 0U, THREADFLAGS_APARTMENT, b);
}
};
#endif // TESTEX_H
Test.cpp:
#include <stdio.h>
#include <windows.h>
#include <oleauto.h>
#include <initguid.h>
#include <atlbase.h>
extern CComModule _Module;
#include <atlcom.h>
#include "TestEx.h"
CComModule _Module;
BEGIN_OBJECT_MAP(ObjectMap)
OBJECT_ENTRY(CLSID_CoTest, CCoTest)
END_OBJECT_MAP()
CCoTest::CCoTest()
{
#ifdef DEBUG
printf("CCoTest ctor\n");
#endif
m_iv = 0;
m_sv = SysAllocString(L"");
}
CCoTest::~CCoTest()
{
#ifdef DEBUG
printf("CCoTest dtor\n");
#endif
SysFreeString(m_sv);
}
STDMETHODIMP CCoTest::Add(long a, long b, long *c)
{
*c = a + b;
return S_OK;
}
STDMETHODIMP CCoTest::Concat(BSTR a, BSTR b, BSTR *c)
{
OLECHAR *buf = new OLECHAR[SysStringLen(a) + SysStringLen(b) + 1];
wcscpy(buf, a);
wcscat(buf, b);
*c = SysAllocString(buf);
delete[] buf;
return S_OK;
}
STDMETHODIMP CCoTest::get_IV(long *iv)
{
*iv = m_iv;
return S_OK;
}
STDMETHODIMP CCoTest::put_IV(long iv)
{
m_iv = iv;
return S_OK;
}
STDMETHODIMP CCoTest::get_SV(BSTR *sv)
{
*sv = SysAllocString(m_sv);
return S_OK;
}
STDMETHODIMP CCoTest::put_SV(BSTR sv)
{
SysReAllocString(&m_sv, sv);
return S_OK;
}
BOOL WINAPI DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID lpReserved)
{
if(dwReason == DLL_PROCESS_ATTACH)
{
_Module.Init(ObjectMap, hInstance, &LIBID_TestLibrary);
}
else if(dwReason == DLL_PROCESS_DETACH)
{
_Module.Term();
}
return TRUE;
}
STDMETHODIMP DllGetClassObject(REFCLSID rclsid, REFIID riid, void **ppv)
{
#ifdef DEBUG
printf("DllGetClassObject\n");
#endif
return _Module.GetClassObject(rclsid, riid, ppv);
}
STDMETHODIMP DllCanUnloadNow()
{
return (_Module.GetLockCount() == 0) ? S_OK : S_FALSE;
}
STDMETHODIMP DllRegisterServer()
{
return _Module.RegisterServer(TRUE);
}
STDMETHODIMP DllUnregisterServer()
{
return _Module.UnregisterServer(TRUE);
}
Test.def:
LIBRARY "Test"
EXPORTS
DllMain PRIVATE
DllGetClassObject PRIVATE
DllCanUnloadNow PRIVATE
DllRegisterServer PRIVATE
DllUnregisterServer PRIVATE
Build and registration:
midl Test.idl
cl /LD Test.cpp Test.def oleaut32.lib
regsvr32 Test.dll
TestC.c:
#include <stdio.h>
#include <stdlib.h>
#include <windows.h>
#include <initguid.h>
#include <tchar.h>
#define COBJMACROS
#include "Test.h"
void ReturnCheck(LPTSTR func, HRESULT res)
{
if(res != S_OK)
{
TCHAR buffer[1000];
FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, 0, res, 0, buffer, sizeof(buffer), 0);
_tprintf("%s: %s\n", func, buffer);
exit(1);
}
}
int main()
{
HRESULT res;
ITest *pTest;
int v, iv;
BSTR s1, s2, s3, sv;
CoInitialize(NULL);
printf("COM/C:\n");
res = CoCreateInstance(&CLSID_CoTest, NULL, CLSCTX_INPROC_SERVER, &IID_ITest, (void**)&pTest);
ReturnCheck(_T("CoCreateInstance"), res);
res = ITest_Add(pTest, 123, 456, &v);
ReturnCheck(_T("Add"), res);
printf("123 + 345 = %d\n", v);
s1 = SysAllocString(L"ABC");
s2 = SysAllocString(L"XYZ");
res = ITest_Concat(pTest, s1, s2, &s3);
ReturnCheck(_T("Concat"), res);
printf("%d %ls + %d %ls = %d %ls\n", SysStringLen(s1), s1, SysStringLen(s2), s2, SysStringLen(s3), s3);
SysFreeString(s1);
SysFreeString(s2);
SysFreeString(s3);
ITest_get_IV(pTest, &iv);
ITest_get_SV(pTest, &sv);
printf("IV = %d SV = %d %ls\n", iv, SysStringLen(sv), sv);
SysFreeString(sv);
ITest_put_IV(pTest, 123);
ITest_put_SV(pTest, SysAllocString(L"ABC"));
ITest_get_IV(pTest, &iv);
ITest_get_SV(pTest, &sv);
printf("IV = %d SV = %d %ls\n", iv, SysStringLen(sv), sv);
SysFreeString(sv);
ITest_Release(pTest);
CoUninitialize();
return 0;
}
Build and run:
cl TestC.c Test_i.c ole32.lib oleaut32.lib
TestC
What is happening:
TestCPP3.cpp:
#include <stdio.h>
#include <windows.h>
#include <initguid.h>
#include <tchar.h>
#import "Test.tlb" no_namespace named_guids
void ReturnCheck(LPTSTR func, HRESULT res)
{
if(res != S_OK)
{
TCHAR buffer[1000];
FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, 0, res, 0, buffer, sizeof(buffer), 0);
_tprintf("%s: %s\n", func, buffer);
exit(1);
}
}
int main()
{
HRESULT res;
CoInitialize(NULL);
printf("COM/C++ with CreateInstance:\n");
try
{
ITestPtr spTest;
res = spTest.CreateInstance(CLSID_CoTest);
ReturnCheck(_T("CreateInstance"), res);
long v = spTest->Add(123L, 456L);
printf("123 + 345 = %d\n", v);
BSTR s1 = SysAllocString(L"ABC");
BSTR s2 = SysAllocString(L"XYZ");
BSTR s3 = spTest->Concat(s1, s2);
printf("%d %ls + %d %ls = %d %ls\n", SysStringLen(s1), s1, SysStringLen(s2), s2, SysStringLen(s3), s3);
SysFreeString(s1);
SysFreeString(s2);
SysFreeString(s3);
BSTR sv = spTest->GetSV();
printf("IV = %d SV = %d %ls\n", spTest->GetIV(), SysStringLen(sv), sv);
SysFreeString(sv);
spTest->PutIV(123);
spTest->PutSV(SysAllocString(L"ABC"));
sv = spTest->GetSV();
printf("IV = %d SV = %d %ls\n", spTest->GetIV(), SysStringLen(sv), sv);
SysFreeString(sv);
spTest = NULL;
}
catch (_com_error er)
{
_tprintf("%s\n", er.ErrorMessage());
}
CoUninitialize();
return 0;
}
Build and run:
cl /EHsc TestCPP3.cpp ole32.lib oleaut32.lib
TestCPP3
TestCPP4.cpp:
#include <stdio.h>
#include <windows.h>
#include <initguid.h>
#include <tchar.h>
#import "Test.tlb" no_namespace named_guids
void ReturnCheck(LPTSTR func, HRESULT res)
{
if(res != S_OK)
{
TCHAR buffer[1000];
FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, 0, res, 0, buffer, sizeof(buffer), 0);
_tprintf("%s: %s\n", func, buffer);
exit(1);
}
}
int main()
{
HRESULT res;
CoInitialize(NULL);
printf("COM/C++ with constructor:\n");
try
{
ITestPtr spTest(__uuidof(CoTest));
long v = spTest->Add(123, 456);
printf("123 + 345 = %d\n", v);
BSTR s1 = SysAllocString(L"ABC");
BSTR s2 = SysAllocString(L"XYZ");
BSTR s3 = spTest->Concat(s1, s2);
printf("%d %ls + %d %ls = %d %ls\n", SysStringLen(s1), s1, SysStringLen(s2), s2, SysStringLen(s3), s3);
SysFreeString(s1);
SysFreeString(s2);
SysFreeString(s3);
BSTR sv = spTest->GetSV();
printf("IV = %d SV = %d %ls\n", spTest->GetIV(), SysStringLen(sv), sv);
SysFreeString(sv);
spTest->PutIV(123);
spTest->PutSV(SysAllocString(L"ABC"));
sv = spTest->GetSV();
printf("IV = %d SV = %d %ls\n", spTest->GetIV(), SysStringLen(sv), sv);
SysFreeString(sv);
spTest = NULL;
}
catch (_com_error er)
{
_tprintf("%s\n", er.ErrorMessage());
}
CoUninitialize();
return 0;
}
Build and run:
cl /EHsc TestCPP4.cpp ole32.lib oleaut32.lib
TestCPP4
TestCS.cpp:
using System;
using Test;
public class MainClass
{
public static void Main(string[] args)
{
Console.WriteLine("COM/C# static");
CoTest cotest = new CoTestClass();
ITest test = (ITest)cotest;
Console.WriteLine("123 + 456 = {0}", test.Add(123, 456));
Console.WriteLine("ABC + XYZ = {0}", test.Concat("ABC", "XYZ"));
Console.WriteLine("IV = {0} SV = {1}", test.IV, test.SV);
test.IV = 123;
test.SV = "ABC";
Console.WriteLine("IV = {0} SV = {1}", test.IV, test.SV);
}
}
Build and run:
tlbimp /out=TestWrap.dll /namespace:Test Test.tlb
csc /platform:x86 /r:TestWrap.dll TestCS.cs
TestCS
TestPas.pas:
program TestPas;
{$mode delphi}{$H+}
uses
ActiveX, TestLibrary_1_0_TLB;
var
test : ITest;
begin
writeln('COM/Pascal static:');
CoInitialize(nil);
test := CoCoTest.Create;
writeln('123 + 456 = ', test.Add(123, 456));
writeln('ABC + XYZ = ', test.Concat('ABC', 'XYZ'));
writeln('IV = ', test.IV, ' SV = ', test.SV);
test.IV := 123;
test.SV := 'ABC';
writeln('IV = ', test.IV, ' SV = ', test.SV);
CoUninitialize;
end.
Build and run:
importtl Test.tlb
fpc TestLibrary_1_0_TLB.pas
fpc TestPas.pas
TestPas
What is happening:
TestVBS.vbs:
WScript.Echo "COM/VBS:"
Set test = CreateObject("Test.CoTest")
WScript.Echo "123 + 456 = " & CStr(test.Add(CLng(123), CLng(456)))
WScript.Echo "ABC + XYZ = " & test.Concat("ABC", "XYZ")
WScript.Echo "IV = " & CStr(test.IV) & " SV = " & test.SV
test.IV = 123
test.SV = "ABC"
WScript.Echo "IV = " & CStr(test.IV) & " SV = " & test.SV
Set test = Nothing
Build and run:
cscript TestVBS.vbs
TestCSDyn.cs:
using System;
public class MainClass
{
public static void Main(string[] args)
{
Console.WriteLine("COM/C# dynamic");
dynamic test = Activator.CreateInstance(Type.GetTypeFromProgID("Test.CoTest"));
Console.WriteLine("123 + 456 = {0}", test.Add(123, 456));
Console.WriteLine("ABC + XYZ = {0}", test.Concat("ABC", "XYZ"));
Console.WriteLine("IV = {0} SV = {1}", test.IV, test.SV);
test.IV = 123;
test.SV = "ABC";
Console.WriteLine("IV = {0} SV = {1}", test.IV, test.SV);
}
}
Build and run:
csc /platform:x86 TestCSDyn.cs
TestCSDyn
TestPasDyn.pas:
program TestPasDyn;
uses
ActiveX,ComObj;
var
test : Variant;
begin
writeln('COM/Pascal dynamic:');
CoInitialize(nil);
test := CreateOLEObject('Test.CoTest');
writeln('123 + 456 = ', test.Add(123, 456));
writeln('ABC + XYZ = ', test.Concat('ABC', 'XYZ'));
writeln('IV = ', test.IV, ' SV = ', test.SV);
test.IV := 123;
test.SV := 'ABC';
writeln('IV = ', test.IV, ' SV = ', test.SV);
CoUninitialize;
end.
Build and run:
fpc TestPasDyn.pas
TestPasDyn
TestJava.java:
import com.jacob.activeX.ActiveXComponent;
import com.jacob.com.Variant;
public class TestJava {
public static void main(String[] args) {
System.out.println("COM/Java with Jacob ActiveXComponent");
ActiveXComponent test = new ActiveXComponent("Test.CoTest");
System.out.println("123 + 456 = " + test.invoke("Add", new Variant(123), new Variant(456)).getInt());
System.out.println("ABC + XYZ = " + test.invoke("Concat", new Variant("ABC"), new Variant("XYZ")).getString());
System.out.println("IV = " + test.getPropertyAsInt("IV") + " SV = " + test.getPropertyAsString("SV"));
test.setProperty("IV", 123);
test.setProperty("SV", "ABC");
System.out.println("IV = " + test.getPropertyAsInt("IV") + " SV = " + test.getPropertyAsString("SV"));
}
}
Build and run:
javac -cp %JACOBDIR%\jacob.jar TestJava.java
java -Djava.library.path=%JACOBDIR% -cp .;%JACOBDIR%\jacob.jar TestJava
Nobody will use IDispatch with C++, but it is still relevant to see the code to understand what is happening in other languages.
TestCPP5.cpp:
#include <stdio.h>
#include <windows.h>
#include <initguid.h>
#include <tchar.h>
#import "Test.tlb" no_namespace named_guids
void ReturnCheck(LPTSTR func, HRESULT res)
{
if(res != S_OK)
{
TCHAR buffer[1000];
FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, 0, res, 0, buffer, sizeof(buffer), 0);
_tprintf("%s: %s\n", func, buffer);
exit(1);
}
}
int main()
{
HRESULT res;
CoInitialize(NULL);
printf("COM/C++ via IDispatch:\n");
CLSID clsid;
res = CLSIDFromProgID(L"Test.CoTest", &CLSid);
ReturnCheck(_T("CLSIDFromProgID"), res);
IDispatch *pDisp = NULL;
res = CoCreateInstance(clsid, NULL, CLSCTX_SERVER, IID_IDispatch, (void **)&pDisp);
ReturnCheck(_T("CoCreateInstance"), res);
DISPID dispid;
LPOLESTR func;
VARIANT args[2], retval;
DISPPARAMS params;
params.rgvarg = args;
params.rgdispidNamedArgs = 0;
params.cNamedArgs = 0;
// Add
params.cArgs = 2;
func = L"Add";
res = pDisp->GetIDsOfNames(IID_NULL, &func, 1, LOCALE_SYSTEM_DEFAULT, &dispid);
ReturnCheck(_T("IDispatch GetIDsOfNames"), res);
VariantInit(&args[0]);
args[0].vt = VT_I4;
args[0].lVal = 456;
VariantInit(&args[1]);
args[1].vt = VT_I4;
args[1].lVal = 123;
VariantInit(&retval);
res = pDisp->Invoke(dispid, IID_NULL, LOCALE_SYSTEM_DEFAULT, DISPATCH_METHOD, ¶ms, &retval, NULL, NULL);
ReturnCheck(_T("IDispatch Invoke"), res);
printf("123 + 345 = %d\n", retval.lVal);
// Concat
params.cArgs = 2;
func = L"Concat";
res = pDisp->GetIDsOfNames(IID_NULL, &func, 1, LOCALE_SYSTEM_DEFAULT, &dispid);
ReturnCheck(_T("IDispatch GetIDsOfNames"), res);
VariantInit(&args[0]);
args[0].vt = VT_BSTR;
args[0].bstrVal = SysAllocString(L"ABC");
VariantInit(&args[1]);
args[1].vt = VT_BSTR;
args[1].bstrVal = SysAllocString(L"XYZ");
VariantInit(&retval);
res = pDisp->Invoke(dispid, IID_NULL, LOCALE_SYSTEM_DEFAULT, DISPATCH_METHOD, ¶ms, &retval, NULL, NULL);
ReturnCheck(_T("IDispatch Invoke"), res);
printf("3 ABC + 3 XYZ = %d %ls\n", SysStringLen(retval.bstrVal), retval.bstrVal);
// Get
params.cArgs = 0;
func = L"IV";
res = pDisp->GetIDsOfNames(IID_NULL, &func, 1, LOCALE_SYSTEM_DEFAULT, &dispid);
ReturnCheck(_T("IDispatch GetIDsOfNames"), res);
VariantInit(&retval);
res = pDisp->Invoke(dispid, IID_NULL, LOCALE_SYSTEM_DEFAULT, DISPATCH_PROPERTYGET, ¶ms, &retval, NULL, NULL);
ReturnCheck(_T("IDispatch Invoke"), res);
printf("IV = %d", retval.lVal);
params.cArgs = 0;
func = L"SV";
res = pDisp->GetIDsOfNames(IID_NULL, &func, 1, LOCALE_SYSTEM_DEFAULT, &dispid);
ReturnCheck(_T("IDispatch GetIDsOfNames"), res);
VariantInit(&retval);
res = pDisp->Invoke(dispid, IID_NULL, LOCALE_SYSTEM_DEFAULT, DISPATCH_PROPERTYGET, ¶ms, &retval, NULL, NULL);
ReturnCheck(_T("IDispatch Invoke"), res);
printf(" SV = %d %ls\n", SysStringLen(retval.bstrVal), retval.bstrVal);
// Put
DISPID temp = DISPID_PROPERTYPUT; // these 3 lines are necessary
params.rgdispidNamedArgs = &temp;
params.cNamedArgs = 1;
//
params.cArgs = 1;
func = L"IV";
res = pDisp->GetIDsOfNames(IID_NULL, &func, 1, LOCALE_SYSTEM_DEFAULT, &dispid);
ReturnCheck(_T("IDispatch GetIDsOfNames"), res);
VariantInit(&args[0]);
args[0].vt = VT_I4;
args[0].lVal = 123;
res = pDisp->Invoke(dispid, IID_NULL, LOCALE_SYSTEM_DEFAULT, DISPATCH_PROPERTYPUT, ¶ms, NULL, NULL, NULL);
ReturnCheck(_T("IDispatch Invoke"), res);
params.cArgs = 1;
func = L"SV";
res = pDisp->GetIDsOfNames(IID_NULL, &func, 1, LOCALE_SYSTEM_DEFAULT, &dispid);
ReturnCheck(_T("IDispatch GetIDsOfNames"), res);
VariantInit(&args[0]);
args[0].vt = VT_BSTR;
args[0].bstrVal = SysAllocString(L"ABC");
res = pDisp->Invoke(dispid, IID_NULL, LOCALE_SYSTEM_DEFAULT, DISPATCH_PROPERTYPUT, ¶ms, NULL, NULL, NULL);
ReturnCheck(_T("IDispatch Invoke"), res);
//
params.rgdispidNamedArgs = 0;
params.cNamedArgs = 0;
// Get
params.cArgs = 0;
func = L"IV";
res = pDisp->GetIDsOfNames(IID_NULL, &func, 1, LOCALE_SYSTEM_DEFAULT, &dispid);
ReturnCheck(_T("IDispatch GetIDsOfNames"), res);
VariantInit(&retval);
res = pDisp->Invoke(dispid, IID_NULL, LOCALE_SYSTEM_DEFAULT, DISPATCH_PROPERTYGET, ¶ms, &retval, NULL, NULL);
ReturnCheck(_T("IDispatch Invoke"), res);
printf("IV = %d", retval.lVal);
params.cArgs = 0;
func = L"SV";
res = pDisp->GetIDsOfNames(IID_NULL, &func, 1, LOCALE_SYSTEM_DEFAULT, &dispid);
ReturnCheck(_T("IDispatch GetIDsOfNames"), res);
VariantInit(&retval);
res = pDisp->Invoke(dispid, IID_NULL, LOCALE_SYSTEM_DEFAULT, DISPATCH_PROPERTYGET, ¶ms, &retval, NULL, NULL);
ReturnCheck(_T("IDispatch Invoke"), res);
printf(" SV = %d %ls\n", SysStringLen(retval.bstrVal), retval.bstrVal);
//
pDisp->Release();
CoUninitialize();
return 0;
}
Build and run:
cl /EHsc TestCPP5.cpp ole32.lib oleaut32.lib
TestCPP5
TestPHP.php:
<?php
echo "COM/PHP:\r\n";
$test = new com('Test.CoTest');
echo sprintf("123 + 456 = %d\r\n", $test->Add(123, 456));
echo sprintf("ABC + XYZ = %s\r\n", $test->Concat("ABC", "XYZ"));
echo sprintf("IV = %d SV = %s\r\n", $test->IV, $test->SV);
$test->IV = 123;
$test->SV = "ABC";
echo sprintf("IV = %d SV = %s\r\n", $test->IV, $test->SV);
?>
Build and run:
php TestPHP.php
TestPy.py:
from win32com.client import *
import pythoncom
print("COM/Python win32:")
test = Dispatch("Test.CoTest")
print("123 + 345 = %d" % test.Add(123, 456))
print("ABC + XYZ = %s" % test.Concat("ABC", "XYZ"))
print("IV = %d SV = %s" % (test.IV, test.SV))
test.IV = 123
test.SV = "ABC"
print("IV = %d SV = %s" % (test.IV, test.SV))
Build and run:
python TestPy.py
What is happening:
In this iteration we will:
Note that the second interface is not available using scripting interface.
Note that default value is not possible in all languages,
Test.idl:
import "oaidl.idl";
// ITest2
[object, uuid(2F237FFD-A218-4056-8258-7D8A731D8B8F), oleautomation, dual, pointer_default(unique)]
interface ITest2 : IDispatch
{
[id(1)] HRESULT Sum([in,defaultvalue(0)] long v1, [in,defaultvalue(0)] long v2, [in,defaultvalue(0)] long v3, [in,defaultvalue(0)] long v4, [in,defaultvalue(0)] long v5,
[in,defaultvalue(0)] long v6, [in,defaultvalue(0)] long v7, [in,defaultvalue(0)] long v8, [in,defaultvalue(0)] long v9, [in,defaultvalue(0)] long v10,
[out,retval] long *res);
}
// ITest
[object, uuid(F621FEC0-3FE3-4256-AE47-22DAC5CDA176), oleautomation, dual, pointer_default(unique)]
interface ITest : IDispatch
{
[id(1)] HRESULT Add([in] long a, [in,defaultvalue(1)] long b, [out,retval] long *c);
[id(2)] HRESULT Concat([in] BSTR a, [in,defaultvalue(L"X")] BSTR b, [out,retval] BSTR *c);
[id(3),propget] HRESULT IV([out, retval] long *iv);
[id(3),propput] HRESULT IV([in] long iv);
[id(4),propget] HRESULT SV([out, retval] BSTR *sv);
[id(4),propput] HRESULT SV([in] BSTR sv);
[id(5)] HRESULT Another([out,retval] ITest **test);
}
// Test library
[uuid(61E1E003-C924-4df7-A443-2E865C87663D), version(1.0)]
library TestLibrary
{
importlib("stdole32.tlb");
[uuid(8CD6DD8F-B2E8-4e3f-BF00-37B7F9C4FB2A)]
coclass CoTest
{
[default] interface ITest;
interface ITest2;
};
};
TestEx.h:
#ifndef TESTEX_H
#define TESTEX_H
#include <windows.h>
#include <tchar.h>
#include <atlbase.h>
#include <atlcom.h>
#include "Test_i.c"
#include "Test.h"
class ATL_NO_VTABLE CCoTest : public CComObjectRootEx<CComSingleThreadModel>, public CComCoClass<CCoTest, &CLSID_CoTest>, public IDispatchImpl<ITest, &IID_ITest, &LIBID_TestLibrary>, public IDispatchImpl<ITest2, &IID_ITest2, &LIBID_TestLibrary>
{
private:
long m_iv;
BSTR m_sv;
public:
CCoTest();
virtual ~CCoTest();
BEGIN_COM_MAP(CCoTest)
COM_INTERFACE_ENTRY(ITest)
COM_INTERFACE_ENTRY(ITest2)
COM_INTERFACE_ENTRY2(IDispatch, ITest)
END_COM_MAP()
// ITest
STDMETHODIMP Add(long a, long b, long *c);
STDMETHODIMP Concat(BSTR a, BSTR b, BSTR *c);
STDMETHODIMP get_IV(long *iv);
STDMETHODIMP put_IV(long iv);
STDMETHODIMP get_SV(BSTR *sv);
STDMETHODIMP put_SV(BSTR sv);
STDMETHODIMP Another(ITest **test);
// ITest2
STDMETHODIMP Sum(long v1, long v2, long v3, long v4, long v5, long v6, long v7, long v8, long v9, long v10, long *res);
DECLARE_REGISTRY(CLSID_CoTest, _T("Test.CoTest.1"), _T("Test.CoTest"), 0U, THREADFLAGS_APARTMENT)
};
#endif // TESTEX_H
Test.cpp:
#include <stdio.h>
#include <windows.h>
#include <oleauto.h>
#include <initguid.h>
#include <atlbase.h>
extern CComModule _Module;
#include <atlcom.h>
#include "TestEx.h"
CComModule _Module;
BEGIN_OBJECT_MAP(ObjectMap)
OBJECT_ENTRY(CLSID_CoTest, CCoTest)
END_OBJECT_MAP()
CCoTest::CCoTest()
{
#ifdef DEBUG
printf("CCoTest ctor\n");
#endif
m_iv = 0;
m_sv = SysAllocString(L"");
}
CCoTest::~CCoTest()
{
#ifdef DEBUG
printf("CCoTest dtor\n");
#endif
SysFreeString(m_sv);
}
STDMETHODIMP CCoTest::Add(long a, long b, long *c)
{
*c = a + b;
return S_OK;
}
STDMETHODIMP CCoTest::Concat(BSTR a, BSTR b, BSTR *c)
{
OLECHAR *buf = new OLECHAR[SysStringLen(a) + SysStringLen(b) + 1];
wcscpy(buf, a);
wcscat(buf, b);
*c = SysAllocString(buf);
delete[] buf;
return S_OK;
}
STDMETHODIMP CCoTest::get_IV(long *iv)
{
*iv = m_iv;
return S_OK;
}
STDMETHODIMP CCoTest::put_IV(long iv)
{
m_iv = iv;
return S_OK;
}
STDMETHODIMP CCoTest::get_SV(BSTR *sv)
{
*sv = SysAllocString(m_sv);
return S_OK;
}
STDMETHODIMP CCoTest::put_SV(BSTR sv)
{
SysReAllocString(&m_sv, sv);
return S_OK;
}
STDMETHODIMP CCoTest::Another(ITest **test)
{
CComObject<CCoTest> *pTest;
CComObject<CCoTest>::CreateInstance(&pTest);
pTest->QueryInterface(IID_ITest, (void **)test);
return S_OK;
}
STDMETHODIMP CCoTest::Sum(long v1, long v2, long v3, long v4, long v5, long v6, long v7, long v8, long v9, long v10, long *res)
{
*res = v1 + v2 + v3 + v4 + v5 + v6 + v7 + v8 + v9 + v10;
return S_OK;
}
BOOL WINAPI DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID lpReserved)
{
if(dwReason == DLL_PROCESS_ATTACH)
{
_Module.Init(ObjectMap, hInstance, &LIBID_TestLibrary);
}
else if(dwReason == DLL_PROCESS_DETACH)
{
_Module.Term();
}
return TRUE;
}
STDMETHODIMP DllGetClassObject(REFCLSID rclsid, REFIID riid, void **ppv)
{
#ifdef DEBUG
printf("DllGetClassObject\n");
#endif
return _Module.GetClassObject(rclsid, riid, ppv);
}
STDMETHODIMP DllCanUnloadNow()
{
return (_Module.GetLockCount() == 0) ? S_OK : S_FALSE;
}
STDMETHODIMP DllRegisterServer()
{
return _Module.RegisterServer(TRUE);
}
STDMETHODIMP DllUnregisterServer()
{
return _Module.UnregisterServer(TRUE);
}
Test.def:
LIBRARY "Test"
EXPORTS
DllMain PRIVATE
DllGetClassObject PRIVATE
DllCanUnloadNow PRIVATE
DllRegisterServer PRIVATE
DllUnregisterServer PRIVATE
Build and registration:
midl Test.idl
cl /LD Test.cpp Test.def oleaut32.lib
regsvr32 Test.dll
TestC.c:
#include <stdio.h>
#include <stdlib.h>
#include <windows.h>
#include <initguid.h>
#include <tchar.h>
#define COBJMACROS
#include "Test.h"
void ReturnCheck(LPTSTR func, HRESULT res)
{
if(res != S_OK)
{
TCHAR buffer[1000];
FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, 0, res, 0, buffer, sizeof(buffer), 0);
_tprintf("%s: %s\n", func, buffer);
exit(1);
}
}
int main()
{
HRESULT res;
ITest *pTest, *another, *temp;
ITest2 *pTest2;
int v, iv, sum;
BSTR s1, s2, s3, sv;
CoInitialize(NULL);
printf("COM/C:\n");
res = CoCreateInstance(&CLSID_CoTest, NULL, CLSCTX_INPROC_SERVER, &IID_ITest, (void**)&pTest);
ReturnCheck(_T("CoCreateInstance"), res);
res = ITest_Add(pTest, 123, 456, &v);
ReturnCheck(_T("Add"), res);
printf("123 + 345 = %d\n", v);
s1 = SysAllocString(L"ABC");
s2 = SysAllocString(L"XYZ");
res = ITest_Concat(pTest, s1, s2, &s3);
ReturnCheck(_T("Concat"), res);
printf("%d %ls + %d %ls = %d %ls\n", SysStringLen(s1), s1, SysStringLen(s2), s2, SysStringLen(s3), s3);
SysFreeString(s1);
SysFreeString(s2);
SysFreeString(s3);
ITest_get_IV(pTest, &iv);
ITest_get_SV(pTest, &sv);
printf("IV = %d SV = %d %ls\n", iv, SysStringLen(sv), sv);
SysFreeString(sv);
ITest_put_IV(pTest, 123);
ITest_put_SV(pTest, SysAllocString(L"ABC"));
ITest_get_IV(pTest, &iv);
ITest_get_SV(pTest, &sv);
printf("IV = %d SV = %d %ls\n", iv, SysStringLen(sv), sv);
SysFreeString(sv);
ITest_Another(pTest, &another);
res = ITest_Add(another, 123, 456, &v);
ReturnCheck(_T("Add"), res);
printf("Another 123 + 345 = %d\n", v);
ITest_Release(another);
ITest_Release(pTest);
res = CoCreateInstance(&CLSID_CoTest, NULL, CLSCTX_INPROC_SERVER, &IID_ITest, (void**)&temp);
ReturnCheck(_T("CoCreateInstance"), res);
res = ITest2_QueryInterface(temp, &IID_ITest2, &pTest2);
ReturnCheck(_T("QueryInterface"), res);
ITest2_Sum(pTest2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, &sum);
printf("SUM = %d\n", sum);
ITest2_Sum(pTest2, 1, 2, 3, 4, 5, 0, 0, 0, 0, 0, &sum);
printf("SUM 1..5 = %d\n", sum);
ITest2_Sum(pTest2, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, &sum);
printf("SUM 1..10 = %d\n", sum);
ITest2_Release (pTest2);
ITest_Release (temp);
CoUninitialize();
return 0;
}
Build and run:
cl TestC.c Test_i.c ole32.lib oleaut32.lib
TestC
What is happening:
TestCPP3.cpp:
#include <stdio.h>
#include <windows.h>
#include <initguid.h>
#include <tchar.h>
#import "Test.tlb" no_namespace named_guids
void ReturnCheck(LPTSTR func, HRESULT res)
{
if(res != S_OK)
{
TCHAR buffer[1000];
FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, 0, res, 0, buffer, sizeof(buffer), 0);
_tprintf("%s: %s\n", func, buffer);
exit(1);
}
}
int main()
{
HRESULT res;
CoInitialize(NULL);
printf("COM/C++ with CreateInstance:\n");
try
{
ITestPtr spTest;
res = spTest.CreateInstance(CLSID_CoTest);
ReturnCheck(_T("CreateInstance"), res);
long v = spTest->Add(123L, 456L);
printf("123 + 345 = %d\n", v);
BSTR s1 = SysAllocString(L"ABC");
BSTR s2 = SysAllocString(L"XYZ");
BSTR s3 = spTest->Concat(s1, s2);
printf("%d %ls + %d %ls = %d %ls\n", SysStringLen(s1), s1, SysStringLen(s2), s2, SysStringLen(s3), s3);
SysFreeString(s1);
SysFreeString(s2);
SysFreeString(s3);
BSTR sv = spTest->GetSV();
printf("IV = %d SV = %d %ls\n", spTest->GetIV(), SysStringLen(sv), sv);
SysFreeString(sv);
spTest->PutIV(123);
spTest->PutSV(SysAllocString(L"ABC"));
sv = spTest->GetSV();
printf("IV = %d SV = %d %ls\n", spTest->GetIV(), SysStringLen(sv), sv);
SysFreeString(sv);
ITestPtr another = spTest->Another();
printf("Another 123 + 345 = %d\n", another->Add(123L, 456L));
another = NULL;
spTest = NULL;
ITestPtr temp;
res = temp.CreateInstance(CLSID_CoTest);
ReturnCheck(_T("CreateInstance"), res);
ITest2Ptr spTest2 = temp;
printf("SUM = %d\n", spTest2->Sum(0, 0, 0, 0, 0, 0, 0, 0, 0, 0));
printf("SUM 1..5 = %d\n", spTest2->Sum(1, 2, 3, 4, 5, 0, 0, 0, 0, 0));
printf("SUM 1..10 = %d\n", spTest2->Sum(1, 2, 3, 4, 5, 6, 7, 8, 9, 10));
spTest2 = NULL;
temp = NULL;
}
catch (_com_error er)
{
_tprintf("%s\n", er.ErrorMessage());
}
CoUninitialize();
return 0;
}
Build and run:
cl /EHsc TestCPP3.cpp ole32.lib oleaut32.lib
TestCPP3
TestCPP4.cpp:
#include <stdio.h>
#include <windows.h>
#include <initguid.h>
#include <tchar.h>
#import "Test.tlb" no_namespace named_guids
void ReturnCheck(LPTSTR func, HRESULT res)
{
if(res != S_OK)
{
TCHAR buffer[1000];
FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, 0, res, 0, buffer, sizeof(buffer), 0);
_tprintf("%s: %s\n", func, buffer);
exit(1);
}
}
int main()
{
HRESULT res;
CoInitialize(NULL);
printf("COM/C++ with constructor:\n");
try
{
ITestPtr spTest(__uuidof(CoTest));
long v = spTest->Add(123, 456);
printf("123 + 345 = %d\n", v);
BSTR s1 = SysAllocString(L"ABC");
BSTR s2 = SysAllocString(L"XYZ");
BSTR s3 = spTest->Concat(s1, s2);
printf("%d %ls + %d %ls = %d %ls\n", SysStringLen(s1), s1, SysStringLen(s2), s2, SysStringLen(s3), s3);
SysFreeString(s1);
SysFreeString(s2);
SysFreeString(s3);
BSTR sv = spTest->GetSV();
printf("IV = %d SV = %d %ls\n", spTest->GetIV(), SysStringLen(sv), sv);
SysFreeString(sv);
spTest->PutIV(123);
spTest->PutSV(SysAllocString(L"ABC"));
sv = spTest->GetSV();
printf("IV = %d SV = %d %ls\n", spTest->GetIV(), SysStringLen(sv), sv);
SysFreeString(sv);
ITestPtr another = spTest->Another();
printf("Another 123 + 345 = %d\n", another->Add(123L, 456L));
another = NULL;
spTest = NULL;
ITestPtr temp(__uuidof(CoTest));
ITest2Ptr spTest2 = temp;
printf("SUM = %d\n", spTest2->Sum(0, 0, 0, 0, 0, 0, 0, 0, 0, 0));
printf("SUM 1..5 = %d\n", spTest2->Sum(1, 2, 3, 4, 5, 0, 0, 0, 0, 0));
printf("SUM 1..10 = %d\n", spTest2->Sum(1, 2, 3, 4, 5, 6, 7, 8, 9, 10));
spTest2 = NULL;
temp = NULL;
}
catch (_com_error er)
{
_tprintf("%s\n", er.ErrorMessage());
}
CoUninitialize();
return 0;
}
Build and run:
cl /EHsc TestCPP4.cpp ole32.lib oleaut32.lib
TestCPP4
TestCS.cpp:
using System;
using Test;
public class MainClass
{
public static void Main(string[] args)
{
Console.WriteLine("COM/C# static");
CoTest cotest = new CoTestClass();
ITest test = (ITest)cotest;
Console.WriteLine("123 + 456 = {0}", test.Add(123, 456));
Console.WriteLine("123 + default = {0}", test.Add(123));
Console.WriteLine("ABC + XYZ = {0}", test.Concat("ABC", "XYZ"));
Console.WriteLine("ABC + default = {0}", test.Concat("ABC"));
Console.WriteLine("IV = {0} SV = {1}", test.IV, test.SV);
test.IV = 123;
test.SV = "ABC";
Console.WriteLine("IV = {0} SV = {1}", test.IV, test.SV);
CoTest another = test.Another();
Console.WriteLine("Another 123 + 345 = {0}", another.Add(123, 456));
CoTest cotest2 = new CoTestClass();
ITest2 test2 = (ITest2)cotest2;
Console.WriteLine("Sum = {0}", test2.Sum());
Console.WriteLine("Sum 1..5 = {0}", test2.Sum(1, 2, 3, 4, 5));
Console.WriteLine("Sum 1..10 = {0}", test2.Sum(1, 2, 3, 4, 5, 6, 7, 8, 9, 10));
}
}
Build and run:
tlbimp /out=TestWrap.dll /namespace:Test Test.tlb
csc /platform:x86 /r:TestWrap.dll TestCS.cs
TestCS
TestPas.pas:
program TestPas;
{$mode delphi}{$H+}
uses
ActiveX, TestLibrary_1_0_TLB;
var
test, another, temp : ITest;
test2 : ITest2;
begin
writeln('COM/Pascal static:');
CoInitialize(nil);
test := CoCoTest.Create;
writeln('123 + 456 = ', test.Add(123, 456));
writeln('ABC + XYZ = ', test.Concat('ABC', 'XYZ'));
writeln('IV = ', test.IV, ' SV = ', test.SV);
test.IV := 123;
test.SV := 'ABC';
writeln('IV = ', test.IV, ' SV = ', test.SV);
another := test.Another; (* seems to leak a reference ???? *)
writeln('Another 123 + 456 = ', another.Add(123, 456));
temp := CoCoTest.Create;
temp.QueryInterface(IID_ITest2, test2);
writeln('Sum = ', test2.Sum(0, 0, 0, 0, 0, 0, 0, 0, 0, 0));
writeln('Sum 1..5 = ', test2.Sum(1, 2, 3, 4, 5, 0, 0, 0, 0, 0));
writeln('Sum 1..10 = ', test2.Sum(1, 2, 3, 4, 5, 6, 7, 8, 9, 10));
CoUninitialize;
end.
Build and run:
importtl Test.tlb
fpc TestLibrary_1_0_TLB.pas
fpc TestPas.pas
TestPas
What is happening:
TestVBS.vbs:
WScript.Echo "COM/VBS:"
Set test = CreateObject("Test.CoTest")
WScript.Echo "123 + 456 = " & CStr(test.Add(CLng(123), CLng(456)))
WScript.Echo "123 + default = " & CStr(test.Add(CLng(123)))
WScript.Echo "ABC + XYZ = " & test.Concat("ABC", "XYZ")
WScript.Echo "ABC + default = " & test.Concat("ABC")
WScript.Echo "IV = " & CStr(test.IV) & " SV = " & test.SV
test.IV = 123
test.SV = "ABC"
WScript.Echo "IV = " & CStr(test.IV) & " SV = " & test.SV
Set another = test.Another()
WScript.Echo "Another 123 + 456 = " & CStr(another.Add(CLng(123), CLng(456)))
Set another = Nothing
' second non-default interface is not accessible
Set test = Nothing
Build and run:
cscript TestVBS.vbs
TestCSDyn.cs:
using System;
public class MainClass
{
public static void Main(string[] args)
{
Console.WriteLine("COM/C# dynamic");
dynamic test = Activator.CreateInstance(Type.GetTypeFromProgID("Test.CoTest"));
Console.WriteLine("123 + 456 = {0}", test.Add(123, 456));
Console.WriteLine("123 + default = {0}", test.Add(123));
Console.WriteLine("ABC + XYZ = {0}", test.Concat("ABC", "XYZ"));
Console.WriteLine("ABC + default = {0}", test.Concat("ABC"));
Console.WriteLine("IV = {0} SV = {1}", test.IV, test.SV);
test.IV = 123;
test.SV = "ABC";
Console.WriteLine("IV = {0} SV = {1}", test.IV, test.SV);
dynamic another = test.Another();
Console.WriteLine("Another 123 + 345 = {0}", another.Add(123, 456));
// second non-default interface is not accessible
}
}
Build and run:
csc /platform:x86 TestCSDyn.cs
TestCSDyn
TestPasDyn.pas:
program TestPasDyn;
uses
ActiveX,ComObj;
var
test, another : Variant;
begin
writeln('COM/Pascal dynamic:');
CoInitialize(nil);
test := CreateOLEObject('Test.CoTest');
writeln('123 + 456 = ', test.Add(123, 456));
writeln('123 + default = ', test.Add(123));
writeln('ABC + XYZ = ', test.Concat('ABC', 'XYZ'));
writeln('ABC + default = ', test.Concat('ABC'));
writeln('IV = ', test.IV, ' SV = ', test.SV);
test.IV := 123;
test.SV := 'ABC';
writeln('IV = ', test.IV, ' SV = ', test.SV);
another := test.Another;
writeln('Another 123 + 456 = ', another.Add(123, 456));
(* second non-default interface is not accessible *)
CoUninitialize;
end.
Build and run:
fpc TestPasDyn.pas
TestPasDyn
TestJava.java:
import com.jacob.activeX.ActiveXComponent;
import com.jacob.com.Variant;
public class TestJava {
public static void main(String[] args) {
System.out.println("COM/Java with Jacob ActiveXComponent");
ActiveXComponent test = new ActiveXComponent("Test.CoTest");
System.out.println("123 + 456 = " + test.invoke("Add", new Variant(123), new Variant(456)).getInt());
System.out.println("123 + default = " + test.invoke("Add", new Variant(123)).getInt());
System.out.println("ABC + XYZ = " + test.invoke("Concat", new Variant("ABC"), new Variant("XYZ")).getString());
System.out.println("ABC + default = " + test.invoke("Concat", new Variant("ABC")).getString());
System.out.println("IV = " + test.getPropertyAsInt("IV") + " SV = " + test.getPropertyAsString("SV"));
test.setProperty("IV", 123);
test.setProperty("SV", "ABC");
System.out.println("IV = " + test.getPropertyAsInt("IV") + " SV = " + test.getPropertyAsString("SV"));
ActiveXComponent another = test.invokeGetComponent("Another");
System.out.println("Another 123 + 456 = " + another.invoke("Add", new Variant(123), new Variant(456)).getInt());
// second non-default interface is not accessible
}
}
Build and run:
javac -cp %JACOBDIR%\jacob.jar TestJava.java
java -Djava.library.path=%JACOBDIR% -cp .;%JACOBDIR%\jacob.jar TestJava
Nobody will use IDispatch with C++, but it is still relevant to see the code to understand what is happening in other languages.
TestCPP5.cpp:
#include <stdio.h>
#include <windows.h>
#include <initguid.h>
#include <tchar.h>
#import "Test.tlb" no_namespace named_guids
void ReturnCheck(LPTSTR func, HRESULT res)
{
if(res != S_OK)
{
TCHAR buffer[1000];
FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, 0, res, 0, buffer, sizeof(buffer), 0);
_tprintf("%s: %s\n", func, buffer);
exit(1);
}
}
int main()
{
HRESULT res;
CoInitialize(NULL);
printf("COM/C++ via IDispatch:\n");
CLSID clsid;
res = CLSIDFromProgID(L"Test.CoTest", &clsid);
ReturnCheck(_T("CLSIDFromProgID"), res);
IDispatch *pDisp = NULL;
res = CoCreateInstance(clsid, NULL, CLSCTX_SERVER, IID_IDispatch, (void **)&pDisp);
ReturnCheck(_T("CoCreateInstance"), res);
DISPID dispid;
LPOLESTR func;
VARIANT args[2], retval;
DISPPARAMS params;
params.rgvarg = args;
params.rgdispidNamedArgs = 0;
params.cNamedArgs = 0;
// Add
params.cArgs = 2;
func = L"Add";
res = pDisp->GetIDsOfNames(IID_NULL, &func, 1, LOCALE_SYSTEM_DEFAULT, &dispid);
ReturnCheck(_T("IDispatch GetIDsOfNames"), res);
VariantInit(&args[0]);
args[0].vt = VT_I4;
args[0].lVal = 123;
VariantInit(&args[1]);
args[1].vt = VT_I4;
args[1].lVal = 456;
VariantInit(&retval);
res = pDisp->Invoke(dispid, IID_NULL, LOCALE_SYSTEM_DEFAULT, DISPATCH_METHOD, ¶ms, &retval, NULL, NULL);
ReturnCheck(_T("IDispatch Invoke"), res);
printf("123 + 456 = %d\n", retval.lVal);
// Add default
params.cArgs = 1;
func = L"Add";
res = pDisp->GetIDsOfNames(IID_NULL, &func, 1, LOCALE_SYSTEM_DEFAULT, &dispid);
ReturnCheck(_T("IDispatch GetIDsOfNames"), res);
VariantInit(&args[0]);
args[0].vt = VT_I4;
args[0].lVal = 123;
VariantInit(&retval);
res = pDisp->Invoke(dispid, IID_NULL, LOCALE_SYSTEM_DEFAULT, DISPATCH_METHOD, ¶ms, &retval, NULL, NULL);
ReturnCheck(_T("IDispatch Invoke"), res);
printf("123 + default = %d\n", retval.lVal);
// Concat
params.cArgs = 2;
func = L"Concat";
res = pDisp->GetIDsOfNames(IID_NULL, &func, 1, LOCALE_SYSTEM_DEFAULT, &dispid);
ReturnCheck(_T("IDispatch GetIDsOfNames"), res);
VariantInit(&args[0]);
args[0].vt = VT_BSTR;
args[0].bstrVal = SysAllocString(L"ABC");
VariantInit(&args[1]);
args[1].vt = VT_BSTR;
args[1].bstrVal = SysAllocString(L"XYZ");
VariantInit(&retval);
res = pDisp->Invoke(dispid, IID_NULL, LOCALE_SYSTEM_DEFAULT, DISPATCH_METHOD, ¶ms, &retval, NULL, NULL);
ReturnCheck(_T("IDispatch Invoke"), res);
printf("3 ABC + 3 XYZ = %d %ls\n", SysStringLen(retval.bstrVal), retval.bstrVal);
// Concat default
params.cArgs = 1;
func = L"Concat";
res = pDisp->GetIDsOfNames(IID_NULL, &func, 1, LOCALE_SYSTEM_DEFAULT, &dispid);
ReturnCheck(_T("IDispatch GetIDsOfNames"), res);
VariantInit(&args[0]);
args[0].vt = VT_BSTR;
args[0].bstrVal = SysAllocString(L"ABC");
VariantInit(&retval);
res = pDisp->Invoke(dispid, IID_NULL, LOCALE_SYSTEM_DEFAULT, DISPATCH_METHOD, ¶ms, &retval, NULL, NULL);
ReturnCheck(_T("IDispatch Invoke"), res);
printf("3 ABC + default = %d %ls\n", SysStringLen(retval.bstrVal), retval.bstrVal);
// Get
params.cArgs = 0;
func = L"IV";
res = pDisp->GetIDsOfNames(IID_NULL, &func, 1, LOCALE_SYSTEM_DEFAULT, &dispid);
ReturnCheck(_T("IDispatch GetIDsOfNames"), res);
VariantInit(&retval);
res = pDisp->Invoke(dispid, IID_NULL, LOCALE_SYSTEM_DEFAULT, DISPATCH_PROPERTYGET, ¶ms, &retval, NULL, NULL);
ReturnCheck(_T("IDispatch Invoke"), res);
printf("IV = %d", retval.lVal);
params.cArgs = 0;
func = L"SV";
res = pDisp->GetIDsOfNames(IID_NULL, &func, 1, LOCALE_SYSTEM_DEFAULT, &dispid);
ReturnCheck(_T("IDispatch GetIDsOfNames"), res);
VariantInit(&retval);
res = pDisp->Invoke(dispid, IID_NULL, LOCALE_SYSTEM_DEFAULT, DISPATCH_PROPERTYGET, ¶ms, &retval, NULL, NULL);
ReturnCheck(_T("IDispatch Invoke"), res);
printf(" SV = %d %ls\n", SysStringLen(retval.bstrVal), retval.bstrVal);
// Put
DISPID temp = DISPID_PROPERTYPUT; // these 3 lines are necessary
params.rgdispidNamedArgs = &temp;
params.cNamedArgs = 1;
//
params.cArgs = 1;
func = L"IV";
res = pDisp->GetIDsOfNames(IID_NULL, &func, 1, LOCALE_SYSTEM_DEFAULT, &dispid);
ReturnCheck(_T("IDispatch GetIDsOfNames"), res);
VariantInit(&args[0]);
args[0].vt = VT_I4;
args[0].lVal = 123;
res = pDisp->Invoke(dispid, IID_NULL, LOCALE_SYSTEM_DEFAULT, DISPATCH_PROPERTYPUT, ¶ms, NULL, NULL, NULL);
ReturnCheck(_T("IDispatch Invoke"), res);
params.cArgs = 1;
func = L"SV";
res = pDisp->GetIDsOfNames(IID_NULL, &func, 1, LOCALE_SYSTEM_DEFAULT, &dispid);
ReturnCheck(_T("IDispatch GetIDsOfNames"), res);
VariantInit(&args[0]);
args[0].vt = VT_BSTR;
args[0].bstrVal = SysAllocString(L"ABC");
res = pDisp->Invoke(dispid, IID_NULL, LOCALE_SYSTEM_DEFAULT, DISPATCH_PROPERTYPUT, ¶ms, NULL, NULL, NULL);
ReturnCheck(_T("IDispatch Invoke"), res);
//
params.rgdispidNamedArgs = 0;
params.cNamedArgs = 0;
// Get
params.cArgs = 0;
func = L"IV";
res = pDisp->GetIDsOfNames(IID_NULL, &func, 1, LOCALE_SYSTEM_DEFAULT, &dispid);
ReturnCheck(_T("IDispatch GetIDsOfNames"), res);
VariantInit(&retval);
res = pDisp->Invoke(dispid, IID_NULL, LOCALE_SYSTEM_DEFAULT, DISPATCH_PROPERTYGET, ¶ms, &retval, NULL, NULL);
ReturnCheck(_T("IDispatch Invoke"), res);
printf("IV = %d", retval.lVal);
params.cArgs = 0;
func = L"SV";
res = pDisp->GetIDsOfNames(IID_NULL, &func, 1, LOCALE_SYSTEM_DEFAULT, &dispid);
ReturnCheck(_T("IDispatch GetIDsOfNames"), res);
VariantInit(&retval);
res = pDisp->Invoke(dispid, IID_NULL, LOCALE_SYSTEM_DEFAULT, DISPATCH_PROPERTYGET, ¶ms, &retval, NULL, NULL);
ReturnCheck(_T("IDispatch Invoke"), res);
printf(" SV = %d %ls\n", SysStringLen(retval.bstrVal), retval.bstrVal);
// second non-default interface is not accessible
//
pDisp->Release();
CoUninitialize();
return 0;
}
Build and run:
cl /EHsc TestCPP5.cpp ole32.lib oleaut32.lib
TestCPP5
TestPHP.php:
<?php
echo "COM/PHP:\r\n";
$test = new com('Test.CoTest');
echo sprintf("123 + 456 = %d\r\n", $test->Add(123, 456));
echo sprintf("123 + default = %d\r\n", $test->Add(123));
echo sprintf("ABC + XYZ = %s\r\n", $test->Concat("ABC", "XYZ"));
echo sprintf("ABC + default = %s\r\n", $test->Concat("ABC"));
echo sprintf("IV = %d SV = %s\r\n", $test->IV, $test->SV);
$test->IV = 123;
$test->SV = "ABC";
echo sprintf("IV = %d SV = %s\r\n", $test->IV, $test->SV);
$another = $test->Another();
echo sprintf("Another 123 + 456 = %d\r\n", $another->Add(123, 456));
// second non-default interface is not accessible
?>
Build and run:
php TestPHP.php
TestPy.py:
from win32com.client import *
import pythoncom
print("COM/Python win32:")
test = Dispatch("Test.CoTest")
print("123 + 345 = %d" % test.Add(123, 456))
print("123 + default = %d" % test.Add(123))
print("ABC + XYZ = %s" % test.Concat("ABC", "XYZ"))
print("ABC + default = %s" % test.Concat("ABC"))
print("IV = %d SV = %s" % (test.IV, test.SV))
test.IV = 123
test.SV = "ABC"
print("IV = %d SV = %s" % (test.IV, test.SV))
another = test.Another()
print("Another 123 + 345 = %d" % another.Add(123, 456))
# second non-default interface is not accessible
Build and run:
python TestPy.py
What is happening:
Now we will try drop the use of registry and use manifests instead.
Note that manifests only work with some languages.
Test.idl:
import "oaidl.idl";
// ITest2
[object, uuid(2F237FFD-A218-4056-8258-7D8A731D8B8F), oleautomation, dual, pointer_default(unique)]
interface ITest2 : IDispatch
{
[id(1)] HRESULT Sum([in,defaultvalue(0)] long v1, [in,defaultvalue(0)] long v2, [in,defaultvalue(0)] long v3, [in,defaultvalue(0)] long v4, [in,defaultvalue(0)] long v5,
[in,defaultvalue(0)] long v6, [in,defaultvalue(0)] long v7, [in,defaultvalue(0)] long v8, [in,defaultvalue(0)] long v9, [in,defaultvalue(0)] long v10,
[out,retval] long *res);
}
// ITest
[object, uuid(F621FEC0-3FE3-4256-AE47-22DAC5CDA176), oleautomation, dual, pointer_default(unique)]
interface ITest : IDispatch
{
[id(1)] HRESULT Add([in] long a, [in,defaultvalue(1)] long b, [out,retval] long *c);
[id(2)] HRESULT Concat([in] BSTR a, [in,defaultvalue(L"X")] BSTR b, [out,retval] BSTR *c);
[id(3),propget] HRESULT IV([out, retval] long *iv);
[id(3),propput] HRESULT IV([in] long iv);
[id(4),propget] HRESULT SV([out, retval] BSTR *sv);
[id(4),propput] HRESULT SV([in] BSTR sv);
[id(5)] HRESULT Another([out,retval] ITest **test);
}
// Test library
[uuid(61E1E003-C924-4df7-A443-2E865C87663D), version(1.0)]
library TestLibrary
{
importlib("stdole32.tlb");
[uuid(8CD6DD8F-B2E8-4e3f-BF00-37B7F9C4FB2A)]
coclass CoTest
{
[default] interface ITest;
interface ITest2;
};
};
TestEx.h:
#ifndef TESTEX_H
#define TESTEX_H
#include <windows.h>
#include <tchar.h>
#include <atlbase.h>
#include <atlcom.h>
#include "Test_i.c"
#include "Test.h"
class ATL_NO_VTABLE CCoTest : public CComObjectRootEx<CComSingleThreadModel>, public CComCoClass<CCoTest, &CLSID_CoTest>, public IDispatchImpl<ITest, &IID_ITest, &LIBID_TestLibrary>, public IDispatchImpl<ITest2, &IID_ITest2, &LIBID_TestLibrary>
{
private:
long m_iv;
BSTR m_sv;
public:
CCoTest();
virtual ~CCoTest();
BEGIN_COM_MAP(CCoTest)
COM_INTERFACE_ENTRY(ITest)
COM_INTERFACE_ENTRY(ITest2)
COM_INTERFACE_ENTRY2(IDispatch, ITest)
END_COM_MAP()
// ITest
STDMETHODIMP Add(long a, long b, long *c);
STDMETHODIMP Concat(BSTR a, BSTR b, BSTR *c);
STDMETHODIMP get_IV(long *iv);
STDMETHODIMP put_IV(long iv);
STDMETHODIMP get_SV(BSTR *sv);
STDMETHODIMP put_SV(BSTR sv);
STDMETHODIMP Another(ITest **test);
// ITest2
STDMETHODIMP Sum(long v1, long v2, long v3, long v4, long v5, long v6, long v7, long v8, long v9, long v10, long *res);
DECLARE_REGISTRY(CLSID_CoTest, _T("Test.CoTest.1"), _T("Test.CoTest"), 0U, THREADFLAGS_APARTMENT)
};
#endif // TESTEX_H
Test.cpp:
#include <stdio.h>
#include <windows.h>
#include <oleauto.h>
#include <initguid.h>
#include <atlbase.h>
extern CComModule _Module;
#include <atlcom.h>
#include "TestEx.h"
CComModule _Module;
BEGIN_OBJECT_MAP(ObjectMap)
OBJECT_ENTRY(CLSID_CoTest, CCoTest)
END_OBJECT_MAP()
CCoTest::CCoTest()
{
#ifdef DEBUG
printf("CCoTest ctor\n");
#endif
m_iv = 0;
m_sv = SysAllocString(L"");
}
CCoTest::~CCoTest()
{
#ifdef DEBUG
printf("CCoTest dtor\n");
#endif
SysFreeString(m_sv);
}
STDMETHODIMP CCoTest::Add(long a, long b, long *c)
{
*c = a + b;
return S_OK;
}
STDMETHODIMP CCoTest::Concat(BSTR a, BSTR b, BSTR *c)
{
OLECHAR *buf = new OLECHAR[SysStringLen(a) + SysStringLen(b) + 1];
wcscpy(buf, a);
wcscat(buf, b);
*c = SysAllocString(buf);
delete[] buf;
return S_OK;
}
STDMETHODIMP CCoTest::get_IV(long *iv)
{
*iv = m_iv;
return S_OK;
}
STDMETHODIMP CCoTest::put_IV(long iv)
{
m_iv = iv;
return S_OK;
}
STDMETHODIMP CCoTest::get_SV(BSTR *sv)
{
*sv = SysAllocString(m_sv);
return S_OK;
}
STDMETHODIMP CCoTest::put_SV(BSTR sv)
{
SysReAllocString(&m_sv, sv);
return S_OK;
}
STDMETHODIMP CCoTest::Another(ITest **test)
{
CComObject<CCoTest> *pTest;
CComObject<CCoTest>::CreateInstance(&pTest);
pTest->QueryInterface(IID_ITest, (void **)test);
return S_OK;
}
STDMETHODIMP CCoTest::Sum(long v1, long v2, long v3, long v4, long v5, long v6, long v7, long v8, long v9, long v10, long *res)
{
*res = v1 + v2 + v3 + v4 + v5 + v6 + v7 + v8 + v9 + v10;
return S_OK;
}
BOOL WINAPI DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID lpReserved)
{
if(dwReason == DLL_PROCESS_ATTACH)
{
_Module.Init(ObjectMap, hInstance, &LIBID_TestLibrary);
}
else if(dwReason == DLL_PROCESS_DETACH)
{
_Module.Term();
}
return TRUE;
}
STDMETHODIMP DllGetClassObject(REFCLSID rclsid, REFIID riid, void **ppv)
{
#ifdef DEBUG
printf("DllGetClassObject\n");
#endif
return _Module.GetClassObject(rclsid, riid, ppv);
}
STDMETHODIMP DllCanUnloadNow()
{
return (_Module.GetLockCount() == 0) ? S_OK : S_FALSE;
}
STDMETHODIMP DllRegisterServer()
{
return _Module.RegisterServer(TRUE);
}
STDMETHODIMP DllUnregisterServer()
{
return _Module.UnregisterServer(TRUE);
}
Test.def:
LIBRARY "Test"
EXPORTS
DllMain PRIVATE
DllGetClassObject PRIVATE
DllCanUnloadNow PRIVATE
DllRegisterServer PRIVATE
DllUnregisterServer PRIVATE
Build and registration:
midl Test.idl
cl /LD Test.cpp Test.def oleaut32.lib
mt -dll:Test.dll -tlb:Test.tlb -out:Test.dll.manifest
cscript replace.vbs "Test.dll.manifest" "<comClass clsid=#{CFF35242-7454-4B39-AAD8-138661FD4E01}#" "<comClass progid=#Test.CoTest# clsid=#{CFF35242-7454-4B39-AAD8-138661FD4E01}#"
TestC.c:
#include <stdio.h>
#include <stdlib.h>
#include <windows.h>
#include <initguid.h>
#include <tchar.h>
#define COBJMACROS
#include "Test.h"
void ReturnCheck(LPTSTR func, HRESULT res)
{
if(res != S_OK)
{
TCHAR buffer[1000];
FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, 0, res, 0, buffer, sizeof(buffer), 0);
_tprintf("%s: %s\n", func, buffer);
exit(1);
}
}
int main()
{
HRESULT res;
ITest *pTest, *another, *temp;
ITest2 *pTest2;
int v, iv, sum;
BSTR s1, s2, s3, sv;
CoInitialize(NULL);
printf("COM/C:\n");
res = CoCreateInstance(&CLSID_CoTest, NULL, CLSCTX_INPROC_SERVER, &IID_ITest, (void**)&pTest);
ReturnCheck(_T("CoCreateInstance"), res);
res = ITest_Add(pTest, 123, 456, &v);
ReturnCheck(_T("Add"), res);
printf("123 + 345 = %d\n", v);
s1 = SysAllocString(L"ABC");
s2 = SysAllocString(L"XYZ");
res = ITest_Concat(pTest, s1, s2, &s3);
ReturnCheck(_T("Concat"), res);
printf("%d %ls + %d %ls = %d %ls\n", SysStringLen(s1), s1, SysStringLen(s2), s2, SysStringLen(s3), s3);
SysFreeString(s1);
SysFreeString(s2);
SysFreeString(s3);
ITest_get_IV(pTest, &iv);
ITest_get_SV(pTest, &sv);
printf("IV = %d SV = %d %ls\n", iv, SysStringLen(sv), sv);
SysFreeString(sv);
ITest_put_IV(pTest, 123);
ITest_put_SV(pTest, SysAllocString(L"ABC"));
ITest_get_IV(pTest, &iv);
ITest_get_SV(pTest, &sv);
printf("IV = %d SV = %d %ls\n", iv, SysStringLen(sv), sv);
SysFreeString(sv);
ITest_Another(pTest, &another);
res = ITest_Add(another, 123, 456, &v);
ReturnCheck(_T("Add"), res);
printf("Another 123 + 345 = %d\n", v);
ITest_Release(another);
ITest_Release(pTest);
res = CoCreateInstance(&CLSID_CoTest, NULL, CLSCTX_INPROC_SERVER, &IID_ITest, (void**)&temp);
ReturnCheck(_T("CoCreateInstance"), res);
res = ITest2_QueryInterface(temp, &IID_ITest2, &pTest2);
ReturnCheck(_T("QueryInterface"), res);
ITest2_Sum(pTest2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, &sum);
printf("SUM = %d\n", sum);
ITest2_Sum(pTest2, 1, 2, 3, 4, 5, 0, 0, 0, 0, 0, &sum);
printf("SUM 1..5 = %d\n", sum);
ITest2_Sum(pTest2, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, &sum);
printf("SUM 1..10 = %d\n", sum);
ITest2_Release (pTest2);
ITest_Release (temp);
CoUninitialize();
return 0;
}
TestC.exe.manifest:
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1">
<assemblyIdentity type="win32" name="TestC.exe" version="1.0.0.0" />
<dependency>
<dependentAssembly>
<assemblyIdentity type="win32" name="Test.dll" version="1.0.0.0" />
</dependentAssembly>
</dependency>
</assembly>
Build and run:
cl TestC.c Test_i.c ole32.lib oleaut32.lib
TestC
What is happening:
TestCPP3.cpp:
#include <stdio.h>
#include <windows.h>
#include <initguid.h>
#include <tchar.h>
#import "Test.tlb" no_namespace named_guids
void ReturnCheck(LPTSTR func, HRESULT res)
{
if(res != S_OK)
{
TCHAR buffer[1000];
FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, 0, res, 0, buffer, sizeof(buffer), 0);
_tprintf("%s: %s\n", func, buffer);
exit(1);
}
}
int main()
{
HRESULT res;
CoInitialize(NULL);
printf("COM/C++ with CreateInstance:\n");
try
{
ITestPtr spTest;
res = spTest.CreateInstance(CLSID_CoTest);
ReturnCheck(_T("CreateInstance"), res);
long v = spTest->Add(123L, 456L);
printf("123 + 345 = %d\n", v);
BSTR s1 = SysAllocString(L"ABC");
BSTR s2 = SysAllocString(L"XYZ");
BSTR s3 = spTest->Concat(s1, s2);
printf("%d %ls + %d %ls = %d %ls\n", SysStringLen(s1), s1, SysStringLen(s2), s2, SysStringLen(s3), s3);
SysFreeString(s1);
SysFreeString(s2);
SysFreeString(s3);
BSTR sv = spTest->GetSV();
printf("IV = %d SV = %d %ls\n", spTest->GetIV(), SysStringLen(sv), sv);
SysFreeString(sv);
spTest->PutIV(123);
spTest->PutSV(SysAllocString(L"ABC"));
sv = spTest->GetSV();
printf("IV = %d SV = %d %ls\n", spTest->GetIV(), SysStringLen(sv), sv);
SysFreeString(sv);
ITestPtr another = spTest->Another();
printf("Another 123 + 345 = %d\n", another->Add(123L, 456L));
another = NULL;
spTest = NULL;
ITestPtr temp;
res = temp.CreateInstance(CLSID_CoTest);
ReturnCheck(_T("CreateInstance"), res);
ITest2Ptr spTest2 = temp;
printf("SUM = %d\n", spTest2->Sum(0, 0, 0, 0, 0, 0, 0, 0, 0, 0));
printf("SUM 1..5 = %d\n", spTest2->Sum(1, 2, 3, 4, 5, 0, 0, 0, 0, 0));
printf("SUM 1..10 = %d\n", spTest2->Sum(1, 2, 3, 4, 5, 6, 7, 8, 9, 10));
spTest2 = NULL;
temp = NULL;
}
catch (_com_error er)
{
_tprintf("%s\n", er.ErrorMessage());
}
CoUninitialize();
return 0;
}
TestCPP3.exe.manifest:
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1">
<assemblyIdentity type="win32" name="TestCPP3.exe" version="1.0.0.0" />
<dependency>
<dependentAssembly>
<assemblyIdentity type="win32" name="Test.dll" version="1.0.0.0" />
</dependentAssembly>
</dependency>
</assembly>
Build and run:
cl /EHsc TestCPP3.cpp ole32.lib oleaut32.lib
TestCPP3
TestCPP4.cpp:
#include <stdio.h>
#include <windows.h>
#include <initguid.h>
#include <tchar.h>
#import "Test.tlb" no_namespace named_guids
void ReturnCheck(LPTSTR func, HRESULT res)
{
if(res != S_OK)
{
TCHAR buffer[1000];
FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, 0, res, 0, buffer, sizeof(buffer), 0);
_tprintf("%s: %s\n", func, buffer);
exit(1);
}
}
int main()
{
HRESULT res;
CoInitialize(NULL);
printf("COM/C++ with constructor:\n");
try
{
ITestPtr spTest(__uuidof(CoTest));
long v = spTest->Add(123, 456);
printf("123 + 345 = %d\n", v);
BSTR s1 = SysAllocString(L"ABC");
BSTR s2 = SysAllocString(L"XYZ");
BSTR s3 = spTest->Concat(s1, s2);
printf("%d %ls + %d %ls = %d %ls\n", SysStringLen(s1), s1, SysStringLen(s2), s2, SysStringLen(s3), s3);
SysFreeString(s1);
SysFreeString(s2);
SysFreeString(s3);
BSTR sv = spTest->GetSV();
printf("IV = %d SV = %d %ls\n", spTest->GetIV(), SysStringLen(sv), sv);
SysFreeString(sv);
spTest->PutIV(123);
spTest->PutSV(SysAllocString(L"ABC"));
sv = spTest->GetSV();
printf("IV = %d SV = %d %ls\n", spTest->GetIV(), SysStringLen(sv), sv);
SysFreeString(sv);
ITestPtr another = spTest->Another();
printf("Another 123 + 345 = %d\n", another->Add(123L, 456L));
another = NULL;
spTest = NULL;
ITestPtr temp(__uuidof(CoTest));
ITest2Ptr spTest2 = temp;
printf("SUM = %d\n", spTest2->Sum(0, 0, 0, 0, 0, 0, 0, 0, 0, 0));
printf("SUM 1..5 = %d\n", spTest2->Sum(1, 2, 3, 4, 5, 0, 0, 0, 0, 0));
printf("SUM 1..10 = %d\n", spTest2->Sum(1, 2, 3, 4, 5, 6, 7, 8, 9, 10));
spTest2 = NULL;
temp = NULL;
}
catch (_com_error er)
{
_tprintf("%s\n", er.ErrorMessage());
}
CoUninitialize();
return 0;
}
TestCPP4.exe.manifest:
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1">
<assemblyIdentity type="win32" name="TestCPP4.exe" version="1.0.0.0" />
<dependency>
<dependentAssembly>
<assemblyIdentity type="win32" name="Test.dll" version="1.0.0.0" />
</dependentAssembly>
</dependency>
</assembly>
Build and run:
cl /EHsc TestCPP4.cpp ole32.lib oleaut32.lib
TestCPP4
What is happening:
Now we will try and call the COM component remotely.
In process COM look like:
Remote COM aka DCOM look like:
MSRPC is the Microsoft flavor of the standard DCE/RPC.
Note that not all COM interfaces support remote access.
Test.idl:
import "oaidl.idl";
// ITest
[object, uuid(1B255E86-9F4C-442c-8987-947BD9D6AC7C), oleautomation, dual, pointer_default(unique)]
interface ITest : IDispatch
{
[id(1)] HRESULT Add([in] long a, [in] long b, [out,retval] long *c);
[id(2)] HRESULT Concat([in] BSTR a, [in] BSTR b, [out,retval]BSTR *c);
[id(3),propget] HRESULT IV([out, retval] long *iv);
[id(3),propput] HRESULT IV([in] long iv);
[id(4),propget] HRESULT SV([out, retval] BSTR *sv);
[id(4),propput] HRESULT SV([in] BSTR sv);
}
// Test library
[uuid(105A7B5F-67E9-4d10-A22D-B3C989904332), version(1.0)]
library TestLibrary
{
importlib("stdole32.tlb");
[uuid(A178C19F-C64B-4a96-8D6C-83698E5B7A70)]
coclass CoTest
{
[default] interface ITest;
};
};
TestEx.h:
#ifndef TESTEX_H
#define TESTEX_H
#include <windows.h>
#include <tchar.h>
#include <atlbase.h>
#include <atlcom.h>
#include "Test_i.c"
#include "Test.h"
class ATL_NO_VTABLE CCoTest : public CComObjectRootEx<CComSingleThreadModel>, public CComCoClass<CCoTest, &CLSID_CoTest>, public IDispatchImpl<ITest, &IID_ITest, &LIBID_TestLibrary>
{
private:
long m_iv;
BSTR m_sv;
public:
CCoTest();
virtual ~CCoTest();
BEGIN_COM_MAP(CCoTest)
COM_INTERFACE_ENTRY(ITest)
COM_INTERFACE_ENTRY2(IDispatch, ITest)
END_COM_MAP()
// ITest
STDMETHODIMP Add(long a, long b, long *c);
STDMETHODIMP Concat(BSTR a, BSTR b, BSTR *c);
STDMETHODIMP get_IV(long *iv);
STDMETHODIMP put_IV(long iv);
STDMETHODIMP get_SV(BSTR *sv);
STDMETHODIMP put_SV(BSTR sv);
static HRESULT WINAPI UpdateRegistry(BOOL b)
{
return _Module.UpdateRegistryClass(CLSID_CoTest, _T("Test.CoTest.1"), _T("Test.CoTest"), 0U, THREADFLAGS_APARTMENT, b);
}
};
#endif // TESTEX_H
Test.cpp:
#include <stdio.h>
#include <windows.h>
#include <oleauto.h>
#include <initguid.h>
#include <atlbase.h>
extern CComModule _Module;
#include <atlcom.h>
#include "TestEx.h"
CComModule _Module;
BEGIN_OBJECT_MAP(ObjectMap)
OBJECT_ENTRY(CLSID_CoTest, CCoTest)
END_OBJECT_MAP()
CCoTest::CCoTest()
{
#ifdef DEBUG
printf("CCoTest ctor\n");
#endif
m_iv = 0;
m_sv = SysAllocString(L"");
}
CCoTest::~CCoTest()
{
#ifdef DEBUG
printf("CCoTest dtor\n");
#endif
SysFreeString(m_sv);
}
STDMETHODIMP CCoTest::Add(long a, long b, long *c)
{
*c = a + b;
return S_OK;
}
STDMETHODIMP CCoTest::Concat(BSTR a, BSTR b, BSTR *c)
{
OLECHAR *buf = new OLECHAR[SysStringLen(a) + SysStringLen(b) + 1];
wcscpy(buf, a);
wcscat(buf, b);
*c = SysAllocString(buf);
delete[] buf;
return S_OK;
}
STDMETHODIMP CCoTest::get_IV(long *iv)
{
*iv = m_iv;
return S_OK;
}
STDMETHODIMP CCoTest::put_IV(long iv)
{
m_iv = iv;
return S_OK;
}
STDMETHODIMP CCoTest::get_SV(BSTR *sv)
{
*sv = SysAllocString(m_sv);
return S_OK;
}
STDMETHODIMP CCoTest::put_SV(BSTR sv)
{
SysReAllocString(&m_sv, sv);
return S_OK;
}
BOOL WINAPI DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID lpReserved)
{
if(dwReason == DLL_PROCESS_ATTACH)
{
_Module.Init(ObjectMap, hInstance, &LIBID_TestLibrary);
}
else if(dwReason == DLL_PROCESS_DETACH)
{
_Module.Term();
}
return TRUE;
}
STDMETHODIMP DllGetClassObject(REFCLSID rclsid, REFIID riid, void **ppv)
{
#ifdef DEBUG
printf("DllGetClassObject\n");
#endif
return _Module.GetClassObject(rclsid, riid, ppv);
}
STDMETHODIMP DllCanUnloadNow()
{
return (_Module.GetLockCount() == 0) ? S_OK : S_FALSE;
}
STDMETHODIMP DllRegisterServer()
{
return _Module.RegisterServer(TRUE);
}
STDMETHODIMP DllUnregisterServer()
{
return _Module.UnregisterServer(TRUE);
}
Test.def:
LIBRARY "Test"
EXPORTS
DllMain PRIVATE
DllGetClassObject PRIVATE
DllCanUnloadNow PRIVATE
DllRegisterServer PRIVATE
DllUnregisterServer PRIVATE
Build and registration:
midl Test.idl
cl /LD Test.cpp Test.def oleaut32.lib
regsvr32 Test.dll
There are two ways to host a COM component:
We will use a custom host program.
server.cpp:
#include <stdio.h>
#include <windows.h>
#include <initguid.h>
#include <tchar.h>
#import "Test.tlb" no_namespace named_guids
void ReturnCheck(LPTSTR func, HRESULT res)
{
if(res != S_OK)
{
TCHAR buffer[1000];
FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, 0, res, 0, buffer, sizeof(buffer), 0);
_tprintf("%s: %s\n", func, buffer);
exit(1);
}
}
int main()
{
HRESULT res;
CoInitializeEx(NULL, COINIT_MULTITHREADED);
IClassFactory *pCF;
res = CoGetClassObject(CLSID_CoTest, CLSCTX_INPROC_SERVER, NULL, IID_IClassFactory, (void **)&pCF);
ReturnCheck(_T("CoGetClassObject"), res);
DWORD dwR;
res = CoRegisterClassObject(CLSID_CoTest, pCF, CLSCTX_LOCAL_SERVER, REGCLS_SUSPENDED|REGCLS_MULTIPLEUSE, &dwR);
ReturnCheck(_T("CoRegisterClassObject"), res);
CoResumeClassObjects();
printf("Press enter to exit");
char dummy[80];
fgets(dummy, sizeof(dummy), stdin);
CoRevokeClassObject(dwR);
pCF->Release();
CoUninitialize();
return 0;
}
TestC.c:
#include <stdio.h>
#include <stdlib.h>
#include <windows.h>
#include <initguid.h>
#include <tchar.h>
#define COBJMACROS
#include "Test.h"
void ReturnCheck(LPTSTR func, HRESULT res)
{
if(res != S_OK)
{
TCHAR buffer[1000];
FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, 0, res, 0, buffer, sizeof(buffer), 0);
_tprintf("%s: %s\n", func, buffer);
exit(1);
}
}
int main()
{
HRESULT res;
COSERVERINFO csi;
MULTI_QI mqi[1];
ITest *pTest;
int v, iv;
BSTR s1, s2, s3, sv;
CoInitialize(NULL);
printf("COM/C:\n");
memset(&csi, 0, sizeof(csi));
csi.pwszName = L"\\\\ARNEPC4";
memset(mqi, 0, sizeof(mqi));
mqi[0].pIID = &IID_ITest;
res = CoCreateInstanceEx(&CLSID_CoTest, NULL, CLSCTX_REMOTE_SERVER, &csi, 1, mqi);
ReturnCheck(_T("CoCreateInstanceEx"), res);
pTest = (ITest *)mqi[0].pItf;
res = ITest_Add(pTest, 123, 456, &v);
ReturnCheck(_T("Add"), res);
printf("123 + 345 = %d\n", v);
s1 = SysAllocString(L"ABC");
s2 = SysAllocString(L"XYZ");
res = ITest_Concat(pTest, s1, s2, &s3);
ReturnCheck(_T("Concat"), res);
printf("%d %ls + %d %ls = %d %ls\n", SysStringLen(s1), s1, SysStringLen(s2), s2, SysStringLen(s3), s3);
SysFreeString(s1);
SysFreeString(s2);
SysFreeString(s3);
ITest_get_IV(pTest, &iv);
ITest_get_SV(pTest, &sv);
printf("IV = %d SV = %d %ls\n", iv, SysStringLen(sv), sv);
SysFreeString(sv);
ITest_put_IV(pTest, 123);
ITest_put_SV(pTest, SysAllocString(L"ABC"));
ITest_get_IV(pTest, &iv);
ITest_get_SV(pTest, &sv);
printf("IV = %d SV = %d %ls\n", iv, SysStringLen(sv), sv);
SysFreeString(sv);
ITest_Release(pTest);
CoUninitialize();
return 0;
}
Build and run:
cl TestC.c Test_i.c ole32.lib oleaut32.lib
TestC
What is happening:
TestCPP2.cpp:
#include <stdio.h>
#include <stdlib.h>
#include <windows.h>
#include <initguid.h>
#include <tchar.h>
//#include "TestEx.h"
#import "Test.tlb" no_namespace named_guids
void ReturnCheck(LPTSTR func, HRESULT res)
{
if(res != S_OK)
{
TCHAR buffer[1000];
FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, 0, res, 0, buffer, sizeof(buffer), 0);
_tprintf("%s: %s\n", func, buffer);
exit(1);
}
}
int main()
{
HRESULT res;
CoInitialize(NULL);
printf("COM/C++ with CoCreateInstanceEx\n");
COSERVERINFO csi;
memset(&csi, 0, sizeof(csi));
csi.pwszName = L"\\\\ARNEPC4";
MULTI_QI mqi[1];
memset(mqi, 0, sizeof(mqi));
mqi[0].pIID = &IID_ITest;
res = CoCreateInstanceEx(CLSID_CoTest, NULL, CLSCTX_REMOTE_SERVER, &csi, 1, mqi);
ReturnCheck(_T("CoCreateInstanceEx"), res);
ITest *pTest = (ITest *)mqi[0].pItf;
try
{
long int v = pTest->Add(123, 456);
printf("123 + 456 = %d\n", v);
BSTR s1 = SysAllocString(L"ABC");
BSTR s2 = SysAllocString(L"XYZ");
BSTR s3 = pTest->Concat(s1, s2);
printf("%d %ls + %d %ls = %d %ls\n", SysStringLen(s1), s1, SysStringLen(s2), s2, SysStringLen(s3), s3);
SysFreeString(s1);
SysFreeString(s2);
SysFreeString(s3);
BSTR sv = pTest->GetSV();
printf("IV = %d SV = %d %ls\n", pTest->GetIV(), SysStringLen(sv), sv);
SysFreeString(sv);
pTest->PutIV(123);
pTest->PutSV(SysAllocString(L"ABC"));
sv = pTest->GetSV();
printf("IV = %d SV = %d %ls\n", pTest->GetIV(), SysStringLen(sv), sv);
SysFreeString(sv);
pTest->Release();
}
catch (_com_error er)
{
_tprintf("%s\n", er.ErrorMessage());
}
CoUninitialize();
return 0;
}
Build and run:
cl /EHsc TestCPP2.cpp ole32.lib oleaut32.lib
TestCPP2
TestPas.pas:
program TestPas;
{$mode delphi}{$H+}
uses
ActiveX, TestLibrary_1_0_TLB;
var
test : ITest;
begin
writeln('COM/Pascal static:');
CoInitialize(nil);
test := CoCoTest.CreateRemote('\\ARNEPC4');
writeln('123 + 456 = ', test.Add(123, 456));
writeln('ABC + XYZ = ', test.Concat('ABC', 'XYZ'));
writeln('IV = ', test.IV, ' SV = ', test.SV);
test.IV := 123;
test.SV := 'ABC';
writeln('IV = ', test.IV, ' SV = ', test.SV);
CoUninitialize;
end.
Build and run:
importtl Test.tlb
fpc TestLibrary_1_0_TLB.pas
fpc TestPas.pas
TestPas
What is happening:
TestVBS.vbs:
WScript.Echo "COM/VBS:"
Set test = CreateObject("Test.CoTest", "\\ARNEPC4")
WScript.Echo "123 + 456 = " & CStr(test.Add(CLng(123), CLng(456)))
WScript.Echo "ABC + XYZ = " & test.Concat("ABC", "XYZ")
WScript.Echo "IV = " & CStr(test.IV) & " SV = " & test.SV
test.IV = 123
test.SV = "ABC"
WScript.Echo "IV = " & CStr(test.IV) & " SV = " & test.SV
Set test = Nothing
Build and run:
cscript TestVBS.vbs
TestCSDyn.cs:
using System;
public class MainClass
{
public static void Main(string[] args)
{
Console.WriteLine("COM/C# dynamic");
dynamic test = Activator.CreateInstance(Type.GetTypeFromProgID("Test.CoTest", @"\\ARNEPC4"));
Console.WriteLine("123 + 456 = {0}", test.Add(123, 456));
Console.WriteLine("ABC + XYZ = {0}", test.Concat("ABC", "XYZ"));
Console.WriteLine("IV = {0} SV = {1}", test.IV, test.SV);
test.IV = 123;
test.SV = "ABC";
Console.WriteLine("IV = {0} SV = {1}", test.IV, test.SV);
}
}
Build and run:
csc /platform:x86 TestCSDyn.cs
TestCDDyn
Nobody will use IDispatch with C++, but it is still relevant to see the code to understand what is happening in other languages.
TestCPP5.cpp:
#include <stdio.h>
#include <windows.h>
#include <initguid.h>
#include <tchar.h>
#import "Test.tlb" no_namespace named_guids
void ReturnCheck(LPTSTR func, HRESULT res)
{
if(res != S_OK)
{
TCHAR buffer[1000];
FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, 0, res, 0, buffer, sizeof(buffer), 0);
_tprintf("%s: %s\n", func, buffer);
exit(1);
}
}
int main()
{
HRESULT res;
CoInitialize(NULL);
printf("COM/C++ via IDispatch:\n");
CLSID clsid;
res = CLSIDFromProgID(L"Test.CoTest", &CLSID);
ReturnCheck(_T("CLSIDFromProgID"), res);
COSERVERINFO csi;
memset(&csi, 0, sizeof(csi));
csi.pwszName = L"\\\\ARNEPC4";
MULTI_QI mqi[1];
memset(mqi, 0, sizeof(mqi));
mqi[0].pIID = &IID_IDispatch;
res = CoCreateInstanceEx(clsid, NULL, CLSCTX_REMOTE_SERVER, &csi, 1, mqi);
ReturnCheck(_T("CoCreateInstanceEx"), res);
IDispatch *pDisp = (IDispatch *)mqi[0].pItf;
DISPID dispid;
LPOLESTR func;
VARIANT args[2], retval;
DISPPARAMS params;
params.rgvarg = args;
params.rgdispidNamedArgs = 0;
params.cNamedArgs = 0;
// Add
params.cArgs = 2;
func = L"Add";
res = pDisp->GetIDsOfNames(IID_NULL, &func, 1, LOCALE_SYSTEM_DEFAULT, &dispid);
ReturnCheck(_T("IDispatch GetIDsOfNames"), res);
VariantInit(&args[0]);
args[0].vt = VT_I4;
args[0].lVal = 456;
VariantInit(&args[1]);
args[1].vt = VT_I4;
args[1].lVal = 123;
VariantInit(&retval);
res = pDisp->Invoke(dispid, IID_NULL, LOCALE_SYSTEM_DEFAULT, DISPATCH_METHOD, ¶ms, &retval, NULL, NULL);
ReturnCheck(_T("IDispatch Invoke"), res);
printf("123 + 345 = %d\n", retval.lVal);
// Concat
params.cArgs = 2;
func = L"Concat";
res = pDisp->GetIDsOfNames(IID_NULL, &func, 1, LOCALE_SYSTEM_DEFAULT, &dispid);
ReturnCheck(_T("IDispatch GetIDsOfNames"), res);
VariantInit(&args[0]);
args[0].vt = VT_BSTR;
args[0].bstrVal = SysAllocString(L"ABC");
VariantInit(&args[1]);
args[1].vt = VT_BSTR;
args[1].bstrVal = SysAllocString(L"XYZ");
VariantInit(&retval);
res = pDisp->Invoke(dispid, IID_NULL, LOCALE_SYSTEM_DEFAULT, DISPATCH_METHOD, ¶ms, &retval, NULL, NULL);
ReturnCheck(_T("IDispatch Invoke"), res);
printf("3 ABC + 3 XYZ = %d %ls\n", SysStringLen(retval.bstrVal), retval.bstrVal);
// Get
params.cArgs = 0;
func = L"IV";
res = pDisp->GetIDsOfNames(IID_NULL, &func, 1, LOCALE_SYSTEM_DEFAULT, &dispid);
ReturnCheck(_T("IDispatch GetIDsOfNames"), res);
VariantInit(&retval);
res = pDisp->Invoke(dispid, IID_NULL, LOCALE_SYSTEM_DEFAULT, DISPATCH_PROPERTYGET, ¶ms, &retval, NULL, NULL);
ReturnCheck(_T("IDispatch Invoke"), res);
printf("IV = %d", retval.lVal);
params.cArgs = 0;
func = L"SV";
res = pDisp->GetIDsOfNames(IID_NULL, &func, 1, LOCALE_SYSTEM_DEFAULT, &dispid);
ReturnCheck(_T("IDispatch GetIDsOfNames"), res);
VariantInit(&retval);
res = pDisp->Invoke(dispid, IID_NULL, LOCALE_SYSTEM_DEFAULT, DISPATCH_PROPERTYGET, ¶ms, &retval, NULL, NULL);
ReturnCheck(_T("IDispatch Invoke"), res);
printf(" SV = %d %ls\n", SysStringLen(retval.bstrVal), retval.bstrVal);
// Put
DISPID temp = DISPID_PROPERTYPUT; // these 3 lines are necessary
params.rgdispidNamedArgs = &temp;
params.cNamedArgs = 1;
//
params.cArgs = 1;
func = L"IV";
res = pDisp->GetIDsOfNames(IID_NULL, &func, 1, LOCALE_SYSTEM_DEFAULT, &dispid);
ReturnCheck(_T("IDispatch GetIDsOfNames"), res);
VariantInit(&args[0]);
args[0].vt = VT_I4;
args[0].lVal = 123;
res = pDisp->Invoke(dispid, IID_NULL, LOCALE_SYSTEM_DEFAULT, DISPATCH_PROPERTYPUT, ¶ms, NULL, NULL, NULL);
ReturnCheck(_T("IDispatch Invoke"), res);
params.cArgs = 1;
func = L"SV";
res = pDisp->GetIDsOfNames(IID_NULL, &func, 1, LOCALE_SYSTEM_DEFAULT, &dispid);
ReturnCheck(_T("IDispatch GetIDsOfNames"), res);
VariantInit(&args[0]);
args[0].vt = VT_BSTR;
args[0].bstrVal = SysAllocString(L"ABC");
res = pDisp->Invoke(dispid, IID_NULL, LOCALE_SYSTEM_DEFAULT, DISPATCH_PROPERTYPUT, ¶ms, NULL, NULL, NULL);
ReturnCheck(_T("IDispatch Invoke"), res);
//
params.rgdispidNamedArgs = 0;
params.cNamedArgs = 0;
// Get
params.cArgs = 0;
func = L"IV";
res = pDisp->GetIDsOfNames(IID_NULL, &func, 1, LOCALE_SYSTEM_DEFAULT, &dispid);
ReturnCheck(_T("IDispatch GetIDsOfNames"), res);
VariantInit(&retval);
res = pDisp->Invoke(dispid, IID_NULL, LOCALE_SYSTEM_DEFAULT, DISPATCH_PROPERTYGET, ¶ms, &retval, NULL, NULL);
ReturnCheck(_T("IDispatch Invoke"), res);
printf("IV = %d", retval.lVal);
params.cArgs = 0;
func = L"SV";
res = pDisp->GetIDsOfNames(IID_NULL, &func, 1, LOCALE_SYSTEM_DEFAULT, &dispid);
ReturnCheck(_T("IDispatch GetIDsOfNames"), res);
VariantInit(&retval);
res = pDisp->Invoke(dispid, IID_NULL, LOCALE_SYSTEM_DEFAULT, DISPATCH_PROPERTYGET, ¶ms, &retval, NULL, NULL);
ReturnCheck(_T("IDispatch Invoke"), res);
printf(" SV = %d %ls\n", SysStringLen(retval.bstrVal), retval.bstrVal);
//
pDisp->Release();
CoUninitialize();
return 0;
}
Build and run:
cl /EHsc TestCPP5.cpp ole32.lib oleaut32.lib
TestCPP5
TestPHP.php:
<?php
echo "COM/PHP:\r\n";
$test = new com('Test.CoTest', 'ARNEPC4');
echo sprintf("123 + 456 = %d\r\n", $test->Add(123, 456));
echo sprintf("ABC + XYZ = %s\r\n", $test->Concat("ABC", "XYZ"));
echo sprintf("IV = %d SV = %s\r\n", $test->IV, $test->SV);
$test->IV = 123;
$test->SV = "ABC";
echo sprintf("IV = %d SV = %s\r\n", $test->IV, $test->SV);
?>
Build and run:
php TestPHP.php
TestPY.py:
from win32com.client import *
import pythoncom
print("COM/Python win32:")
test = DispatchEx("Test.CoTest", "\\\\ARNEPC5")
print("123 + 345 = %d" % test.Add(123, 456))
print("ABC + XYZ = %s" % test.Concat("ABC", "XYZ"))
print("IV = %d SV = %s" % (test.IV, test.SV))
test.IV = 123
test.SV = "ABC"
print("IV = %d SV = %s" % (test.IV, test.SV))
Build and run:
python TestPY.py
What is happening:
Hopefully the previous sections gave some understanding of how COM works.
Several COM related technologies exist:
Some other articles have examples of COM too:
Version | Date | Description |
---|---|---|
1.0 | August 6th 2021 (but I have been working on this since 2010!) |
Initial version |
1.1 | May 31st 2022 | Add DCOM section |
1.2 | June 6th 2022 | Add PHP and Python examples |
See list of all articles here
Please send comments to Arne Vajhøj