使用msvc命令行编译静态库和动态库
因为最近园子在审核,先发在了CSDN。但我更喜欢博客园,现在搬运过来
编写一个静态库
编写要打包为静态库的函数,内容如下:
// jclib.cpp
int func(int a, int b)
{
return a + b;
}
在msvc开发人员命令提示符
中执行
cl /c jclib.cpp
生成jclib.obj
,这是编译产生的中间文件。然后使用lib
工具将其打包为静态链接库*.lib文件。
lib jclib.obj
产生jclib.lib
。
写个头文件声明函数,供其他模块调用。
// jclib.h
int func(int,int);
接下来写个程序调用静态库中的func
函数。
// demo.cpp
#include "jclib.h"
#include <stdio.h>
#pragma comment(lib,"./jclib.lib")//这里表明要链接的库,也可以在编译选项中指定
int main()
{
int a =1;
int b = 3;
int c = 3;
printf("c=%d\n",c);
c = func(a,b);//这个函数就在静态库中
printf("c=%d\n",c);
return 0;
}
编译链接测试例子
cl /EHsc demo.cpp
如果在demo.cpp
中没有写#pragma comment
语句,则执行如下编译命令
cl /EHsc demo.cpp jclib.lib
编写一个动态库
写法一
编写动态库中的函数
//jcdll.cpp
__declspec(dllexport) int add(int a, int b){
return a + b;
}
__declspec(dllexport) int subtract(int a, int b){
return a - b;
}
__declspec(dllexport)
表示函数要导出给其他模块使用,如果代码里不写这个声明,那么就需要后边编译的时候用.def
文件来说明要导出哪些函数。(导出的意思是,有些函数在dll中是私有的,并不想被外界访问,那么就不导出这些函数)。
编写动态库头文件,声明函数,供其他模块调用
// jcdll.h
int __cdecl add(int,int);
int __cdecl subtract(int,int);
编译生成动态库
cl /LD jcdll.cpp
产生jcdll.dll
,jcdll.lib
,jcdll.exp
编写测试程序,调用jcdll
中的函数
// demo.cpp
#include "jcdll.h"
#include <stdio.h>
#pragma comment(lib,"./jcdll.lib")
int main()
{
int d = 1;
int e = 4;
int f = add(d,e);
printf("f=%d\n",f);
}
编译测试程序
cl /EHsc demo.cpp
写法二
这里仅说明和方法一操作不一样的地方
jcdll.cpp
中不写__declspec
在编译时使用.def文件来定义要导出的函数,分号开头是注释
; jcdll.def
; this is comment
LIBRARY jcdll.dll
EXPORTS
add @1
subtract @2
EXPORTS字段可以这样写:
; 只导出函数名
func1
; 导出函数名和序号
func2 @2
; 只导出序号
func3 @3 noname
编译动态库
cl /LD /DEF: jcdll.def jcdll.cpp
注意:使用def文件定义导出函数和使用__declspec(dllexport)导出,产生的导出符号是不太一样的,使用dumpbin /exports jcdll.dll
查看导出符号,发现前者导出符号和函数名完全一样,后者会被编译器打乱一点,变成?add@@YAHHH@Z
和?subtract@@YAHHH@Z
,对于隐式链接来说无妨,编译器会处理,对于显式链接,也就是用GetProcAddress
来获取函数指针时,要写?add@@YAHHH@Z
才能获取到函数指针。
另外,在dll中可以定义DllMain
来对dll加载和释放时进行操作。这个函数在加载dll加载和卸载时将被调用。这里就不展开了,有兴趣可以查阅其他资料。
#include <windows.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;
}
// https://blog.csdn.net/a7055117a/article/details/47733247
显式加载dll
// 加载动态库,如果动态库没有资源文件,只有代码,就没必要用LoadLibraryEx, 用LoadLibrary就行了,参数少
// 写在这里只是记下它的用法
// 需要包含<windows.h>
//HINSTANCE hDll = LoadLibrary(TEXT("jcdll.dll"));
HINSTANCE hDll = LoadLibraryEx(TEXT("jcdll.dll"), NULL, 0);
typedef int (*FUNCTYTE)(int, int); //函数指针
if (hDll != NULL)
{
// 建议使用def文件定义导出函数,这样的话只要写"add"就可,不必搞这些
//乱七八糟的函数名
FUNCTYTE add = (FUNCTYTE)GetProcAddress(hDll, "?add@@YAHHH@Z");
if (add)
printf("add(1,2)=%d\n", add(1, 2));
else
printf("GetProcAddress() failed: %d\n",GetLastError());
}
FreeLibrary(hDll);