日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

COM组件(MFC篇)

發布時間:2024/3/26 编程问答 29 豆豆
生活随笔 收集整理的這篇文章主要介紹了 COM组件(MFC篇) 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

目 錄

第1章 創建進程內組件????1

1.1 目標????1

1.2 創建項目????3

1.2.1 VC++6.0????3

1.2.2 VC++2010????4

1.2.3 VC++6.0與VC++2010的區別????6

1.3 升級項目????6

1.3.1 增加接口定義文件????6

1.3.2 覆蓋應用程序類的InitInstance函數????7

1.3.3 導出COM函數????7

1.3.4 修改rc文件????9

1.4 增加COM類????10

1.4.1 VC++6.0????10

1.4.2 VC++2010????11

1.4.3 項目結構????13

1.4.4 VC++6.0與VC++2010的區別????14

1.5 增加方法????14

1.5.1 VC++6.0????14

1.5.2 VC++2010????15

1.6 增加屬性????17

1.6.1 VC++6.0????17

1.6.2 VC++2010????17

1.7 刪除方法、屬性????18

1.7.1 VC++6.0????18

1.7.2 VC++2010????19

1.8 編碼????19

1.8.1 增加成員變量????19

1.8.2 初始化成員變量????19

1.8.3 實現Add????20

1.8.4 實現Reset????20

1.8.5 實現GetCount????20

1.8.6 實現GetAverage????20

1.8.7 實現GetStdDev????21

1.9 注冊、注銷????21

1.10 再論增加COM類????22

第2章 創建進程外組件????23

2.1 創建項目????23

2.2 升級項目????23

2.2.1 增加接口定義文件????23

2.2.2 修改rc文件????24

2.2.3 修改應用程序類的InitInstance函數????24

2.2.4 實現COM類????25

2.3 注冊、注銷????27

第3章 VC++使用組件????29

3.1 #import????29

3.2 MFC包裝類????30

3.2.1 VC++6.0生成包裝類????30

3.2.2 VC++2010生成包裝類????31

3.2.3 包裝類的使用????33

3.3 C語言調用????33

第4章 VB6.0使用組件????37

4.1 前期綁定????37

4.1.1 引用類型庫????37

4.1.2 查看類型庫????38

4.1.3 編碼????39

4.2 后期綁定????39

?

?

第1章 創建進程內組件

1.1 目標

本章的目標是使用MFC創建一個進程內COM組件。在此組件里,將實現COM類CStatistic及COM接口IStatistic,用來進行統計計算。IStatistic的詳細信息如下:

1、方法

void Reset();????????????????//重新開始統計計算

void Add(double dVal);????????//增加一個數據

2、屬性

long????Count;????????????????//返回數據個數

double????Average;????????????//返回平均值

double????StdDev;????????????????//返回標準差

CStatistic的VC++代碼如下:

#include <MATH.H>

?

class CStatistic

{

public:

CStatistic()????????????{Reset();}

public:

void Reset()????????{memset(this,0,sizeof(*this));}

void Add(double dVal)

{

++m_nCount; ????????????//樣本個數

m_dSum += dVal; ????????//所有樣本值的和

m_dSum2 += dVal * dVal; ????//所有樣本值的平方和

}

public://屬性定義(VC++的語法)

__declspec(property(get=GetCount)) ????ULONG????????Count;

__declspec(property(get=GetAverage)) ????double????????Average;

__declspec(property(get=GetStdDev)) ????double????????StdDev;

public:

ULONG GetCount() const????????{return m_nCount;}

double GetAverage() const

{

if(m_nCount)

{

return m_dSum / m_nCount;

}

return 0.0;

}

double GetStdDev() const

{

double d = 0.0;

if(m_nCount > 1)

{

d = (m_nCount * m_dSum2 - m_dSum * m_dSum)

/ (m_nCount * (m_nCount - 1));

if(d > 0.0)

{

d = sqrt(d);

}

}

return d;

}

private:

ULONG????m_nCount; ????//樣本個數

double????????m_dSum; ????//所有樣本值的和

double????????m_dSum2; ????//所有樣本值的平方和

};

測試代碼如下:

ULONG????????n????=????0;

double????????d????=????0.0;

CStatistic????s;

s.Reset();

s.Add(1.0);

s.Add(2.0);

s.Add(3.0);

s.Add(4.0);

n????=????s.Count; ????//(1,2,3,4)的個數

d????=????s.Average; ????//(1,2,3,4)的平均值

d????=????s.StdDev; ????//(1,2,3,4)的標準差

s.Add(5.0);

n????=????s.Count; ????//(1,2,3,4,5)的個數

d????=????s.Average; ????//(1,2,3,4,5)的平均值

d????=????s.StdDev; ????//(1,2,3,4,5)的標準差

這個類的優點在于:它能實時獲得樣本數據的平均值、標準差,且不用把樣本數據存入數組,因此可以連續的長時間工作。

1.2 創建項目

1.2.1 VC++6.0

運行VC++6.0,新建"MFC AppWizard(dll)"項目,如下圖所示。配置好項目名稱、項目目錄后,單擊"OK"按鈕。

圖1.1

下圖顯示的界面中,選擇"Regular DLL with MFC statically linked",表示創建一個MFC Regular DLL,這個DLL將靜態鏈接MFC共享庫。這就意味著,運行時這個DLL不再需要MFC42.dll。非MFC客戶程序使用此DLL時,能夠減少運行時的依賴項。

一定要勾中"Automation"復選框,它是實現COM組件的關鍵。

單擊"Finish"按鈕。

圖1.2

顯示界面如下,單擊"OK"按鈕,完成項目創建。

圖1.3

1.2.2 VC++2010

運行VC++2010,新建"MFC DLL"項目,如下圖所示。配置好項目名稱、項目目錄后,單擊"OK"按鈕。

圖1.4

顯示創建向導,界面如下面兩張圖所示:

圖1.5 創建向導——頁面一

頁面二的配置與VC++6.0的完全相同。

圖1.6 創建向導——頁面二

單擊上圖的"Finish"按鈕,完成項目的創建。

1.2.3 VC++6.0與VC++2010的區別

1、VC++6.0缺少DllUnregisterServer函數。這意味著VC++6.0編譯生成的COM組件不能通過regsvr32 /u comDLLmfc.dll進行注銷;

2、DllRegisterServer函數里,VC++6.0缺少對類型庫的注冊,即缺少代碼AfxOleRegisterTypeLib(AfxGetInstanceHandle(),_tlid);

3、CcomDLLmfcApp::InitInstance函數里,VC++6.0沒有調用CWinApp::InitInstance()。

建議:采取VC++2010生成的代碼。

1.3 升級項目

假定有一個MFC Regular DLL項目,創建時未勾中"Automation"選項。如何將其轉換為COM組件?

1.3.1 增加接口定義文件

增加<dspName>.odl文件至DLL項目,其內容如下:

//<dspName>.odl

[uuid(71719C6D-3058-4B13-8C91-9DD49848FADF), version(1.0)]

library <TypeLibName>

{

importlib("stdole32.tlb");

importlib("stdole2.tlb");

//{{AFX_APPEND_ODL}}

//}}AFX_APPEND_ODL}}

};

說明:

1、<dspName>是dsp文件名;

2、71719C6D-3058-4B13-8C91-9DD49848FADF是類型庫的GUID,為避免重復,請替換成其它值。最簡單的辦法就是使用VC++6.0生成的頭文件里的宏。如:#if !defined(AFX_DLGDLG_H__8715C0CF_88F1_4CD3_B8E5_64FBCF26B052__INCLUDED_)中的8715C0CF_88F1_4CD3_B8E5_64FBCF26B052就是一個隨機的GUID,把下劃線替換為減號即可使用;

3、version(1.0)是類型庫的版本號。1是主版本號,0是次版本號;

4、<TypeLibName>是類型庫的名稱,請根據實際需要做相應的修改。

1.3.2 覆蓋應用程序類的InitInstance函數

請覆蓋應用程序類的InitInstance函數,并在該函數內增加代碼COleObjectFactory::RegisterAll();

BOOL CcomDLLmfcApp::InitInstance()

{

CWinApp::InitInstance();

COleObjectFactory::RegisterAll();

return TRUE;

}

1.3.3 導出COM函數

請實現四個COM函數并導出:DllCanUnloadNow、DllGetClassObject、DllRegisterServer、DllUnregisterServer。代碼如下:

const GUID CDECL _tlid = { 0x71719C6D, 0x3058, 0x4B13

, { 0x8C, 0x91, 0x9D, 0xD4, 0x98, 0x48, 0xFA, 0xDF } };

const WORD _wVerMajor = 1;

const WORD _wVerMinor = 0;

?

STDAPI DllGetClassObject(REFCLSID rclsid, REFIID riid,LPVOID* ppv)

{

AFX_MANAGE_STATE(AfxGetStaticModuleState());

return AfxDllGetClassObject(rclsid, riid, ppv);

}

STDAPI DllCanUnloadNow(void)

{

AFX_MANAGE_STATE(AfxGetStaticModuleState());

return AfxDllCanUnloadNow();

}

STDAPI DllRegisterServer(void)

{

AFX_MANAGE_STATE(AfxGetStaticModuleState());

if (!AfxOleRegisterTypeLib(AfxGetInstanceHandle(), _tlid))

return SELFREG_E_TYPELIB;

if (!COleObjectFactory::UpdateRegistryAll())

return SELFREG_E_CLASS;

return S_OK;

}

STDAPI DllUnregisterServer(void)

{

AFX_MANAGE_STATE(AfxGetStaticModuleState());

if (!AfxOleUnregisterTypeLib(_tlid, _wVerMajor, _wVerMinor))

return SELFREG_E_TYPELIB;

if (!COleObjectFactory::UpdateRegistryAll(FALSE))

return SELFREG_E_CLASS;

return S_OK;

}

說明:

1、_tlid、_wVerMajor、_wVerMinor依次是類型庫的GUID、主版本號、次版本號。請與接口定義文件內容保持一致;

2、VC++6.0的AfxOleUnregisterTypeLib函數只有一個參數,請借鑒VC++.NET的代碼。

def文件里導出這四個函數:

EXPORTS

????DllCanUnloadNow????????PRIVATE

????DllGetClassObject????????PRIVATE

????DllRegisterServer????????PRIVATE

????DllUnregisterServer????????PRIVATE

1.3.4 修改rc文件

rc文件里增加"1 TYPELIB "<dspName>.tlb"",如下表所示:

3 TEXTINCLUDE

... ... ...

"#endif\r\n"

"1 TYPELIB ""<dspName>.tlb""\r\n"

"\0"

END

修改rc文件并保存,VC++會把3 TEXTINCLUDE與END之間的語句自動插入到rc文件的尾部。相當于在rc文件中增加了1 TYPELIB "<dspName>.tlb"。還有一種更為簡便的方法:直接把1 TYPELIB "<dspName>.tlb"增加到rc2文件里。

注意:請將<dspName>替換為實際的名稱。

對于VC++2010而言,需要設置資源編譯器的"Additional Include Directeries"目錄。在此,增加<dspName>.tlb所在的目錄。如果不增加這個目錄,編譯rc文件時,將會因為找不到<dspName>.tlb而失敗。

圖1.7

1.4 增加COM類

現在,往項目里增加COM類。

1.4.1 VC++6.0

單擊【Insert】【New Class...】菜單項

圖1.8

顯示界面如下(如果不顯示如下界面,可能就是缺少clw文件或clw文件內容有誤。請關閉項目,刪除aps、clw、ncb、opt文件。再次打開項目,然后按下Ctrl+W,重新建立clw文件。)

"Class type"請選擇"MFC Class"。

"Base class"請選擇"CCmdTarget"或其派生類,因為COM組件功能就是由CCmdTarget實現的。

"Automation"必須選擇"Createable by type ID"。這個就是COM類的ProgID。客戶端程序可以根據這個ProgID實例化COM類。

單擊"OK"按鈕,完成COM類的創建。

圖1.9

1.4.2 VC++2010

單擊【Project】【Add Class...】菜單項

圖1.10

選中"MFC Class",然后單擊"Add"按鈕

圖1.11

增加MFC類的界面與VC++6.0的類似。配置好后,單擊"Finish"按鈕,完成COM類的創建。

圖1.12

1.4.3 項目結構

VC++6.0的類視圖里增加了"CStatistic"和"IStatistic"。

IStatistic是COM接口,客戶端程序通過它訪問COM組件。

CStatistic是COM類,真正的工作由它來完成。

圖1.13

同時,odl文件也發生了變化,如下表所示。增加了接口IStatistic(dispinterface表示這個接口派生自IDispatch)。增加了COM類Statistic,這個COM類實現了接口IStatistic。

[ uuid(71719C6D-3058-4B13-8C91-9DD49848FADF), version(1.0) ]

library ComDLLmfc

{

????importlib("stdole32.tlb");

????importlib("stdole2.tlb");

????[ uuid(D5FC59B4-255F-415B-933F-08B97A23CD58) ]

????dispinterface IStatistic

????{

????????properties:

????????????//{{AFX_ODL_PROP(CStatistic)

????????????//}}AFX_ODL_PROP

????????methods:

????????????//{{AFX_ODL_METHOD(CStatistic)

????????????//}}AFX_ODL_METHOD

????};

????[ uuid(E43D739C-1270-4B71-B9DB-D3C74FEDDA19) ]

????coclass Statistic

????{

????????[default] dispinterface IStatistic;

????};

????//{{AFX_APPEND_ODL}}

????//}}AFX_APPEND_ODL}}

};

1.4.4 VC++6.0與VC++2010的區別

VC++6.0創建出來的COM類與VC++2010創建出來的COM類,其最大區別在于:VC++2010指明了COM類的線程模型。如下表所示:

VC++6.0

IMPLEMENT_OLECREATE(CStatistic, "comDLLmfc.Statistic", 0xe43d739c, 0x1270, 0x4b71, 0xb9, 0xdb, 0xd3, 0xc7, 0x4f, 0xed, 0xda, 0x19)

VC++2010

IMPLEMENT_OLECREATE_FLAGS(CStatistic, "comDLLmfc.Statistic", afxRegApartmentThreading, 0x2260a7da, 0xc066, 0x4dd2, 0xaa, 0x61, 0x67, 0xb7, 0xab, 0x7b, 0xca, 0x13)

使用的宏也不同,一個是IMPLEMENT_OLECREATE,另一個是IMPLEMENT_OLECREATE_FLAGS。

1.5 增加方法

1.5.1 VC++6.0

鼠標右鍵單擊接口IStatistic,彈出菜單中單擊【Add Method...】菜單項

圖1.14

下圖就是增加方法的界面。這里增加了方法void Add(double dVal)。單擊"OK"按鈕,完成方法的增加。

圖1.15

可使用同樣的方法,增加方法void Reset()。

1.5.2 VC++2010

鼠標右鍵單擊接口IStatistic,彈出菜單中單擊【Add】【Add Method...】菜單項。

圖1.16

增加方法的界面如下。與VC++6.0的大致相同。單擊"Finish"按鈕,完成方法void Add(DOUBLE dVal)的添加。

圖1.17

可使用同樣的方法,增加方法void Reset()。

1.6 增加屬性

1.6.1 VC++6.0

在圖1.14中,單擊【Add Property...】菜單項。顯示界面如下:

這里增加了屬性long Count。注意:沒有設置"Set function",說明這個屬性是只讀屬性。

單擊"OK"按鈕,完成屬性的增加。

圖1.18

同樣方法,可以增加屬性double Average和double StdDev。

1.6.2 VC++2010

在圖1.16中,單擊【Add Property...】菜單項。顯示界面如下:

這里增加了屬性ULONG Count。注意:沒有設置"Set function",說明這個屬性是只讀屬性。

單擊"Finish"按鈕,完成屬性的增加。

圖1.19

同樣方法,可以增加屬性double Average和double StdDev。

1.7 刪除方法、屬性

添加方法、屬性時,如果名稱或參數輸入錯誤,就需要刪除它,然后重新添加。

1.7.1 VC++6.0

按下Ctrl+W,啟動類向導界面。顯示如下圖所示。

進入"Automation"頁面,"Class name"下拉列表里選擇"CStatistic"(COM類)。"External names"里選擇需要刪除的屬性或方法,單擊"Delete"按鈕,然后單擊"OK"按鈕即可刪除選中的屬性或方法。

"External names"列表里,"M"表示方法,"C"表示自定義屬性。

圖1.20

1.7.2 VC++2010

VC++2010里刪除屬性、方法,似乎只能手動進行,相當的麻煩。

1.8 編碼

1.8.1 增加成員變量

請給CStatistic增加三個成員變量

private:

ULONG????m_nCount;

double????????m_dSum;

double????????m_dSum2;

1.8.2 初始化成員變量

CStatistic::CStatistic()

{

EnableAutomation();????

AfxOleLockApp();

m_nCount????????=????0;

m_dSum????????=????0.0;

m_dSum2????????=????0.0;

}

1.8.3 實現Add

void CStatistic::Add(double dVal)

{

AFX_MANAGE_STATE(AfxGetStaticModuleState());

++m_nCount; ????????????????????//樣本個數

m_dSum????+=????dVal; ????????????//所有樣本值的和

m_dSum2????+=????dVal * dVal; ????//所有樣本值的平方和

}

1.8.4 實現Reset

void CStatistic::Reset()

{

AFX_MANAGE_STATE(AfxGetStaticModuleState());

m_nCount????=????0;

m_dSum????=????0.0;

m_dSum2????=????0.0;

}

1.8.5 實現GetCount

long CStatistic::GetCount()

{

AFX_MANAGE_STATE(AfxGetStaticModuleState());

return m_nCount;

}

1.8.6 實現GetAverage

double CStatistic::GetAverage()

{

AFX_MANAGE_STATE(AfxGetStaticModuleState());

if(m_nCount)

{

return m_dSum / m_nCount;

}

return 0.0;

}

1.8.7 實現GetStdDev

double CStatistic::GetStdDev()

{

AFX_MANAGE_STATE(AfxGetStaticModuleState());

double d = 0.0;

if(m_nCount > 1)

{

d = (m_nCount * m_dSum2 - m_dSum * m_dSum)

/ (m_nCount * (m_nCount - 1));

if(d > 0.0)

{

d = sqrt(d);

}

}

return d;

}

1.9 注冊、注銷

編譯comDLLmfc,即可得到進程內COM組件comDLLmfc.dll。使用它之前,需要注冊。

注冊組件可使用如下任意一條命令。它們原理相同:都是載入comDLLmfc.dll,然后調用DllRegisterServer函數

regsvr32 comDLLmfc.dll

Rundll32 comDLLmfc.dll,DllRegisterServer

注銷組件可使用如下任意一條命令。它們原理相同:都是載入comDLLmfc.dll,然后調用DllUnregisterServer函數

regsvr32 /u comDLLmfc.dll

Rundll32 comDLLmfc.dll,DllUnregisterServer

注意:VC++2010可以編譯生成64位的COM組件。在64位操作系統上,regsvr32.exe和Rundll32.exe將自動識別COM組件是32位的還是64位的。注冊信息會寫入注冊表的如下幾個位置。注意這里的<ProgID>其實就是comDLLmfc.Statistic。

HKEY_LOCAL_MACHINE\SOFTWARE\Classes\TypeLib\

HKEY_LOCAL_MACHINE\SOFTWARE\Classes\Interface\

HKEY_LOCAL_MACHINE\SOFTWARE\Classes\CLSID

HKEY_LOCAL_MACHINE\SOFTWARE\Classes\<ProgID>

對于64位組件,注冊程序直接訪問上述注冊表項;對于32位組件,注冊程序會將上述注冊表項映射至32位的注冊表項。如此一來,同一個組件的32位、64位是可以同時注冊在64位Windows上的,它們互不干涉。

1.10 再論增加COM類

在圖1.9和圖1.12中,"Automation"有三個選項:None、Automation、Createable by type ID。三者有何區別?

"None"表示創建一個普通的C++類,也就是說選擇此項,創建出來的就不是COM類了。

"Createable by type ID"創建出來的是COM類,而且它可以根據輸入的ProgID實例化。

"Automation"創建出來的雖然也是COM類,但是它缺少了下面這幾條語句:

1、缺少IMPLEMENT_OLECREATE

缺少下面這條語句,意味著COM類CStatistic不能被注冊、注銷,也不可能被客戶端程序實例化。

IMPLEMENT_OLECREATE(CStatistic, "comDLLmfc.Statistic", 0xe43d739c, 0x1270, 0x4b71, 0xb9, 0xdb, 0xd3, 0xc7, 0x4f, 0xed, 0xda, 0x19)

2、因為COM類CStatistic不能被實例化,因此其構造函數里缺少了AfxOleLockApp(),析構函數里缺少了AfxOleUnlockApp()。

AfxOleLockApp()增加COM組件的引用計數,AfxOleUnlockApp()減小COM組件的引用計數。當引用計數為零時,DllCanUnloadNow函數里的AfxDllCanUnloadNow將返回TRUE,此時COM組件才能被FreeLibrary。

?

?

第2章 創建進程外組件

2.1 創建項目

創建進程外組件的操作很簡單:創建MFC EXE項目時,勾中"Automation"選項即可。本文就不進行說明了。

2.2 升級項目

本章的重點在于說明如何將一個MFC EXE項目升級為COM進程外組件。其要點就是用CCmdTarget的派生類做為COM類,供客戶端程序調用。

2.2.1 增加接口定義文件

請增加<dspName>.odl至項目,其內容如下:

[ uuid(B4573BE3-F956-4B7D-86AD-7628AE22CD9A), version(1.0) ]

library <TypeLibName>

{

importlib("stdole32.tlb");

importlib("stdole2.tlb");

//{{AFX_APPEND_ODL}}

//}}AFX_APPEND_ODL}}

};

注意:

1、<dspName>是dsp文件名;

2、B4573BE3-F956-4B7D-86AD-7628AE22CD9A是類型庫的GUID。為避免重復,請替換成其它值。最簡單的辦法就是使用VC++6.0生成的頭文件里的宏。如:#if !defined(AFX_DLGDLG_H__8715C0CF_88F1_4CD3_B8E5_64FBCF26B052__INCLUDED_)中的8715C0CF_88F1_4CD3_B8E5_64FBCF26B052就是一個隨機的GUID,把下劃線替換為減號即可使用;

3、version(1.0)是類型庫的版本號。1是主版本號,0是次版本號;

4、<TypeLibName>是類型庫的名稱,請根據實際需要做相應的修改。

2.2.2 修改rc文件

為了把類型庫信息嵌入exe,需要修改rc文件。使用記事本打開rc文件,找到3 TEXTINCLUDE,在END之前增加一條語句"1 TYPELIB ""<dspName>.tlb""\r\n"

3 TEXTINCLUDE

BEGIN

... ... ...

"#endif\r\n"

"1 TYPELIB ""<dspName>.tlb""\r\n"

"\0"

END

修改rc文件并保存,VC++會把3 TEXTINCLUDE與END之間的語句自動插入到rc文件的尾部。相當于在rc文件中增加了1 TYPELIB "<dspName>.tlb"。還有一種更為簡便的方法:直接把1 TYPELIB "<dspName>.tlb"增加到rc2文件里。

注意:請將<dspName>替換為實際的名稱。

2.2.3 修改應用程序類的InitInstance函數

修改應用程序類的InitInstance函數

//B4573BE3-F956-4B7D-86AD-7628AE22CD9A

const GUID CDECL BASED_CODE _tlid =

{ 0xB4573BE3,0xF956,0x4B7D

,{0x86,0xAD,0x76,0x28,0xAE,0x22, 0xCD,0x9A}};

const WORD _wVerMajor = 1;

const WORD _wVerMinor = 0;

?

BOOL CXXXApp::InitInstance()

{

... ... ...

AfxOleInit(); //增加此函數

... ... ...

CCommandLineInfo cmdInfo;

ParseCommandLine(cmdInfo);

if(cmdInfo.m_bRunEmbedded || cmdInfo.m_bRunAutomated)

{//COM客戶端啟動本程序

COleTemplateServer::RegisterAll();

}

else if(cmdInfo.m_nShellCommand == CCommandLineInfo::AppUnregister)

{//從注冊表里注銷

COleObjectFactory::UpdateRegistryAll(FALSE);

AfxOleUnregisterTypeLib(_tlid, _wVerMajor, _wVerMinor);

return FALSE;

}

else

{ //下面兩行代碼用于注冊本組件及類型庫

COleObjectFactory::UpdateRegistryAll();

AfxOleRegisterTypeLib(AfxGetInstanceHandle(), _tlid);

if (cmdInfo.m_nShellCommand == CCommandLineInfo::AppRegister)

{//注冊之后不運行其它代碼

return FALSE;

}

}

}

注意:

1、VC++6.0不支持CCommandLineInfo::AppRegister;

2、上面代碼中的_tlid、_wVerMajor、_wVerMinor依次是類型庫的GUID、主版本號、次版本號。它應與COM.odl文件的內容保持一致。

2.2.4 實現COM類

MFC實現的COM類必須派生自CCmdTarget。實現COM類有兩種思路:一是新建一個類;二是修改已有的類,使其變成COM類。具體而言,如果EXE項目里包含文檔類,就可以修改文檔類為COM類,否則可以新建一個。

進程外組件項目里增加一個COM類與進程內組件項目里增加一個COM類的操作完全相同。請參考上一章的內容。本節重點講述如何修改文檔類,使之成為COM類。

1、修改文檔類的頭文件

DECLARE_MESSAGE_MAP()之后,添加如下代碼:

// Generated OLE dispatch map functions

//{{AFX_DISPATCH(CTestDoc)

//}}AFX_DISPATCH

DECLARE_DISPATCH_MAP()

DECLARE_INTERFACE_MAP()

2、修改文檔類的實現文件

構造函數前,增加如下內容

BEGIN_DISPATCH_MAP(CTestDoc, CDocument)

????//{{AFX_DISPATCH_MAP(CTestDoc)

????//}}AFX_DISPATCH_MAP

END_DISPATCH_MAP()

?

// {07AD2F15-3DC9-4ED6-85E6-A42005A11978}

static const IID IID_ITest =

{ 0x7AD2F15, 0x3DC9, 0x4ED6

, { 0x85, 0xE6, 0xA4, 0x20, 0x5, 0xA1, 0x19, 0x78 } };

?

BEGIN_INTERFACE_MAP(CTestDoc, CDocument)

????INTERFACE_PART(CTestDoc, IID_ITest, Dispatch)

END_INTERFACE_MAP()

構造函數和析構函數,增加如下代碼:

CTestDoc::CTestDoc()

{

EnableAutomation();

AfxOleLockApp();

}

?

CTestDoc::~CTestDoc()

{

AfxOleUnlockApp();

}

3、修改接口定義文件內容

增加COM接口和COM類

// Primary dispatch interface for CStatistic

[ uuid(07AD2F15-3DC9-4ED6-85E6-A42005A11978) ]

dispinterface IStatistic

{

????properties:

????// NOTE - ClassWizard will maintain property information here.

????// Use extreme caution when editing this section.

????//{{AFX_ODL_PROP(CStatistic)

????//}}AFX_ODL_PROP

????methods:

????// NOTE - ClassWizard will maintain method information here.

????// Use extreme caution when editing this section.

????//{{AFX_ODL_METHOD(CStatistic)

????//}}AFX_ODL_METHOD

};

// Class information for CStatistic

[ uuid(4FE1F183-045A-4AA8-A35D-3C365C4679B7) ]

coclass Statistic

{

????[default] dispinterface IStatistic;

};

4、應用程序類增加如下成員變量

COleTemplateServer m_server;

5、修改應用程序類的InitInstance函數

在AddDocTemplate(pDocTemplate);之后增加一行代碼:

m_server.ConnectTemplate(clsid, pDocTemplate, TRUE);

clsid是COM類Statistic的GUID,請與接口定義文件內容保持一致,如:

// {4FE1F183-045A-4AA8-A35D-3C365C4679B7}

static const CLSID clsid = { 0x4FE1F183, 0x45A, 0x4AA8

,{0xA3,0x5D,0x3C,0x36,0x5C,0x46,0x79,0xB7}};

2.3 注冊、注銷

假定進程外組件的文件名為<ComExeName>,如:C:\App.exe,則:

注冊組件可使用下表任意一條命令:

<ComExeName> /Register

<ComExeName> /Regserver

<ComExeName> /RegisterPerUser

<ComExeName> /RegserverPerUser

注銷組件可使用下表任意一條命令:

<ComExeName> /Unregister

<ComExeName> /Unregserver

<ComExeName> /UnregisterPerUser

<ComExeName> /UnregserverPerUser

注意:

1、VC++6.0不支持命令開關/Register、/Regserver、/RegisterPerUser、/RegserverPerUser,也不支持CCommandLineInfo::AppRegister。直接運行程序即可完成注冊;

2、VC++6.0不支持命令開關/RegisterPerUser、/RegserverPerUser、/UnregisterPerUser、/UnregserverPerUser。VC++2010支持,此時InitInstance函數里的cmdInfo.m_bRegisterPerUser 為 TRUE;

3、注冊時InitInstance函數里的ParseCommandLine(cmdInfo);將解析命令行開關,發現/Register、/Regserver、/RegisterPerUser、/RegserverPerUser之一時,會設置cmdInfo.m_nShellCommand為CCommandLineInfo::AppRegister。此時,注冊組件的代碼被執行(其實每次正常運行程序,以下注冊組件的代碼也會被執行)。

COleObjectFactory::UpdateRegistryAll();

AfxOleRegisterTypeLib(AfxGetInstanceHandle(), _tlid);

4、注銷時InitInstance函數里的ParseCommandLine(cmdInfo);將解析命令行開關,發現/Unregister、/Unregserver、/UnregisterPerUser、/UnregserverPerUser之一時,會設置cmdInfo.m_nShellCommand為CCommandLineInfo::AppUnregister。此時,注銷組件的代碼被執行。

COleObjectFactory::UpdateRegistryAll(FALSE);

AfxOleUnregisterTypeLib(_tlid, _wVerMajor, _wVerMinor);

5、COM客戶端調用時,會啟動進程外組件,并增加命令開關/Embedding或/Automation。此時,cmdInfo.m_bRunEmbedded 或 cmdInfo.m_bRunAutomated 為 TRUE。以下代碼將被執行

COleTemplateServer::RegisterAll();

?

?

第3章 VC++使用組件

3.1 #import

如果客戶端程序由C++編寫而成,可以使用#import。代碼如下:

#import "G:\VC\comDLLmfc\Release\comDLLmfc.dll" no_namespace

?

void Test()

{

CoInitialize(NULL);

ULONG????n????=????0;

double????????d????=????0.0;

try

{

IStatisticPtr s(__uuidof(Statistic));

s->Reset();

s->Add(1.0);

s->Add(2.0);

s->Add(3.0);

s->Add(4.0);

n????=????s->Count; ????????//(1,2,3,4)的個數

d????=????s->Average; ????//(1,2,3,4)的平均值

d????=????s->StdDev; ????????//(1,2,3,4)的標準差

s->Add(5.0);

n????=????s->Count; ????????//(1,2,3,4,5)的個數

d????=????s->Average; ????//(1,2,3,4,5)的平均值

d????=????s->StdDev; ????????//(1,2,3,4,5)的標準差

}//運行到此,智能指針s將被析構,s->Relase將被自動調用

catch(_com_error&e)

{

AfxMessageBox(e.Description() + "\n" + e.ErrorMessage());

}

CoUninitialize();

}

#import根據comDLLmfc.dll里的類型庫生成comDLLmfc.tlh和comDLLmfc.tli。查看comDLLmfc.tlh就可以知道COM組件(comDLLmfc.dll)的接口類IStatistic的定義。查看comDLLmfc.tli就能知道IStatistic的方法是如何實現的。

MFC編寫的COM組件,均為自動化接口,即COM接口派生自IDispatch。對屬性、方法的訪問,均是通過IDispatch::Invoke函數實現。

3.2 MFC包裝類

MFC包裝類就是生成一個派生自COleDispatchDriver的C++類,通過這個類去訪問COM組件的屬性、方法。它只能包裝自動化接口,因此MFC編寫的COM組件都可以被MFC類包裝起來。其操作如下:

3.2.1 VC++6.0生成包裝類

按下Ctrl+W,啟動類向導界面。進入Automation頁面,單擊"Add Class..."按鈕。彈出菜單中,單擊【From a type library...】菜單項。

圖3.1

下圖所示的界面里,選擇COM組件,然后單擊"打開"按鈕。

圖3.2

VC++6.0顯示如下界面。

"Class name"上面的列表里,列出了組件里所有的COM接口;

"Class name"下面的文本框,是將要生成的包裝類名稱,它的基類注定是COleDispatchDriver。

接著指定包裝類的頭文件、實現文件。

最后單擊"OK"按鈕。VC++6.0將創建包裝類。

圖3.3

3.2.2 VC++2010生成包裝類

單擊【Project】【Add Class...】菜單項

圖3.4

選中"MFC Class From TypeLib",然后單擊"Add"按鈕。

圖3.5

"Available type libraries"下拉列表框里選擇類型庫。單擊">>"按鈕,再單擊"Finish"按鈕,將生成包裝類。

圖3.6

注意:單擊上圖的"File"單選框,就可以指定一個文件。通過這個文件生成包裝類。

3.2.3 包裝類的使用

void Test()

{

CoInitialize(NULL);

ULONG????n????=????0;

double????????d????=????0.0;

{

IStatistic s;

if(s.CreateDispatch(_T("comDLLmfc.Statistic")))

{

s.Reset();

s.Add(1.0);

s.Add(2.0);

s.Add(3.0);

s.Add(4.0);

n????=????s.GetCount();????//(1,2,3,4)的個數

d????=????s.GetAverage();????//(1,2,3,4)的平均值

d????=????s.GetStdDev();????//(1,2,3,4)的標準差

s.Add(5.0);

n????=????s.GetCount();????//(1,2,3,4,5)的個數

d????=????s.GetAverage();????//(1,2,3,4,5)的平均值

d????=????s.GetStdDev();????//(1,2,3,4,5)的標準差

}

}//運行到此,s 被析構。IDispatch被自動Release

CoUninitialize();

}

注意:CreateDispatch函數的參數"comDLLmfc.Statistic"就是COM類的ProgID,也就是圖1.9和圖1.12里的"Createable by type ID"。

與import方法相比,MFC包裝類只能用于MFC程序。

3.3 C語言調用

使用C語言也可以訪問COM組件,下面是示例代碼:

#include <windows.h>

#include <MALLOC.H>

void Reset(IDispatch*s)

{

DISPPARAMS????dispparams;

VARIANT????????vaResult;

UINT????????????nArgErr = (UINT)-1;

memset(&dispparams,0,sizeof(dispparams));

VariantInit(&vaResult);

s->lpVtbl->Invoke(s,0x5,&IID_NULL,0,DISPATCH_METHOD

????????????????,&dispparams,&vaResult, NULL, &nArgErr);

VariantClear(&vaResult);

}

void Add(IDispatch*s,double dVal)

{

DISPPARAMS????dispparams;

VARIANT????????vaResult;

UINT????????????nArgErr = (UINT)-1;

memset(&dispparams,0,sizeof(dispparams));

dispparams.cArgs = 1;

dispparams.rgvarg = (VARIANTARG*)malloc(sizeof(VARIANTARG));

dispparams.rgvarg->vt = VT_R8;

dispparams.rgvarg->dblVal = dVal;

VariantInit(&vaResult);

s->lpVtbl->Invoke(s,0x4,&IID_NULL,0,DISPATCH_METHOD

????????????????,&dispparams,&vaResult, NULL, &nArgErr);

VariantClear(&vaResult);

free(dispparams.rgvarg);

}

ULONG GetCount(IDispatch*s)

{

DISPPARAMS????dispparams;

VARIANT????????vaResult;

UINT????????nArgErr????=?????(UINT)-1;

ULONG????????nCount????????=????0;

memset(&dispparams,0,sizeof(dispparams));

VariantInit(&vaResult);

s->lpVtbl->Invoke(s,1,&IID_NULL,0,DISPATCH_PROPERTYGET

????????????????,&dispparams,&vaResult, NULL, &nArgErr);

nCount = vaResult.lVal;

VariantClear(&vaResult);

return nCount;

}

double GetAverage(IDispatch*s)

{

DISPPARAMS????dispparams;

VARIANT????????vaResult;

UINT????????????nArgErr????????=?????(UINT)-1;

double????????????d????????????=????0.0;

memset(&dispparams,0,sizeof(dispparams));

VariantInit(&vaResult);

s->lpVtbl->Invoke(s,2,&IID_NULL,0,DISPATCH_PROPERTYGET

????????????,&dispparams,&vaResult, NULL, &nArgErr);

d = vaResult.dblVal;

VariantClear(&vaResult);

return d;

}

double GetStdDev(IDispatch*s)

{

DISPPARAMS????dispparams;

VARIANT????????vaResult;

UINT????????nArgErr????????=????(UINT)-1;

double????????d????????????=????0.0;

memset(&dispparams,0,sizeof(dispparams));

VariantInit(&vaResult);

s->lpVtbl->Invoke(s,3,&IID_NULL,0,DISPATCH_PROPERTYGET

????????????,&dispparams,&vaResult, NULL, &nArgErr);

d = vaResult.dblVal;

VariantClear(&vaResult);

return d;

}

void main()

{

const IID DIID_IStatistic = {0xD5FC59B4,0x255F,0x415B

????????,{0x93,0x3F,0x08,0xB9,0x7A,0x23,0xCD,0x58}};

const CLSID CLSID_Statistic = {0xE43D739C,0x1270,0x4B71

????????,{0xB9,0xDB,0xD3,0xC7,0x4F,0xED,0xDA,0x19}};

IDispatch*????s????=????NULL;

ULONG????n????=????0;

double????????d????=????0.0;

CoInitialize(NULL);

CoCreateInstance(&CLSID_Statistic,NULL

????????,CLSCTX_INPROC_SERVER,&DIID_IStatistic,&s);

Reset(s);

Add(s,1.0);

Add(s,2.0);

Add(s,3.0);

Add(s,4.0);

n = GetCount(s); ????????//(1,2,3,4)的個數

d = GetAverage(s); ????//(1,2,3,4)的平均值

d = GetStdDev(s); ????//(1,2,3,4)的標準差

Add(s,5.0);

n = GetCount(s); ????????//(1,2,3,4,5)的個數

d = GetAverage(s); ????//(1,2,3,4,5)的平均值

d = GetStdDev(s); ????//(1,2,3,4,5)的標準差

s->lpVtbl->Release(s);

CoUninitialize();

}

說明:

1、MFC編寫的COM組件,其接口均派生自IDispatch,即自動化接口。對屬性、方法的訪問必須通過IDispatch::Invoke函數;

2、上述代碼參考了MFC包裝類。

?

?

第4章 VB6.0使用組件

4.1 前期綁定

4.1.1 引用類型庫

單擊【Project】【References...】菜單項

圖4.1

列表中勾中COM組件,然后單擊"OK"按鈕。也可以單擊"Browse..."按鈕,選擇一個文件,然后單擊"OK"按鈕。

圖4.2

4.1.2 查看類型庫

單擊【View】【Object Browser】菜單項

圖4.3

可以看到類型庫"comDLLmfc"里的COM接口,及其方法、屬性。

圖4.4

4.1.3 編碼

Dim n As Long

Dim d As Double

Dim s As New ComDLLmfc.Statistic '創建COM對象

s.Reset

s.Add 1#

s.Add 2#

s.Add 3#

s.Add 4#

n = s.Count????????'(1,2,3,4)的個數

d = s.Average????????'(1,2,3,4)的平均值

d = s.StdDev????????'(1,2,3,4)的標準差

s.Add 5#

n = s.Count????????'(1,2,3,4,5)的個數

d = s.Average????????'(1,2,3,4,5)的平均值

d = s.StdDev????????'(1,2,3,4,5)的標準差

Set s = Nothing????'釋放COM對象

4.2 后期綁定

后期綁定不用引用類型庫,通過CreateObject函數創建COM類。示例代碼如下:

Dim n As Long

Dim d As Double

Dim s As Object

Set s = CreateObject("comDLLmfc.Statistic") '創建COM對象

s.Reset

s.Add 1#

s.Add 2#

s.Add 3#

s.Add 4#

n = s.Count????????'(1,2,3,4)的個數

d = s.Average????????'(1,2,3,4)的平均值

d = s.StdDev????????'(1,2,3,4)的標準差

s.Add 5#

n = s.Count????????'(1,2,3,4,5)的個數

d = s.Average????????'(1,2,3,4,5)的平均值

d = s.StdDev????????'(1,2,3,4,5)的標準差

Set s = Nothing????'釋放COM對象

注意:后期綁定的代碼執行效率比前期綁定要低。
---------------------?
作者:hanford?
來源:CSDN?
原文:https://blog.csdn.net/hanford/article/details/53340597?
版權聲明:本文為博主原創文章,轉載請附上博文鏈接!

總結

以上是生活随笔為你收集整理的COM组件(MFC篇)的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。