參考了大佬的文章
首先,體會一下靜態編譯:
創建Win32Project,選DLL,添加一個.h和.cpp文件
點擊生成解決方案,然后去debug目錄下拷貝.lib和.h文件。
新建一個控制台程序,添加一個main.cpp,效果如下:
(也可以在vs工具欄里添加,不過寫代碼更快更易懂方便)
靜態編譯部分到此結束。
以下是動態編譯:
創建win32動態dll項目,添加.h和.cpp文件
在同一個工作環境下添加另一個win控制台應用程序,添加main文件如下
這樣以后就可以調用了,DLL內的函數分為兩種:
(1)DLL導出函數,可供應用程序調用;
(2) DLL內部函數,只能在DLL程序使用,應用程序無法調用它們。
以上就是動態鏈接庫內容,編寫dll時還可以添加一個.def文件,專門用於描述導出什么函數作為接口,如下:
在之前dll的.h和.cpp基礎上作改動:(頭文件里可以不用聲明deal函數)
以上全部內容,手把手跟着做就可以,優缺點設計原因自己解釋。
DLL的靜態調用:
(與動態調用的不同點:編譯過程中就通過lib文件向exe文件中導入函數的信息,所以就不存在運行時再LoadLibrary)
(與普通函數的不同點:普通函數調用查函數表找地址運行就好,DLL函數查表得dll內存中位置及該函數序號,然后找dll調用該函數。ps:會有DllMain函數的調用,在該進程/線程開始及結束時)
總而言之,靜態調用相對方便,windows幫了不少忙,動態調用很麻煩,但靈活性高(運行效率高)
(1)告訴編譯器與DLL相對應的.lib文件所在的路徑及文件名,#pragma comment(lib,"dllTest.lib")就是起這個作用。
程序員在建立一個DLL文件時,連接器會自動為其生成一個對應的.lib文件,該文件包含了DLL 導出函數的符號名及序號(並不含有實際的代碼)。在應用程序里,.lib文件將作為DLL的替代文件參與編譯。
(2)聲明導入函數,extern "C" __declspec(dllimport) add(int x,int y)語句中的__declspec(dllimport)發揮這個作用。
靜態調用方式不再需要使用系統API來加載、卸載DLL以及獲取DLL中導出函數的地址。這是因為,當程序員通過靜態鏈接方式編譯生成應用程序時,應用程序中調用的與.lib文件中導出符號相匹配的函數符號將進入到生成的EXE 文件中,.lib文件中所包含的與之對應的DLL文件的文件名也被編譯器存儲在 EXE文件內部。當應用程序運行過程中需要加載DLL文件時,Windows將根據這些信息發現並加載DLL,然后通過符號名實現對DLL 函數的動態鏈接。這樣,EXE將能直接通過函數名調用DLL的輸出函數,就象調用程序內部的其他函數一樣。
關於DllMain函數我覺得百度百科的這一篇很詳盡
我在win32 dll項目.cpp文件里聲明DllMain函數,在vs項目屬性-->C/C++預編譯器去掉_USRDLL一項,再自己寫DllMain如下
DllMain的官方聲明如下:
BOOL WINAPI DllMain( _In_ HINSTANCE hinstDLL, // 指向自身的句柄 _In_ DWORD fdwReason, // 調用原因 _In_ LPVOID lpvReserved // 隱式加載和顯式加載 ); // 以上內容來自MSDN
關於其他語言調用DLL
1 #ifndef LIB_H 2 #define LIB_H 3 int /*__stdcall*/ add(int x, int y); 4 5 #endif // LIB
關於調用DLL中的全局變量
/*DLL項目部分*/ //DLL.h #ifndef LIB_H #define LIB_H extern "C" int /*__stdcall*/ add(int x, int y); extern "C" int dllGlobalVar; #endif // LIB //DLL.cpp #include "WinDll.h" #include <Windows.h> #include <stdio.h> int dllGlobalVar = 1997; int deal(int x, int y){ return 100 - x - y; } int /*__stdcall*/ add(int x, int y) { return deal(x, y); } BOOL APIENTRY DllMain(HANDLE hModule, DWORD ul_reason_for_call, LPVOID lpReserved) { switch (ul_reason_for_call) { case DLL_PROCESS_ATTACH: printf("procss attach"); dllGlobalVar = 1157; break; case DLL_PROCESS_DETACH: printf("procss detach, global var is%d\n", dllGlobalVar); dllGlobalVar = 0; break; case DLL_THREAD_ATTACH: printf("thread attach"); break; case DLL_THREAD_DETACH: printf("thread detach"); break; default: printf("can't default"); } return TRUE; } /* .def文件 LIBRARY WinDll EXPORTS add @ 1 dllGlobalVar DATA */ /*控制台程序部分*/ //main.cpp #include <stdio.h> #include <Windows.h> #pragma comment(lib, "..\\Debug\\WinDll.lib") extern "C" int __declspec(dllimport) dllGlobalVar; extern "C" int __declspec(dllimport) add(int, int); int main(int argc, char **argv) { printf("\ndllGLobalVar:%d\n", dllGlobalVar); dllGlobalVar = 2018; return 0; }
以上即C代碼導出dll的全部內容,下面進入到C++導出類:
懶得寫了,C++類的導出和C差不多,類的聲明如下
#ifndef POINT_H #define POINT_H #ifdef DLL_FILE class __declspec(dllexport) point //導出類point #else class __declspec(dllimport) point //導入類point #endif { public: float y; float x; point(); point(float x_coordinate, float y_coordinate); }; #endif
這樣的話生成lib和dll文件以后拷到需要的工程目錄下,
#pragma comment(lib, "path")
一句就可以了。
例如OpenCV庫,就需要在QT的pro文件里寫上
INCLUDEPATH += D:/opencv/build/include CONFIG(debug, debug|release): { LIBS += -LD:/opencv/build/x64/vc12/lib \ -lopencv_core2413d \ -lopencv_imgproc2413d \ -lopencv_highgui2413d \ -lopencv_ml2413d \ -lopencv_video2413d \ -lopencv_features2d2413d \ -lopencv_calib3d2413d \ -lopencv_objdetect2413d \ -lopencv_contrib2413d \ -lopencv_legacy2413d \ -lopencv_flann2413d } else:CONFIG(release, debug|release): { LIBS += -LD:/opencv/build/x64/vc12/lib \ -lopencv_core2413 \ -lopencv_imgproc2413 \ -lopencv_highgui2413 \ -lopencv_ml2413 \ -lopencv_video2413 \ -lopencv_features2d2413 \ -lopencv_calib3d2413 \ -lopencv_objdetect2413 \ -lopencv_contrib2413 \ -lopencv_legacy2413 \ -lopencv_flann2413 }
目的就是導入庫文件來進行靜態鏈接,可見我幾乎每天都在用但是今天才發現。
類的動態編譯也一樣,略。
關於DLL的方面告一小結,接下來要去整MFC了。
-------------2018.01.11----------------