dll動態庫生成與調用(1):生成dll動態庫、C程序調用動態庫
dll動態庫生成與調用(2):Java程序利用JNI、JNA調用動態庫
文章簡介
本文介紹了使用C語言去生成一個動態庫,並使用C程序去調用這個動態庫。
當然,你可能想用C++來創建自己的調用庫,按照文中的步驟直接調用肯定是會出現問題的,這是因爲在C++中,爲了面向對象中的重載等特性,會在編譯中將函數的名字做相應修改,你可以將要調用的內容採用extern "C"
進行修飾。
extern "C"
有兩層含義:
第一,被它限定的函數或變量是extern類型的。
第二,被它修飾的變量和函數是按照C語言方式編譯和連接的。
開發環境
IDE: Visual Studio 2015、IDEA 2019.1
JNA(Java庫): 3.5.2
JDK: 1.8
文件目錄結構
C:
JNI_test項目中的dll.h、dll.c
用於生成dll動態庫文件,JNI_test_call項目中的call.c
用於調用dll庫。
注: 如果是VS新手,建議這兩個項目不要放在同一個解決方案中,爲這兩個項目各創建一個解決方案,能避開一些VS使用上的問題。
一、動態庫的生成
注: 在項目JNI_test中進行操作。
1.新建項目,用於生成dll庫
在VS中新建項目:Win32控制檯應用程序->控制檯應用程序/DLL->空項目
我一般是在控制檯應用程序中進行函數調試,再調VS選項進行DLL生成,這樣比較方便,如果你直接選擇DLL,就不需要調設置了。
設置在 項目屬性頁 的 常規->配置類型 中,將配置類型從 “.exe” 改爲 “.dll” 即可,如下所示:
不一定需要空項目,如果選擇創建的不是空項目,會默認帶幾個文件,沒影響。
2.編寫動態庫的.h頭文件、.c源文件
dll.h
// 宏定義
#define C_API _declspec(dllexport)
// 在DLL中,將被調用的函數
C_API int start(char *);
第2行定義一個宏,用C_API
替代_declspec(dllexport)
。當然,可以不定義宏,直接在函數前面寫_declspec(dllexport)
也是沒問題的。
第4行聲明一個函數,將在dll.c中進行實現。
dll.c
#include<stdio.h>
#include"dll.h"
C_API int start(char *string) {
printf("\n\n");
printf("=========DLL out start=========\n");
printf("call print: %s\n",string);// 打印傳過來的字符傳
printf("=========DLL out end=========\n\n");
return 0;
}
第2行,別忘了包含頭文件。
3.生成動態庫文件
在VS的“生成”菜單中進行生成,會產生和項目名同名的.dll
和.lib
文件,庫的生成就完成了。
二、在C語言程序中調用dll動態庫
在項目JNI_test_call中進行操作。本文寫了2種調用dll的方法。
推薦第二種調用方法。
1.第一種調用方法:僅需要.dll文件(“顯式鏈接”)
注意: 本調用方法採用的是動態庫的顯式鏈接。(可以看文章末尾 補充閱讀第5項)主要需要2個Windows的API:LoadLibrary
和GetProcAddress
。
1.C語言庫代碼:
call.c
#include<stdio.h>
#include<windows.h> // 用於庫調用函數
#include<tchar.h> // 用於_T等編碼轉換宏
int main() {
HINSTANCE hDll;
int(*startCall)(char *);// 函數指針,用於存放dll中要調用的函數
LPCWSTR szString = _T("JNI_test.dll");// 將char字符串轉換爲LPCWSTR格式,不然無法找到dll文件
// 加載庫(有2種加載方式)
//hDll = LoadLibraryEx("E:/JNI_test/JNI_test.dll", NULL, LOAD_WITH_ALTERED_SEARCH_PATH);// 庫的絕對路徑
hDll = LoadLibrary(szString);// 將dll移到當前程序目錄下,可以直接用dll名替代
if (hDll == NULL) { // 庫加載失敗
printf("%s", "failed to load dll!\n");
}
else { // 庫加載成功
// 獲取start函數的內存地址
startCall = GetProcAddress(hDll,"start");
// 利用函數指針調用函數
startCall("hello, I'm JNI_test_call!");
}
FreeLibrary(hDll);
return 0;
}
_T
宏 用於將char字符串轉換成LPCWSTR,詳細說明可以看 本文末尾的 錯誤記錄第1.2和1.3 或者 參考文章第8項。
2.dll文件的調用路徑:
在調用程序生成目錄下(產生.exe
的目錄),放入我們要調用的.dll
文件,並不需要.lib
文件。
2.第二種調用方法:需要.h,.lib,.dll文件(推薦此方法)
1.C語言庫的調用程序代碼:
call.c
#include<stdio.h>
#include"dll.h"
//#pragma comment(lib, "JNI_test.lib")
int main() {
// 當做普通行數調用即可
start("hello,I'm JNI_test_call!");
return 0;
}
2.配置.h
頭文件:
再在call.c
中添加對動態庫頭文件的引用:
#include"dll.h"
3.配置.lib
、.dll
文件:
配置lib和dll文件所在目錄:
配置需要調用的lib文件:
注意: “附加依賴項“可以在call.c代碼中添加#pragma comment(lib, "JNI_test.lib")
來進行代替。
4.運行項目:
最後在VS中運行本項目即可,結果如下:
三、附加內容
1. 錯誤記錄
1.1 LNK2019 LNK1120 無法解析的外部符號 “xxxx1”" ,該符號在函數 xxxx2 中被引用
問題的原因在於,找不到被我們引用的函數的聲明。
xxxx1
是我們要引用的函數,函數的聲明和實現在其它的文件中。
xxxx2
是使用xxxx1
的函數。舉個例子:如果我們在main()
中使用start()
,如果出現此錯誤,那麼會報無法解析的外部符號 “start”" ,該符號在函數main中被引用
解決方法:
-
檢查函數名是否書寫正確,確認一下你包含的頭文件中是否有函數的聲明
-
檢查頭文件配置是否正確
1.2 warning C4133: “函數”: 從“char [xx]”到“LPCWSTR”的類型不兼容
第一種解決方法:(不推薦)
在項目的屬性頁中找到 常規->字符集:把 “使用 Unicode 字符集” 改爲 “使用多字節字符集”,如下所示:
第二種解決方法:
使用_T
宏,對char字符串進行轉換,關鍵代碼如下:
需要包含頭文件:#include<tchar.h>
LPCWSTR szString = _T("JNI_test.dll");// 字符串轉換
hDll = LoadLibrary(szString);// 加載庫
用這種方法,就不需要改變項目所使用的字符集。
1.3 無法加載庫
主要的原因&解決方法:
-
庫的路徑錯誤,查看庫是否存在以及庫的路徑是否在我們配置的地方。
-
顯式加載庫中的字符串編碼格式不符合調用要求,可以參考本文中錯誤記錄的第1.2項,進行處理。
1.4 調整VS項目屬性頁,但卻沒有效果
這個問題在以前的文章中有提到過,VS設置如果沒有效果,那麼應該先檢查 VS項目配置頁 是否和 當前項目生成的配置 相符,如下所示:
如果並不是上面的設置的問題,那麼你可能是你改動的設置對你要解決的問題沒有效果。對此的建議是,將VS的錯誤提示再多在網上進行查詢,找到問題的,如果問題查詢不到,你可以將提示中的關鍵詞抽取出來,列在搜索框中進行查詢。另外,項目中的警告,也應當注意,如果有心有餘力的話,不妨將警告也解決掉吧。
2. 參考文章
- VS 編寫c++dll庫文件(推薦閱讀,有講到本文章中的一些基礎知識)
- c語言調用dll文件
- c語言怎麼調用dll文件?
- LoadLibrary加載動態庫失敗的思考
- loadlibrary()失敗的問題
- warning C4133: “函數”: 從“char [5]”到“LPCWSTR”的類型不兼容
- C語言創建動態dll和調用dll(visual studio 2013環境下)
- 在vs中char類型的實參與LPCWSTR類型的形參類型不兼容怎麼解決?(推薦閱讀,內容提及:UNICODE和ASCII碼之間的關係、LPCTSTR是什麼)
3. 補充閱讀
- #pragma_百度百科
- #ifndef的用法(可使C++中的.h頭文件只被包含一次)
- extern和extern “C”(如果使用C++,則需要了解)
- dllMain函數的作用(這個函數可以不寫,如果沒有,會自動調用缺省的dllMain)
- 使用LoadLibrary調用DLL(文中有介紹“顯式鏈接”和“隱式鏈接”)
- 關於“Error: “const char *” 類型的實參與 "LPCWSTR"類型的形參不兼容”錯誤的解決方案(推薦閱讀,內容提及:字符串編碼格式的介紹)