如何手工编写和调用标准Cjs调用dll动态链接库库

C语言的DLL编写与调用_百度文库
两大类热门资源免费畅读
续费一年阅读会员,立省24元!
C语言的DLL编写与调用
上传于||文档简介
&&C​语​言​的​D​L​L​编​写​与​调​用
阅读已结束,如果下载本文需要使用0下载券
想免费下载更多文档?
定制HR最喜欢的简历
下载文档到电脑,查找使用更方便
还剩6页未读,继续阅读
定制HR最喜欢的简历
你可能喜欢& &静态库和动态库的使用包括两个方面,1是使用已有的库(调用过程),2是编写一个库供别人使用(创建过程)。这里不讲述过多的原理,只说明如何编写,以及不正确编写时会遇见的问题。
& //注:本文先从简单到复杂,动态库的部分先说明了静态链接方式,比较简单,若想看动态链接过程会遇到的问题可直接跳过。
& & & & & 后面说明动态链接方式有关extern &C” 、名字改变、 __stdcall 的影响的问题。
1)创建过程
& &在VS环境下创建一个 “Win32 Static Libarary” 工程StaticLib,添加头文件lib.h和源文件lib.cpp
int&add(int&a,int&b);
int&add(int&a,int&b)
&&&&return&a+b;
  Build之后会发现Debug下生成了StaticLib.lib &静态库文件。 & 将lib.h和StaicLib.lib给别人,别人就可以使用库中的函数add了。
2)调用过程
& & 新建一个简单的控制台工程,只有一个StaticLibCall.cpp 。 将lib.h和StaticLib.lib放在同目录下。
comment(lib,&StaticLib.lib&)
&iostream&
using&namespace&
int&main()
= add(2,3);
&&&&cout&&t&&
&&&&getchar();
  编译链接运行成功。 & &
& & &此处若在add(2,3)设置断点,调试态 F11进入函数可以跳入到StaticLib.cpp中进行执行。可以知道静态库在调用过程中是会和源文件一起编译链接的。
& & &其中 &#pragma comment(lib,&StaticLib.lib&) 是用来说明静态库调用,也可以在VS界面上设置:依次选择tools、options、directories、library files添加。
  标准Turbo C2.0中的C库函数(我们用来的scanf、printf、memcpy、strcpy等)就来自这种静态库。F11同样可以进入对应的**.c文件中。
& &动态库比静态库的创建和调用都复杂。因为动态库中的函数分为两种,1种为内部函数,只供库内部使用。 2种为导出函数,只有声明为导出函数,才可以给别人使用。
& &创建时声明导出方式有2种: &1.__declspec(dllexport) 方式
& & & & & & & & & & & & & & & & & & & & 2.DEF文件方式
& 使用时链接动态库方式也有2种: &1.静态链接方式 :同静态库的调用方式 &&#pragma comment(lib,&***.lib&)
& & & & & & & & & & & & & & & & & & & & & & &2.动态链接方式:使用Win32系列函数:LoadLibrary(...) &GetProcAddress(...) &FreddLibrary(...)
& 所以,组合起来有4种方式完成动态库的 ”创建和使用“ 的过程。 由于不同交叉的方式中注意的问题不同,所以分别说明。
1)__declspec(dllexport) 方式导出方式,静态方式链接
&&&A.创建:&&在VS环境下创建一个 “Win32 DLL ” 工程AddDll,添加头文件dll.h和源文件dll.cpp
__declspec(dllexport)&int&&add(int&x,&int&y);
__declspec(dllexport)&int&&add(int&x,&int&y)
&&&&return&x
  Build之后发现,Debug中生成了AddDll.lib和AddDll.dll文件。 &将.h .lib .dll提供给别人,别人就可以使用动态库中的add函数了。
&B. 静态方式调用:
& & 新建一个控制台工程,只有一个AddDllCall.cpp 文件。 并且将 dll.h放在同目录下,AddDll.lib &AddDll.dll 放在该工程的Debug下。
&注意:因为动态库是在运行时才调用,所以必须放在运行时的目录下,否则会找不到dll库。
&iostream&
using&namespace&std
comment(lib,&AddDll.lib&)
int&_tmain(int&argc,
_TCHAR* argv[])
= add(2,3);
&&&&cout&&t&&
&&&&getchar();
&&&&return&0;
  运行成功。
& 注:有些人说,导出方式为__declspec(dllexport)时,调用时的函数声明需要为__declspec(dllimport) add(int a,int b);这里测验发现并没有这个问题
& 调用时#include ”dll.h& 只有一句话 int add(int a,int b);正常的函数声明。
&2)DEF文件导出函数,静态链接方式调用
&&&A.创建:&&在VS环境下创建一个 “Win32 DLL ” 工程AddDll,添加头文件dll.h和源文件dll.cpp ,并添加DEF文件:dll.def 文件
int&&add(int&x,&int&y);
int&&add(int&x,&int&y)
&&&&return&x
  编译链接生成AddDll.lib&AddDll.dll . 将*.h *.lib *.dll提供给别人。
B. 静态方式调用:
& & 新建一个控制台工程,只有一个AddDllCall.cpp 文件。 并且将 dll.h,AddDll.lib 放在同目录下。 &AddDll.dll 放在该工程的Debug下。
&注意:此处与上处不同,这里的&AddDll.lib 必须和.h一起放在AddDllCall.cpp的同目录下,否则会找不到lib.或者显示指定路径。
& 代码同上,只是放了不同的这三个文件。
3)__declspec(dllexport) 方式导出函数,动态链接方式使用动态库
A.创建:&同1)中一样&在VS环境下创建一个 “Win32 DLL ” 工程AddDll,添加头文件dll.h和源文件dll.cpp 。代码也一样
B.动态链接方式:
&&新建一个控制台工程,只有一个AddDllCall.cpp 文件。 同样将 lib 和 dll放在Debug下。代码不多解释,可以参看文章最后给出的链接。
&iostream&
using&namespace&
typedef&int(*lpAddFun)(int,&int);&
warning(disable:4996)
int&_tmain(int&argc,
_TCHAR* argv[])
&&&&HINSTANCE&hD&
&&&&lpAddFun
= LoadLibrary(TEXT(&..//Debug//AddDll.dll&));
&&&&if&(hDll
&&&&&&&&addFun
= (lpAddFun)GetProcAddress(hDll,&&add&);
&&&&&&&&if&(addFun
&&&&&&&&&&&&int&result
= addFun(2, 3);
&&&&&&&&&&&&cout&&
&&&&&&&&FreeLibrary(hDll);
&&&&getchar();
&&&&return&0;
  运行,发现没反应,没有输出想要的5。这里强调,调用方式没有任何问题,声明函数,再获得函数地址,用指针调用,没有任何问题。
& & &那问题出在哪里了呢???
& 调试状态发现addFun句柄返回了null,也就是 GetProcAddress(hDll, &add&); 没有正确的函数。这是为什么呢?通过函数名“add&查找函数,有问题吗?
& & &了解过这块的人会发现,很多动态库的导出函数 都有extern &C& __decl~~~ 这里extern &C& 是什么意思呢?是不是这个的原因呢?
& &&&没错,问题就出在extern &C&上。 &因为没有加extern &C&之前,动态库里的add函数根本不叫”add&.
&回到创建的代码,使用工具Depends. &( 该工具找不到可以在命令行 cmd 下 输入Depend.exe 就弹出来了。)
&发现导出的函数不叫&add&,而是一大串 ?add@YAHHH@Z 。这是因为C语言和C++编译方式不同造成的。
&所以GetProcAddress用 &add&去查找时,根本找不到函数。
将创建的代码其他都不变,就把add函数的声明和定义前,加上extern &C& 。Build之后再看&
导出的函数名变成了 “add&. 使用这个lib和dll会发现GetProcAddress 正常执行了,也输出了5.
& & & 所以在用__declspec(dllexport)方式导出函数时,一般都添加extern ”C&. 且给对方提供的头文件中函数的声明为import&
externa & C& __declspec(import)
int add(int a,int b);
  注意:extern&&C&只解决了C和C++语方之间调用的问题,它只能用于导出全局函数这种情况而不能导出一个类的成员函数。
另外如果导出函数的调用约定发生改变,即使使用了extern&&C&,编译后的函数名还是会发生改编。比如我们加入_stdcall关键字说明调用约定为C调用约定.
将创建当中的函数声明和定义修改为如下:
extern&&C&&&__declspec(dllexport)&&int&__stdcall
add(int&x,&int&y)
&&&&return&x
  查看发现,函数名还是改变了。
& & &关于__stdcall 和 __cdecl可以参看编译原理的东西。windows下多用__stdcall的方式,CALLBACK,WINAPI看宏定义发现都是__stdcall的重定义。
& &而C/c++语言默认__cdecl的方式。
& &调用方式不同不仅影响函数名的变化,最主要影响函数栈和回收等问题。接下的DEF文件中我们也会看到。
4)DEF文件方式导出函数,动态方式调用
&A.创建 & 同2)中,提供 .h .lib .dll&
& &B.使用 &同3)中使用过程&
使用Depend工具查看创建的文件,发现,add依然叫add. 所以调用时用add函数名可以找到,正确执行。
但是当加上__stdcall调用方式后,发现,add依然叫add 。&
& 但是提供给调用函数调用时发现,编译链接通过,运行的时错误如下:
& &调试发现,add函数正确执行了,并返回了5 ,但是main函数返回的时候,出现了如上运行错误。
& 这是因为,向上面所说的,__stdcall等调用方式不仅影响函数名字,更重要的是影响 函数栈的调用方式和回收方式。具体参考,编译原理 的&运行时刻环境&一章。
因此,两种导出方式都无法解决__stdcall调用方式的问题。DLL文件与调用方必须是同一种方式,否则会函数不匹配 或者 函数栈出错。
总结: 综上对比,可以发现,静态调用方式很简单,不用考虑很多。因为静态方式是由编译器完成了一起编译,因此使用就像使用内部函数一样。
& & & &动态方式是用系统APII来加载、卸载DLL以及获取DLL中导出函数的地址,由程序决定加载和释放的位置。
& & & &动态调用方式必须使用LoadLibrary GetProcAddress函数用指针调用,而不能直接使用 add(2,3).
& & & DEF导出方式更简单,不用写太多,考虑太多。
另外,还有导出变量和导出类的内容。此处不详说。
只说明,导出变量需注意,使用的时候,得到的是地址,使用内容必须 cout&& *(int*) g_value &&
& & & & &导出类的时候,两边都需要有类的定义,创建方用__declspec(export) &调用方用 __declspec(import)&&.
参考:&http://blog.csdn.net/friday5pm/article/details/1532226
& & & & &http://blog.csdn.net/anye3000/article/details/7481481
参考知识库
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
访问:1072次
排名:千里之外
原创:15篇
转载:11篇
(1)(4)(17)(3)(2)动态链接库中使用c++类的方法 - 博客频道 - CSDN.NET
专注于Linux智能设备与云开发
以前的关于动态链接库的写法多是直接采用extern 'C'的方式来实现,但是如果采用类来编写就会出现问题了,因为类在编译时是不能确定入口点位置的。这样就产生了问题。怎么来使用呢?前两个方法是在linux下直接使用普通函数调用的方法来实现的。后边的是在vc++中使用宏来对入口点动态调整输入输出__declspec(dllimport)和__declspec(dllexport)
& 方法1: & 把类作为参数传入 & 接口函数中去: &
& //----------------------- & myclass.h & 文件----------------- & &
& #ifndef & MYCLASS_H &
& #define & MYCLASS_H &
& class & myclass &
& & & public: &
& & & & & myclass(){} &
& & & & & ~myclass() & {} &
& & & public: &
& & & & & int & sum(int & a,int & b); &
& & & private: &
& & & & & int & _a; &
& & & & & int & _b; &
& #ifdef & & SHARED &
& int & & & & & (*sum)(myclass & *my,int & a,int & b); &
& int & & & & & sum(myclass & *my,int & a,int & b); &
& #endif &
& #endif & // & MYCLASS_H &
& //----------------------- & myclass.cpp & 文件----------------- & &
& #include & &myclass.h& & &
& int & myclass::sum(int & a,int & b) &
& & & int & c & = & a & + & &
& & & return & &
& int & & sum(myclass & *my,int & a,int & b) &
& & int & c & = & my-&sum(a,b); &
& & return & &
& //----------------------- & my.cpp & 测试文件 & 文件----------------- & &
& #include & &iostream& &
& #include &dlfcn.h& &
& #define & SOFILE &./my.so& &
& #define SHARED &
& #include &myclass.h& &
& using & namespace & &
& int & main(int & argc, & char & *argv[]) &
& & & myclass & &
& & & void & * &
& & & char & * &
& & cout&&&动态链接库应用示范&&& &
& & dp=dlopen(SOFILE,RTLD_LAZY); /* & 打开动态链接库 & */ &
& & if & (dp==0) /* & 若打开失败则退出 & */ &
& & & cout&&&若打开失败则退出 & & dlopen&&& & &
& & & return(1); &
& & sum=(int(*)(myclass & *my,int & a,int & b))dlsym(dp,&sum&); /* & 定位求和函数 & */ &
& & error=dlerror(); /* & 检测错误 & */ &
& & if & (error) /* & 若出错则退出 & */ &
& & & cout&&&若出错则退出 & :定位求和函数 & sum&&& &
& & & return(1); &
& & & int & a & = & 10; &
& & & int & b & = & 100; &
& & & int & c & = & sum(&my,a,b); /* & 调用此共享函数 & */ &
& & & cout&&&c & = & &&& &
& & & system(&PAUSE&); &
& & & return & 0; &
& g++ & myclass.h & myclass.cpp & -shared & -o & my.so&&&&&&&&
& g++ & my.cpp & -rdynamic & -lds & -o & my.exe&&&&&&&&&&&&&&&
& ********************************************************************** &
& 方法2: & 声名一个类的全局变量 & 然后在接口中使用这个全局变量: &
& //----------------------- & myclass.h & 文件----------------- & &
& #ifndef & MYCLASS_H &
& #define & MYCLASS_H &
& & * & No & description &
& class & myclass &
& & & public: &
& & & & & myclass(){} &
& & & & & ~myclass() & {} &
& & & public: &
& & & & & int & sum(int & a,int & b); &
& & & private: &
& & & & & int & _a; &
& & & & & int & _b; &
& //声名一个类的全局变量 & 然后在接口中使用这个全局变量: &
& extern & & myclass & &
& #ifdef & & SHARED &
& int & & & & & (*sum)(int & a,int & b); &
& int & & & & & sum(int & a,int & b); &
& #endif &
& #endif & // & MYCLASS_H &
& //----------------------- & myclass.cpp & 文件----------------- & &
& #include & &myclass.h& & &
& int & myclass::sum(int & a,int & b) &
& & & int & c & = & a & + & &
& & & return & &
& int & & sum(int & a,int & b) &
& & int & c & = & my.sum(a,b); &
& & return & &
& //----------------------- & my.cpp & 测试文件 & 文件----------------- & &
& #include & &iostream& &
& #include &dlfcn.h& &
& #define & SOFILE &./my.so& &
& #define SHARED &
& #include &myclass.h& &
& using & namespace & &
& int & main(int & argc, & char & *argv[]) &
& & & //myclass & &
& & & void & * &
& & & char & * &
& & cout&&&动态链接库应用示范&&& &
& & dp=dlopen(SOFILE,RTLD_LAZY); /* & 打开动态链接库 & */ &
& & if & (dp==0) /* & 若打开失败则退出 & */ &
& & & cout&&&若打开失败则退出 & & dlopen&&& & &
& & & return(1); &
& & sum=(int(*)(int & a,int & b))dlsym(dp,&sum&); /* & 定位求和函数 & */ &
& & error=dlerror(); /* & 检测错误 & */ &
& & if & (error) /* & 若出错则退出 & */ &
& & & cout&&&若出错则退出 & :定位求和函数 & sum&&& &
& & & return(1); &
& & & int & a & = & 10; &
& & & int & b & = & 100; &
& & & int & c & = & sum(a,b); /* & 调用此共享函数 & */ &
& & & cout&&&c & = & &&& &
& & & system(&PAUSE&); &
& & & return & 0; &
& g++ & myclass.h & myclass.cpp & -shared & -o & my.so&
& g++ & my.cpp & -rdynamic & -lds & -o & my.exe&
*************************************************************
方法三:&&&
在 DLL 的输出头文件中用上:
#ifdef MYLIBAPI
#define MYLIBAPI __declspec(dllimport)
在 DLL 的 cpp 文件中,引用头文件的前面加上:
#define MYLIBAPI __declspec(dllexport)
注意,这时候 extern “C” 已经没有了。因为我们要输出的就是 C++ 类。
同时,在类的头文件中,类的定于前面写上:
class MYLIBAPI classname {
这就 OK 啦。
*************************************************&&&&&&&
#include &iostream&
class DLLClass{
& & & & public:
& & & & & & & & // exported member function
& & & & & & & & __declspec(dllexport) void functionA(void)
& & & & & & & & {
& & & & & & & & & & & & cout && &In Function A of the exported function& &&
& & & & & & & & & & & &
& & & & & & & & }
// exported class
class __declspec(dllexport) ExportDLLClass{
& & & & public:
& & & & & & & & void functionB(void)
& & & & & & & & {
& & & & & & & & & & & & cout && &In Function B of the exported class& &&
& & & & & & & & & & & &
& & & & & & & & }
// exported instance of the DLLClass
__declspec(dllexport) DLLC
保存成TestDLL.cpp然后进行编译。呵呵,因为是用Editplus写的代码,所以要手动编译哦^^
我用的是VS 2005的CL编译接口CL.exe。在Cmd下:CL/c TestDLL.cpp
此时就生成了TestDLL.obj,然后进行链接:link TestDLL.obj /DLL
此时将生成TestDLL.dll,TestDll.exp,TestDll.lib三个文件
工作暂告一个段落。。。。
然后开始写调用TestDLL.dll的CallDLL.exe文件:
&class DLLClass{
& & & & public:
& & & & & & & & // imported member function
& && &&&__declspec(dllimport) void functionA(void);
// imported class
class __declspec(dllimport) ExportDLLClass{
& & & & public:
& & & & & & & & void functionB(void);
// imported instance of the DLLClass
__declspec(dllimport) DLLC
int main(void)
& & & & ExportDLLClass TestC
& && && && && && && &test.functionA();
& && && && && && && &TestClass.functionB();
& & & & return 0;
排名:第1458名
Linux开发微信
linux_developer
Linux开发群
(78)(2)(55)(13)(8)(2)(1)(2)(2)(1)(4)(4)(5)(4)(12)(1)(2)(23)(2)如何手工编写和调用标准C动态链接库
如何手工编写和调用标准C动态链接库(windows & dll)
vc6.0cl.exelink.exe
1.本例介绍在命令行(Console)环境下制作dll的方法
2.读者动手前,请确保在windows中安装有编译、链接工具和必要的函数库文件。
3.本例使用标准C语言实现.
4.本例中使用路径均为机器上的绝对路径,读者需根据实际情况调整。
& Microsoft的编译器cl.exe
& MIcrosoft链接器link.exe
dll制作步骤:
1.编写dll函数实现源代码adddll.c
int & addtest(int a, int b)
Return( a + b );
2.编写dll函数输出定义文件adddll.def.
LIBRARY & adddll
addtest & @1
3.编译dll源码,生成dll,lib文件.
3.1 & 新建命令行窗口
3.2 & 设置PATH & |
& INCLUDE & | &
LIB & 3个环境变量.
SET & PATH=C:\Program Files\Microsoft
Visual Studio\VC98\B%PATH%
SET INCLUDE=C:\Program Files\Microsoft Visual
Studio\VC98\%INCLUDE%
SET & LIB=C:\Program Files\Microsoft
Visual Studio\VC98\%LIB%
也可以在系统的环境变量中去添加。
编译adddll.c
cd & D:\dlltest &
(adddll.c和adddll.def所在目录) cl & /c &
adddll.c &
链接adddll.obj,生成adddll.dll, adddll.lib两个文件.
link & /def: adddll.def &
/dll & adddll.obj
4.测试dll函数.
4.1 & 编写测试代码
extern int addtest(char* &
int & main(int &
argc,char* argv[])
&&&&&&&&&&
int a, b,c;
&&&&&&&&&&
&&&&&&&&&&
&&&&&&&&&&
c = addtest( a, b );
printf(“ %a + %b = %c\n”, c );
return & 0;
编译测试代码calladd.c
4.3 & 链接calladd.obj和 &adddll.lib,生成可执行文件calldll.exe
link & calladd.obj &
adddll.lib
运行calladd.exe,屏幕输出:
10 + 20 = 30
至此,一个dll构造完毕.
下面是一点补充:
如果要在c++下,或者win32 mfc下使用标准c写的dll,必须把上面的声明 extern int say_hello(char* name);
改成:extern & "C "
& int & say_hello(char*
已投稿到:
以上网友发言只代表其个人观点,不代表新浪网的观点或立场。}

我要回帖

更多关于 vs2015编写动态链接库 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信