多语言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;
}