DLL(三)显示链接

隐式链接虽然实现较为简单,但除了必须的.dll文件之外还需要DLL的.h文件(虽然不一定必须)和.lib文件。这在那些只提供.dll文件的场合就无法使用,而这种情况下我们只能用显示链接了。

显示链接:

是有应用程序在运行过程,由其空间中的线程决定是否调用DLL中的输出函数。

void  Cdll_testDlg::OnBnClickedButton1()
{
    
//  TODO: 在此添加控件通知处理程序代码
    HINSTANCE hInst;
    hInst
= LoadLibrary(L " dll1.dll " );
    typedef 
int  ( * ADDPTR)( int  a, int  b); // 定义一个函数指针
    ADDPTR add;  // add函数指针变量
     if (hInst != NULL){ // 载入DLL成功
        add = (ADDPTR)GetProcAddress(hInst, " add " ); // 去除dll中函数名为add的这个函数的地址
        
// 如果发生了函数的名字改编这里是不是就会失败啊!
         if (NULL != add){ // 找到DLL中的add函数
            CString str;
            
/*
            注意:因为这里编译器默认定义了unicode了吧,解决办法如下
            Format ( L"%c",s ) ; 
            或者Format ( _T("%c"),s ) ;
            
*/
            str.Format(L
" 5+3=%d " ,add( 5 , 3 ));
            MessageBox(str);
        }
else {
            MessageBox(L
" 获取函数地址失败 " );
        }
    }
else {
        MessageBox(L
" 加载DLL失败 " );
    }
}

 

把dll1.dll拷贝到当前目录下发现加载DLL没有问题,但是提示获取函数地址失败,我想到难道是dll1.dll中的add函数名被改编了,非常有可能。

我看了一下dll1的工程的代码发现果然如此就一个dll1.cpp,而且代码是下面这样的

_declspec(dllexport)  int  add( int  a, int  b)
{
    
return  a + b;
}

_declspec(dllexport) 
int  subtract( int  a, int  b)
{
    
return  a - b;
}

dumpbin了一下,当然会像下面这样了!

这样当然会名字改变,我又不想用extern"C"的方式来做,因为考虑到通用性dll中的函数调用应该做出_stdcall而不是用默认的_cdecal

那样的话即使用extern"C"也没法阻止名字改编了,所以我们就应该用.def文件。

于是定义了dll1.def文件加到工程中,并在项目属性的连接器那加上了.def(方法看DLL(一)),dll1.cpp改成下面这样

/* _declspec(dllexport) */   int  add( int  a, int  b)
{
    
return  a + b;
}

/* _declspec(dllexport) */   int  subtract( int  a, int  b)
{
    
return  a - b;
}

然后再dumpbin,这下输出就是

没有发生名字改编这下dll_test程序应该可以正常运行了吧,果然不出所料,成功的输出了

为什么用显示连接呢?

1)隐式链接需要.dll .lib .h而显示连接原则上只需要.dll

2) 我们看到的一些程序通常都不是一个.dll而是多个.dll,那如果做成隐式链接的话那在.exe启动的时候会把所有的.dll都加载到进程的地址空间这样的做的话会造成启动的缓慢,而且可能在程序运行过程中可能.dll中的很多函数我们都不需要去访问。可能是在某个条件发生的时候才会访问某个dll中的某个输出函数。如果隐式链接的话所有点函数都已经被加载进了,这样浪费资源(所以的DLL和所有DLL中的函数)。这种情况下我们可以动态加载。在某个条件发生的时候再loadlibary(*.dll)

然后GetProcAddress找到某个函数,用了之后再freeLibary(*.dll)这种显示连接其实是一种动态链接。

有一个地方我们要注意一下:

如果我们为了把.dll做的更加通用,改成下面这样

/*
 dll1.cpp:改变调用约定
 
*/
_stdcall 
int  add( int  a, int  b)
{
    
return  a + b;
}

_stdcall 
int  subtract( int  a, int  b)
{
    
return  a - b;
}

这样的调用约定的改变并不会改变函数的名字,就是不会发生名字改编,因为我们有dll1.def已经定死了

但是我们在dll1_test中如果还是按原先的方式进行调用就会出错。我们将改之后的dll1.dll替换掉原先dll1_test中的dll,但是调用函数的代码不改变。

那会怎么样,也许我们认为尽管改变了调用约定,但是函数没有发生名字改编,所以应该无需该代码啊。但是实际情况是让我们失望的,必须如下改动

// typedef int (*ADDPTR)(int a,int b);
typedef  int  (_stdcall  * ADDPTR)( int  a, int  b);

也就是说使用dll的用户要遵照dll的创建者规定的调用约定进行调用,不然就无法正确调用,只是需要我们注意到。

 我们还要注意到一点就是这种显示链接dll库的做法,用dumpbin -imports dll_test.exe命令时是无法看出加载了哪个DLL的信息的。

 

转载于:https://www.cnblogs.com/NeuqUstcIim/archive/2009/01/13/1374928.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值