c++在进行库开发的时候通常会依赖其它第三方库,或者是用c++部署深度学习模型时需要在库中加载深度学习模型。如果我们的库发布给业务端用,则需要把第三方库或者深度学习模型一并打包发送给业务端,并且还要配置好路径,这样做灵活性低,容易出错,很容易丢失依赖文件导致业务端实现不了功能。本文将记录一种方法,使用资源加载与释放的方式将第三方依赖库或深度学习模型一并发布给业务端使用。下面以文件followhand.onnx加载与释放为例:

1.c++ exe模式下的资源加载与释放

第一步:将鼠标移至项目名称位置,右键->添加->资源
avatar
第二步:导入资源,并输入资源类型(如:ONNX等)
avatar

第三步:打开resourse.h文件查看资源ID
avatar

操作完以上步骤接下来编写代码进行资源解析释放,代码如下:

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
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <Windows.h>
#include "resource.h"

using namespace std;

typedef VOID(*Func)(VOID);

BOOL ReleaseResource(UINT resourceId, TCHAR* resourceType, TCHAR* fileName)
{
// 查找资源
HRSRC hRsrc = FindResource(NULL, MAKEINTRESOURCE(resourceId), resourceType);
if (hRsrc == NULL)
return FALSE;

// 获取资源大小
DWORD dwSize = SizeofResource(NULL, hRsrc);
if (dwSize <= 0)
return FALSE;

// 载入资源
HGLOBAL hGlobal = LoadResource(NULL, hRsrc);
if (hGlobal == NULL)
return FALSE;

// 锁定资源,并返回指向资源第一字节的指针
LPVOID lpRes = LockResource(hGlobal);
if (lpRes == NULL)
return FALSE;

// 创建文件
HANDLE hFile = CreateFile(fileName, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
if (hFile == NULL)
return FALSE;

// 将资源写入本地
DWORD dwWriten = 0;
BOOL bRes = WriteFile(hFile, lpRes, dwSize, &dwWriten, NULL);
if (bRes == FALSE || dwWriten <= 0)
return FALSE;

CloseHandle(hFile);
return TRUE;
}

int main()
{
// 将UNICODE字符转为多字节
char ty_name[10] = "ONNX";
TCHAR TY_NAME[100];

#ifdef UNICODE
MultiByteToWideChar(CP_ACP, 0, ty_name, -1, TY_NAME, 100);
#else
strcpy(TY_NAME, ty_name);
#endif // UNICODE

char res_name[20] = "followhand.onnx";
TCHAR RES_NAME[200];
#ifdef UNICODE
MultiByteToWideChar(CP_ACP, 0, res_name, -1, RES_NAME, 100);
#else
strcpy(RES_NAME, res_name);
#endif // UNICODE

BOOL res = ReleaseResource(IDR_ONNX1, TY_NAME, RES_NAME);

return 0 ;
}

ReleaseResource()函数会将资源释放到.exe同级目录下。

2.c++ dll模式下的资源加载与释放

上面我们在exe模式下获取释放了资源,但是如果在dll模式下ReleaseResource()函数中的FindResource()函数则会返回null,即找不到资源,查看错误码是1813。
出现上述问题的主要原因是FindResource()的文件句柄传递问题,在上面我们将文件句柄传递为NULL,即为.exe文件句柄,所以在dll模式下找不到我们要找的资源,因此我们需要获取我们的文件句柄,将其传递给FindResource()函数。获取文件句柄的方式是调用GetModuleHandle()函数。

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
36
37
38
39
40
41
42
43
44
45
46
47
48
49
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <Windows.h>
#include "../resource.h"

using namespace std;

typedef VOID(*Func)(VOID);

BOOL ReleaseResource(TCHAR* fileName)
{
// 获取文件句柄
HMODULE handle = GetModuleHandle(TEXT("dzFollowHandAI.dll"));

// 找到资源
HRSRC hRsrc = FindResource(handle, MAKEINTRESOURCE(IDR_ONNX1), TEXT("ONNX"));
if (hRsrc == NULL)
return FALSE;

// 获取资源大小
DWORD dwSize = SizeofResource(handle, hRsrc);
if (dwSize <= 0)
return FALSE;

// 载入资源
HGLOBAL hGlobal = LoadResource(handle, hRsrc);
if (hGlobal == NULL)
return FALSE;

// 锁定资源,并返回指向资源第一字节的指针
LPVOID lpRes = LockResource(hGlobal);
if (lpRes == NULL)
return FALSE;

// 创建文件
HANDLE hFile = CreateFile(fileName, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
if (hFile == NULL)
return FALSE;

// 将资源写入本地
DWORD dwWriten = 0;
BOOL bRes = WriteFile(hFile, lpRes, dwSize, &dwWriten, NULL);
if (bRes == FALSE || dwWriten <= 0)
return FALSE;

CloseHandle(hFile);
return TRUE;
}

其它操作步骤同上。