C++ 之lib,dll,stl

__declspec(dllexport) int FUNCTION(int a, int b)
_declspec(dllexport) int __stdcall test2(int a, int b)
_declspec(dllexport)和添加def文件
将一个函数声名为导出函数,就是说这个函数要被其他程序调用,即作为DLL的一个对外函数接口。通常它和extern“C”合用,形式如下:

extern "C"
{
     __declspec(dllexport) void FUNCTION()
    { 
 
    } 

这是由于在制作DLL导出函数时由于C ++存在函数重载,因此__declspec(dllexport)FUNCTION(int,int)在DLL会被装饰,例如被装饰成为function_int_int,而且不同的编译器decorate的方法不同,造成了在用GetProcAddress的的取得FUNCTION地址时的不便,使用外部的“C”时,上述的装饰不会发生,因为ç没有函数重载,如此一来被外部的“C”修饰的函数,就不具备重载能力。
模块定义(.def)文件是包含一个或多个描述各种DLL属性的模块语句的文本文件
.1,二者的目的都是将公共符号导入到应用程序中或从DLL导出函数
.2,添加__declspec(dllexport)是为了提供不使用.def文件从.EXE或.DLL导出函数的简单方法
.3,如果不使用__declspec(dllimport)或__declspec(dllexport)导出DLL函数,则DLL需要.def文件
.4,并不是任何时候选择添加__declspec(dllexport)而放弃.def的方式都是好的。如果DLL是提供给VC ++用户使用的,只需要把编译DLL时产生的。 lib提供给用户,它可以很轻松地调用你的DLL。但是如果DLL是供VB,PB,Delphi用户使用的,那么会产生一个小麻烦。因为VC ++对于__declspec(dllexport)声明的函数会进行名称转换,如下面的函数:__ declspec 
     (dllexport)int __stdcall IsWinNT() 
     会转换为IsWinNT @ 0,这样你在VB中必须这样声明: 
     声明函数IsWinNT Lib“my.dll”别名“IsWinNT @ 0”()As Long 
     @的后面的数由于参数类型不同而可能不同。这显然不太方便。所以如果要想避免这种转换,就要使用.DEF文件方式。
def文件实际上更方便规定了符号
如果调用该DLL的是一个C ++程序(同一个编译器的版本)是没有问题的。但是,如果调用该DLL是一个其它语言的程序(如C#,VB),则会出错。究其原因,是因为在C ++中存在函数的重载,允许函数重名,因此在编译器生成的DLL的时候,为了区别重名的程序,其会将进行一定算法进行名称转换。
因此,我们直接通过函数名add是无法找到该函数的,从而导致调用失败。为了解决这一问题,我们往往在函数前面再加一个extern “C”, 使用C方式的函数命名规则。
这样,我们就需要在每一个函数签名加上“ extern ”C“ _declspec (dllexport ) ” 这一长串声明。如果需要导出的函数较多则显得非常繁琐,也非常难看。为了简化这一过程, MS引入了高清文件网求方便我们操作。

使用默认值文件比较简单,只需要在项目中添加一个DEF文件,然后把我们要导出的函数放在DEF文件中即可。
最后记得在链接器选项中选中使用的DEF文件(默认情况下,添加DEF文件时会自动加上该选项,无需手动更改)。
LIBRARY语句说明.def文件相应的DLL;
EXPORTS语句后列出要导出函数的名称。可以在.def文件中的导出函数名后加@n,表示要导出函数的序号为n(在进行函数调用时,这个序号将发挥其作用);
.def 文件中的注释由每个注释行开始处的分号 (;) 指定,且注释不能与语句共享一行。 
__stdcall:Windows API默认的函数调用协议。
__cdecl:C/C++默认的函数调用协议。
__fastcall:适用于对性能要求较高的场合。
__stdcall:函数参数由右向左入栈。
__cdecl:函数参数由右向左入栈。
__fastcall:从左开始不大于4字节的参数放入CPU的ECX和EDX寄存器,其余参数从右向左入栈。

接着回dll

在Visual Studio中,预编译的头文件通常命名为“pch.h”(用于基于控制台的应用程序),但可以使用不同的名称,或者根本不使用它.预编译头文件(如果有)由项目设置决定.

如果预编译头文件是“pch.h”并且编译选项是/ Yu,则Visual Studio将不会在源文件中的#include“pch.h”之前编译任何内容;它假设源中包含该行的所有代码都已编译.

如果工程很大,头文件很多,而有几个头文件又是经常要用的,那么

1。把这些头文件全部写到一个头文件里面去,比如写到preh.h

2。写一个preh.c,里面只一句话:#include "preh.h"

3。对于preh.c,在project setting里面设置creat precompiled headers,对于其他

.c文件,设置use precompiled header file

效果很明显,不用precompiled header,编译一次我可以去上个厕所,用

precompiled header,编译的时候,我可以站起来伸个懒腰,活动活动就差不多啦
所谓的预编译头就是把一个工程中的那一部分代码,预先编译好放在一个文件里(通常是

以.pch为扩展名的),这个文件就称为预编译头文件这些预先编译好的代码可以是任何的

C/C++代码--------甚至是inline的函数,但是必须是稳定的,在工程开发的过程中不会

被经常改变。如果这些代码被修改,则需要重新编译生成预编译头文件。注意生成预编

译头文件是很耗时间的。同时你得注意预编译头文件通常很大,通常有6-7M大。注意及

时清理那些没有用的预编译头文件。

也许你会问:现在的编译器都有Time stamp的功能,编译器在编译整个工程的时候,它

只会编译那些经过修改的文件,而不会去编译那些从上次编译过,到现在没有被修改过

的文件。那么为什么还要预编译头文件呢?答案在这里,我们知道编译器是以文件为单

位编译的,一个文件经过修改后,会重新编译整个文件,当然在这个文件里包含的所有

头文件中的东西(.eg Macro, Preprocesser )都要重新处理一遍。VC的预编译头文件

保存的正是这部分信息。以避免每次都要重新处理这些头文件。

预编译头的作用:

根据上文介绍,预编译头文件的作用当然就是提高便宜速度了,有了它你没有必要每次

都编译那些不需要经常改变的代码。编译性能当然就提高了。

在所有的预处理指令中,#pragma 指令可能是最复杂的了,它的作用是设定编译器的状态或者是指示编译器完成一些特定的动作。#pragma指令对每个编译器给出了一个方法,在保持与C和C++语言完全兼容的情况下,给出主机或操作系统专有的特征。依据定义,编译指示是机器或操作系统专有的,且对于每个编译器都是不同的。

静态链接库中不能再包含其他的动态链接库或者静态库,而在动态链接库中还可以再包含其他的动态或静态链接库。 
 动态库就是在需要调用其中的函数时,根据函数映射表找到该函数然后调入堆栈执行。如果在当前工程中有多处对dll文件中同一个函数的调用,那么执行时,这个函数只会留下一份拷贝。但是如果有多处对lib文件中同一个函数的调用,那么执行时,该函数将在当前程序的执行空间里留下多份拷贝,而且是一处调用就产生一份拷贝。
静态加载的步骤:

(1) 包含DLL中导出的头文件。

(2) 采用#pragma comment(lib,"..\\debug\\libTest.lib")导入动态库生成的*.lib头文件。或在 projectàsettingsàLinkeràInput的Additional Dependencies中加入lib文件。

(3) 将动态库生成的*.dll文件放到EXE或DLL的同一目录下。
也就是说,静态动态都有。
extern "C"
{
    __declspec(dllexport) int __stdcall FUNCTION(int a, int b)

_declspec(dllexport) int __stdcall test2(int a, int b)

必须要有这个dll文件。
目前以lib后缀的库有两种,一种为静态链接库,另一种为动态连接库的导入库 (Import Libary,简称“导入库”)。
静态库是一个或者多个obj文件的打包,所以有人干脆把从obj文件生成lib的过程称为Archive,即合并到一起。比如你链接一个静态库,如果其中有错,它会准确的找到是哪个obj有错,即静态lib只是壳子。跟压缩包一样。

动态库一般会有对应的导入库,方便程序静态载入动态链接库,否则你可能就需要自己LoadLibary调入DLL文件,然后再手工GetProcAddress获得对应函数了。有了导入库,你只需要链接导入库后按照头文件函数接口的声明调用函数就可以了。

导入库和静态库的区别很大,他们实质是不一样的东西。静态库本身就包含了实际执行代码、符号表等等,而对于导入库而言,其实际的执行代码位于动态库中,导入库只包含了地址符号表等,确保程序找到对应函数的一些基本地址信息。

这也是实际上很多开源代码发布的惯用方式:

1. 预编译的开发包:包含一些.dll文件和一些.lib文件。其中这里的.lib就是导入库,而不要错以为是静态库。但是引入方式和静态库一样,要在链接路径上添加找到这些.lib的路径。 而.dll则最好放到最后产生的应用程序exe执行文件相同的目录。这样运行时,就会自动调入动态链接库。
3. 如果你只有dll,并且你知道dll中函数的函数原型,那么你可以直接在自己程序中使用LoadLibary调入DLL文件,GetProcAddress获取函数地址,然后调用。

首先windows下的动态加载是靠三个函数来实现的

#include

LoadLibrary();

GetProcAddress();

FreeLibrary();

这三个函数我们可以理解为对文件的

open();

read/write();

close();
LPCSTR是Win32和VC++所使用的一种字符串数据类型。LPCSTR被定义成是一个指向以'\0'结尾的常量字符的指针。
LPWSTR是wchar_t字符串
wchar_t是C/C++的字符类型,是一种扩展的存储方式。wchar_t类型主要用在国际化程序的实现中,但它不等同于unicode编码。unicode编码的字符一般以wchar_t类型存储。
wchar_t数据类型一般为16位或32位,但不同的C或C++库有不同的规定,如GNU Libc规定wchar_t为32位,总之,wchar_t所能表示的字符数远超char型。
C标准并没有规定char 应该是unsigned还是signed,C标准定义了三种类型:char、signed char、unsigned char在不同的编译器下char可能是有符号数,也有可能是无符号数(意思是取决于编译器)
宽字符和窄字符最大的区别就是占字节大小的不同
LPCWSTR是一个指向unicode编码字符串的32位指针,所指向字符串是wchar型,而不是char型。
LPSTR和LPWSTR是Win32和VC++所使用的一种字符串数据类型。LPSTR被定义成是一个指向以NULL(‘\0’)结尾的32位ANSI字符数组指针,而LPWSTR是一个指向以NULL结尾的64位双字节字符数组指针
一般加一个"L"就可以了。
反正C++的肯定是会篡改的,用篡改后的也一定会成功的。底层C++对函数的命名方式会比C语言复杂,一个C++的工程调用一个库函数只会根据.dll文件的命名去查找匹配,并不会分析它是用什么编译器编译过的,所以我们C++调用的中库必须加上extern "C"关键字,再者作业上 也是没有说的
HINSTANCE 是“句柄型”数据类型。相当于装入到了内存的资源的ID。HINSTANCE对应的资源是instance.句柄实际上是一个 无符号长整数。
Handle 是代表系统的内核对象,如文件句柄,线程句柄,进程句柄。

HMODULE 是代表应用程序载入的模块,win32系统下通常是被载入模块的线性地址。

HINSTANCE 在win32下与HMODULE是相同的东西,在Win32下还存在主要是因为win16

程序使用HINSTANCE来区别task。

说明已经读到这个文件了,为什么不行呢?

输出文件(.exp)
输出文件以.exp为扩展名,包含了输出的函数和数据的信息,链接程序使用它来创建DLL动态链接库。

生成类库项目时除了生成dll文件外,还会生成一个同名的pdb文件,它是一个程序数据库文件,保存着调试和项目状态信息,使用这些信息可以对程序的调试配置进行增量链接。pdb文件包含了编译后程序指向源代码的位置信息,用于调试的时候定位到源代码,主要是用来方便调试的。
在程序发布为release模式时,建议将 pdb文件删除, 同时,对外发布的时候,也把 pdb删除,有利于保护程序。 
 

 

 

 

 

 

 

 

 

 

 

 

 

 

你可能感兴趣的:(C语言)