您的位置:首页 > 其它

VS2008创建和调用DLL动态链接库

2014-01-22 13:00 459 查看


VS2008创建和调用DLL(动态链接库)文件,error LNK2019: 无法解析的外部符号,该符号在函数 _wmain 中被引用,创建DLL时,不能生成*.lib文件

       本文能给您解决的问题:

                 1,vs2008下创建dll动态链接库,以及其他程序调用此动态链接库

                 2,error LNK2019: 无法解析的外部符号 _add,该符号在函数 _wmain 中被引用

                 3,在创建DLL的过程中,不能生成*.lib文件,此处的.lib文件为动态连接库的导入库,与静态链接库不同。

                       目前以lib后缀的库有两种,一种为静态链接库(Static Libary,以下简称“静态库”),另一种为动态连接库(DLL,以下简称“动态库”)的导入库(Import Libary,以下简称“导入库”)。

                 4,error C2664:“LoadLibraryW”: 不能将参数
1 从“const char *”转换为“LPCWSTR,

一、DLL的创建

1.      新建testdll工程

    选择“文件”à“新建”à“项目”,项目类型选择Visual C++的Win32,右侧选择“Win32控制台应用程序”,输入项目名称,点击确定,如下图:

 



 

    点击确定后弹出应用程序向导:应用程序类型选择“DLL”,附加选项勾选“空项目”,替他保持不变。如下图所示:



 

2.      添加testdll.h文件

    经过第一步之后,VS2008已经为我们创建好了一个空的项目,如下图所示,右键点击“头文件”,依次选择“添加”,“新建项”,在弹出的对话框选择“头文件(.h)”,输入名称“testdll.h”,最后点击“添加”,完成添加testdll.h操作。



 



 

在添加的testdll.h文件添加如下代码:

    #ifndef LIB_H

    #define LIB_H

    extern "C" int __declspec(dllexport)add(int x, int y);

    #endif

 

  注意:

  1. extern "C" 表示其他外部调用该方法时,是按照C语言的形式编译的。

  2. 此处的 “__declspec(dllexport)”表示为dll导出函数,不添加此关键字的话,说明该方法不能被外部调用,只能在dll内部使用。

   如果不添加“__declspec(dllexport)”,在生成dll后,外部程序调用后,会出现“error LNK2019: 无法解析的外部符号 _add,该符号在函数 _wmain 中被引用”错误。
 

3.      添加testdll.cpp文件

 

    按照第2部操作步骤,继续添加testdll.cpp文件,如下图所示:



 

    在生成的新的testdll.cpp中添加如下代码:

    #include "testdll.h"

    int add(int x, int y)

    {

       return x + y;

    }

 

4.      生成DLL文件

    经过以上3步,DLL所需文件已经具备,点击Ctrl+Alt+F7,编译此工程。如图所示:



 

    此时,打开工程所在路径,查看生成的DLL文件。我这里是:E:\vs2008pro\testdll\Debug;可以查看到生成的5个文件,如图所示:



 

    需要说明一点的就是:如果在testdll.cpp中注释掉#include"testdll.h"一行时,DLL工程也能编译成功,只不过最后生成的文件是3个,如下图所示,少了lib和exp文件。这里可能是MS做了一些优化,有时其他情况也会导致不会生成lib文件,比如你的dll函数中带有指针等等;解决的办法是:

    1.在工程上右键 -> 添加 -> 新建项 -> 选"模块定义文件(.def)" -> 随便输入个名字 -> 添加

    2.项目属性->链接器->输入->模块定义文件->在里面添上刚才的*def即可解决问题



 

二、DLL的调用

    生成了DLL文件,那么下一步就是如何在其他的工程中进行调用。

    首先,DLL的调用分为“动态调用”和“静态调用”两种方式,关于静态调用以及动态调用的优缺点本文不再赘述。此处,首先展示了如何进行动态调用,在本章的第四小节给出了静态调用的方法。

1.      新建calldll工程

    与上一节新建工程类似,不过这次需要注意的是“解决方案”选择“添入解决方案”,如图所示:



 

    在弹出的“应用程序向导”页面,所有选项保持默认,直接点击“完成”。



 

2.    修改calldll.cpp文件

    修改calldll.cpp代码内容如下:

#include "stdafx.h"

#include <windows.h>

#include <iostream>

using namespace std;

typedef int(*lpAddFun)(int, int); //宏定义函数指针类型

int _tmain(int argc, _TCHAR* argv[])

{

 HINSTANCE hDll; //DLL句柄

 lpAddFun addFun; //函数指针

 hDll = LoadLibrary(L"..\\Debug\\testdll.dll");

 if (hDll != NULL)

 {

  addFun = (lpAddFun)GetProcAddress(hDll, "add");

  if (addFun != NULL)

  {

   int result = addFun(2, 3);

   cout<<"result is "<<result<<endl;

  }

  FreeLibrary(hDll);

 }

 else

 {

 cout<<"hDll is null"<<endl;

 }

 return 0;

}

    需要说明的是:

    hDll=LoadLibrary(L"..\\Debug\\testdll.dll");LoadLibrary参数中的大写L必须添加,如果不添加会报错:error
C2664:“LoadLibraryW”: 不能将参数 1 从“const char *”转换为“LPCWSTR,

    原因是:工程只支持UNICODE字符。字符串前加L,可以将字符串转换成unicode的字符串。

    另外还有一种解决办法:LoadLibrary(_T("..\\Debug\\testdll.dll")),具体参考百度。

3.      运行

    在运行之前,还要进行一个非常重要的操作,就是将calldll工程“设为启动项目”

    如图所示:



 

    最后,点击Ctrl+Alt+F7,编译此工程。用Ctrl+F5运行!





 

 4.      静态调用

    将testdll.dll和testdll.lib两个文件拷贝到E:\vs2008pro\testdll\calldll目录下,修改calldll.cpp代码如下:

    #include "stdafx.h"

    #include <windows.h>

    #include <iostream>

    using namespace std;

    #pragma comment(lib,"testdll.lib")

    extern "C" __declspec(dllimport) int add(int x,int y);  //此处也可不加 __declspec(dllimport),加上之后效率会高

    int _tmain(int argc, _TCHAR* argv[])

    {

         int result = add(2,3);

         cout<<"results is "<< result <<endl;

    }

    用Ctrl+Alt+F7编译,用Ctrl+F5运行!

        1. 在 extern "C" __declspec(dllimport) int add(int x,int y)中,如果不加 __declspec(dllimport),则因为add在DLL中,连接器就无法得到add的直接地址;在32位系统下,连接器将生成一个thunk包含add的调用地址,这样生成的代码比较大而且速度慢;使用__declspec(dllimport) 则直接告诉连接器直接调用DLL中add的地址,而不使用thunk,这样的代码比使用thunk代码小而且要快。

        2.dll的静态调用,还有一种方式,就是:包含dll的头文件,导入*.lib库,然后直接调用dll库中的方式。此方法与静态链接库的调用方式相同。

        #include "stdafx.h"

        #include <windows.h>

        #include <iostream>

        #include "testdll.h"   //为了简单起见,将头文件也拷贝到当前工程目录,然后直接引用。

         using namespace std;

        #pragma comment(lib,"testdll.lib")

         int _tmain(int argc, _TCHAR* argv[])

        {

             int result = add(2,3);

             cout<<"results is "<< result <<endl;

        }

        3.动态链接方法:LoadLibrary()/GetProcessAddress()和FreeLibrary(),使用这种方式的程序并不在一开始就完成动态链接,而是直到真正调用动态库代码时,载入程序才计算(被调用的那部分)动态代码的逻辑地址,然后等到某个时候,程序又需要调用另外某块动态代码时,载入程序又去计算这部分代码的逻辑地址,所以,这种方式使程序初始化时间较短,但运行期间的性能比不上静态链接的程序。

三、总结

    需要注意的地方有:

    1.如果不添加“__declspec(dllexport)”,在生成dll后,外部程序调用后,会出现“error LNK2019: 无法解析的外部符号 _add,该符号在函数 _wmain 中被引用”错误。

    2. 需要说明一点的就是:如果在testdll.cpp中注释掉#include"testdll.h"一行时,DLL工程也能编译成功,只不过最后生成的文件是3个,少了lib和exp文件。有时其他情况也会导致不会生成lib文件,解决的办法是:

1.在工程上右键 -> 添加 -> 新建项 -> 选"模块定义文件(.def)" -> 随便输入个名字 -> 添加

2.项目属性->链接器->输入->模块定义文件->在里面添上刚才的*def即可解决问题

    3.LoadLibrary参数中的大写L必须添加,如果不添加会报错:error C2664:“LoadLibraryW”:
不能将参数 1 从“const char *”转换为“LPCWSTR,

  原因是:工程只支持UNICODE字符。字符串前加L,可以将字符串转换成unicode的字符串。

  另外还有一种解决办法:LoadLibrary(_T("..\\Debug\\testdll.dll")),具体参考百度。

      4.动态链接方法:LoadLibrary()/GetProcessAddress()和FreeLibrary(),使用这种方式的程序并不在一开始就完成动态链接,而是直到真正调用动态库代码时,载入程序才计算(被调用的那部分)动态代码的逻辑地址,然后等到某个时候,程序又需要调用另外某块动态代码时,载入程序又去计算这部分代码的逻辑地址,所以,这种方式使程序初始化时间较短,但运行期间的性能比不上静态链接的程序。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: