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; }
留言
張貼留言