COM objects in plain C...

Started by CommonTater, June 03, 2012, 08:26:50 PM

Previous topic - Next topic

CommonTater

Well known musician, hardware designer and C programmer Jeff Glatt has authored an 8 part series on using COM objects in plain C... I found it to be very helpful.  It's clearly written and loaded with examples.  If you haven't seen it yet, you can access the whole series from his page at The Code Project...

http://www.codeproject.com/script/Articles/MemberArticles.aspx?amid=88625

Thanks Jeff.


Vortex

COM is very powerful but it can be very complicated too. Thanks for the tutorials.
Code it... That's all...

CommonTater

Quote from: Vortex on June 03, 2012, 11:10:46 PM
COM is very powerful but it can be very complicated too. Thanks for the tutorials.

Well, since it seems my time has come to learn this I've been doing some searching for information... Thought I'd pass this one along.  I know Jeff from back in my "pro-sound" days so I trust it's mostly accurate.  If I find more, I'll pass them along as well...


czerny

Hallo,

I have read the first two parts of 'COM in plain C' and build corresponding Pelles C projects. If anyone has problems with this, I can post them.

EdPellesC99

Thanks Tater, for the lead for future interests for me !

TimoVJL

Time pasts, but those tutorials are still valuable :)

A tiny example for testing IExample2 without COM registration.
Easier to develop similar one.
May the source be with you

jj2007

Hi Timo,
Can you explain in a few words what the program does? Just curious...

TimoVJL

#7
That example loads IExample2.dll
ask IClassFactory pointer using exported function DllGetClassObject()
create object /CreateInstance IExample2 to the pointer pExample2
release IClassFactory
call methods/functions SetString() and GetString()
finally release IExample2

In this way it's easier to develop functions and debug those.
int __cdecl main(void)
{
IClassFactory *pClassFactory;
HMODULE hModule = LoadLibrary(TEXT("iexample2\\IExample2.dll"));
if (!hModule) return 1;
HRESULT (WINAPI*DllGetClassObject)(REFCLSID,REFIID,LPVOID) =
(HRESULT(WINAPI*)(REFCLSID,REFIID,LPVOID))GetProcAddress(hModule, TEXT("DllGetClassObject"));
if (!DllGetClassObject) return 2;
HRESULT hr = DllGetClassObject(&CLSID_IExample2, &IID_IClassFactory, (void**)&pClassFactory);
printf("hr=%X\n", hr);
if (!hr) {
IExample2 *pExample2;
hr = pClassFactory->lpVtbl->CreateInstance(pClassFactory, NULL, &IID_IExample2, (void **)&pExample2);
pClassFactory->lpVtbl->Release(pClassFactory);
printf("hr=%X\n", hr);
BSTR bstr1 = SysAllocString(L"test");
pExample2->lpVtbl->SetString(pExample2, bstr1);
BSTR bstr2;
pExample2->lpVtbl->GetString(pExample2, &bstr2);
printf("%ls\n", bstr2);
SysFreeString(bstr2);
pExample2->lpVtbl->Release(pExample2);
}
return 0;
}


Hopefully we see something more in this thread.
Experts could fix code and tell us what other things have to care.

EDIT:
After dll debug next example:
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <ole2.h>
#pragma comment(lib, "ole32")
#pragma comment(lib, "oleaut32")
#include "IExample2.h"
void ErrorString(HRESULT errno);
int __cdecl main(void)
{
IExample2 *pExample2 = NULL;
CoInitialize(NULL);
HRESULT hr = CoCreateInstance(&CLSID_IExample2, NULL, CLSCTX_ALL, &IID_IExample2, (void**)&pExample2);
printf("hr=%X\n", hr);
if (!hr && pExample2) {
printf("hr=%X\n", hr);
BSTR bstr1 = SysAllocString(L"test");
pExample2->lpVtbl->SetString(pExample2, bstr1);
BSTR bstr2;
pExample2->lpVtbl->GetString(pExample2, &bstr2);
printf("%ls\n", bstr2);
SysFreeString(bstr2);
pExample2->lpVtbl->Release(pExample2);
} else ErrorString(hr);
CoUninitialize();
return 0;
}
void ErrorString(HRESULT errno)
{
#define BUFSIZE 1000
static TCHAR szMsgBuf[BUFSIZE];

FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM
, NULL, errno
, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT)
, (LPTSTR) szMsgBuf, BUFSIZE
, NULL);
printf("%Xh %s\n", errno, szMsgBuf);
}

Registration-Free COM 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="IExample2Test.exe" version="1.0.0.0" />
  <file name="IExample2.dll">
    <comClass progid="IExample2.Test"
       clsid="{520F4CFD-61C6-4EED-8004-C26D514D3D19}"
       description="IExample2 Control" />
  </file>
</assembly>


EDIT: small tool to create manifest lines:#define UNICODE
#define _UNICODE
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <ole2.h>
#include <tchar.h>
#include <stdio.h>

#pragma comment(lib, "ole32.lib")
#pragma comment(lib, "oleaut32.lib")

//int DumpTLib(WCHAR *szFileName)
int __cdecl wmain(int argc, wchar_t **argv)
{
LPTYPELIB pITypeLib;
WCHAR wsGuid[40];

if (!LoadTypeLibEx(argv[1], REGKIND_NONE, &pITypeLib))
{
BSTR bstrLib;
pITypeLib->lpVtbl->GetDocumentation(pITypeLib, -1, &bstrLib, 0, 0, 0);
TLIBATTR *pLibAttr;
pITypeLib->lpVtbl->GetLibAttr(pITypeLib, &pLibAttr);
StringFromGUID2(&pLibAttr->guid, wsGuid, sizeof(wsGuid));
_tprintf(_T("<file name=\"%ls\">\n<typelib tlbid=\"%ls\" />\n"),
argv[1], wsGuid);
pITypeLib->lpVtbl->ReleaseTLibAttr(pITypeLib, pLibAttr);
UINT tiCount = pITypeLib->lpVtbl->GetTypeInfoCount(pITypeLib);
for (UINT i = 0; i < tiCount; i++)
{
TYPEKIND TypeKind;
BSTR bstrName;
LPTYPEINFO pITypeInfo;
pITypeLib->lpVtbl->GetTypeInfoType(pITypeLib, i, &TypeKind);
pITypeLib->lpVtbl->GetDocumentation(pITypeLib, i, &bstrName, 0, 0, 0);
if (!pITypeLib->lpVtbl->GetTypeInfo(pITypeLib, i, &pITypeInfo))
{
TYPEATTR *pTypeAttr;
if (!pITypeInfo->lpVtbl->GetTypeAttr(pITypeInfo, &pTypeAttr))
{
if (TypeKind == TKIND_COCLASS) {
StringFromGUID2(&pTypeAttr->guid, wsGuid, sizeof(wsGuid));
//_tprintf(_T("<comClass clsid=\"%ls\" />\n"), wsGuid);
_tprintf(_T("<comClass clsid=\"%ls\" progid=\"%ls.%ls\"/>\n"),
wsGuid, bstrLib, bstrName);
}
pITypeInfo->lpVtbl->ReleaseTypeAttr(pITypeInfo, pTypeAttr);
}
pITypeInfo->lpVtbl->Release(pITypeInfo);
}
if (bstrName) SysFreeString(bstrName);
}
if (bstrLib) SysFreeString(bstrLib);
_tprintf(_T("</file>\n"));
}
return 0;
}
May the source be with you

jj2007

Hi Timo,
Just tried to build this project with the new Pelles C version, and it shows plenty of warnings.
Building DllMainStartup.obj.
Building IExample2.obj.
C:\PellesC\Timo\Timo\COM\iexample2\IExample2.c(209): warning #2030: '=' used in a conditional expression.
C:\PellesC\Timo\Timo\COM\iexample2\IExample2.c(222): warning #2071: Overflow or truncation in constant expression.
C:\PellesC\Timo\Timo\COM\iexample2\IExample2.c(229): warning #2030: '=' used in a conditional expression.
Building IExample2.dll.
POLINK: warning: Unrecognized keyword 'PRIVATE' in module-definition file 'C:\PellesC\Timo\Timo\COM\iexample2\IExample2.def'; ignored.
POLINK: warning: Unrecognized keyword 'PRIVATE' in module-definition file 'C:\PellesC\Timo\Timo\COM\iexample2\IExample2.def'; ignored.
Creating object: C:\PellesC\Timo\Timo\COM\iexample2\IExample2.exp
Creating library: C:\PellesC\Timo\Timo\COM\iexample2\IExample2.lib
Done.

It builds, but VBS complains that the ActiveX component cannot create the IExample2.object

TimoVJL

That dll need a typelib and registeration for that :(
Use midl compiler from SDK to make typelib.

Those warnings, add !=0if ((hr = loadMyTypeInfo())!=0) return(hr);cast with (ULONG)return((ULONG)DISP_E_UNKNOWNINTERFACE);and remove or comment out that PRIVATE
May the source be with you