早綁定就是在編譯期已經確定了類型,編譯后代碼執行效率很高;晚綁定是在運行期才確定類型。晚綁定需要在運行期確定類型,所以效率比較低,但是帶來了很大的靈活性。本人認為動態調用算是晚綁定的具體形式了,也就是不需要頭文件和Lib,而調用dll里面的函數。
目前的基于ATL開發的插件體系軟件多采用前者,即需要idl編譯出的.h和_i.c文件,這種方式有時候比較死板,缺少動態機制。其實采用下面介紹的任何一種技術都可以構造出自己的插件體系,并且可以設計的很靈活。
以下是我所了解的三種動態調用方法(例子基于VS2005):
1、DLL函數調用:(獲取函數地址)
假設DllFunc.dll里面有一個函數:int Add(int a, int b),在客戶端調用步驟如下:
//
?加載動態庫
????hModule?
=
?LoadLibrary(_T(
"
DllFunc.dll
"
));
????
if
(hModule)

????
{
????????
//
?獲取函數地址
????????fnAdd?
=
?(LPFunc)GetProcAddress(hModule,?
"
Add
"
);
????????
if
(fnAdd)

????????
{
????????????
//
?調用函數
????????????iRet?
=
?fnAdd(
2
,?
4
);
????????????printf(
"
Result?is?%d
"
,?iRet);
????????}
????????
//
?釋放動態庫
????????FreeLibrary(hModule);
????????hModule?
=
?NULL;
????}
2、COM組件功能調用(IDispatch的Invoke)
假設有一個組件,ProgID為“ATLFunc.MyMath.1”,有屬性和接口方法如下:

interface?IMyMath?:?IDispatch
{
????[propget,?id(1),?helpstring("屬性?Result")]?HRESULT?Result([out,?retval]?LONG*?pVal);
????[propput,?id(1),?helpstring("屬性?Result")]?HRESULT?Result([in]?LONG?newVal);
????[id(2),?helpstring("方法Add")]?HRESULT?Add(LONG?a,?LONG?b,[out,?retval]?LONG*?pC);
};在客戶端調用步驟如下:
//?初始化COM庫
????CoInitialize(NULL);
????//?根據ProgID創建組件
????hr?=?pIMyMath.CoCreateInstance(_T("ATLFunc.MyMath.1"));
????if(SUCCEEDED(hr))

????
{
????????//?獲取Add函數的DISPID
????????hr?=?pIMyMath->GetIDsOfNames(IID_NULL,?&szMember,?1,?LOCALE_USER_DEFAULT,?&dispid);
????????if(hr?==?S_OK)

????????
{
????????????pvarsIn?=?new?CComVariant[2];
????????????pvarsIn[0].vt?=?VT_I4;
????????????pvarsIn[0].intVal?=?2;
????????????pvarsIn[1].vt?=?VT_I4;
????????????pvarsIn[1].intVal?=?4;

????????????DISPPARAMS?dispParam?=?
{pvarsIn,?NULL,?2,?0?};
????????????//?調用Add函數
????????????VariantInit(&vtOut);
????????????hr?=?pIMyMath->Invoke(dispid,?IID_NULL,?LOCALE_USER_DEFAULT,?DISPATCH_METHOD,?&dispParam,?&vtOut,?NULL,?NULL);
????????????if(SUCCEEDED(hr))

????????????
{
????????????????printf("%d",?vtOut.intVal);
????????????}
????????????VariantClear(&vtOut);
????????????delete[]pvarsIn;
????????????pvarsIn?=?NULL;
????????}
????????//?其實如果我們知道接口方法或者屬性的DISPID,可以直接通過DISPID來調用
????????VariantInit(&vtOut);

????????DISPPARAMS?dispParam?=?
{NULL,?NULL,?0,?0?};
????????hr?=?pIMyMath->Invoke(1,?IID_NULL,?LOCALE_USER_DEFAULT,?DISPATCH_PROPERTYGET,?&dispParam,?&vtOut,?NULL,?NULL);
????????if(SUCCEEDED(hr))

????????
{
????????????printf("%d",?vtOut.intVal);
????????}
????????VariantClear(&vtOut);
????}
????//?在CoUninitialize之前釋放COM組件,否則導致COM庫卸載后釋放組件會出問題
????pIMyMath?=?NULL;
????//?釋放COM庫
????CoUninitialize();
3、.NET組件功能調用:(反射技術)假設一CLR類庫程序集中有一個類MyMath,含有方法int Add(int a, int b),在客戶端調用步驟如下:
try

????
{
????????//?加載程序集
????????pAssembly?=?Assembly::Load(L"CLRFunc");
????????//?獲取程序集中CLRFunc.MyMath類型
????????pType?=?pAssembly->GetType(L"CLRFunc.MyMath",?true,?true);
????????//?獲取方法信息
????????pMI?=?pType->GetMethod(L"Add");
????????//?創建CLRFunc.MyMath類型實例
????????pObject?=?Activator::CreateInstance(pType);
????????p[0]?=?2;
????????p[1]?=?4;
????????//?調用方法
????????iRet?=?safe_cast<INT32^>(pMI->Invoke(pObject,?p));
????????System::Console::WriteLine(iRet);
????}
????catch?(Exception^?exp)

????
{
????????System::Console::WriteLine(exp->Message);
????}所謂的插件技術不過是在主程序上指定一套接口,所有遵循接口的可加載模塊都是插件。主程序可以采用上面的方法去加載任意的dll,調用方法功能,只要滿足就是插件,這樣插件體系不再局限于COM接口級別,一個插件可以采用以上三種形式去實現。
示例代碼。
posted on 2006-07-24 22:00
萬連文 閱讀(5139)
評論(14) 編輯 收藏 引用 所屬分類:
亂七八糟