#include <windows.h>
#include <stdio.h>
#include <olectl.h>
#include <objbase.h>
#include <assert.h> /* crash early ;- */
#include <containers.h>
#include <str.h>
#include <gc.h>
#include "adder_ex.h"
/* define this while building the COM DLL */
#ifdef BUILD_DLL
#define EXPORT __declspec(dllexport)
#else
#define EXPORT
#endif
static DWORD OutstandingObjects; /* count on how many times IClassFactories CreateInstance was called */
static DWORD LockCount; /* Reference counter */
static char ourProgID[] = "MyAdder.class";
static char *ProgVersion = ".1";
static short int majorVersion = 1;
static short int minorVersion = 0;
static int gRegEntryMax;
struct registry_entry_value {
String value_name;
String value;
};
struct registry_entry {
HKEY rootKey;
String path;
String subkey;
struct registry_entry_value **values;
};
struct registry_entry *comEntries;
// Where I store a pointer to my type library's TYPEINFO
static ITypeInfo *MyTypeInfo;
static IAdderVtbl IAdder_Vtbl;
/* We play a bit dirty, the only thing which we must keep in mind:
the first element of the Struct must be the *lpVtbl pointer
but we can extend this struct with stuff we like. The other option
would have been to declare file global variables to keep the data
*/
typedef struct my_interface {
IAdderVtbl *lpVtbl;
ULONG count; /* Reference Counter */
} MyInterface;
/* helper function to print a readable error message */
static void ShowSysErrMsg(DWORD errCode) {
char errMsg[512];
DWORD dwRC;
dwRC = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
0, errCode, 0, errMsg, 512,
NULL);
if (0 == dwRC) {
fprintf(stderr, "Failed to format system error message properly\n");
} else {
fprintf(stderr, "System Error Message: %s\n", errMsg);
}
}
/*************************************************/
/* Start implementing the classFactory interface */
/* Query Interface */
static HRESULT STDMETHODCALLTYPE cfQueryInterface(IClassFactory *This, REFIID riid, void **ppv)
{
// Make sure the caller wants either an IUnknown or an IClassFactory.
// In either case, we return the same IClassFactory pointer passed to
// us since it can also masquerade as an IUnknown
if (IsEqualIID(riid, &IID_IUnknown)
|| IsEqualIID(riid, &IID_IClassFactory))
{
// Call my IClassFactory's AddRef
This->lpVtbl->AddRef(This);
// Return (to the caller) a ptr to my IClassFactory
*ppv = This;
return(NOERROR);
}
// We don't know about any other GUIDs
*ppv = 0;
return(E_NOINTERFACE);
}
// IClassFactory's AddRef()
static ULONG STDMETHODCALLTYPE cfAddRef(IClassFactory *This)
{
// Someone is obtaining my IClassFactory, so inc the count of
// pointers that I've returned which some app needs to Release()
InterlockedIncrement(&OutstandingObjects);
// Since we never actually allocate/free an IClassFactory (ie, we
// use just 1 static one), we don't need to maintain a separate
// reference count for our IClassFactory. We'll just tell the caller
// that there's at least one of our IClassFactory objects in existance
return(1);
}
// IClassFactory's Release
static ULONG STDMETHODCALLTYPE cfRelease(IClassFactory *This)
{
// Someone is obtaining my IClassFactory, so inc the count of
// pointers that I've returned which some app needs to Release()
InterlockedDecrement(&OutstandingObjects);
// Since we never actually allocate/free an IClassFactory (ie, we
// use just 1 static one), we don't need to maintain a separate
// reference count for our IClassFactory. We'll just tell the caller
// that there's at least one of our IClassFactory objects in existance
return(1);
}
/* start Implementation of the IClassFactory */
static HRESULT STDMETHODCALLTYPE cfCreateInstance(
IClassFactory * This,
/* [unique][in] */ IUnknown *pUnkOuter,
/* [in] */ REFIID riid,
/* [iid_is][out] */ void **ppvObject)
{
HRESULT hr;
MyInterface *real_if;
IAdder *my_if;
*ppvObject = NULL;
if (pUnkOuter) {
return CLASS_E_NOAGGREGATION;
}
real_if = CoTaskMemAlloc(sizeof(*real_if));
/* ATTENTION we need room for the other elements in the structure */
if (NULL == real_if) {
#ifdef DEBUG
fprintf(stderr, "malloc This in CreateInstance failed\n");
#endif
return E_OUTOFMEMORY;
}
real_if->lpVtbl = &IAdder_Vtbl;
real_if->count = 1;
/* put other initializatio values here */
/* seen from a logical point of view this cast is superfluous. The out
line of both structures is exactly the same but MyInterface2 just has
a few extra fields, However too keep the compiler happy this cas it a "needed" thing
maybe some MACRO doing the cast would be appropriate here */
my_if = (IAdder*) real_if;
hr = IAdder_Vtbl.QueryInterface(my_if, riid, ppvObject);
if (FAILED(hr)){
CoTaskMemFree(real_if);
}
IAdder_Vtbl.Release(my_if);
if (SUCCEEDED(hr)) InterlockedIncrement(&OutstandingObjects);
return hr;
}
static HRESULT STDMETHODCALLTYPE cfLockServer(IAdder *This, BOOL fLock){
if (fLock){
InterlockedIncrement(&OutstandingObjects);
} else {
InterlockedDecrement(&OutstandingObjects);
}
return S_OK;
}
/* assignment of the function to the factory function table */
static IClassFactoryVtbl IClassFactory_Vtbl = {
cfQueryInterface,
cfAddRef,
cfRelease,
cfCreateInstance,
cfLockServer
};
struct IClassFactory MyIClassFactory = {&IClassFactory_Vtbl};
/* end of the class factory implementation */
/* function needed for export of a proper COM DLL */
/* Now this is important to the COM stuff this is needed for our component to be a "proper"
citizen in the COM world, this elements must be exported AFAIKT */
HRESULT EXPORT PASCAL DllGetClassObject(REFCLSID objGuid, REFIID riid, void **factoryHandle)
{
register HRESULT hr;
IAdder * my_if = NULL;
// Check that the caller is passing our IExample GUID. That's the
// only object our DLL implements
if (IsEqualCLSID(objGuid, &CLSID_CoIAdder))
{
// Fill in the caller's handle with a pointer to our IClassFactory object.
// We'll let our IClassFactory's QueryInterface do that, because it also
// checks the IClassFactory GUID and does other book-keeping
hr = cfQueryInterface(&MyIClassFactory, riid, factoryHandle);
}
else
{
// We don't understand this GUID. It's obviously not for our DLL.
// Let the caller know this by clearing his handle and returning
// CLASS_E_CLASSNOTAVAILABLE
*factoryHandle = 0;
hr = CLASS_E_CLASSNOTAVAILABLE;
}
return(hr);
}
HRESULT EXPORT PASCAL DllCanUnloadNow(void)
{
// If someone has retrieved pointers to any of our objects, and
// not yet Release()'ed them, then we return S_FALSE to indicate
// not to unload this DLL. Also, if someone has us locked, return
// S_FALSE
#ifdef DEBUG
MessageBox(NULL, "In DllCanUnloadNow", "Unloading?", MB_OK);
#endif
if ((0 == OutstandingObjects) && (0 == LockCount)){
return S_OK;
} else {
return S_FALSE;
}
}
/* end of the needed function for COM DLLs */
/* Implementation of our Interface */
/* This code is all written by me to implement the COM Inproc Server.
Our tasks:
We have to implement at first the three function from the
IUnkown interface, all this functions just get file scope. The never ever should
be use outside, the only access is via the function pointers of the *lpVtbl struct
which in the end call the implemented functions */
/* We keep this order strict in this order QueryInterface, AddRef, Release
every other way is doomed to fail */
/* Start IUnkown Implementation */
static HRESULT STDMETHODCALLTYPE QueryInterface (IAdder *This, REFIID vTableGuid, void **ppv){
HRESULT h_rc;
if (IsEqualIID(vTableGuid, &IID_IUnknown)
|| IsEqualIID(vTableGuid, &IID_IDispatch)
|| IsEqualIID(vTableGuid, &IID_IAdder)) {
*ppv = This;
This->lpVtbl->AddRef(This);
/* standard C syntax
alternatively one can use the lcc-win32 extension and write
this->AddRef(), which is much nicer but neither Standard C nor Windows C
I'm not fully sure if one can use it that way or of one has to use casts even in C
for the time being I let it stay as is */
h_rc = S_OK;
} else {
*ppv = NULL;
h_rc = E_NOINTERFACE;
}
return h_rc;
}
static ULONG STDMETHODCALLTYPE AddRef(IAdder *This) {
/* do it a bit longish */
ULONG result;
MyInterface *my_if = (MyInterface*) This;
InterlockedIncrement(&my_if->count);
result = my_if->count;
return result;
}
static ULONG STDMETHODCALLTYPE Release(IAdder *This) {
ULONG result;
MyInterface *my_if = (MyInterface*) This;
InterlockedDecrement(&my_if->count);
result = my_if->count;
if (0 == result){
CoTaskMemFree(my_if);
InterlockedDecrement(&OutstandingObjects);
}
return result;
}
/* end Implementation of the IUnknown Interface */
/* start IDispatch Interface */
// This is just a helper function for the IDispatch functions below
static HRESULT loadMyTypeInfo(void)
{
register HRESULT hr;
LPTYPELIB pTypeLib;
// Load our type library and get a ptr to its TYPELIB. Note: This does an
// implicit pTypeLib->lpVtbl->AddRef(pTypeLib)
if (!(hr = LoadRegTypeLib(&LIBID_IAdderLibrary, majorVersion, minorVersion, 0, &pTypeLib)))
{
// Get Microsoft's generic ITypeInfo, giving it our loaded type library. We only
// need one of these, and we'll store it in a global Tell Microsoft this is for
// our IExample2's VTable, by passing that VTable's GUID
if (!(hr = pTypeLib->lpVtbl->GetTypeInfoOfGuid(pTypeLib, &IID_IAdder, &MyTypeInfo)))
{
// We no longer need the ptr to the TYPELIB now that we've given it
// to Microsoft's generic ITypeInfo. Note: The generic ITypeInfo has done
// a pTypeLib->lpVtbl->AddRef(pTypeLib), so this TYPELIB ain't going away
// until the generic ITypeInfo does a pTypeLib->lpVtbl->Release too
pTypeLib->lpVtbl->Release(pTypeLib);
// Since caller wants us to return our ITypeInfo pointer,
// we need to increment its reference count. Caller is
// expected to Release() it when done
MyTypeInfo->lpVtbl->AddRef(MyTypeInfo);
}
}
return(hr);
}
// GetTypeInfoCount()
static HRESULT STDMETHODCALLTYPE GetTypeInfoCount(IAdder *This, UINT *pCount)
{
*pCount = 1;
return(S_OK);
}
// IExample2's GetTypeInfo()
static HRESULT STDMETHODCALLTYPE GetTypeInfo(IAdder *This, UINT itinfo, LCID lcid, ITypeInfo **pTypeInfo)
{
register HRESULT hr;
// Assume an error
*pTypeInfo = 0;
if (itinfo)
hr = ResultFromScode(DISP_E_BADINDEX);
// If our ITypeInfo is already created, just increment its ref count. NOTE: We really should
// store the LCID of the currently created TYPEINFO and compare it to what the caller wants.
// If no match, unloaded the currently created TYPEINFO, and create the correct one. But since
// we support only one language in our IDL file anyway, we'll ignore this
else if (MyTypeInfo)
{
MyTypeInfo->lpVtbl->AddRef(MyTypeInfo);
hr = 0;
}
else
{
// Load our type library and get Microsoft's generic ITypeInfo object. NOTE: We really
// should pass the LCID to match, but since we support only one language in our IDL
// file anyway, we'll ignore this
hr = loadMyTypeInfo();
}
if (!hr) *pTypeInfo = MyTypeInfo;
return(hr);
}
// GetIDsOfNames()
static HRESULT STDMETHODCALLTYPE GetIDsOfNames(IAdder *This, REFIID riid,
LPOLESTR *rgszNames, UINT cNames,
LCID lcid, DISPID *rgdispid)
{
if (!MyTypeInfo)
{
register HRESULT hr;
if ((hr = loadMyTypeInfo())) return(hr);
}
// Let OLE32.DLL's DispGetIDsOfNames() do all the real work of using our type
// library to look up the DISPID of the requested function in our object
return(DispGetIDsOfNames(MyTypeInfo, rgszNames, cNames, rgdispid));
}
static HRESULT STDMETHODCALLTYPE Invoke(IAdder *This, DISPID dispid, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS *params, VARIANT *result, EXCEPINFO *pexcepinfo, UINT *puArgErr)
{
// We implement only a "default" interface
if (!IsEqualIID(riid, &IID_NULL))
return(DISP_E_UNKNOWNINTERFACE);
// We need our type lib's TYPEINFO (to pass to DispInvoke)
if (!MyTypeInfo)
{
register HRESULT hr;
if ((hr = loadMyTypeInfo())) return(hr);
}
// Let OLE32.DLL's DispInvoke() do all the real work of calling the appropriate
// function in our object, and massaging the passed args into the correct format
return(DispInvoke(This, MyTypeInfo, dispid, wFlags, params, result, pexcepinfo, puArgErr));
}
/* end IDispatch interface implementation */
extern int adder_fun(int a, int b);
HRESULT STDMETHODCALLTYPE doADD( IAdder * This,
LONG a,
LONG b,
LONG *result)
{
*result = adder_fun(a,b);
return S_OK;
}
/* end our interface functions */
/* the end of IAdder implementation */
HINSTANCE g_hinstDll;
BOOL WINAPI LibMain(HINSTANCE instance, DWORD fdwReason, LPVOID lpvReserved)
{
#ifdef DEBUG
MessageBox(NULL, "In DllMain", "Info", MB_OK);
#endif
switch (fdwReason)
{
case DLL_PROCESS_ATTACH:
{
// Clear static counts
OutstandingObjects = LockCount = 0;
g_hinstDll = instance;
// Initialize my IClassFactory with the pointer to its VTable
MyIClassFactory.lpVtbl = &IClassFactory_Vtbl;
// We don't need to do any thread initialization
DisableThreadLibraryCalls(instance);
}
}
return(1);
}
static String StringFromUUID(REFGUID ri) {
char GUID_Format[] = TEXT("{%08lX-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X}");
char bufUUID[40];
String result;
sprintf(bufUUID, GUID_Format,
((REFCLSID)ri)->Data1, ((REFCLSID)ri)->Data2, ((REFCLSID)ri)->Data3, ((REFCLSID)ri)->Data4[0],
((REFCLSID)ri)->Data4[1], ((REFCLSID)ri)->Data4[2], ((REFCLSID)ri)->Data4[3],
((REFCLSID)ri)->Data4[4], ((REFCLSID)ri)->Data4[5], ((REFCLSID)ri)->Data4[6],
((REFCLSID)ri)->Data4[7]);
result = new_string(bufUUID);
return result;
}
static void addNewValue(String pathInRegistry, String keyToAdd,
String regValue) {
struct registry_entry_value *oneRegEntry;
comEntries[gRegEntryMax].rootKey = HKEY_CLASSES_ROOT;
comEntries[gRegEntryMax].path = Strdup(pathInRegistry);
comEntries[gRegEntryMax].subkey = Strdup(keyToAdd);
comEntries[gRegEntryMax].values = GC_MALLOC(sizeof(*oneRegEntry) * 2);
oneRegEntry = GC_MALLOC(sizeof(*oneRegEntry));
oneRegEntry->value_name = invalid_stringA;
oneRegEntry->value = Strdup(regValue);
comEntries[gRegEntryMax].values[0] = oneRegEntry;
comEntries[gRegEntryMax].values[1] = NULL;
gRegEntryMax++;
}
static void build_comEntries(void) {
comEntries = GC_MALLOC(sizeof(*comEntries) * 5);
LPOLESTR uuidAsString;
String classIDUUID;
String typeLibUUID;
String clsidPath;
HRESULT hRC;
DWORD dwRC, errCode;
char dllName[MAX_PATH];
String ourProgIDWithVersion;
struct registry_entry_value *oneRegEntry;
String hStr;
gRegEntryMax = 0;
dwRC = GetModuleFileName(g_hinstDll, dllName, MAX_PATH);
if (0 == dwRC) {
errCode = GetLastError();
ShowSysErrMsg(errCode);
}
classIDUUID = StringFromUUID(&CLSID_CoIAdder);
typeLibUUID = StringFromUUID(&LIBID_IAdderLibrary);
comEntries[gRegEntryMax].rootKey = HKEY_CLASSES_ROOT;
comEntries[gRegEntryMax].path = invalid_stringA;
comEntries[gRegEntryMax].subkey = new_string(ourProgID);
comEntries[gRegEntryMax].values = NULL;
gRegEntryMax++;
clsidPath = new_string("CLSID");
addNewValue(ourProgID, clsidPath, classIDUUID);
Strcat(clsidPath, "\\");
Strcat(clsidPath, classIDUUID);
comEntries[gRegEntryMax].rootKey = HKEY_CLASSES_ROOT;
comEntries[gRegEntryMax].path = new_string("CLSID");
comEntries[gRegEntryMax].subkey = Strdup(classIDUUID);
comEntries[gRegEntryMax].values = NULL;
gRegEntryMax++;
ourProgIDWithVersion = Strdup(ourProgID);
Strcat(ourProgIDWithVersion, ProgVersion);
addNewValue(clsidPath, new_string("ProgID"), ourProgIDWithVersion);
addNewValue(clsidPath, new_string("VersionIndependentProgID"), ourProgID);
addNewValue(clsidPath, new_string("InprocServer32"), new_string(dllName));
addNewValue(clsidPath, new_string("TypeLib"), Strdup(typeLibUUID));
}
static void free_comEntries(void) {
comEntries = NULL; /* let GC take place */
}
int register_one_row(struct registry_entry *anEntry) {
HKEY hKey = NULL;
HKEY subKey = NULL;
int result = 1;
LONG lRC;
DWORD dwRC;
DWORD disp;
char *keyToCreateOrOpen = NULL;
char *nKey = NULL;
if (anEntry->path != invalid_stringA) {
keyToCreateOrOpen = (char*) anEntry->path;
}
lRC = RegOpenKeyEx(anEntry->rootKey, keyToCreateOrOpen, 0,
KEY_WRITE, &hKey);
if (ERROR_SUCCESS != lRC) {
/* error handling */
result = -1;
} else {
if (anEntry->subkey != invalid_stringA) {
nKey = (char*) anEntry->subkey;
}
lRC = RegCreateKeyEx(hKey, nKey, 0, 0, REG_OPTION_NON_VOLATILE, KEY_WRITE,
NULL, &subKey, &disp);
if (ERROR_SUCCESS != lRC) {
dwRC = GetLastError();
ShowSysErrMsg(dwRC);
result = -1;
} else {
struct registry_entry_value *value;
if (anEntry->values) {
char *value_name = NULL;
for (int i = 0; ; i++) {
value = anEntry->values[i];
if (value == NULL) {
break;
}
if (value->value_name != invalid_stringA) {
value_name = (char*) value->value_name;
}
lRC = RegSetValueEx(subKey, value_name, 0, REG_SZ,
(char*) value->value, Strlen(value->value) + 1);
if (ERROR_SUCCESS != lRC) {
dwRC = GetLastError();
ShowSysErrMsg(dwRC);
result = -1;
}
}
}
lRC = RegCloseKey(subKey);
if (ERROR_SUCCESS != lRC) {
dwRC = GetLastError();
ShowSysErrMsg(dwRC);
result = -1;
}
}
}
lRC = RegCloseKey(hKey);
if (ERROR_SUCCESS != lRC) {
dwRC = GetLastError();
ShowSysErrMsg(dwRC);
result = -1;
}
return result;
}
STDAPI EXPORT DllUnregisterServer(void) {
HRESULT hRC;
LONG lRC;
DWORD dwRC;
char regPath[512] = {0};
struct registry_entry *regEntry;
build_comEntries();
hRC = UnRegisterTypeLib(&LIBID_IAdderLibrary , majorVersion, minorVersion, LANG_NEUTRAL, SYS_WIN32);
if (FAILED(hRC)) {
fprintf(stderr, "failed to unregister typelib with UUID = %s\n", (char*) StringFromUUID(&LIBID_IAdderLibrary));
}
for (int i = gRegEntryMax - 1 ; i > 0; i--) {
regEntry = &comEntries[i];
if (regEntry->path != invalid_stringA) {
if (regEntry->subkey != invalid_stringA) {
sprintf(regPath, "%s\\%s", (char*) regEntry->path, (char*) regEntry->subkey);
} else {
sprintf(regPath, "%s", (char*) regEntry->path);
}
} else {
sprintf(regPath, "%s", (char*) regEntry->subkey);
}
lRC = RegDeleteKey(regEntry->rootKey, regPath);
if (ERROR_SUCCESS != lRC) {
dwRC = GetLastError();
ShowSysErrMsg(dwRC);
}
}
free_comEntries();
return hRC;
}
static String replace_file_extension(String file_name, String to_extension) {
String result = Strdup(file_name);
String extension = Strrchr(result, '.');
Strrepl(result, (char*) extension, (char*) to_extension);
return result;
}
STDAPI EXPORT DllRegisterServer(void){
int i = 0;
int iRC;
wchar_t *wch;
size_t stRC;
char file_name[MAX_PATH];
OLECHAR tlbName[2*MAX_PATH];
String sFileName;
HRESULT hRC = S_OK;
ITypeLib *pITypeLib = NULL;
struct registry_entry *regEntry;
build_comEntries();
for (i = 0; i < gRegEntryMax; i++){
regEntry = &comEntries[i];
iRC = register_one_row(regEntry);
if (iRC < 0) {
free_comEntries();
return SELFREG_E_CLASS;
}
}
GetModuleFileName(g_hinstDll, file_name, MAX_PATH);
sFileName = new_string(file_name);
sFileName = replace_file_extension(sFileName, new_string(".tlb"));
stRC = mbstowcs(tlbName, (char*)sFileName, Strlen(sFileName) + 1);
if ((size_t)-1 == stRC) {
fprintf(stderr, "Can not convert %s to wide char\n", file_name);
hRC = E_UNEXPECTED;
}
if (SUCCEEDED(hRC)) {
hRC = LoadTypeLibEx(tlbName, REGKIND_REGISTER, &pITypeLib);
if (FAILED(hRC)) {
switch(hRC){
case E_INVALIDARG:
fprintf(stderr, "Invalid Argument\n");
break;
case TYPE_E_INVALIDSTATE:
fprintf(stderr, "Invalid state\n");
break;
case TYPE_E_CANTLOADLIBRARY:
fprintf(stderr, "Library can not be loaded\n");
break;
default:
fprintf(stderr, "Failed to register %s\n", (char*) sFileName);
}
hRC = SELFREG_E_TYPELIB;
} else {
pITypeLib->lpVtbl->Release(pITypeLib);
pITypeLib = NULL;
}
}
if (S_OK != hRC) {
hRC = DllUnregisterServer();
}
free_comEntries();
return hRC;
}
/* We now have to register the functions in our virtual function table
ATTENTION: Order is extremly important it must obey the complete order in
every implemetend Interface, to control you better check the header file
and put into this table every element in exactly the same order as
in the header file */
static IAdderVtbl IAdder_Vtbl = {
QueryInterface,
AddRef,
Release,
GetTypeInfoCount,
GetTypeInfo,
GetIDsOfNames,
Invoke,
doADD
};