动态库总结

本文详细介绍了DLL和LIB的区别,以及如何在Visual C++中创建和使用DLL动态库。内容涵盖预编译头、externC、动态库与静态库的工作原理,以及显式调用和隐式调用DLL的方法。通过示例代码展示了如何编译和调用DLL函数,并提供了处理加载DLL时可能出现的问题的解决办法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

研究了几天,对大致使用有了了解。
如果要编写dll动态库,首先要了解以下的知识,不着急写代码。

需要掌握的知识:

1.预编译头,预处理命令,extern “C”,typedef,#pragmaonce等的解释和使用
2.dll,lib是什么
3.创建dll程序,生成dll或者是lib文件
4.有几种调用dll的方式。显式调用/隐式调用
多看看别人是怎么写的。融合进来。快速参透。
MSDN:https://docs.microsoft.com/zh-cn/cpp/build/dlls-in-visual-cpp?view=msvc-160&viewFallbackFrom=vs-2015

一、lib和dll的定义以及区别

参考:
https://www.cnblogs.com/TenosDoIt/p/3203137.html

dll:动态库。动态链接库是一个包含可由多个程序同时使用的代码和数据的库
lib:静态库。在链接步骤中,连接器将从库文件取得所需的代码,复制到生成的可执行文件中

二者区别:
静态库中的lib:该LIB包含函数代码本身(即包括函数的索引,也包括实现),在编译时直接将代码加入程序当中
动态库中的lib:该LIB包含了函数所在的DLL文件和文件中函数位置的信息(索引),函数实现代码由运行时加载在进程空间中的DLL提供

总之,lib是编译时用到的,dll是运行时用到的。如果要完成源代码的编译,只需要lib;如果要使动态链接的程序运行起来,只需要dll。

二、生成dll/lib

参考:
https://www.cnblogs.com/lanhaicode/p/10798385.html
微软:https://docs.microsoft.com/zh-cn/cpp/build/walkthrough-creating-and-using-a-dynamic-link-library-cpp?view=msvc-140

小细节:
如果只有.h使用了_declspec(dllexport)那么就只会生成dll文件
如果.cpp也使用了_declspec(dllexport)那么就会生成dll文件,也有lib文件

1.预处理命令的使用详解-#if,#endif,#undef,#ifdef,#else,#elif
参考:https://www.cnblogs.com/lanhaicode/p/10546514.html

2.extern "C"的作用详解
参考:extern “C”是指示编译器这部分代码按C语言(而不是C++)的方式进行编译

三、调用dll

1.显式调用
只使用.dll
显示调用必须使用extern "C"修饰符修饰函数。隐式调用可以使用任何类型

2.隐式调用
只使用.h+.lib+.dll
添加头文件,依赖库,和动态链接库才能成功编译工程。

参考代码:
创建dll动态库工程即可。
dll .h

/*!
* Copyright (C) 2021 爱做尼啊
* 版权所有。
* 代码仅用于实验
\file: my_dll.cpp
\brief dll头文件
\author wqj
\Date 2021/5
*/
#pragma once
//和#pragma once/二选一
//下面这个由语言支持所以移植性好,#pragma once 可以避免名字冲突
#ifndef __MYDLL_H__
#define __MYDLL_H__

//声明--可以使用它们从 DLL 中导出或向其中导入函数、数据和对象
//当编译时,头文件不参加编译,所以.cpp文件中先定义,后头文件被包含进来,
//因此外部使用时,为dllexport,而在内部编译时,则为dllimport
#ifdef MYDLL_EXPORTS
#define MYDLL_API __declspec(dllexport)
#else
#define MYDLL_API __declspec(dllimport)
#endif


//编译器以c语言方式编译该函数
/**
* @brief 计算和
* @param[in] a 输入值
* @param[in] b 输入值
* @return 数据
*/
extern "C" MYDLL_API append_int(char* s, int i, char* out);

#endif


dll .cpp

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

extern "C" MYDLL_API append_int(char* s, int i, char* out)
{
	return a + b;
}



调用代码:

/*!
* Copyright (C) 2021 爱做尼啊
* 版权所有。
* 代码仅用于测试
\file: main.cpp
\brief 演示用文件
\author wqj
\Date 2021/5
*/
#define MOD 1
#include "my_dll.h"
#include <iostream>  
#include <windows.h>
#pragma comment(lib,"MyDll.lib")
#pragma warning(disable:4996)

//显式链接 
void UseExplicit()
{
	//定义指向dll中函数的函数指针--定义一种类型的别名
	typedef int(*lpfnDllFunc1/*别名*/)(char* s, int i, char* out);

	HINSTANCE hDLL;
	lpfnDllFunc1 fun;
	//加载DLL文件,找不到则返回空
	//LoadLibraryEx可以加载DLL模块,而无需调用DLL的DllMain函数。
	//LoadLibraryEx可以针对模块永远不会执行的情况进行优化的方式加载模块,就像加载数据文件一样加载模块。
	//参数参考MSDN 
	//小知识:不管dll原本是什么名字,改了名字后照样可以加载
	hDLL = LoadLibraryEx("../Debug/MyDll.dll",NULL, LOAD_WITH_ALTERED_SEARCH_PATH);

	if (NULL != hDLL)
	{
		//获取DLL中导出函数的地址(函数名)
		fun = (lpfnDllFunc1)GetProcAddress(hDLL, "append_int");
		if (NULL != fun)
		{
			char *out = (char *)malloc(1024);

			//char * int char *
			std::cout<<fun("hello", 1988,out);

			printf("out:%s\n", out);
			printf("out:%d\n", strlen(out));
			free(out);
		}
		//卸载DLL文件
		FreeLibrary(hDLL);
	}
	else
	{
		std::cout << GetLastError() << std::endl;
		std::cout << "not find dll" << std::endl;
	}
}
//隐式链接
void UseImplicit()
{
	char *out = (char *)malloc(1024);
	append_int("hello", 1988, out);
	printf("out:%s\n", out);
	printf("out:%d\n", strlen(out));
	free(out);
}

int main()
{
	if (MOD == 1)
	{
		UseExplicit();
	}
	else {
		UseImplicit();
	}

	system("pause");
	return 0;
}

加载dll报错

https://docs.microsoft.com/zh-cn/windows/win32/api/libloaderapi/nf-libloaderapi-loadlibraryexw
当你得dll依赖了其他dll时,此时加载dll需要你把依赖得所有dll都放在同一目录下,否则会报错找不到动态库模块。
在这里插入图片描述

以下3种加载方式都可以加载。

	//HMODULE m_hDll_HavePrint = LoadLibrary(_T("E://Project//xxxx.dll"));
	//HMODULE m_hDll_HavePrint = LoadLibraryEx(_T("E:\\Project\\xxxx.dll"), NULL, LOAD_WITH_ALTERED_SEARCH_PATH);
	HMODULE m_hDll_HavePrint = LoadLibraryEx(_T(".//xxxx.dll"), NULL, LOAD_WITH_ALTERED_SEARCH_PATH);

获取错误信息GetLastError()

把以下代码放到可能会出错的位置即可。

LPVOID lpMsgBuf;
		FormatMessage(
			FORMAT_MESSAGE_ALLOCATE_BUFFER |
			FORMAT_MESSAGE_FROM_SYSTEM |
			FORMAT_MESSAGE_IGNORE_INSERTS,
			NULL,
			GetLastError(),
			MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
			(LPTSTR)&lpMsgBuf,
			0,
			NULL
		);
		MessageBox((LPCTSTR)lpMsgBuf, _T("Error"), MB_OK | MB_ICONINFORMATION);

修改时间:2021-11-21

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值