多語言COM程序設計

C與C++ MFC調用COM接口的示例 編輯

預處理器指令#import用於包含一個類型庫的信息。類型庫的內容被轉化為C++的若干類,大部分描述為COM的interface。預處理器指令#import創建兩個頭文件:

  • 主頭文件(.TLH):類似於MIDL編譯器產生的文件,但還包含編譯器產生的代碼與數據。包含7個部分(除了頭尾的boilerplate,其他部分被包含在IDL文件library語句指定的命名空間中):
    • Heading boilerplate: 最開始的注釋為呢子、#include <comdef.h>(定義了一些標準的宏), 其他初始化信息.
    • Forward references 與 typedefs: 包括了結構的生命,typedef、枚舉類型聲明
    • Smart pointer declarations: 模板類 _com_ptr_t 是智能指針實現,封裝了interface指針,不再需要調用AddRef, Release, QueryInterface函數。此外,還隱藏了調用CoCreateInstance函數創建一個新的COM對象。在這部分,使用宏語句_COM_SMARTPTR_TYPEDEF創建COM interface的 _com_ptr_t特化模板類的類型定義(typedef)。
    • Type library items(Typeinfo declarations): 主要包含類的定義,由ITypeLib:GetTypeInfo函數返回的類型項。枚舉類型的定義。
    • Optional old-style GUID definition: 包含命名的GUID常量的初始化。
    • #include次要頭文件
    • Footer boilerplate: #pragma pack(pop).
  • 次要頭文件(.TLI):包含編譯器產生的成員函數的實現。主頭文件包含(#include)次要頭文件。

這2個頭文件使用類型庫文件的時間戳,預處理器在執行#import時會檢查頭文件時間戳以避免不必要的操作。編譯器會讀和編譯這2個頭文件,如同主頭文件被用#include包含。


#include <comdef.h>
#import "D:\\MyPrograms\\C Sharp\\ClassLibrary1\\ClassLibrary1\\bin\\Debug\\ClassLibrary1.tlb"
using namespace ClassLibrary1;
//在这个COM类库中,定义了COM接口AddComInterface,包含了成员函数iadd
int main()
{
	// 方法零:
	int dresult;
	CString strResult;
	CoInitialize(NULL);//NULL 换成0 也可以
	AddComInterfacePtr p_Add(__uuidof(AddComService));//AddComInterfacePtr是由#import预编译指令创建的*.tlh中定义的智能指针。参数是CoClass_ID
	dresult = p_Add->iadd(1, 2);
	
	//方法一:
	CLSID clsid;
	CLSIDFromProgID(OLESTR("ClassLibrary1.AddComInterface.1"), &clsid);
	CComPtr<AddComInterface> pGetRes;//COM接口的智能指针
	pGetRes.CoCreateInstance(clsid); //参数是CoClass_ID
	int k = pGetRes->iadd(1, 2);  
	pGetRes.Release();//

	//方法二:
	//CLSID clsid;
	CLSIDFromProgID(OLESTR("ClassLibrary1.AddComInterface.1"), &clsid);
	AddComInterface* ptr;//COM接口的指针
	HRESULT hr = ::CoCreateInstance(clsid, NULL, CLSCTX_INPROC_SERVER, __uuidof(AddComInterface), (LPVOID*)&ptr);
	k = ptr->iadd(1, 2); 

	//方法三:
	//CLSID clsid;
	CLSIDFromProgID(OLESTR("ClassLibrary1.AddComInterface.1"), &clsid);
	//AddComInterface* ptr;//COM接口的指针
	IClassFactory* p_classfactory;
	hr = ::CoGetClassObject(clsid, CLSCTX_INPROC_SERVER, NULL, IID_IClassFactory, (LPVOID*)&p_classfactory);
	p_classfactory->CreateInstance(NULL, __uuidof(AddComInterface), (LPVOID*)&ptr);
	k = ptr->iadd(1, 2);

	//方法四:
	//直接从dll中得到DllGetClassObject,接着生成类对象及类实例(这方法可以使组件不用在注册表里注册) 
	typedef HRESULT(__stdcall * pfnHello)(REFCLSID, REFIID, void**);
	pfnHello fnHello = NULL;
	HINSTANCE hdllInst = LoadLibrary("D:\\MyPrograms\\C Sharp\\ClassLibrary1\\ClassLibrary1\\bin\\Debug\\ClassLibrary1.dll");
	fnHello = (pfnHello)GetProcAddress(hdllInst, "DllGetClassObject");
	if (fnHello != 0)
	{
	  //CLSID clsid;
	  CLSIDFromProgID(OLESTR("ClassLibrary1.AddComInterface.1"), &clsid);
	  IClassFactory* pcf = NULL;
	  HRESULT hr = (fnHello)(clsid,IID_IClassFactory,(void**)&pcf);
	  if (SUCCEEDED(hr) && (pcf != NULL))
	  {
		AddComInterface* pGetRes = NULL;
	        hr = pcf->CreateInstance(NULL, __uuidof(AddComInterface), (void**)&pGetRes);
		if (SUCCEEDED(hr) && (pGetRes != NULL))
		{
		  k = ptr->iadd(1, 2);
		  pGetRes->Release();
		}
		pcf->Release();
	   }
	}
	FreeLibrary(hdllInst);

	//方法五:
	//通过添加COM类型库的MFC包装类,前提是必须是MFC项目且COM组件的接口必须是派生自IDispatch,具体方法:
        //Add-->Class...--->MFC--->MFC Class From TypeLib, 选择"我的模板库.tlb", 选择想生成的接口的包装类
        //或者Project-->Class Wizard-->Add Class--->MFC Class From TypeLib--->Available type libraries / File
        //选择需要调用的Interfaces,就会自动生成相应的.h文件.就可以在MFC应用程序中使用COM组件了.
        //主要关注7个接口:_Application、Workbooks、_Workbook、Worksheets、_Worksheet、Range、Font
        //对上述的接口的.h文件,注释掉#import "C:\\Program Files (x86)\\Microsoft Office\\Office15\\EXCEL.EXE" no_namespace
        //如果crange.h(335): error C2059: syntax error : ','  那么把代码VARIANT DialogBox()改名如VARIANT DialogBox_(),再次编译通过
	 CAddComInterface getRest;
	 if (getRest.CreateDispatch("ClassLibrary1.AddComInterface.1") != 0)
	 {
	   k = getRest.iadd(1, 2);
	   getRest.ReleaseDispatch();
	 }

    CoUninitialize();
    return 0;
}