白驹过隙
学而不思则罔,思而不学则怠
posts - 4,  comments - 3,  trackbacks - 0

前一篇文章中是用隐式方法调用 DLL 的。下面介绍显式调用。

显式的调用就是指在应用程序中用 LoadLibrary MFC 提供的 AfxLoadLibrary 显式的将自己所做的动态连接库调进来,动态连接库的文件名即是上面两个函数的参数,再用 GetProcAddress() 获取想要引入的函数。

 

在前面的基础上再建一个测试工程,代码如下:

#include "stdafx.h"

#include <iostream>

//#include "Try.h"

using namespace std;

 

typedef int (*Fn_FunType)() ;

 

int main()

{

 

    HINSTANCE hInstance = 0;

    hInstance = LoadLibrary("Try.dll") ;

    if (hInstance)

    {

       Fn_FunType pFun = (Fn_FunType)::GetProcAddress(hInstance ,"fnTry") ;

       if (pFun)

       {

           cout<<pFun()<<endl;

       }

    }

    int n = 10 ;

n = *(int *)::GetProcAddress(hInstance ,"nTry") ;

    cout<<n<<endl;

    system("pause") ;

    return 0 ;

}

 

  上面的代码貌似没上面问题,但是实际上第一个 cout 永远也不会执行,第二个 cout 输出的是 10 。为什么会这样?

先来看看,上面的代码在 DLL 中是个什么模样,用 VS 自带的 dependency walker 可以查看 DLL 文件,该工具在   Microsoft Visual Studio 8/ common7/tools/bin 下。如下图所示:

 



   原来,
"C" 或者 "C++" 函数在编译器内部(编译和链接)通过修饰名识别。修饰名是编译器在编译函数定义或者原型时生成的字符串。有些情况下使用函数的修饰名是必要的,如在模块定义文件里头指定输出 "C++" 重载函数、构造函数、析构函数,又如在汇编代码里调

"C"" "C++" 函数等。关于修饰名后面会讲到。

 

将上面的代码稍微作一下修改:

       Fn_FunType pFun = (Fn_FunType)::GetProcAddress(hInstance ," ?fnTry@@YAHXZ") ;

int n = *(int *)::GetProcAddress(hInstance ,"?nTry@@3HA") ;

Ok, 现在可以正常输出了。

 

 

上面的方法可以导出全局函数和变量,但是却无法导出成员函数和成员变量。如下:

typedef void (CTry::*Fn_FooType)() ;

int main()

{

 

    HINSTANCE hInstance = 0;

    hInstance = LoadLibrary("Try.dll") ;

    CTry *pTry = (CTry *)_alloca(sizeof(CTry));

    Fn_FooType pFun = (Fn_FooType)::GetProcAddress(hInstance, "?print@CTry@@QAEXXZ") ;
(pTry->*pFun)() ;

Return 0 ;
}

 

编译器会提示, FARPROC 类型不能转换成 Fn_FooType 类型。因为 C++ 中,成员函数的指针是个比较特殊的东西。对普通的函数指针来说,可以视为一个地址 , 在需要的时候可以任意转换并直接调用。但对成员函数来说,调用的时候也必须采用特殊的语法。 C++ 专门为成员指针准备了三个运算符 : "::*" 用于指针的声明,而 "->*" ".*" 用来调用指针指向的函数。而且对成员函数指针不能进行类型转换。简单的说,每个成员函数指针都是一个独有的类型,无法转换到任何其它类型。

 

但是在 C++ 中,总是有办法的。 使用 union 类型可以逃避 C++ 的类型转换检测。为了通用,使用模板传入数据类型。

template <classDest, classSrc>

Dest force_cast(Srcsrc)

{

    union

    {

       Dest d;

       Src s;

    } convertor;

 

    convertor.s = src;

    return convertor.d;

}

 

上面的代码变为:

typedef void (CTry::*Fn_FooType)() ;

 

int main()

{

 

    HINSTANCE hInstance = 0;

    hInstance = LoadLibrary("Try.dll") ;

    if (hInstance)

    {

      

       CTry *pTry = (CTry *)_alloca(sizeof(CTry));

 

       FARPROC fp   = ::GetProcAddress(hInstance ,"?print@CTry@@QAEXXZ") ;

       Fn_FooType pFun = force_cast<Fn_FooType>(fp) ;

       if (pFun)

       {

           (pTry->*pFun)() ;

       }

    }

    return 0 ;

}

 

posted on 2009-03-06 00:54 隙中驹 阅读(339) 评论(1)  编辑 收藏 引用

FeedBack:
# re: DLL学习笔记2
2009-03-12 09:30 | guest
图片的路径是“e:/1.bmp”,你本地应该可以显示,但是其他地方都看不到图了。
请修改下图片链接。  回复  更多评论
  

只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   博问   Chat2DB   管理



<2024年12月>
24252627282930
1234567
891011121314
15161718192021
22232425262728
2930311234

常用链接

留言簿(2)

随笔档案(4)

文章分类(1)

文章档案(1)

友情链接

最新随笔

搜索

  •  

最新评论

  • 1. re: DLL学习笔记 3
  • 图片的路径是“e:/1.bmp”,你本地应该可以显示,但是其他地方都看不到图了。
    请修改图片链接。
  • --guest
  • 2. re: DLL学习笔记2
  • 图片的路径是“e:/1.bmp”,你本地应该可以显示,但是其他地方都看不到图了。
    请修改下图片链接。
  • --guest
  • 3. re: DLL学习笔记1
  • 好,我跟着你一起学习
  • --bk

阅读排行榜

评论排行榜