vs编译生成动态库

说明

windows版本,vs2019

创建一个动态库

新建一c++项目,创建一个dll类型项目。
在头文件中添加一个mylib.h文件:
·

#pragma once

#ifndef MYLIB_H
#define MYLIB_H

extern "C" __declspec(dllexport) void Hello();
extern "C" __declspec(dllexport) int Add(int a, int b);

template<typename T>
 __declspec(dllexport) T __stdcall Jiafa(T a, T b) {
    return a + b;
}

template __declspec(dllexport) int __stdcall Jiafa<int>(int a, int b);
template __declspec(dllexport) double __stdcall Jiafa<double>(double a, double b);

#endif

在该文件之中,声明了两个需要导入到dll中的普通函数。
同时,声明和实现了一个模板函数,并将模板函数进行了实例化。

在源文件中添加一个c++文件

// mylib.cpp
#include "pch.h"
#include <iostream>

// mylib.cpp
#include "pch.h"
#include <iostream>
#include "mylib.h"

using namespace std;

void Hello() {
    cout << "Hello, world!" << endl;
}

int Add(int a, int b) {
    return a + b;
}

编译生成dll文件。注意,生成的.dll文件和.lib文件和头文件,是需要提供给其他项目工程(调用dll中的函数)的。

问题:为什么要引用pch.h文件?

预编译使用,并且需要放在引用文件的首行。

问题:extern “C” 的作用有哪些?

extern “C” 是一个 C++ 语言的特性,用于指示编译器将某段代码按照 C 语言的规则进行编译和链接。在编写动态库时,使用 extern “C” 可以确保动态库中的函数能够按照 C 语言的规则进行导出和调用,从而提高动态库的可移植性和互操作性。

extern “C” 的作用包括:

消除 C++ 名字修饰
在 C++ 中,函数名会被编译器进行修饰,以便支持函数重载等特性。这种修饰会将函数名变得很长,不方便在 C 语言中进行链接。使用 extern “C” 可以告诉编译器不要对函数名进行修饰,从而使函数名变得简短、易于理解,并且能够在 C 语言中进行链接。

例如,在 C++ 中定义一个函数:

	int Add(int a, int b) {
	    return a + b;
	}

编译器会将函数名修饰为 _Z3Addii,而在使用 extern “C” 修饰后,编译器会将函数名保持不变,即为 Add。

指定函数调用约定
在不同的操作系统和编译器中,函数调用的约定可能不同。使用 extern “C” 可以指定函数调用的约定为 C 语言的约定,从而保证函数能够正确地被调用。

例如,在 Windows 操作系统中,使用 __stdcall 约定传递函数参数和返回值。在使用 extern “C” __stdcall 修饰后,编译器会生成按照 __stdcall 约定进行传递参数和返回值的代码。

支持动态链接
动态链接库中的函数需要按照 C 语言的规则进行导出,以便其他程序能够正确地链接和调用这些函数。使用 extern “C” 修饰动态链接库中的函数可以确保它们按照 C 语言的规则进行导出,并且能够被其他程序正确地链接和调用。

在使用 extern “C” 修饰动态链接库时,需要注意的是,在 C++ 中,函数参数和返回值的类型和数量可能会影响函数的名字,因此需要确保函数参数和返回值的类型和数量与函数声明中的一致,否则可能导致链接错误。

问题:模板类、模板函数的导出,需要注意什么呢?

注意,需要将模板函数实例化。

使用dll

创建一个新项目,用于测试和调用dll中的函数。
我手动创建了一个include文件夹,用于存放dll相关的头文件。需设置如下:
在这里插入图片描述
我手动创建了lib文件夹,用于存放dll和lib文件,将dll和lib文件拷贝到该路径,并进行设置。
在这里插入图片描述
设置“附加依赖项”。
在这里插入图片描述
写一个cpp文件,调用dll中的函数。

// main.cpp

#include <iostream>
#include <Windows.h>
#include "mylib.h"

using namespace std;

extern __declspec(dllimport) int __stdcall Jiafa<int>(int a, int b);
extern __declspec(dllimport) int Add(int a, int b);

int main() {
    int result1 = Add(1, 2);
    cout << "result = " << result1 << endl;

    HINSTANCE hDLL = LoadLibrary(TEXT("mydll.dll"));
    if (hDLL == NULL) {
        cout << "Error: " << GetLastError() << endl;
        return 1;
    }
    typedef void (*HelloFunc)();
    HelloFunc helloFunc = (HelloFunc)GetProcAddress(hDLL, "Hello");
    if (helloFunc == NULL) {
        cout << "Error: " << GetLastError() << endl;
        return 2;
    }

    helloFunc();

    typedef int(*pAdd)(int, int);
    pAdd Add = (pAdd)GetProcAddress(hDLL, "Add");
    if (Add != NULL) {
        int result = Add(1, 2);
        cout << "result = " << result << endl;
    }

    int result = Jiafa<int>(3, 4);
    cout << "result = " << result << endl;

    FreeLibrary(hDLL);
    return 0;
}

问题:设置正确仍然无法链接到dll。

将dll拷贝到了exe所在路径下。这个问题待修改。

最后输出如下:
在这里插入图片描述

OK,所有的函数都调用到了。

模板类导出到DLL中,并测试调用

具体工程的设置,类同上文,这里不具体描述,直接给出代码。

DLL中的头文件和源文件

//头文件,tmpclss.h
#pragma once

#ifndef TMPCLSS_H
#define TMPCLSS_H

#ifdef MYTEMCLASS_EXPORTS
#define TMPCLSS_API __declspec(dllexport)
#define TMPCLSS_TEMPLATE __declspec(dllexport)
#else 
#define TMPCLSS_API __declspec(dllimport)
#define TMPCLSS_TEMPLATE __declspec(dllimport)
#endif

template <typename T>
class TMPCLSS_TEMPLATE Tmpclss
{
public:
	Tmpclss() {};
	T Add(T a, T b);
};
extern "C" template  class TMPCLSS_TEMPLATE Tmpclss<int>;
extern "C" template  class TMPCLSS_TEMPLATE Tmpclss<double>;

class TMPCLSS_API EasyClss
{
public:
	EasyClss() {};
	int Add(int a, int b);
};
#endif

源文件tmpclss.cpp

#include "pch.h"
#include "tmpclss.h"


using namespace std;

template <typename T>
T Tmpclss<T>::Add(T a, T b)
{
	return a + b;
}


int EasyClss::Add(int a, int b)
{
	return a + b;
}

测试调用代码如下:

#include <iostream>
#include "tmpclss.h"

using namespace std;

int main()
{
	cout << "Hello World!" << endl;
	Tmpclss<int> hisclass;
	int result = hisclass.Add(7, 8);
	cout << "result = " << result << endl;

	EasyClss easyobject;
	int result1 = easyobject.Add(7, 8);
	cout << "result1 = " << result1 << endl;	
	return 0;
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值