早绑定就是在编译期已经确定了类型,编译后代码执行效率很高;晚绑定是在运行期才确定类型。晚绑定需要在运行期确定类型,所以效率比较低,但是带来了很大的灵活性。本人认为动态调用算是晚绑定的具体形式了,也就是不需要头文件和Lib,而调用dll里面的函数。
目前的基于ATL开发的插件体系软件多采用前者,即需要idl编译出的.h和_i.c文件,这种方式有时候比较死板,缺少动态机制。其实采用下面介绍的任何一种技术都可以构造出自己的插件体系,并且可以设计的很灵活。
以下是我所了解的三种动态调用方法(例子基于VS2005):
1、DLL函数调用:(获取函数地址)
假设DllFunc.dll里面有一个函数:int Add(int a, int b),在客户端调用步骤如下:
		
				 //
				 加载动态库
				//
				 加载动态库
				
						
						 hModule 
				=
				 LoadLibrary(_T(
				"
				DllFunc.dll
				"
				));
				
				    hModule 
				=
				 LoadLibrary(_T(
				"
				DllFunc.dll
				"
				));
 if
				(hModule)
    
				if
				(hModule)

 
    
				
						 {
				
				
						{
 //
						 获取函数地址
        
						//
						 获取函数地址
						
								
								 fnAdd 
						=
						 (LPFunc)GetProcAddress(hModule, 
						"
						Add
						"
						);
						
						        fnAdd 
						=
						 (LPFunc)GetProcAddress(hModule, 
						"
						Add
						"
						);
 if
						(fnAdd)
        
						if
						(fnAdd)

 
        
						
								 {
						
						
								{
 //
								 调用函数
            
								//
								 调用函数
								
										
										 iRet 
								=
								 fnAdd(
								2
								, 
								4
								);
								
								            iRet 
								=
								 fnAdd(
								2
								, 
								4
								);
 printf(
								"
								Result is %d
								"
								, iRet);
            printf(
								"
								Result is %d
								"
								, iRet);
 }
        }
						
						
								
								 //
						 释放动态库
        
						//
						 释放动态库
						
								
								 FreeLibrary(hModule);
						
						        FreeLibrary(hModule);
 hModule 
						=
						 NULL;
        hModule 
						=
						 NULL;
 }
    }
				
		 
		
				
2、COM组件功能调用(IDispatch的Invoke)
假设有一个组件,ProgID为“ATLFunc.MyMath.1”,有属性和接口方法如下:

 interface IMyMath : IDispatch
interface IMyMath : IDispatch {
{
 [propget, id(1), helpstring("属性 Result")] HRESULT Result([out, retval] LONG* pVal);
    [propget, id(1), helpstring("属性 Result")] HRESULT Result([out, retval] LONG* pVal);
 [propput, id(1), helpstring("属性 Result")] HRESULT Result([in] LONG newVal);
    [propput, id(1), helpstring("属性 Result")] HRESULT Result([in] LONG newVal);
 [id(2), helpstring("方法Add")] HRESULT Add(LONG a, LONG b,[out, retval] LONG* pC);
    [id(2), helpstring("方法Add")] HRESULT Add(LONG a, LONG b,[out, retval] LONG* pC);
 };
};在客户端调用步骤如下:
 // 初始化COM库
// 初始化COM库
 CoInitialize(NULL);
    CoInitialize(NULL);
 // 根据ProgID创建组件
    // 根据ProgID创建组件
 hr = pIMyMath.CoCreateInstance(_T("ATLFunc.MyMath.1"));
    hr = pIMyMath.CoCreateInstance(_T("ATLFunc.MyMath.1"));
 if(SUCCEEDED(hr))
    if(SUCCEEDED(hr))

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

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

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

 
             {
{
 printf("%d", vtOut.intVal);
                printf("%d", vtOut.intVal);
 }
            }
 VariantClear(&vtOut);
            VariantClear(&vtOut);
 delete[]pvarsIn;
            delete[]pvarsIn;
 pvarsIn = NULL;
            pvarsIn = NULL;
 }
        }
 // 其实如果我们知道接口方法或者属性的DISPID,可以直接通过DISPID来调用
        // 其实如果我们知道接口方法或者属性的DISPID,可以直接通过DISPID来调用
 VariantInit(&vtOut);
        VariantInit(&vtOut);

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

 
         {
{
 printf("%d", vtOut.intVal);
            printf("%d", vtOut.intVal);
 }
        }
 VariantClear(&vtOut);
        VariantClear(&vtOut);
 }
    }
 // 在CoUninitialize之前释放COM组件,否则导致COM库卸载后释放组件会出问题
    // 在CoUninitialize之前释放COM组件,否则导致COM库卸载后释放组件会出问题
 pIMyMath = NULL;
    pIMyMath = NULL;
 // 释放COM库
    // 释放COM库
 CoUninitialize();
    CoUninitialize();
3、.NET组件功能调用:(反射技术)假设一CLR类库程序集中有一个类MyMath,含有方法int Add(int a, int b),在客户端调用步骤如下:
 try
try

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

 
     {
{
 System::Console::WriteLine(exp->Message);
        System::Console::WriteLine(exp->Message);
 }
    }所谓的插件技术不过是在主程序上指定一套接口,所有遵循接口的可加载模块都是插件。主程序可以采用上面的方法去加载任意的dll,调用方法功能,只要满足就是插件,这样插件体系不再局限于COM接口级别,一个插件可以采用以上三种形式去实现。 
示例代码。
	
posted on 2006-07-24 22:00 
万连文 阅读(5171) 
评论(14)  编辑 收藏 引用  所属分类: 
乱七八糟