万星星@豌豆荚 欢迎加入我们
一个吃软饭的男人!!!!!我只想写程序####
微博:http://weibo.com/wanlianwen
posts - 172,  comments - 1253,  trackbacks - 0

早绑定就是在编译期已经确定了类型,编译后代码执行效率很高;晚绑定是在运行期才确定类型。晚绑定需要在运行期确定类型,所以效率比较低,但是带来了很大的灵活性。本人认为动态调用算是晚绑定的具体形式了,也就是不需要头文件和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, 20 };
            
// 调用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, 00 };
        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"truetrue);
        
// 获取方法信息
        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 万连文 阅读(4855) 评论(14)  编辑 收藏 引用 所属分类: 乱七八糟

FeedBack:
# re: C++的三种动态调用方法
2006-07-24 22:49 | 创系
这个跟语言无关,是win平台相关的,建议改一下题目  回复  更多评论
  
# re: 三种动态调用方法
2006-07-24 22:54 | 万连文
累,至少dll是可以移植到不同平台的。其他我不清楚,只会windows开发。  回复  更多评论
  
# re: 三种动态调用方法
2006-07-24 23:21 | 创系
>>至少dll是可以移植到不同平台的。
dll不能在linux下跑(用虚拟机的除外),C++中没有支持这些平台相关特性的机制.  回复  更多评论
  
# re: 三种动态调用方法
# re: 三种动态调用方法
2006-07-25 00:06 | 创系
我看了,那个只是用的一个机制使得代码可以跨平台,但是dll是不能运行在linux上面的,从低层的角度来说dll是PE文件格式的,而linux下面的可执行文件都是elf文件格式的.跨平台的库多了去了,一般都是用的#ifdefine window之类的技巧.  回复  更多评论
  
# re: 三种动态调用方法
2006-07-25 07:58 | 万连文
好吧,就这样,我实在不懂linux,因为我不是它的爱好者,我本身也没有提到可以使用到linux上。希望朋友们多一点贡献,拿出点实在东西让大家一同学习。  回复  更多评论
  
# re: 三种动态调用方法
2006-07-25 10:08 | 不是万连文
实在没有看明白这跟C++有什么关系?

linux下的动态库是so,ELF格式的,可以用dlopen/dlsym调用,相当于LoadLibrary/GetProcAddress
  回复  更多评论
  
# re: 三种动态调用方法
2006-07-25 21:01 | Jerry Cat
"实在没有看明白这跟C++有什么关系? ", 怎么没关系? VC不是C++? 也是C++的一个分支嘛; DLL难道不是用C++语言写的? VC既是C++一个成功的IDE, 也扩充了C++, 包括一个规范工整的C++库: MFC, 还有"better C++" : COM, COM+

为万仔说句公道话啦  回复  更多评论
  
# re: 三种动态调用方法
2006-07-26 10:07 | 任我行
VC不是C++? 也是C++的一个分支嘛?
真的是这样吗?真是在误导别人!
并不只有C++才能写DLL,
动态库也并不只在Win中运行,别的系统也可以有,只是格式和名称不一样而已。  回复  更多评论
  
# re: 三种动态调用方法
2006-07-26 14:00 | 创系
万兄和那个说公道话的人的言论让我很是无语....到此为止,罢了  回复  更多评论
  
# re: 三种动态调用方法
2006-07-26 15:02 | 万连文
这样看来还是Java好,直接跨平台,省心。没了工具语言之争,没有烦恼。
我倒没有其他意思,只是不愿意在概念上争论,虽然知道VC是实现了利用C++语言的一种开发工具,一旦用起来、说起来就又不想分开了,太麻烦,清者自清,绕着自绕。  回复  更多评论
  
# re: 三种动态调用方法
2006-07-26 15:11 | 爱上小白
我自己写了一个dll调用接口的类. 不过比较简单, 不知道值不值得说.
typedef void* (*_ObjectCreater)();
typedef void (*_ObjectDestroyer)(void*);

template <typename _OBJ>
class clClassModule
{
private:
HMODULE m_hModule;
_OBJ* m_pObject;
_ObjectCreater m_objCreater;
_ObjectDestroyer m_objDestroyer;
public:
clClassModule(std::string strDllFile);
~clClassModule();

_OBJ* inst();
};
#ifndef USE_DLL_OBJECT
#define USE_DLL_OBJECT extern "C" DLL_EXP void* ObjectCreater();\
extern "C" DLL_EXP void ObjectDestroyer(void*);
#endif//USE_DLL_OBJECT

#ifndef DLL_OBJECT
#define DLL_OBJECT(OBJECT) void* ObjectCreater(){ return new OBJECT; }\
void ObjectDestroyer(void* _obj){ OBJECT* pObj = (OBJECT*)_obj; delete pObj; }
#endif//DLL_OBJECT
//这里把实现暂时忽略



使用方法非常简单,
在dll中定义一个类A, 然后使用DLL_OBJECT(A);就可以了.

在调用dll的代码中USE_DLL_OBJECT;, 然后就可以使用clClassModule<A>建立dll的实例. 使用clClassModule<A>::inst();导出A的类.
比如:
clClassModule<A> cm;
A* a = cm.inst();
就可以使用a的接口了(类A). 不过A这个类在dll中应该是一个虚基类, 原理我就不提了, 看看COM的实现就明白的.

不知道这样算不算一种调用dll的方式?
  回复  更多评论
  
# re: 三种动态调用方法
2006-10-18 14:00 | 不吃鱼的猫
我作了个基于插件的应用环境,是用.NET实现的,现在用在公司的资源管理系统上,感觉还不错,.net的实现起来比较简单吧,以前试过用COM,当时还参考MMC,结果是个累啊。。。  回复  更多评论
  
# re: 三种动态调用方法
2007-03-06 09:37 | 公平正义
过于浅薄  回复  更多评论
  

只有注册用户登录后才能发表评论。
【推荐】超50万行VC++源码: 大型组态工控、电力仿真CAD与GIS源码库
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理


简历下载
联系我

<2006年7月>
2526272829301
2345678
9101112131415
16171819202122
23242526272829
303112345

常用链接

留言簿(66)

随笔分类

随笔档案

相册

搜索

  •  

最新评论

阅读排行榜

评论排行榜