日本搞逼视频_黄色一级片免费在线观看_色99久久_性明星video另类hd_欧美77_综合在线视频

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

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

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

本文章將以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版本等兼容性問題。

生活不易,碼農辛苦
如果您覺得本網站對您的學習有所幫助,可以手機掃描二維碼進行捐贈
程序員人生
------分隔線----------------------------
分享到:
------分隔線----------------------------
關閉
程序員人生
主站蜘蛛池模板: 国产精品18久久久久久久网站 | 国产一区二区视频在线 | 视频福利一区 | 欧美成人生活片 | 欧美福利一区 | 夜夜操网站 | 成人免费毛片片v | 亚洲一区二区精品视频 | 综合久久久久久久 | 在线久草| av播播 | 天堂网站 | 欧美午夜精品一区二区三区电影 | 久久h| 国产午夜精品久久久久久免费视 | 国产av毛片 | 国产在线视频一区 | 精品国产乱码久久久久久丨区2区 | 国产精品久久久久久久免费看 | 日皮视频免费观看 | 日本一区二区三区免费观看 | 懂色av午夜一区二区三区蜜桃 | 久久视频在线 | 日本激情一区二区 | 亚洲精品成人 | 99精品在线视频观看 | 国产精品久久久免费视频 | 国产精品久久一区二区三区, | 久久成人免费视频 | 粉嫩精品一区二区三区在线观看 | 97精品一区二区三区 | 国产区一区二区三区 | 黄色毛片在线 | 国产综合一区二区 | 欧美一区二区三区免费 | 人人爱av | 秋霞在线观看秋 | www.国产毛片 | 在线观看视频黄 | 99精品热| 国产一区二区三区久久 |