C++ dll動態程式庫建置架構筆記(CMake build) -5 如何使用LoadLibraryA()讀取class架構dll
上一篇介紹dll裡建立class,但他是使用header.h+.lib+.dll方式讀取dll程式,這篇要講解我如何使用LoadLibraryA()讀取,先說程式碼會很多,因為要把整個class重新改寫成function型式,這方法應該能讓C++
dll程式在C語言上執行。這其實是我一開始學習建置dll的方式,順便紀錄一下,可能還有更好的方式我還沒研究到。
一、void pointer說明
這個功能很強大,他可以指向所有資料型態的指標,我這邊把他拿來指向整個class。在外部使用的人只會看到這是一個void
pointer,不知道他其實是一個class,所以可以將整個class隱藏在dll內部。
二、建立class指標
假如有一個 class 名稱是 foo。
class foo
{
void Add(int b);
...
};
使用 void pointer 指標指向 class。
//create void pointer typedef void* foo_handle; //void pointer指標指向新建立的class foo_handle foo_h = (foo_handle)new foo();
三、刪除空指標裡的class
當 class 不再使用時,或程式結束時,要將佔用的資源釋放,pointer 要指向
NULL,因為通常會檢查這個指標如果非 NULL 表示指向的 class 還存在著。
//刪除class delete (foo*)foo_h; //將void pointer 指向空 foo_h = NULL;
四、在dll裡建立\刪除class
我這邊使用 extern "C" __declspec(dllexport) 建立dll function。
typedef void* foo_handle;
extern "C" __declspec(dllexport) foo_handle createClass()
{
return (foo_handle)new foo();
}
extern "C" __declspec(dllexport) int destroyClass(foo_handle &handle)
{
if(!handle) return -1;
delete (foo*)handle; //刪除class
handle = NULL; //pointer 指向空
return 0;
}
使用 createClass() 取得新建立 class 的位置,資料型態為 foo_handle。
釋放的時候將 foo_handle 輸入至 destroyClass(),他會刪除 class
並將 foo_handle 指定成 NULL。
五、class裡的函數
要將 class 的指標傳入"foo_handle handle",檢查 handle 是不是 NULL,如果是 NULL
表示 class 沒建立或是已經遭刪除,如果不是 NULL 轉換回原本的class樣子,在進行
class 內部函數的操作。
extern "C" __declspec(dllexport) int Add_C(foo_handle handle, int b)
{
if(!handle) return -1;
foo* p_foo = (foo*)handle; //換回原本class樣子
p_foo ->Add(b); //操作foo裡的Add()
return 0;
}
六、實作Dll部分
這邊使用上一篇文章的範例做延伸。
之前說明過 SV_EXTERN_C → extern
"C",SV_EXPORTS → __declspec(dllexport)。
Dll_class_c\math_class\include\my_math_class_c.h
#ifndef my_math_H_
#define my_math_H_
#include "SVdef.h"
struct newNumber
{
int i;
int j;
};
class math_class
{
public:
newNumber a;
math_class(int value);
void input_a(int value);
void get_a(newNumber &a_);
void Add(int b);
};
// ============ use LoadLibraryA() =============================================
#define checkHandle(_handle) { if (!_handle) return -2; }
typedef void* type_math_handle;
SV_EXTERN_C SV_EXPORTS type_math_handle createMathClass_C(int a);
SV_EXTERN_C SV_EXPORTS int destroyMathClass_C(type_math_handle &handle);
SV_EXTERN_C SV_EXPORTS int get_a_C(type_math_handle handle, int &i, int &j);
SV_EXTERN_C SV_EXPORTS int Add_C(type_math_handle handle, int b);
#endif //my_math_H_
Dll_class_c\math_class\src\my_math_class_c.cpp
#include "my_math_class_c.h"
math_class::math_class(int value)
{
input_a(value);
}
void math_class::input_a(int value)
{
a.i = value;
a.j = value*10;
}
void math_class::get_a(newNumber &a_)
{
a_ = a;
}
void math_class::Add(int b)
{
a.i+=b;
a.j+=b;
}
// ============ use LoadLibraryA() =============================================
SV_EXTERN_C SV_EXPORTS type_math_handle createMathClass_C(int a)
{
return (type_math_handle)new math_class(a);
}
SV_EXTERN_C SV_EXPORTS int destroyMathClass_C(type_math_handle &handle)
{
checkHandle(handle);
delete (math_class *)handle;
handle = NULL;
return 0;
}
SV_EXTERN_C SV_EXPORTS int get_a_C(type_math_handle handle, int &i, int &j)
{
i = j = 0;
checkHandle(handle);
math_class *p_math_class = (math_class *)handle;
newNumber a;
p_math_class->get_a(a);
i = a.i;
j = a.j;
return 0;
}
SV_EXTERN_C SV_EXPORTS int Add_C(type_math_handle handle, int b)
{
checkHandle(handle);
math_class *p_math_class = (math_class *)handle;
p_math_class->Add(b);
return 0;
}
七、實作執行dll的部分
這裡需要定義每個 function 原始的樣子,在來宣告 void pointer 去 handle class "typedef void* type_math_handle;",名稱要與 dll 內設定的一樣,之後將每個 function 的 address 讀入,記得要先創建 class "createMathClass()" 後才能使用,一個簡單的 class 被搞得非常複雜,所以我覺得非必要真的不要用,如果這個 class 很大的話真的會寫得很累人。
Dll_class_c\main\test_withoutLib.cpp
#include <stdio.h>
#include <windows.h>
struct My_math_dll
{
private:
HMODULE hLib;
bool enable = false;
public:
// handle class 的 void pointer
typedef void* type_math_handle;
//定義每個 function 原始的樣子
typedef type_math_handle(*createMathClass_C)(int a);
typedef int(*destroyMathClass_C)(type_math_handle &handle);
typedef int(*get_a_C)(type_math_handle handle, int &i, int &j);
typedef int(*Add_C)(type_math_handle handle, int b);
//宣告 function 在這邊使用時的名稱
type_math_handle math_h = NULL;
createMathClass_C createMathClass;
destroyMathClass_C destroyMathClass;
get_a_C get_a;
Add_C Add;
~My_math_dll()
{
release();
}
int loadAPI()
{
// 讀入 dll
hLib = LoadLibraryA("Dll_class_math_c.dll");
// If the dll file fails, the memory address will be 0.
printf_s("Dll_class_math_c.dll address:%p\n", hLib);
if (!hLib)
{
printf_s("load Dll_class_math_c.dll fail!\n");
return 0;
}
// 將每個 function 的 address 讀入
createMathClass = (createMathClass_C)GetProcAddress(hLib, "createMathClass_C");
destroyMathClass = (destroyMathClass_C)GetProcAddress(hLib, "destroyMathClass_C");
get_a = (get_a_C)GetProcAddress(hLib, "get_a_C");
Add = (Add_C)GetProcAddress(hLib, "Add_C");
// Check the memory address obtained by the function API.
// If the function API fails, the memory address will be 0.
printf_s("API address: %p %p\n", createMathClass, Add);
enable = true;
return 0;
}
void release()
{
if (enable)
{
destroyMathClass(math_h); //刪除 class
FreeLibrary(hLib);
enable = false;
}
}
};
int main()
{
int a = 10;
int b = 20;
int i,j;
My_math_dll dll = My_math_dll();
dll.loadAPI();
dll.math_h = dll.createMathClass(a); //先創建class
dll.get_a(dll.math_h, i, j);
printf("get_a_C %d %d\n", i, j);
dll.Add(dll.math_h, b);
dll.get_a(dll.math_h, i, j);
printf("Add_C %d %d\n", i, j);
dll.release();
return 0;
}
留言
張貼留言