Qt、C++与C#动态调用C语言封装的dll

RT
本文记录使用C封装动态库dll,并使用C++、Qt及C#进行dll库的显式调用方式。
C语言封装dll与C++有不同,只做记录。文章包含dll项目源码与C++调用方式及Qt调用方式。
本文项目使用VS2019编译。项目地址:https://github.com/LisonLiou/dll_master

1. C语言dll项目master.h与master.c文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// master.h
#ifndef MASTER_H
#define MASTER_H

#define MASTER_API __declspec(dllexport)

#ifdef __cplusplus
extern "C"
{
#endif // __cplusplus

MASTER_API int divide(int param);


#ifdef __cplusplus
}
#endif // __cplusplus


#endif // !

1
2
3
4
5
6
7
8
// master.c
#include "master.h"

int divide(int param)
{
return param / 2;
}

2. C++调用方式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#include <iostream>
#include <Windows.h>
#include "cdll/master.h"

typedef int __declspec(dllimport)(*FUNC_divide)(int arg);

int main()
{
HINSTANCE hDll = LoadLibrary(L"cdll\\Dll_Master.dll");

if (hDll == NULL) {
std::cout << "dll load failed " << GetLastError();
}
else {
FUNC_divide eval;
eval = (FUNC_divide)GetProcAddress(hDll, "divide");
int ret = eval(13);

std::cout << "Hello World! " << ret << "\n";

FreeLibrary(hDll);
}
}

3. Qt调用方式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#include <QtCore/QCoreApplication>
#include <QDebug>
#include <QLibrary>

typedef int (*FUNC_divide)(int arg);

int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);

QString pathDll = "cdll/Dll_Master.dll";
QLibrary master(pathDll);
if (master.load()) {

qDebug() << "ffffffffff";

FUNC_divide divide = (FUNC_divide)master.resolve("divide");
qDebug() << "Hello world "<<divide(19);

master.unload();
}
else {
qDebug() << "dll load failed";
}

return a.exec();
}

4. C#调用方式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35

[DllImport("kernel32.dll")]
private extern static IntPtr LoadLibrary(String path);//path 就是dll路径 返回结果为0表示失败。
[DllImport("kernel32.dll")]
private extern static IntPtr GetProcAddress(IntPtr lib, String funcName);//lib是LoadLibrary返回的句柄,funcName 是函数名称 返回结果为0标识失败。
[DllImport("kernel32.dll")]
private extern static bool FreeLibrary(IntPtr lib);

//声明委托
//[UnmanagedFunctionPointerAttribute(CallingConvention.Cdecl)]
[UnmanagedFunctionPointerAttribute(CallingConvention.StdCall)]
delegate int divide(int arg);

static void Main(string[] args)
{
string dllPath = Environment.CurrentDirectory + "\\cdll\\Dll_Master.dll";

IntPtr hLib = LoadLibrary(dllPath);//加载函数
IntPtr apiFunction1 = GetProcAddress(hLib, "divide");//获取函数地址

int i = Marshal.GetLastWin32Error();
if (apiFunction1.ToInt32() == 0)//0表示函数没找到
return;

//获取函数接口,相当于函数指针
divide div = Marshal.GetDelegateForFunctionPointer(apiFunction1, typeof(divide)) as divide;

Console.WriteLine(div(22));

// //释放句柄
FreeLibrary(hLib);

Console.Read();
}

5. dll项目生成事件之后的宏示例

用于在dll编译成功后将文件拷贝到引用dll的项目的输出路径,具体可根据情况自行编写自定义build步骤bash

1
2
3
4
5
xcopy $(LocalDebuggerWorkingDirectory)master.h  D:\repos\c\Dll_Master_Test\cdll /e/h/y/f 
&& xcopy $(TargetPath) D:\repos\c\Dll_Master_Test\cdll /e/h/y/f
&& xcopy $(TargetPath) D:\repos\c\Dll_Master_TestQt\cdll /e/h/y/f
&& xcopy $(TargetPath) D:\repos\c\Dll_Master_TestCSharp\cdll /e/h/y/f

注意事项:

  1. dll文件存放位置需与调用方可执行文件在相同路径,具体看代码
  2. System.BadImageFormatException:“试图加载格式不正确的程序的解决办法 因dll是32位程序,C#调用方可能默认为x64,需要将生成的目标平台改为x86,与dll的目标平台保持一致