多多色-多人伦交性欧美在线观看-多人伦精品一区二区三区视频-多色视频-免费黄色视屏网站-免费黄色在线

國內最全IT社區平臺 聯系我們 | 收藏本站
阿里云優惠2
您當前位置:首頁 > php開源 > php教程 > Windows基礎-動態連接庫的導出與發布

Windows基礎-動態連接庫的導出與發布

來源:程序員人生   發布時間:2016-11-24 08:37:38 閱讀次數:2489次

本文章將以1個機器人底盤通訊程序為例,給大家展現動態鏈接庫的1種簡單暴力但靈活安全的用法:


將你的最外層類修改成適于封裝成DLL的模樣

1般的程序會包括多層的類封裝,這里我們將最外層類撤除,也就是說這個類拆掉后,類里面的函數都成了全局函數。
如果你有1大把public變量的話,聰明的你可以將變量改成查詢函數,將其內聯并直接return便可:

// 1種查詢底盤運行狀態的函數,返回數據包的首地址并報告包長度 unsigned char* QueryState(int queryFlag, unsigned short* pkgLength); // 常見的查詢函數 // 1個人物跟蹤器狀態報告函數(直接返回變量的查詢函數) unsigned int QueryPerson(int queryFlag); // 里面1個大switch然后各種return

如果有構造和析構函數,在public里另寫1個Init和Release函數。

導出DLL(共3步)

第1步:將最外層的類拆開

// 假定我們有這樣1個類 class MecanumController { public: enum ChassisState { CHASSIS_BASE, CHASSIS_VOLTAGE, CHASSIS_CURRENT, CHASSIS_ERRCODE, // ... }; public: bool Open(const char* port_name); unsigned char SendByte(unsigned char data); unsigned char Move(unsigned short TanslationSpeed, short Yaw, short RotationAngleSpeed); unsigned char* QueryState(int queryFlag, unsigned int bytesLength); private: HANDLE usart_handle; unsigned char ReceiveByte(unsigned char data); void Helper(void* lp); };

可以發現,這里有1些private變量,依照常理它應當被封裝起來不可見,這里我們拆開后照舊寫在外便可。
清靜起見,你可以把private里面的聲明放在源文件代碼的上面。
enumtypedef,如果你的函數的參數直接把枚舉名字寫上去了,建議改成int來減少改動。
清靜起見,把enumtypedef放在源文件代碼的上面。
PS:你也能夠再寫1個頭文件,如果頭文件只含枚舉等方便調用的定義內容,還可與DLL1起發布,便于開發者的查看,視封裝的復雜程度而定,自己進行平衡。
頭文件變化以下,class段完全刪除,只剩下全局聲明:

// 類中的public函數 bool Open(const char* port_name); unsigned char SendByte(unsigned char data); unsigned char Move(unsigned short TanslationSpeed, short Yaw, short RotationAngleSpeed); void* QueryState(int queryFlag, unsigned int bytesLength);

在源文件里的變化不過是刪除MecanumController::并把private里的那些聲明拷貝進去。

第2步:在頭文件加入關鍵字

此時,我們的頭文件里只剩下了public函數。
現在,我們在頭文件中加入:

#define XXXAPI extern "C" _declspec(dllexport) // 叫XXXAPI只是1個習慣,XXX代表了1些名字,你也能夠起1個其他的名字或直接不define

并修改這幾個函數聲明,修改后的頭文件像這樣:

//#include"xxx" #define output extern "C" _declspec(dllexport) output bool Open(const char* port_name); output unsigned char SendByte(unsigned char data); output unsigned char Move(unsigned short TanslationSpeed, short Yaw, short RotationAngleSpeed); output void * QueryState(int queryFlag, unsigned int bytesLength);

第3步:編譯并導出DLL

如果你的工程不是DLL的,可以在解決方案里新建1個這里寫圖片描述 并按之前所做的去配置環境,注意,你輸出的DLL名稱是和你工程名1樣的,建議起好名字,如果不嫌麻煩的話你導出后你可以重命名1下。

按這樣配置后就是1個DLL工程了。
這里寫圖片描述

如果你是win32工程且設置為DLL空項目,照上面修改以后在解決方案管理器中對其右鍵點生成便可導出。
這里寫圖片描述

沒有手抖的話,在輸出里會看到以下信息:
這里寫圖片描述

至此,我們導出了1個DLL。

但是DLL還不能直接供他人使用,我們需要寫1個專用于開發者的DLL頭文件


發布DLL(共兩步)

讓我們先看看動態加載DLL是如何實現的

Windows提供以下Windows API用于DLL的裝載、報告函數入口和DLL的卸載
分別是:

// DLL裝載 HINSTANCE LoadLibraryW(LPCWSTR lpLibFileName); // Unicode工程使用wchar_t HINSTANCE LoadLibraryA(LPCSTR lpLibFileName); // MultiByte工程使用char // 報告函數入口 FARPROC GetProcAddress(HINSTANCE hModule, _In_ LPCSTR lpProcName); // DLL卸載 BOOL FreeLibrary(HINSTANCE hLibModule);

LoadLibraryWLoadLibraryA可以通過宏定義LoadLibrary自動選擇正確的函數,所以我們直接叫這個函數為LoadLibrary,如果函數成功讀取并載入DLL于內存,則返回1個非0的HINSTANCE變量,否則返回NULL

FARPROC是1個整形變量(int),在minwindef.h中定義。

#ifdef _WIN64 typedef INT_PTR (FAR WINAPI *FARPROC)(); ... #else typedef int (FAR WINAPI *FARPROC)(); ...

在不同解決方案下的長度視工程的目標平臺而定,x64對應64bit整形,x86對應32bit整形,其它未定義的目標平臺同32bit整形。GetProcAddress本身會返回1個存儲函數入口地址的整形變量。
FreeLibrary正常使用便可,傳入HINSTANCE,如果DLL被成功載入則通過DLL載入時給出的HINSTANCE值來卸載DLL。

1個DLL通過LoadLibraryFreeLibrary可以被屢次使用,對1個DLL文件第1次使用LoadLibrary時,Windows會檢查并將DLL,如果DLL適用則載入內存,DLL占用的計數器加1,當DLL被載入后繼續被其它代碼中的LoadLibrary使用時,Windows會制作1個內存映照來提高空間效力,計數器繼續加1。FreeLibrary是將計數器減1,計數器為0時,Windows從內存中卸載DLL,否則只刪除對應HINSTANCE的映照。(個人理解,不管第幾次載入DLL,DLL只有1個副本存在于內存中,且每次載入都會產生1個內存映照(鏡像)以便于資源管理,對釋放而言,計數器為0時除刪除映照外還多了1個delete操作)

終究我們要封裝成1個類供開發者使用:流程是LoadLibrary,如果成功則用GetProcAddress初始化函數入口,釋放時履行FreeLibrary

第1步:編寫用戶使用的頭文件

我們給開發者的時候還是1個類封裝,頭文件內容以下:

// MecanumController.h #pragma once #include<Windows.h> class MecanumController { public: typedef float * State_Value; typedef unsigned short * State_Code; typedef unsigned char * State_Package; enum ChassisState { CHASSIS_BASE, CHASSIS_VOLTAGE, CHASSIS_CURRENT, CHASSIS_ERRCODE }; public: // INIT&UINIT MecanumController(const char* port_name); // 不建議直接使用,直接使用不能肯定實例是不是可用,且產生未知的dll計數 void Release() { // 實例可用時卸載實例 FreeLibrary(hdll); } __inline static MecanumController * CreateInstance(const char* chassis_port_name) { HINSTANCE hd=LoadLibrary(L"MecanumController.dll"); if(hd == NULL) return NULL; FreeLibrary(hd); return new MecanumController(chassis_port_name); } // 如果實例可工作,返回1個實例地址,否則返回NULL // FUNCTION unsigned char(*SendByte)(unsigned char data); unsigned char(*Move)(unsigned short TanslationSpeed, short Yaw, short RotationAngleSpeed); void *(*QueryState)(int queryFlag, unsigned int bytesLength); private: HINSTANCE hdll; }; MecanumController::MecanumController(const char* port_name) { // 嘗試載入DLL hdll = LoadLibrary(L"MecanumController.dll"); if (hdll == NULL) return; // 初始化串口,這里外部有helper來保證指定串口可用,普通場景不建議在這里寫1個容易失敗的流程 ((bool(*)(const char* port_name))GetProcAddress(hdll, "Open"))(port_name); // 配置函數入口 SendByte = (unsigned char(*)(unsigned char data))GetProcAddress(hdll, "SendByte"); Move = (unsigned char(*)(unsigned short TanslationSpeed, short Yaw, short RotationAngleSpeed))GetProcAddress(hdll, "Move"); QueryState = (void *(*)(int queryFlag, unsigned int bytesLength))GetProcAddress(hdll, "QueryState"); }

初始化分為兩個函數,其中構造函數去履行不會出錯的流程,專有1個實例化函數來履行容易出錯的流程,在確保成功后返回1個實例。

這個類里面長相奇異的就是函數指針了:

// FUNCTION unsigned char(*SendByte)(unsigned char data); unsigned char(*Move)(unsigned short TanslationSpeed, short Yaw, short RotationAngleSpeed); void *(*QueryState)(int queryFlag, unsigned int bytesLength);

由于運算符的優先級關系,我們寫1個函數原型的指針時是這樣
返回類型 (*名字)(參數),這樣就給機器1個帶棧模型的指針。
使用GetProcAddress時需要類型轉換:

SendByte = (unsigned char(*)(unsigned char data))GetProcAddress(hdll, "SendByte"); Move = (unsigned char(*)(unsigned short TanslationSpeed, short Yaw, short RotationAngleSpeed))GetProcAddress(hdll, "Move"); QueryState = (void *(*)(int queryFlag, unsigned int bytesLength))GetProcAddress(hdll, "QueryState");

你也能夠直接靠GetProcAddress履行1個函數:

((bool(*)(const char* port_name))GetProcAddress(hdll, "Open"))(port_name);

為了便于開發者使用并查詢類型定義,我們還要把定義寫進去。
你也能夠寫1個專門的xxxdef.h來存儲大量的定義,但也有可能可能破壞了簡單的封裝,請自行決定。

public: typedef float * State_Value; typedef unsigned short * State_Code; typedef unsigned char* State_Package; enum ChassisState { CHASSIS_BASE, CHASSIS_VOLTAGE, CHASSIS_CURRENT, CHASSIS_ERRCODE };

第2步:測試你的DLL

我們寫1個rundll的程序吧:

// App.c #include "MecanumController.h" #include <iostream> using namespace std; int main() { auto chassis = MecanumController::CreateInstance("COM3"); if (!chassis) { cerr << "找不到MecanumController.dll" << endl; return -1; } for (size_t i = 0; i < 100; i++) chassis->Move(i,0,0); system("pause"); return 0; }

如果1氣呵成,恭喜你,你的DLL可以供他人使用了,同源碼發布1樣,還是要注意目標平臺和Windows版本等兼容性問題。

生活不易,碼農辛苦
如果您覺得本網站對您的學習有所幫助,可以手機掃描二維碼進行捐贈
程序員人生
------分隔線----------------------------
分享到:
------分隔線----------------------------
關閉
程序員人生
主站蜘蛛池模板: 久久精品亚洲综合 | 99久久精品国内 | 热色综合 | 午夜视频网址 | 日本大胆欧美艺术337p | 日本-区二区三区免费精品 日本人69式视频最长 | 成年香蕉大黄美女美女 | 精品国产福利在线观看网址2022 | 性xxxx欧美高清 | 免费看欧美毛片大片免费看 | 欧美激情精品久久久久久久九九九 | 日本免费第一区二区三区 | 九色 在线| 欧美色另类| 日韩a级毛片免费视频 | 精彩视频一区二区三区 | 69视频网站 | 91精品91| 亚洲成在线观看 | 天堂在线视频精品 | 变态 另类 国产 亚洲 | 黄色免费在线网站 | 国产永久高清免费动作片www | 亚洲欧美国产精品久久久 | 麻豆亚洲精品一区二区 | 日韩麻豆| 久久久久久久尹人综合网亚洲 | 免费在线观看一级毛片 | 欧美日韩中文字幕 | 美国人和狍xxxx视频 | 成人αv在线视频高清 | 日韩欧美中文字幕一区二区三区 | 午夜在线视频 | 亚欧精品一区二区三区 | 国产一区视频在线播放 | 亚洲精品亚洲人成毛片不卡 | 欧美高清另类videosbestsex | 日本无卡码免费一区二区三区 | www.中文字幕在线 | 亚洲成a人片在线观看www流畅 | 高清国产一区二区 |