一、創建ATL工程
1、創建ATL項目,取名爲MyATL
2、在ATL項目嚮導中,勾選【支持MFC】(利用MFC測試用)、【支持 COM+ 1.0】和【支持部件註冊器】,其餘的選項默認,點擊完成。
3、右鍵工程名稱,選擇添加類,接下來選擇【ATL簡單對象】。
4、在【ATL簡單對象嚮導】對話框中填入下面內容(可更改爲自己喜歡的類名稱),然後直接點擊完成。
5、切換到類視圖,爲剛剛添加的接口IMyATLClass添加方法。
6、現在來添加2個方法,分別用來計算兩個數之和和彈出MFC對話框。
7、切換到【解決方案資源管理器】,可以看到Sum和PopupDialog的定義。
interface IMyATLClass : IDispatch{
[id(1)] HRESULT Sum([in] LONG para1, [in] LONG para2, [in] LONG* sum);
[id(2)] HRESULT PopupDialog([in] CHAR* text);
};
將參數[in] LONG* sum修改爲[out] LONG* sum,表示這是一個需要輸出的值。
8、打開MyATLClass.cpp,實現添加的兩個方法。
STDMETHODIMP CMyATLClass::Sum(LONG para1, LONG para2, LONG* sum)
{
AFX_MANAGE_STATE(AfxGetStaticModuleState());
// TODO: 在此添加實現代碼
*sum = para1 + para2;
return S_OK;
}
STDMETHODIMP CMyATLClass::PopupDialog(CHAR* text)
{
AFX_MANAGE_STATE(AfxGetStaticModuleState());
// TODO: 在此添加實現代碼
AfxMessageBox((LPCTSTR)text);
return S_OK;
}
9、生成該工程,得到MyATL.dll並在註冊表中註冊。
二、測試ATL組件
1、在上面的工程中添加測試項目。
2、添加用於測試的MFC工程TestATL。
3、運行MFC應用程序嚮導,爲簡單起見,選擇對話框工程,其餘默認,點擊完成。
4、將生成的對話框中【確定】、【取消】按鈕修改如下。
5、雙擊上面的按鈕,在系統生成的函數裏刪除掉代碼CDialogEx::OnOK();如下。
void CTestATLDlg::OnBnClickedOk()
{
// TODO: 在此添加控件通知處理程序代碼
}
void CTestATLDlg::OnBnClickedCancel()
{
// TODO: 在此添加控件通知處理程序代碼
}
6、在TestATL工程中引入由MyATL工程中生成的“MyATL_i.h”、“MyATL_i.c”(這個文件主要用來查看CLSID_MyATLClass和IID_IMyATLClass的值),並在TestATLDlg.cpp中添加MyATL_i.h的引用。
#include "..\MyATL\MyATL_i.h"
7、生成TestATL工程,會出現如下錯誤。
1>d:\projects\test\myatl\myatl\myatl_i.c : fatal error C1853: “Debug\TestATL.pch”預編譯頭文件來自編譯器的早期版本,或者預編譯頭爲 C++ 而在 C 中使用它(或相反)
解決方法是右鍵“MyATL_i.c”->屬性->C/C++->預編譯頭,將“使用(/Yu)”修改爲“不使用預編譯頭”。
再次生成TestATL就不會報錯了。
8、實現Sum按鈕的響應方法。
void CTestATLDlg::OnBnClickedOk()
{
// TODO: 在此添加控件通知處理程序代碼
HRESULT hr = S_OK;
hr = CoInitialize(NULL);
ITypeLib* pTypeLib = NULL;
hr = ::LoadTypeLib(L"..\\Debug\\MyATL.dll", &pTypeLib); //加載並註冊類型庫
if (FAILED(hr))
{
return;
}
int nTypeInfoCount = pTypeLib->GetTypeInfoCount();
if (nTypeInfoCount <= 0) //返回類型庫中的類型說明的數量
{
return;
}
ITypeInfo* pTypeInfo = NULL;
TYPEATTR* pTypeAttr = NULL;
//!!!此處一定要注意導出類在.idl文件中的順序!!!
hr = pTypeLib->GetTypeInfo(2, &pTypeInfo);
hr = pTypeInfo->GetTypeAttr(&pTypeAttr);
CLSID clsid = pTypeAttr->guid;
hr = pTypeLib->GetTypeInfo(3, &pTypeInfo);
hr = pTypeInfo->GetTypeAttr(&pTypeAttr);
IID iid = pTypeAttr->guid;
IMyATLClass* pMyATLClass = NULL;
hr = CoCreateInstance(clsid, NULL, CLSCTX_INPROC_SERVER, iid, (void **)&pMyATLClass);
hr = CoCreateInstance(CLSID_MyATLClass, NULL, CLSCTX_INPROC_SERVER, IID_IMyATLClass, (void **)&pMyATLClass);
int sum = 0;
pMyATLClass->Sum(2, 3, (LONG*)&sum);
pTypeInfo->ReleaseTypeAttr(pTypeAttr);
pTypeInfo->Release();
CoUninitialize();
}
9、將TestATL設置爲啓動項,在CoCreateInstance設置斷點跟蹤調試。啓動應用後,點擊Sum按鈕,進入斷點,在監視窗口看到clsid、iid是和CLSID_MyATLClass、IID_IMyATLClass一一對應的,這就說明在調用pTypeLib->GetTypeInfo的時候,其參數值index是正確的。
10、註釋掉含有CLSID_MyATLClass那一行對CoCreateInstance的調用,添加對Sum的測試代碼,調試運行在監視窗口可以看到運算結果。
11、添加對Popup Dialog的測試,過程略,結果如下。
pMyATLClass->PopupDialog("test ATL");
考慮到編碼問題,將MyATLClass.cpp中對PopupDialog的實現修改爲:
STDMETHODIMP CMyATLClass::PopupDialog(CHAR* text)
{
AFX_MANAGE_STATE(AfxGetStaticModuleState());
// TODO: 在此添加實現代碼
CString str(text);
AfxMessageBox((LPCTSTR)str);
return S_OK;
}
重新生成MyATL,並啓動TestATL測試如下。
12、再看pTypeLib->GetTypeInfo的調用。
程序中的調用是:
hr = pTypeLib->GetTypeInfo(2, &pTypeInfo);
hr = pTypeInfo->GetTypeAttr(&pTypeAttr);
CLSID clsid = pTypeAttr->guid;
hr = pTypeLib->GetTypeInfo(3, &pTypeInfo);
hr = pTypeInfo->GetTypeAttr(&pTypeAttr);
IID iid = pTypeAttr->guid;
注意到其中index的值分別是2、3,表示第3、4個值。另外,
int nTypeInfoCount = pTypeLib->GetTypeInfoCount();
中nTypeInfoCount的值爲4,這下就可以知道pTypeLib中前2個TypeInfo並不是所期望的,而第3、4個纔是我們需要的,爲什麼會這樣?
再次找到MyATL工程中的MyATL.idl文件,找到library MyATLLib的定義,
library MyATLLib
{
importlib("stdole2.tlb");
[
uuid(59E6B9BA-489E-417A-BCE9-2AFFE61F57D1)
]
coclass CompReg
{
[default] interface IComponentRegistrar;
};
[
uuid(03677350-0273-42BA-8F16-C7701493C1DC)
]
coclass MyATLClass
{
[default] interface IMyATLClass;
};
};
可以看到首先定義的是CompReg這個類,它使得生成的dll完成了在註冊表中的註冊,並且它的兩部分也正是pTypeLib->GetTypeInfo的前兩部分,因此GetTypeInfo的index就變成了2和3。
下面修改CompReg和MyATLClass類定義的順序,
library MyATLLib
{
importlib("stdole2.tlb");
[
uuid(03677350-0273-42BA-8F16-C7701493C1DC)
]
coclass MyATLClass
{
[default] interface IMyATLClass;
};
[
uuid(59E6B9BA-489E-417A-BCE9-2AFFE61F57D1)
]
coclass CompReg
{
[default] interface IComponentRegistrar;
};
};
在調用pTypeLib->GetTypeInfo的時候,將參數設置爲0、1,這時候也會成功運行。
最後,我們知道了在library MyATLLib中類定義的順序決定了GetTypeInfo中index參數的值,注意不到這個問題,如果在接口自動化中隨意寫index參數的值,就會一直找不到方向,白白浪費時間。
附: