总结的动态链接库

11 篇文章 0 订阅

一、概念:

1、  静态链接库:

应用程序从函数库中得到所的函数的执行代码,然后把招生代码自身的执行文件中,应用程序在运行时,不再需要函数库的支持。

2、  动态链接库:

应用程序的中不包含函数库中的函数的执行代码,编译和连接时只是包含包含它们的参考,运行时再将它们的执行代码加入内存,所以在程序运行时需要函数的支持。

二、编写DLL

建立一个Windows Dynamic-Link Library(VS2005里直接新建Win32项目里面可以选择DLL和静态链接库等等),选择建一个An Empty Dll Project,加入一个CPP文件(VS2005会自己生成,并且DllMain是在DLL被引入项目时会自动执行),

int add(int x,int y)

{

return x+y;

}
// 这是在一个DLL工项中将一个函数导出,记住就是了~ 没有为什么 

extern "C" _declspec (dllexport) int add3(int x,int y,int z)   

{

        return add(x,y)+z;

}

说明:

1.  Add是一个供DLL中其它函数调用的函数, Add3DLL提供给其它应用程序调用的函数。(Add未导出,未加导出标志的会将其视为一个DLL文件里的私有函数)

2.  DLL中含有输出函数时,编译DLL时会生成DLL文件和LIB文件。LIB文件称为DLL的导入库,它是一个特殊的库文件。它不包含执行代码,只是用来提供给链接器关于DLL函数在DLL中的入口信息,从而使得可执行程序 中也不会包含所调用DLL函数的代码 ,只保留对DLL函数的动态链接参考。

3.  导出函数的两种方法:

a.  使用微软专用的_declspec (dllexport)(如上面的例子)

cpp文件在编译为OBJ文件时要对函数进行重新命名,C语言会把函数name重新命名为_name,C++会重新命名为_name@@decoration

extern "C"表示用C语言的格式将函数重命名。

b.  使用模块定义文件:

模块定义文件即DEF文件,是包含一个或多个模块定义语句的文本文件,用来描述动态链接库的各种属性。

一个最小的·DEF文件包括以下模块定义语句:

l         第一条语句必须是LIBRARY语句,用来说明动态链接库的名字。

l         EXPORTS语句之后列出动态链接库要输出的函数。用户可以在每个要输出的函数指定一个输出序列号,这只要在对应的函数名之后 加上@符号心腹一个数字即可。注意:这个数字应该是从1到输出函数总数之间的没有重复的数字。指定函数的序列号后,动态加载该函数时,就会通过序列号而不是函数名来检索该函数,因此处理速度将会加快,战胜的内存会减少,从而增加动态链接时的效率。

将上面的例子改为用DEF文件输出:

int add(int x,int y)

{

     return x+y;

}

extern "C"  int add3(int x,int y,int z)

{

        return add(x,y)+z;

}

在工程中加入一文本文件,其名字为dll.def,加入以下语句:
LIBRARY       dll
EXPORTS
add3    @    1

三、访问动态链接库:

新建一个基于对话框的MFC工程,

1.  静态调用:

通过编译器提供给应用程序关于DLL的名称,以及DLL函数的链接参考。这种方式不需要在程序中用代码将DLL加载到内存。

a.  DLLLIB文件拷贝到工程目录下,最好在工程的DEBUG目录下也拷贝一份。

b.  ProjectàSettingsàLink中的Object/Library Modules:填入LIB文件的名字。本例中是dll.lib(是lib而不是dll,因为只有在lib中才有dll的入口信息)

c.  在对话框中加入一个按钮控件,添加它的响应函数OnButton1(),在其中加入调用代码:

       CString str;

       str.Format("%d",add3(3,4,5));

       MessageBox(str);

d.  加入函数声明:

l         _declspec (dllexport)导出函数的DLL
//这样引入DLL中的函数以后就可以把所引入的函数如add3当作已有函数来使用了

extern "C"_declspec (dllimport) int add3(int x,int y,int z);

l         DEF文件导出函数的DLL

int add3(int x,int y,int z);

2.  动态调用:

在程序中用语句显式地加载DLL,编译器不需要知道任何关于DLL的信息。

a.  DLL文件拷贝到工程目录下,最好在工程的DEBUG目录下也拷贝一份。

b.  在对话框中加入一个按钮控件,添加它的响应函数OnButton1(),在其中加入调用代码:

CString str;

typedef int (*PADD3)(int x,int y,int z);     

                //定义一种新的数据类型指向函数的指针

PADD3 add3;

HINSTANCE hDll=LoadLibrary("dll");

                //将动态链接库加载到内存

add3=(PADD3)GetProcAddress(hDll,"add3");

               //得到DLL中指定函数的指针

str.Format("%d",add3(3,4,5));

MessageBox(str);

FreeLibrary(hDll);//释放应用程序对DLL的控制权

              说明:如果DLLDEF文件导出函数时为其指定了序列号,比如1,则

                            add3=(PADD3)GetProcAddress(hDll,"add3");

                       可以改为:

                add3=(PADD3)GetProcAddress(hDll, MAKEINTRESOURCE(1));

四、其它:

1.  Windows如何定位DLL

不论是静态调用还是动态调用DLLWindows会按以下顺序寻找DLL

a.  当前路径

b.  Windowssystem路径。用API函数GetSystemDirectory可以得到;

c.  Windows路径。用API函数GetWindowsDirectory可以得到;

d.  环境变量PATH所指定 的路径。

2.  DLL也可以有一个入口函数DllMain(),当然也可以没有。如果有,DllMain会在进程加载、进程缷载、线程加载和线程缷载DLL时自动调用。

3.  为了用户使用方便,应该将导出函数的声明放在一个头文件中。可以使用预处理命令简化更换__declspec(dllexport)__declspec(dllimport)的操作。这样,DLL和应用程序 可以使用相同的头文件:

#ifdef DLL_EXPORTS

#define DLL_API __declspec(dllexport)

#else

#define DLL_API __declspec(dllimport)

#endif

 

导出及导入DLL中的类:

在DLL工程是导出:
    class _declspec (dllexport)  Configure
    {
    public:
        static string get_value_with_key( const string &key, const string &xml_path = "configure.xml" )
        {  ...    }
        static int update( const string &key, const string &new_value, const string &xml_path = "configure.xml" )
        {  ...    }
        static int add( const string &key, const string &value, const string &xml_path = "configure.xml" )
        {  ...    }
        static int earse( const string &key, const string &xml_path = "configure.xml" )
        {  ...    }
        static int initialize( const string &xml_path = "configure.xml" )
        {  ...    }
    };        // end of Configure

 在其它工程中导入:
    class _declspec (dllimport) Configure
    {
    public:
        static int initialize( const string &xml_path = "configure.xml" );
        static int earse( const string &key, const string &xml_path = "configure.xml" );
        static int add( const string &key, const string &value, const string &xml_path = "configure.xml" );
        static int update( const string &key, const string &new_value, const string &xml_path = "configure.xml" );
        static string get_value_with_key( const string &key, const string &xml_path = "configure.xml" );
    };
似乎是必须知道类的原型,在需要引用的工程中进行声明,而定义的工作就交给DLL中去做




 非常重要的一点 -- 需要区分.NET中的DLL调用和非.NET中的DLL调用

 如果在.NET的CLR中需要调用C/C++编写的DLL,应该直接用[DllImport("PictureMatchingDll.dll")] ,甚至都不需要在附加依赖项中指明这个DLL的LIB文件。如果DLL不在当前目录下,则应当在[DllImport(" ")]中给出完整的DLL路径。

 以上的讨论都是指在纯C/C++中去调用另外的用C/C++编写的DLL文件。


      //这应该是在纯C++中调用的方法,适合控制台程序

      //extern "C"_declspec (dllimport) 

      //int FillMatchingResult( vector<PicMatchingResult> &result, const string &pic, const vector<string> &path, const MatchingType &type, const PicMatchingRect &rect);


      //这才是正确的在CLR中调用的方法,不要搞混淆了。

      [DllImport("PictureMatchingDll.dll")] 

      int FillMatchingResult( vector<PicMatchingResult> &result, const string &pic, const vector<string> &path, const MatchingType &type, const PicMatchingRect &rect);

       这个错误整整调了我半天的时间,应该深刻的记住。在CLR中,虽然也可以用前一种方法实现调用,但是会将.NET程序的多线程属性强制转换为 MTAThread 。由于COM组件在MTAThread中会产生不可预期的错误,所以系统自带的文件夹选择框会不显示目录树、调试模式下文件选择会报错等一系列的错误。都是由于COM组件只能在 STAThread 模式下运行有关。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值