Windows 动态链接库编程  

1、介绍

Windows操作系统是应用最关的操作系统,因此动态链接库也为程序员所熟悉,即使对于普通的使用者来说,很多时候也会碰到.dll结尾的文件,这就是动态链接库文件。Windows下的动态链接库可以通过参考头文件和.lib库文件进行编译,从而使得动态链接库隐式地被使用;也可以使用LoadLibrary、GetProcAddress等函数来显式调用动态链接库。

2、语法、导入导出

在Windows编程中,对于要使用或被使用的函数或者变量,需要使用 __declspec 关键字来声明,以告诉编译器该变量或函数不是普通的变量或函数,而是一个动态链接库的接口属性。

如果定义一个要被其他代码使用的函数,可以写成:
__declspec( dllexport ) int add(int a, int b);

如果在该代码中,打算使用另外一个程序中的变量,则可以写成:
__declspec( dllimport ) char *name;

动态链接库通常包含一系列供其他程序使用函数,因此 declspec( dllexport ) 语法形式最为常用。如果动态库需要其他程序中的定义的全局变量,则需要在其他程序中使用导出该变量,在动态链接库中则需要使用 extern declspec( dllexport ) 将该变量声明为外部变量以便使用。

3、链接方式

可以以下列两种方式之一链接到(或加载)DLL:

隐式链接
显式链接

隐式链接有时称为静态加载或加载时动态链接。显式链接有时称为动态加载或运行时动态链接。在隐式链接下,使用 DLL 的可执行文件链接到该 DLL 的创建者所提供的导入库(.lib 文件)。使用 DLL 的可执行文件加载时,操作系统加载此 DLL。客户端可执行文件调用 DLL 的导出函数,就好像这些函数包含在可执行文件内一样。

在显式链接下,使用 DLL 的可执行文件必须进行函数调用以显式加载和卸载该 DLL,并访问该 DLL 的导出函数。客户端可执行文件必须通过函数指针调用导出函数。可执行文件对两种链接方法可以使用同一个 DLL。另外,由于一个可执行文件可隐式链接到某个 DLL,而另一个可显式附加到此 DLL,故这些机制不是互斥的。

4、隐式链接

隐式链接动态链接库比较简单,不予详述。

5、显式链接API函数

显式链接主要涉及到3个API函数( LoadLibrary , GetProcAddress 和 FreeLibrary ),要使用这些函数包含windows.h头文件即可。

(1)HINSTANCE LoadLibrary(LPCSTR lpLibFileName);

该函数用来加载指定动态库文件,并且返回句柄。

参数lpLibFileName为动态链接库的名称。Windows 首先搜索“已知 DLL”,如 Kernel32.dll 和 User32.dll。然后按下列顺序搜索 DLL:

1、当前进程的可执行模块所在的目录。
2、当前目录。
3、Windows 系统目录。GetSystemDirectory 函数检索此目录的路径。
4、Windows 目录。GetWindowsDirectory 函数检索此目录的路径。
5、PATH 环境变量中列出的目录。

(2)FARPROC GetProcAddress (HMODULE hModule, LPCSTR lpProcName);

函数GetProcAddress 用来获取 DLL 导出函数的地址。返回由lpProcName指定的变量或函数指针。

参数hModule为已经加载的动态库文件的句柄。

参数lpProcName为要调用的变量或函数名称。

(3)BOOL FreeLibrary(HMODULE hModule);
从内存中释放hModule所代表的动态链接库。

(4)如果发生错误,可以调用GetLastError()函数或去错误代码。

6、显示链接举例

(1)动态库文件代码:dll_demo.c

#include <stdio.h>

__declspec( dllexport ) int add(int a, int b)
{
printf("calling add\n");
return a + b;
}

该文件中的add()函数计算两个整数之和,并且返回之前打印提示字符串。函数使用 __declspec( dllexport ) 语法来说明函数add(int a, int b)要被导出。

(2)客户端事例代码:main.c

#include <stdio.h>
#include <windows.h>

int main (int argc, char *argv[])
{
int a = 10, b = 20;
int c = 0;
HINSTANCE   hInstLibrary   =   NULL;
int (*add)();

printf ("Load DLL file\n");
if ((hInstLibrary = LoadLibrary(L"dll_demo.dll")) == NULL)
{
   printf ("***LoadLibrary ERROR: %d.\n", GetLastError());
   return 1;
}
if((add = (int (*)())GetProcAddress(hInstLibrary, "add")) == NULL) {
   printf ("***GetProcAddress ERROR: %d.\n", GetLastError());
   return 1;
}

c = add(a, b);
printf("%d + %d = %d\n", a, b, c);

FreeLibrary(hInstLibrary);
return 0;
}

程序利用LoadLibrary函数加载动态链接dll_demo.dll,利用FreeLibrary关闭句柄,利用GetLastError()获取错误代码,利用GetProcAddress定位共享库中的add函数,然后调用该函数执行加法计算,并打印结果。

(3)编译与运行

编译共享库:

在VS.Net中创建一个动态链接库项目,名称为dll_demo,加入文件dll_demo.c,编译后生成dll_demo.dll文件。

编译事例程序:

在VS.Net中创建一个动态链接库项目,名称为dll_main,加入文件main.c,编译后生成dll_main.exe可以执行文件。

运行:

将 dll_demo.dll 和 dll_main.exe 放在同一个目录下,然后双击运行 dll_main.exe。

输出:

Load DLL file
calling add
10 + 20 = 30

7、调用动态链接库中的变量

也可以使用动态链接库中的变量。例如,在动态链接库中定义:

__declspec( dllexport ) int num = 100;

那么可以在事例程序中这样调用:

int *d;
d = (int *)GetProcAddress(hInstLibrary, "num");
printf("num = %d\n", *d);

Windows 动态链接库编程的更多相关文章

  1. Windows Socket 编程_单个服务器对多个客户端简单通讯

    单个服务器对多个客户端程序: 一.简要说明 二.查看效果 三.编写思路 四.程序源代码 五.存在问题 一.简要说明: 程序名为:TcpSocketOneServerToMulClient 程序功能:实 ...

  2. 【Windows】windows核心编程整理(下)

    windows核心编程整理(上) windows核心编程整理(下) 线程的堆栈 每当创建一个线程时,系统就会为线程的堆栈(每个线程有他自己的堆栈)保留一个堆栈空间区域,并将一些物理存储器提交给这个以保 ...

  3. 《windows核心编程系列》十七谈谈dll

    DLL全称dynamic linking library.即动态链接库.广泛应用与windows及其他系统中.因此对dll的深刻了解,对计算机软件开发专业人员来说非常重要. windows中所有API ...

  4. Ligg.EasyWinApp-000: 一款Windows应用编程框架介绍

        本框架(解决方案)是一个Windows应用编程框架和UI库,通过该框架,不需任何代码,通过XML配置文件,搭建任意复杂的Windows应用界面,以类似Execel公式的方式实现基本的过程控制( ...

  5. windows多线程编程星球(一)

    以前在学校的时候,多线程这一部分是属于那种充满好奇但是又感觉很难掌握的部分.原因嘛我觉得是这玩意儿和编程语言无关,主要和操作系统的有关,所以这部分内容主要出现在讲原理的操作系统书的某一章,看完原理是懂 ...

  6. windows核心编程 - 线程同步机制

    线程同步机制 常用的线程同步机制有很多种,主要分为用户模式和内核对象两类:其中 用户模式包括:原子操作.关键代码段 内核对象包括:时间内核对象(Event).等待定时器内核对象(WaitableTim ...

  7. windows核心编程---第九章 同步设备IO与异步设备IO之同步IO

    同步设备IO 所谓同步IO是指线程在发起IO请求后会被挂起,IO完成后继续执行. 异步IO是指:线程发起IO请求后并不会挂起而是继续执行.IO完毕后会得到设备的通知.而IO完成端口就是实现这种通知的很 ...

  8. windows核心编程---第八章 使用内核对象进行线程同步

    使用内核对象进行线程同步. 前面我们介绍了用户模式下线程同步的几种方式.在用户模式下进行线程同步的最大好处就是速度非常快.因此当需要使用线程同步时用户模式下的线程同步是首选. 但是用户模式下的线程同步 ...

  9. storysnail的Windows串口编程笔记

    storysnail的Windows串口编程笔记 作者 He YiJun – storysnail<at>gmail.com 团队 ls 版权 转载请保留本声明! 本文档包含的原创代码根据 ...

随机推荐

  1. docker创建ceph集群

    背景 Ceph官方现在提供两类镜像来创建集群,一种是常规的,每一种Ceph组件是单独的一个镜像,如ceph/daemon.ceph/radosgw.ceph/mon.ceph/osd等:另外一种是最新 ...

  2. mysql数据库备份及还原

    数据库备份代码: package com.gd.test; import java.io.BufferedReader; import java.io.FileOutputStream; import ...

  3. jsp页面固定页面为绝对路径

    1 <!-- 固定到绝对路径 --> 2 <base href="<%=request.getContextPath()%>/"/>

  4. DEDECMS中的几个常见的自定义常量DEDEMEMBER等位置

    http://www.dede58.com/a/dedejq/3567.html dedecms新建栏目时默认都是允许投稿的,可以投稿本来对网站来说是件好事,但是dedecms是开源的,使用太广泛了, ...

  5. 求助帖:android开发初期:为什么我在活动二设置的singInstance模式跑到活动三去了???

    求android开发的高手帮我看看这个问题吧: <activity android:name=".SecondActivity" android:label="Th ...

  6. rsync - 远程同步工具

    一直没有对这个命令太有深入的理解 简介 rsync 即 remote sync,一个远程与本地文件同步工具.rsync 使用的算法能够最小化所需复制的数据,因为它只移动那些修改了的文件. rsync ...

  7. linux_Nginx日志

    错误信息日志配置: 日志文件默认:/application/nginx/logs/erroe.log error_log logs/error.log error; # 不写默认就有,默认error, ...

  8. linux如何自动获取ip地址

    第一步:激活网卡 系统装好后默认的网卡是eth0,用下面的命令将这块网卡激活. # ifconfig eth0 up 第二步:设置网卡进入系统时启动 想要每次开机就可以自动获取IP地址上网,就要设置网 ...

  9. 基于百度地图SDK和Elasticsearch GEO查询的地理围栏分析系统(2)-查询实现

    在上一篇博客中,我们准备好了数据.现在数据已经以我们需要的格式,存放在Elasticsearch中了. 本文讲述如何在Elasticsearch中进行空间GEO查询和聚合查询,以及如何准备ajax接口 ...

  10. jdbc参数

    JDBC连接池参数:    jdbc.initialSize=0       //初始化连接    jdbc.maxActive=30     //连接池的最大数据库连接数,设为0表示无限制    j ...