Win32环境下动态链接库(DLL)编程原理

【C语言提高50】Win32环境下动态链接库(DLL)编程原理 比较大的应用程序都由很多模块组成,这些模块分别完成相对独立的功能,它们彼此协作来完成整个软件系统的工作。其中可能存在一些模块的功能较为通用,在构造其它软件系统时仍会被使用。在构造软件系统时,如果将所有模块的源代码都静态编译到整个应用程序EXE文件中,会产生一些问题:一个缺点是增加了应用程序的大小,它会占用更多的磁盘空间,程序运行时也会消耗较大的内存空间,造成系统资源的浪费;另一个缺点是,在编写大的 阅读详情
比较大应用 程序都由很多模块组成,这些模块分别完成相对独立的功能,它们彼此协作来完成整个软件 系统的工作。其中可能存在一些模块的功能较为通用,在构造其它软件 系统时仍会被使用。在构造软件 系统时,如果将所有模块的源代码都静态编译到整个应用 程序EXE 文件中,会产生一些问题:一个缺点是增加了应用 程序的大小,它会占用更多的磁盘空间, 程序运行时也会消耗较大的内存空间,造成 系统资源的浪费;另一个缺点是,在编写大的EXE 程序时,在每次修改重建时都必须调整编译所有源代码,增加了编译过程的复杂性,也不利于阶段性的单元测试。

  Windows 系统平台上提供了一种完全不同的较有效的编程和运行环境,你可以将独立的 程序模块创建为较小的DLL(Dynamic Linkable Library) 文件,并可对它们单独编译和测试。在运行时,只有当EXE 程序确实要调用这些DLL模块的情况下, 系统才会将它们装载到内存空间中。这种方式不仅减少了EXE 文件的大小和对内存空间的需求,而且使这些DLL模块可以同时被多个应用 程序使用。Microsoft Windows自己就将一些主要的 系统功能以DLL模块的形式实现。例如IE中的一些基本功能就是由DLL 文件实现的,它可以被其它应用 程序调用和集成。

  一般来说,DLL是一种磁盘 文件(通常带有DLL扩展名),它由全局数据、服务函数和资源组成,在运行时被 系统加载到进程的虚拟空间中,成为调用进程的一部分。如果与其它DLL之间没有冲突,该 文件通常映射到进程虚拟空间的同一地址上。DLL模块中包含各种导出函数,用于向外界提供服务。Windows在加载DLL模块时将进程函数调用与DLL 文件的导出函数相匹配。

  在Win32环境中,每个进程都复制了自己的读/写全局变量。如果想要与其它进程共享内存,必须使用内存映射 文件或者声明一个共享数据段。DLL模块需要的堆栈内存都是从运行进程的堆栈中分配出来的。

  DLL现在越来越容易编写。Win32已经大大简化了其编程模式,并有许多来自AppWizard和MFC类库的支持。

   一、导出和导入函数的匹配

  DLL 文件中包含一个导出函数表。这些导出函数由它们的符号名和称为标识号的整数与外界联系起来。函数表中还包含了DLL中函数的地址。当应用 程序加载DLL模块时时,它并不知道调用函数的实际地址,但它知道函数的符号名和标识号。动态链接过程在加载的DLL模块时动态建立一个函数调用与函数地址的对应表。如果重新编译和重建DLL 文件,并不需要修改应用 程序,除非你改变了导出函数的符号名和参数序列。

  简单的DLL 文件只为应用 程序提供导出函数,比较复杂的DLL 文件除了提供导出函数以外,还调用其它DLL 文件中的函数。这样,一个特殊的DLL可以既有导入函数,又有导入函数。这并不是一个问题,因为动态链接过程可以处理交叉相关的情况。

  在DLL代码中,必须像下面这样明确声明导出函数:

__declspec(dllexport) int MyFunction(int n);

  但也可以在模块定义(DEF) 文件中列出导出函数,不过这样做常常引起更多的麻烦。在应用 程序方面,要求像下面这样明确声明相应的输入函数:

__declspec(dllimport) int MyFuncition(int n);

  仅有导入和导出声明并不能使应用 程序内部的函数调用链接到相应的DLL 文件上。应用 程序的项目必须为链接 程序指定所需的输入库(LIB 文件)。而且应用 程序事实上必须至少包含一个对DLL函数的调用。

   二、与DLL模块建立链接

  应用 程序导入函数与DLL 文件中的导出函数进行链接有两种方式:隐式链接和显式链接。所谓的隐式链接是指在应用 程序中不需指明DLL 文件的实际存储路径, 程序员不需关心DLL 文件的实际装载。而显式链接与此相反。

  采用隐式链接方式, 程序员在建立一个DLL 文件时,链接 程序会自动生成一个与之对应的LIB导入 文件。该 文件包含了每一个DLL导出函数的符号名和可选的标识号,但是并不含有实际的代码。LIB 文件作为DLL的替代 文件被编译到应用 程序项目中。当 程序员通过静态链接方式编译生成应用 程序时,应用 程序中的调用函数与LIB 文件中导出符号相匹配,这些符号或标识号进入到生成的EXE 文件中。LIB 文件中也包含了对应的DLL 文件名(但不是完全的路径名),链接 程序将其存储在EXE 文件内部。当应用 程序运行过程中需要加载DLL 文件时,Windows根据这些信息发现并加载DLL,然后通过符号名或标识号实现对DLL函数的动态链接。

  显式链接方式对于集成化的开发语言(例如 VB)比较适合。有了显式链接, 程序员就不必再使用导入 文件,而是直接调用Win32 的LoadLibary函数,并指定DLL的路径作为参数。LoadLibary返回HINSTANCE参数,应用 程序在调用GetProcAddress函数时使用这一参数。GetProcAddress函数将符号名或标识号转换为DLL内部的地址。假设有一个导出如下函数的DLL 文件:

extern "C" __declspec(dllexport) double SquareRoot(double d);

  下面是应用 程序对该导出函数的显式链接的例子:

typedef double(SQRTPROC)(double);
HINSTANCE hInstance;
SQRTPROC* pFunction;
VERIFY(hInstance=::LoadLibrary("c://winnt//system32//mydll.dll"));
VERIFY(pFunction=(SQRTPROC*)::GetProcAddress(hInstance,"SquareRoot"));
double d=(*pFunction)(81.0);//调用该DLL函数

  在隐式链接方式中,所有被应用 程序调用的DLL 文件都会在应用 程序EXE 文件加载时被加载在到内存中;但如果采用显式链接方式, 程序员可以决定DLL 文件何时加载或不加载。显式链接在运行时决定加载哪个DLL 文件。例如,可以将一个带有 字符串资源的DLL模块以英语加载,而另一个以西班牙语加载。应用 程序在用户选择了合适的语种后再加载与之对应的DLL 文件。

   三、使用符号名链接与标识号链接

  在Win16环境中,符号名链接效率较低,所有那时标识号链接是主要的链接方式。在Win32环境中,符号名链接的效率得到了改善。Microsoft现在推荐使用符号名链接。但在MFC库中的DLL版本仍然采用的是标识号链接。一个典型的MFC 程序可能会链接到数百个MFC DLL函数上。采用标识号链接的应用 程序的EXE 文件体相对较小,因为它不必包含导入函数的长 字符串符号名。



   四、编写DllMain函数

  DllMain函数是DLL模块的默认入口点。当Windows加载DLL模块时调用这一函数。 系统首先调用全局对象的构造函数,然后调用全局函数DLLMain。DLLMain函数不仅在将DLL链接加载到进程时被调用,在DLL模块与进程分离时(以及其它时候)也被调用。下面是一个框架DLLMain函数的例子。

HINSTANCE g_hInstance;
extern "C" int APIENTRY DllMain(HINSTANCE hInstance,DWORD dwReason,LPVOID lpReserved)
{
if(dwReason==DLL_PROCESS_ATTACH)
{
TRACE0("EX22A.DLL Initializing!/n");
//在这里进行初始化
}
else if(dwReason=DLL_PROCESS_DETACH)
{
TRACE0("EX22A.DLL Terminating!/n");
//在这里进行清除工作
}
return 1;//成功
}

  如果 程序员没有为DLL模块编写一个DLLMain函数, 系统会从其它运行库中引入一个不做任何操作的缺省DLLMain函数版本。在单个线程启动和终止时,DLLMain函数也被调用。正如由dwReason参数所表明的那样。

   五、模块句柄

  进程中的每个DLL模块被全局唯一的32字节的HINSTANCE句柄标识。进程自己还有一个HINSTANCE句柄。所有这些模块句柄都只有在特定的进程内部有效,它们代表了DLL或EXE模块在进程虚拟空间中的起始地址。在Win32中,HINSTANCE和HMODULE的值是相同的,这个两种类型可以替换使用。进程模块句柄几乎总是等于0x400000,而DLL模块的加载地址的缺省句柄是0x10000000。如果 程序同时使用了几个DLL模块,每一个都会有不同的HINSTANCE值。这是因为在创建DLL 文件时指定了不同的基地址,或者是因为加载 程序对DLL代码进行了重定位。
模块句柄对于加载资源特别重要。Win32 的FindResource函数中带有一个HINSTANCE参数。EXE和DLL都有其自己的资源。如果应用 程序需要来自于DLL的资源,就将此参数指定为DLL的模块句柄。如果需要EXE 文件中包含的资源,就指定EXE的模块句柄。

  但是在使用这些句柄之前存在一个问题,你怎样得到它们呢?如果需要得到EXE模块句柄,调用带有Null参数的Win32函数GetModuleHandle;如果需要DLL模块句柄,就调用以DLL 文件名为参数的Win32函数GetModuleHandle。

   六、应用程序怎样找到DLL文件

  如果应用 程序使用LoadLibrary显式链接,那么在这个函数的参数中可以指定DLL 文件的完整路径。如果不指定路径,或是进行隐式链接,Windows将遵循下面的搜索顺序来定位DLL:

  1. 包含EXE 文件的目录,
  2. 进程的当前工作目录,
  3. Windows 系统目录,
  4. Windows目录,
  5. 列在Path环境变量中的一系列目录。

  这里有一个很容易发生错误的陷阱。如果你使用 VC++进行项目开发,并且为DLL模块专门创建了一个项目,然后将生成的DLL 文件拷贝到 系统目录下,从应用 程序中调用DLL模块。到目前为止,一切正常。接下来对DLL模块做了一些修改后重新生成了新的DLL 文件,但你忘记将新的DLL 文件拷贝到 系统目录下。下一次当你运行应用 程序时,它仍加载了老版本的DLL 文件,这可要当心!

   七、调试DLL程序

  Microsoft 的 VC++是开发和测试DLL的有效工具,只需从DLL项目中运行调试 程序即可。当你第一次这样操作时,调试 程序会向你询问EXE 文件的路径。此后每次在调试 程序中运行DLL时,调试 程序会自动加载该EXE 文件。然后该EXE 文件用上面的搜索序列发现DLL 文件,这意味着你必须设置Path环境变量让其包含DLL 文件的磁盘路径,或者也可以将DLL 文件拷贝到搜索序列中的目录路径下。
大尺寸智能车的CAN驱动控制器制作(一) 本驱动板使用8个半H桥BTS7960驱动,使用CAN接口于车体主控制器通讯,最大承受电压48V 电流20A ,主控为STM32F103VET6,电机控制频率为18KHz,CAN通讯芯片为TJA1042T,全驱动板6-24V宽电压供电,采用24V降5V,5V降3.3V的方案供电,其中经过AMS1117作为降压芯片,可以有效降低主控损毁几率。在这里可以看到555电机相比于其他电机的功率大很多,所以需要大功率的电机驱动,在以往的电机驱动选型中选择了TB6612,L298N等小功率电机驱动。 阅读详情

相关推荐

群晖NAS搭建Git Server避坑指南:从SSH配置到权限管理的完整流程

本文提供了一份详尽的群晖NAS搭建Git Server实战指南。文章从启用并安全配置SSH服务入手,深入解析了用户权限体系设计、SSH连接与仓库初始化、客户端SSH密钥配置等核心步骤,并提供了常见错误排查与生产环境加固方案,帮助开发者成功构建私有、安全的代码托管平台。

rock5的博客 420

Win32环境下动态链接库(DLL)编程原理【转贴】

比较大应用程序都由很多模块组成,这些模块分别完成相对独立的功能,它们彼此协作来完成整个软件系统的工作。其中可能存在一些模块的功能较为通用,在构造其它软件系统时仍会被使用。在构造软件系统时,如果将所有模块的源代码都静态编译到整个应用程序EXE文件中,会产生一些问题:一个缺点是增加了应用程序的大小,它会占用更多的磁盘空间,程序运行时也会消耗较大的内存空间,造成系统资源的浪费;另一个缺点是,在编写大的E

一兰阳光 918

Java开发者必看:Apache PDFBox实现PDF数字签名完整指南(附源码)

本文为Java开发者提供了使用Apache PDFBox实现PDF数字签名的完整指南。详细介绍了从环境配置、核心原理到完整代码实现的全过程,涵盖证书管理、签名位置计算、性能优化及实际应用案例,并附有可直接复用的源码,帮助开发者高效集成PDF数字签名功能。

o1p2q3r的博客 340

C#调用C++动态链接库之Win32dll(函数)、MFCdll(对话框)

最近想把MFC写的对话框程序封装成.dll文件供C#程序调用,其中遇到了很多问题,现记录如下,以供参考! 一、在做MFC对话框封装之前顺带讲下函数的封装,这个相对简单。 1- 首先创建Win32 project,命名为mydll; 2- 应用类型选择DLL,完成; 3- 生成工程目录如下; 4- 在mydll.h头文件中添加代码如下(以实现add函数封装为例);

沐阳2100的博客 1711

基于计算机视觉的Windows自动化工具openclaw-win:从原理到实战

计算机视觉与自动化技术正深刻改变人机交互方式,其核心在于让机器具备“感知-决策-执行”的能力。通过图像采集与处理算法,程序可以识别屏幕上的特定元素;结合模拟输入技术,便能自动执行点击、拖拽等操作,形成完整的自动化闭环。这一技术栈在软件测试、数据采集、重复性办公任务等领域具有重要价值,能够显著提升工作效率。开源项目openclaw-win正是这一理念的工程实践,它整合了图像识别与模拟操作模块,为开发者提供了一个研究自动化技术的具体样本。项目涉及模板匹配、坐标计算等关键技术点,并需处理屏幕适配、操作延迟等实际问

weixin_28745975的博客 334

不止于算力:深入鲲鹏920的片上网络(NoC)与一致性协议HCCS,看华为如何优化数据流转

本文深入解析了鲲鹏920处理器的片上网络(NoC)架构和一致性协议HCCS,揭示了华为如何通过创新的环总线设计和私有协议优化数据流转效率。文章详细介绍了鲲鹏920在多die、多芯片环境下的高效协同机制,包括环总线架构、HCCS协议精妙设计及物理实现创新,为云服务场景提供了高性能解决方案。

weixin_30681121的博客 254

rundll32 执行DLL中的某个函数

rundll32 命令可以执行DLL中的某个函数 先写个DLL文件  Mydll.cpp BOOL APIENTRY DllMain( HMODULE hModule,                       DWORD  ul_reason_for_call,                       LPVOID lpReserved      ){ Messag

1437

sram设计流程+memory compiler整理

→。

qq_50761108的博客 1万+

C接口的封装和设计专题【Win32环境下动态链接库(DLL)编程原理】

4. 还有一个办法,就是把dll的Settings的C/C++选项卡的Code Generation的Use Run-time liberary改成Debug Multithreaded DLL,在Release版本中改成Multithreaded DLL,就可以直接使用new和delete了。简单的DLL文件只为应用程序提供导出函数,比较复杂的DLL文件除了提供导出函数以外,还调用其它DLL文件中的函数。所谓的隐式链接是指在应用程序中不需指明DLL文件的实际存储路径,程序员不需关心DLL文件的实际装载。

Stark的博客 1209

UNET改进77:全网首发-添加CFBlock |

本文介绍了在UNet模型中集成CFBlock注意力机制的实现方法。主要分为三个步骤:1) 创建CFBlock模块,包含卷积注意力机制和MLP;2) 在UNet连接处添加注意力模块;3) 在卷积层中嵌入注意力机制。具体实现包括:定义Conv、MLP和ConvolutionalAttention等基础模块,构建CFBlock核心结构;在UNet跳跃连接处插入注意力模块;将注意力机制整合到DoubleConv和Down模块中。该方法通过在不同位置引入注意力机制,可有效提升模型的特征提取能力。代码实现使用PyTor

AIcurator的博客 388

用 SAP HANA SDA 访问 PostgreSQL:从可行性到落地实践的完整指引

摘要:SAP HANA可通过SDA/SDI技术访问PostgreSQL数据库,支持数据虚拟化和实时复制。主要实现路径包括:1)使用Camel JDBC适配器创建虚拟表进行联邦查询;2)利用PostgreSQL Log Reader适配器实现变更数据捕获(CDC)。实施时需配置Data Provisioning Agent,注意权限和网络要求。官方文档指出,通过SDI+DP Agent的方式比直接SDA连接更稳定可靠。虚拟化查询时,HANA会自动将SQL下推到PostgreSQL执行,优化性能。

2007 年 ~ 2025 年,深耕 SAP 技术 18 年 257

Win32环境下动态链接库(DLL)编程原理(转贴)

Win32环境下动态链接库(DLL)编程原理(转贴)http://www.vckbase.com 比较大应用程序都由很多模块组成,这些模块分别完成相对独立的功能,它们彼此协作来完成整个软件系统的工作。其中可能存在一些模块的功能较为通用,在构造其它软件系统时仍会被使用。在构造软件系统时,如果将所有模块的源代码都静态编译到整个应用程序EXE文件中,会产生一些问题:一个缺点是增加了应用程序的大小,它会占

pc2s的专栏 905

YOLOv8自定义训练:车牌识别全流程(从标注到推理,附可视化代码)

车牌检测全流程实践指南 摘要:本文详细介绍了基于YOLOv8的车牌检测完整实现方案。针对车牌检测中的三大难点(小目标、多角度、光照变化),提供了从数据准备到模型部署的全流程解决方案。重点包括:1)数据采集与标注规范;2)针对小目标的YOLOv8模型优化配置;3)K-means锚框聚类方法;4)训练参数调优策略。通过实际验证,该方法可获得95%以上的检测准确率,并附有完整的代码实现和可视化工具,便于开发者快速复现应用。 (字数:149字)

专注于Python爬虫开发,分享爬虫技巧、项目实战与反爬经验,使用Scrapy、BeautifulSoup等工具,解决数据抓取难题。 851

Win32环境下动态链接库(DLL)编程原理(转贴)http://www.vckbase.com

Win32环境下动态链接库(DLL)编程原理(转贴)http://www.vckbase.com 比较大应用程序都由很多模块组成,这些模块分别完成相对独立的功能,它们彼此协作来完成整个软件系统的工作。其中可能存在一些模块的功能较为通用,在构造其它软件系统时仍会被使用。在构造软件系统时,如果将所有模块的源代码都静态编译到整个应用程序EXE文件中,会产生一些问题:一个缺点是增加了应用程序的大小,它会占

加肥冒 1395

迈瑞BS系列全自动生化分析仪LIS协议接口手册(内含HL7和ASTM)

迈瑞BS系列全自动生化分析仪LIS协议接口手册,内含HL7和ASTM)

文档:亲测可用解决vnc灰屏,黑屏,鼠标大黑叉情况

主要解决vnc-viewer灰屏,黑屏,鼠标大黑叉等现象,详细见文档。

c语言查漏补缺——Win32环境下动态链接库(DLL)编程原理

在c语言查漏补缺总结了笔记,并分享出来。有问题请及时联系博主:Alliswell_WP,转载请注明出处。 目录:一、Win32环境下动态链接库(DLL)编程原理1、导出和导入函数的匹配2、与DLL模块建立链接3、使用符号名链接与标识号链接4、编写DllMain函数5、模块句柄6、应用程序怎样找到DLL文件7、调试DLL程序二、DLL分配的内存如何在EXE里面释放 一、Win32环境下动态...

Alliswell_WP 355

计算机控制技术及应用

《计算机控制技术及应用》通过总结作者的科研成果和吸收国内外的先进理论、方法和技术,以计算机控制系统的网络化、开放化、智能化和集成化发展趋势为主线索,主要介绍计算机控制系统的基本概念和计算机控制系统的典型形式,计算机控制系统的数学描述与基本分析方法,以及数字PID控制器、最少拍控制系统、达林算法、状态变量设计法等设计方法及其各种改进算法。重点突出计算机控制系统输入输出通道接口的设计方法和典型控制系统的设计案例与实现方法等。

Win32环境下动态链接库(DLL)编程原理 (转)

Win32环境下动态链接库(DLL)编程原理 (转)[@more@]李 欣 比较大应用程序都由很多模块组成,这些模块分别完成相对独立的功能,它们彼此协作来完成整个软件系统的工作。其中可能存在一些模块的功能较为通用,在构造其它...

csobhtdx34120的博客 86
上一篇: Windows编程中的堆管理
下一篇: 轻松使用自己的回调函数
iiprogram
博客等级 码龄25年 1068粉丝 1515原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值