早绑定就是在编译期已经确定了类型,编译后代码执行效率很高;晚绑定是在运行期才确定类型。晚绑定需要在运行期确定类型,所以效率比较低,但是带来了很大的灵活性。本人认为动态调用算是晚绑定的具体形式了,也就是不需要头文件和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)
![](/Images/OutliningIndicators/ExpandedBlockStart.gif)
{
//
获取函数地址
fnAdd
=
(LPFunc)GetProcAddress(hModule,
"
Add
"
);
if
(fnAdd)
![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
//
调用函数
iRet
=
fnAdd(
2
,
4
);
printf(
"
Result is %d
"
, iRet);
}
//
释放动态库
FreeLibrary(hModule);
hModule
=
NULL;
}
2、COM组件功能调用(IDispatch的Invoke)
假设有一个组件,ProgID为“ATLFunc.MyMath.1”,有属性和接口方法如下:
![](/Images/OutliningIndicators/ExpandedBlockStart.gif)
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))
![](/Images/OutliningIndicators/ExpandedBlockStart.gif)
{
// 获取Add函数的DISPID
hr = pIMyMath->GetIDsOfNames(IID_NULL, &szMember, 1, LOCALE_USER_DEFAULT, &dispid);
if(hr == S_OK)
![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
pvarsIn = new CComVariant[2];
pvarsIn[0].vt = VT_I4;
pvarsIn[0].intVal = 2;
pvarsIn[1].vt = VT_I4;
pvarsIn[1].intVal = 4;
![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
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))
![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
printf("%d", vtOut.intVal);
}
VariantClear(&vtOut);
delete[]pvarsIn;
pvarsIn = NULL;
}
// 其实如果我们知道接口方法或者属性的DISPID,可以直接通过DISPID来调用
VariantInit(&vtOut);
![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
DISPPARAMS dispParam =
{NULL, NULL, 0, 0 };
hr = pIMyMath->Invoke(1, IID_NULL, LOCALE_USER_DEFAULT, DISPATCH_PROPERTYGET, &dispParam, &vtOut, NULL, NULL);
if(SUCCEEDED(hr))
![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
printf("%d", vtOut.intVal);
}
VariantClear(&vtOut);
}
// 在CoUninitialize之前释放COM组件,否则导致COM库卸载后释放组件会出问题
pIMyMath = NULL;
// 释放COM库
CoUninitialize();
3、.NET组件功能调用:(反射技术)假设一CLR类库程序集中有一个类MyMath,含有方法int Add(int a, int b),在客户端调用步骤如下:
try
![](/Images/OutliningIndicators/ExpandedBlockStart.gif)
{
// 加载程序集
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)
![](/Images/OutliningIndicators/ExpandedBlockStart.gif)
{
System::Console::WriteLine(exp->Message);
}所谓的插件技术不过是在主程序上指定一套接口,所有遵循接口的可加载模块都是插件。主程序可以采用上面的方法去加载任意的dll,调用方法功能,只要满足就是插件,这样插件体系不再局限于COM接口级别,一个插件可以采用以上三种形式去实现。
示例代码。
posted on 2006-07-24 22:00
万连文 阅读(5058)
评论(14) 编辑 收藏 引用 所属分类:
乱七八糟