分享

How to create a COM DLL using VS 2005: C++, ATL / WTL / COM

 quasiceo 2013-12-26
#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
};

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多