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