将自己的类封装为lib的方法

本文详细介绍Windows DLL的创建步骤及使用方法,包括静态链接与显式链接的应用场景与实现方式,适合初学者快速掌握DLL的基本操作。

前言:

Windows API中所有的函数都包含在dll中,其中有3个最重要的DLL。

(1)   Kernel32.dll 它包含那些用于管理内存、进程和线程的函数,例如CreateThread函数;

(2)   User32.dll   它包含那些用于执行用户界面任务(如窗口的创建和消息的传送)的函数,例如CreateWindow函数;

(3)   GDI32.dll    它包含那些用于画图和显示文本的函数。

静态库和动态库

(1)   静态库

         函数和数据被编译进一个二进制文件(通常扩展名为.LIB)。在使用静态库的情况下,在编译链接可执行文件时,链接器从库中复制这些函数和数据并把它们和应用程序的其他模块组合起来创建最终的可执行文件(.Exe文件).当发布产品时,只需要发布这个可执行文件,并不需要发布被使用的静态库。

(2)   动态库

      在使用动态库的时候,往往提供两个文件:一个引入库(.lib)文件和一个DLL(.dll)文件。虽然引入库的后缀名也是”lib”,但是动态库的引入库文件和静态库文件有着本质上的区别,对一个DLL来说,其引入库文件(.lib)包含该DLL导出的函数和变量的符号名,而.dll文件包含该DLL实际的函数和数据。在使用动态库的情况下,在编译链接可执行文件时,只需要链接该DLL的引入库文件,该DLL中的函数代码和数据并不复制到可执行文件中,直到可执行程序运行时,才去加载所需的DLL,将该DLL映射到进程的地址空间外,然后访问DLL中导出的函数。这时,发布产品时,除了发布可执行文件以外,同时还要发布该程序将要调用的动态链接库。

一、DLL的创建 

(1):方法引自于此文: http://blog.youkuaiyun.com/wqvbjhc/article/details/6029168 

              对于图像缺失问题,请访问原文链接.

(2):创建DLL 此文解释已经相当详细,地址:http://hi.baidu.com/lipeiyi2006/item/df1d035734472d9509be17c0

  不过为了防止以后博主删除,还是copy一遍....、


创建项目: Win32->Win32项目,名称:MyDLL
选择DLL ( D) ->完成.

1、新建头文件testdll.h
testdll.h代码如下:

#ifndef TestDll_H_
#define TestDll_H_
#ifdef MYLIBDLL

#define MYLIBDLL extern "C" _declspec(dllimport) 
#else
#define MYLIBDLL extern "C" _declspec(dllexport) 
#endif
MYLIBDLL int Add(int plus1, int plus2);
//You can also write like this:
//extern "C" {
//_declspec(dllexport) int Add(int plus1, int plus2);
//};
#endif

Ps:__declspec(dllexport)的作用,它就是为了省掉在DEF文件中手工定义导出哪些函数的一个方法。当然,如果你的DLL里全是C++的类的话,你无法在DEF里指定导出的函数,只能用__declspec(dllexport)导出类。

*注:extern "C"是可选的,extern "C"是为了保证导出的DLL函数名不发生变化。如果写了extern "C",那么导出和导入都要写,要保证一致。

         如果要导出的是类,不能在类名前加extern "C",切记切记!!编译不会通过的。

如果在新建dll的时候选择空白的文件(即不用vs自带的帮你生成)的话,注意一定要自己手动添加def文件。否则生成不了lib文件。


2、新建源文件testdll.cpp

#include "stdafx.h"
#include "testdll.h"
#include <iostream>

using namespace std;

int Add(int plus1, int plus2)
{
int add_result = plus1 + plus2;
return add_result;
}


3、新建模块定义文件mydll.def
mydll.def代码如下:
LIBRARY "MyDLL"EXPORTSAdd @1

PS:这个在VS2012中可能没有,所以要这样操作:

方法是:在所建工程上单击鼠标右键,在弹出的右键菜单中选择添加-->新建项....---->模块定义文件,在该模块定义文件中写导出函数表,单击确定

Vs2012默认生成dll,但不生成Lib文件。这个.def文件可以生成lib文件。


4、vs2010自动创建dllmain.cpp文件,它定义了DLL 应用程序的入口点。

dllmain.cpp代码如下:
// dllmain.cpp : 定义 DLL 应用程序的入口点。

#include "stdafx.h"
BOOL APIENTRY DllMain( HMODULE hModule,
              DWORD  ul_reason_for_call,

               LPVOID lpReserved
      )

{
switch (ul_reason_for_call)

{
case DLL_PROCESS_ATTACH:

case DLL_THREAD_ATTACH:

case DLL_THREAD_DETACH:

case DLL_PROCESS_DETACH:
  break;
}
return TRUE;
}

最后,编译生成MyDLL.dll文件和MyDLL.lib文件。

1>------ 已启动生成: 项目: MyDLL, 配置: Debug Win32 ------
1>  dllmain.cpp
========== 生成: 成功 1 个,失败 0 个,最新 0 个,跳过 0 个 ==========
1>------ 已启动生成: 项目: MyDLL, 配置: Debug Win32 ------
1>  stdafx.cpp
1>  testdll.cpp
1>  MyDLL.cpp
1>  正在生成代码...
1>     正在创建库 D:\Visual C++\工程\Libaray\MyDLL\Debug\MyDLL.lib 和对象 D:\Visual C++\工程\Libaray\MyDLL\Debug
Ps:errorc2061 语法错误 标识符

错误原因:是因为头文件的顺序不对。

解决方法:调整头文件的顺序。

转自:http://liuyunfeng484.blog.163.com/blog/static/66831715201232810449824/


二、DLL的使用(静态链接、隐式链接)

如何在C++中调用DLL中的函数

  应用程序使用DLL可以采用两种方式:一种是隐式链接,另一种是显式链接。在使用DLL之前首先要知道DLL中函数的结构信息。Visual C++6.0在VC\bin目录下提供了一个名为Dumpbin.exe的小程序,用它可以查看DLL文件中的函数结构。另外,Windows系统将遵循下面的搜索顺序来定位DLL: 1.包含EXE文件的目录,2.进程的当前工作目录, 3.Windows系统目录, 4.Windows目录,5.列在Path环境变量中的一系列目录。

1.隐式链接

  隐式链接就是在程序开始执行时就将DLL文件加载到应用程序当中。实现隐式链接很容易,只要将导入函数关键字_declspec(dllimport)函数名等写到应用程序相应的头文件中就可以了。下面的例子通过隐式链接调用MyDll.dll库中的Min函数。首先生成一个项目为TestDll,在DllTest.h、DllTest.cpp文件中分别输入如下代码:

 

//Dlltest.h
#pragma comment(lib,"MyDll.lib")
extern"C"_declspec(dllimport) int Max(int a,int b);
extern"C"_declspec(dllimport) int Min(int a,int b);

//TestDll.cpp
#include
#include"Dlltest.h"
void main()
{int a;
a=min(8,10)
printf("比较的结果为%d\n",a);

}

  在创建DllTest.exe文件之前,要先将MyDll.dll和MyDll.lib拷贝到当前工程所在的目录下面,也可以拷贝到windows的System目录下。如果DLL使用的是def文件,要删除TestDll.h文件中关键字extern "C"。TestDll.h文件中的关键字Progam commit是要Visual C+的编译器在link时,链接到MyDll.lib文件,当然,开发人员也可以不使用#pragma comment(lib,"MyDll.lib")语句,而直接在工程的Setting->Link页的Object/Moduls栏填入MyDll.lib既可。

2.显式链接

  显式链接是应用程序在执行过程中随时可以加载DLL文件,也可以随时卸载DLL文件,这是隐式链接所无法作到的,所以显式链接具有更好的灵活性,对于解释性语言更为合适。不过实现显式链接要麻烦一些。在应用程序中用LoadLibrary或MFC提供的AfxLoadLibrary显式的将自己所做的动态链接库调进来,动态链接库的文件名即是上述两个函数的参数,此后再用GetProcAddress()获取想要引入的函数。自此,你就可以象使用如同在应用程序自定义的函数一样来调用此引入函数了。在应用程序退出之前,应该用FreeLibrary或MFC提供的AfxFreeLibrary释放动态链接库。下面是通过显式链接调用DLL中的Max函数的例子。

 

#include
#include
void main(void)
{
typedef int(*pMax)(int a,int b);
typedef int(*pMin)(int a,int b);

HINSTANCE hDLL;
PMax Max

HDLL=LoadLibrary("MyDll.dll");//加载动态链接库MyDll.dll文件;

Max=(pMax)GetProcAddress(hDLL,"Max");
A=Max(5,8);

Printf("比较的结果为%d\n",a);

FreeLibrary(hDLL);//卸载MyDll.dll文件;
}

  在上例中使用类型定义关键字typedef,定义指向和DLL中相同的函数原型指针,然后通过LoadLibray()将DLL加载到当前的应用程序中并返回当前DLL文件的句柄,然后通过GetProcAddress()函数获取导入到应用程序中的函数指针,函数调用完毕后,使用FreeLibrary()卸载DLL文件。在编译程序之前,首先要将DLL文件拷贝到工程所在的目录或Windows系统目录下。

  使用显式链接应用程序编译时不需要使用相应的Lib文件。另外,使用GetProcAddress()函数时,可以利用MAKEINTRESOURCE()函数直接使用DLL中函数出现的顺序号,如将GetProcAddress(hDLL,"Min")改为GetProcAddress(hDLL, MAKEINTRESOURCE(2))(函数Min()在DLL中的顺序号是2),这样调用DLL中的函数速度很快,但是要记住函数的使用序号,否则会发生错误。

warning LNK4070exp : warning LNK4070: .EXP中的 /OUT:A.dll指令与输出文件名"../outdir/Debug/B.dll"不同;忽略指令

产生原因是:这个工程的def文件中LIBRARY字段的值和输出文件不一样造成的。

解决办法:修改LIBRARY字段的值,使得和输出文件保持一致。


转自:http://blog.163.com/gost_008/blog/static/87202204200862932528217/

三:DLL的模块定义文件(.DEF  

动态连接库函数或者成员的导出可以用

_declspec(dllexport)来实现,比如为了导出voidSayHello()函数,则在DLL文件中这么声明(或者定义):

_declspec(dllexport)void SayHello();

也可以不用_declspec(dllexport),而采用.def文件来说明要导出的函数或成员:

.def文件的格式:

LIBRARYABC //ABC.dll文件的名字,也就是dll工程的名字

EXPORTS

SayHello@1

这样,就从ABC.dll文件中导出了SayHello这个函数,

后面的那个"@1"的含义为:

从动态连接库文件中导出的函数或者成员可以用名字来标识,也可以用序号来表示,一般情况下用名字来标识,序号由系统来设置,但是也可以用"@1"的格式来自己定义,表示SayHello函数的序号为1

不想使用__declspec(dllexport)而单独使用.def文件导出类,不知道如何实现?1,把你要从dll输出的每个类,都设计一个基类,这是个纯虚,即所有函数都是“virtual...=0;”的。把这些写成一个.h   

      2,把dll里的类都从相应的基类派生,并实现每一个纯虚函数。并给dll设计一个可动态导出的函数,这个函数里   return    new    CxxxInDll;   

      3,在调用dll的程序里定义纯虚基类的指针,并赋予第二步返回的值。   

   现在可以动态加载、调用dll里的类了。看看COM原理,你就有启发了。熟练了后会发现这个方法在小型应用里非常有效!(在大型应用里,由于缺乏生存期控制,所以不安全)   

      需要补充的是:需要为每个类增加定义一个Release函数,函数里delete   this,不要在调用dll的程序里直接delete从第二步获得的指针。


四:我的方法


 对于Vs建立DLL文件的方法过程为:

(1):建立一个空工程,命名

(2):把工程生成文件设置为DLL

(3):添加现有项目,把类里面对外展示的Public成员函数,在头文件函数声明前面添加_declspec(dllexport)

(4):切记:要把构造函数和析构函数前面也加上_declspec(dllexport)

(5):生成dll文件,在工程的Release或者Debug文件夹里面会生成DLL和LIb文件,然后整个项目.h文件,DLL和LIb文件 为最后需要的文件



<think>好的,用户想了解如何在Keil环境中创建和使用C语言的文件(.lib)。首先,我需要整理一下步骤,确保逻辑清晰,并且符合用户的需求。 首先,创建C语言的步骤。用户需要编写源代码,这里要确认函数声明的正确性,比如使用头文件来声明导出函数,而源文件实现这些函数。然后,编译生目标文件,这一步可能需要根据Keil的编译器调整编译选项,比如使用--library_module来生适用的目标文件。接下来,使用管理工具(如Keil的LIB51或LIBARM)将目标文件打包.lib文件,这里要注意不同架构可能需要不同的工具,比如ARM和C51有不同的命令。 然后是使用的部分。用户需要在Keil项目中添加头文件路径,这样编译器能找到函数声明。接着,将.lib文件添加到项目里,这一步可能通过IDE的配置完。最后,在代码中引用头文件并调用函数,编译整个项目时链接器会自动处理的链接。 接下来,注意事项。代码优化方面,通常需要优化大小或速度,但要注意调试时的设置。符号冲突可能发生,所以中的函数命名最好有唯一性。跨平台兼容性也是要考虑的,特别是硬件相关的代码可能需要条件编译。 用户提供的引用中有提到USB驱动和LVGL组件的添加,这可能涉及到的实际应用案例,可以举例说明如何将到项目中。比如引用[2]中的C# DLL可能和C不同,但思路似,而引用[3]中的LVGL组件添加说明文件需要正确包含到工程中,这和Keil中添加.lib的步骤相似。 生相关问题部分,需要涵盖的创建步骤、调试方法、静态与动态的区别、Keil中的优化设置,以及如何确保跨平台兼容性。这些问题可以帮助用户更深入地理解的使用和可能出现的问题。 需要确保所有步骤详细且准确,特别是Keil特定的工具和命令,比如LIBARM和LIB51的区别,以及编译选项的设置。另外,示例代码部分要简单明了,展示头文件和源文件的结构,以及的使用方法。可能还需要提醒用户注意编译器兼容性,比如ARMCC和GCC生的目标文件是否兼容,避免用户混淆。 最后,检查是否符合用户的要求,特别是关于封装和在Keil中使用,避免遗漏关键步骤。同时,确保语言简洁,用中文回答,不使用Markdown格式,行内公式用$...$,独立公式用$$...$$,但在这个问题中可能涉及较少数学公式,主要关注代码和步骤。</think>在Keil环境中创建和使用C语言文件(.lib)的步骤如下: ### 一、创建静态步骤 1. **编写源代码** - 头文件声明导出函数: ```c // mylib.h #ifndef MYLIB_H #define MYLIB_H int add(int a, int b); #endif ``` - 源文件实现函数: ```c // mylib.c #include "mylib.h" int add(int a, int b) { return a + b; } ``` 2. **编译为目标文件** - 在Keil工程中: 1. 新建`Library`型工程 2. 勾选`Create Library`选项[^3] 3. 使用`--library_module`编译选项生`.obj`文件 3. **生文件** - ARM架构:使用`LIBARM`工具 ```bash LIBARM mylib.lib mylib.obj ``` - 8051架构:使用`LIB51`工具 ```bash LIB51 CREATE mylib.lib OBJECT (mylib.obj) ``` ### 二、使用静态步骤 1. **添加头文件路径** 在Keil项目配置中设置`Include Paths`指向头文件目录 2. **链接文件** 将`.lib`文件拖拽到项目的`Target`目录或通过`Options for Target > Library`配置 3. **调用函数** ```c #include "mylib.h" int main() { int result = add(3,5); // 输出8 return 0; } ``` ### 三、关键注意事项 1. **符号可见性**:使用`__declspec(dllexport)`显式声明导出函数(仅限动态) 2. **调试支持**:编译时保留调试信息需添加`--debug`标志 3. **跨平台兼容**:使用`#pragma pack`控制结构体对齐方式[^2]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值