一、需求闡述:
如果我們的項目利用c#開發,到了開發後期需要和java組進行合作,其中有一部分業務邏輯利用c#已經code completed,那麼我們可能會考慮用java來調用現成的c#dll實作需求。前幾天工作上正好遇到這樣一個問題,于是記下開發過程。
當然這隻是個假設,具體情況具體分析,個人認為重構代碼才是王道……
二、原理說明:
其實具體原理我也沒弄太明白,我就根據自己的了解來說吧,抛磚引玉。
因為c#代碼是托管到.net平台上的,是以java不能直接調用c#代碼,于是引入C++中間件,c++項目可以設定項目為clr公共運作時,進而通過引用的方式調用c#相應方法。而jna是可以直接調用c++生成的dll的,于是大緻流程就走通了。c++調用寫好的c#dll,java再調用c++生成的dll中間件,大緻流程就是這樣了,不過其中有很多坑,下面我會細說。
三、運作平台:
系統:Windows 10 x64
開發工具:Visual Studio 2015/2017(我筆記本和公司電腦安裝不同版本,我都有實作過) MyEclipse2014
SDK:jdk-x86、jdk-x64 (dll分為x86和x64平台,和jdk的版本要對應,同一台電腦裝兩個版本的jdk比較煩,我采用的是系統配置jdk32位調試32位dll,然後myeclipse自帶64位jdk調試64位dll)
四、準備工作:
1、首先準備上述運作平台,建議選擇和系統位數一緻的jdk(安裝vs、myeclipse或eclipse或sts);
2、下載下傳jna.jar :
JNA下載下傳(下載下傳jna-4.4.0.jar 和 jna-platform-4.4.0)
五、開始CODE
5.1 生成c#DLL
5.1.1 以管理者方式啟動vs(項目涉及到注冊com元件,必須以管理者啟動才能完成),建立c#項目

5.1.2 設定c#項目
首先,右鍵剛剛建立的Invoke項目,點選屬性。
記得儲存。
然後建立需要被調用的CSharp類代碼。這裡我們建立一些簡單的方法,為了示範效果我們分别對int、string、bool進行操作。如圖:
第一步,完成,幹得漂亮。
5.2 生成c++中間件
5.2.1 建立c++項目并設定屬性
5.2.2 書寫c++代碼
添加cpp檔案
編輯cpp檔案
/**********************************
2017-9-5 21:02:51
聲明需要被java調用的方法,該方法和java接口内部方法保持一緻
預處理語句目的是暴露函數供外部調用。
************************/
#ifdef MYLIBAPI
#else
#define MYLIBAPI extern "C" __declspec(dllimport)
#endif
MYLIBAPI int add(int a, int b); //添加函數聲明
MYLIBAPI char* getString(char* str);
MYLIBAPI int reverse(int flag);
using namespace System;
using namespace Invoke;
//using namespace System::Runtime::InteropServices::m
int add(int a, int b)
{
Method ^method = gcnew Method();
int result = method->add(a, b);
return result;
}
char* getString(char* str)
{
String ^ paraStr = gcnew String(str);
Method ^method = gcnew Method();
String ^resultString = method->getString(paraStr);
char* result = (char*)(void*)System::Runtime::InteropServices::Marshal::StringToCoTaskMemAnsi(resultString);
return result;
}
int reverse(int flag)
{
Method ^method = gcnew Method();
int result = method->reverse(flag);
return result;
}
好了,c++和c#全部工作完成,右鍵生成。
複制下dll生成檔案全名,一會兒java裡面用。
六、編寫java代碼
6.1 建立java project ,注意選擇和dll平台一緻的jdk。然後将之前下載下傳的兩個jna的jar加載到項目裡面,如圖:
6.2 開始寫java 代碼
package com.dyi.test;
import com.sun.jna.Library;
import com.sun.jna.Native;
/**
* 需要引入jna-4.4.0.jar 和 jna-platform-4.4.0
* 包下載下傳位址:https://github.com/java-native-access/jna
* @author stagebo
*
*/
public class InvokeTest {
/**
* 調用示例
* @param args
* @throws Exception
*/
public static void main(String[] args) throws Exception {
System.out.println(System.getProperty("java.version"));//輸出目前jdk版本号
System.out.println(System.getProperty("sun.arch.data.model"));//輸出目前jdk所用平台
CLibrary1 clib = CLibrary1.INSTANCE;
System.out.println("測試傳回結果:"+clib.add(13, 13));
System.out.println("測試傳回結果:"+clib.getString("this is java param."));
System.out.println("測試傳回結果:"+clib.reverse(true));
}
}
/**
* 必要接口,必須包含INSTANCE執行個體和需要調用的方法聲明。
* @author stagebo
*
*/
interface CLibrary1 extends Library {
CLibrary1 INSTANCE = (CLibrary1) Native.
loadLibrary("D:\\vs workplace\\java調用CSDLL示例\\x64\\Release\\CppDll",
CLibrary1.class);
/*需要調用的方法,方法名與c++方法名相同*/
int add(int a,int b);
String getString(String a);
boolean reverse(boolean flag);
}
然後我們運作:
哦豁,報錯了【無效的記憶體通路】,因為java找到了c++dll,但是沒找到c#的dll,其中c++dll我們寫的全路徑名,可以直接找到,那麼c#的dll怎麼找呢。答案是将c#的dll複制到jdk的bin目錄下,jvm就能找到了。
如圖我們将Invoke.dll複制到jdk的bin目錄下:
然後再運作:
nice!對于常用類型中的int、string、boolean都可以順利傳遞了,事實上其他類型的也可以實作,隻要遵循不同語言之間的類型對應關系就可以了,具體的類型關系可以百度。
七、注意事項
7.1 java報錯:Exception in thread "main" java.lang.Error: Invalid memory access
可能原因:
1、c#dll沒有複制到jdk的bin目錄;
2、java和c++之間資料類型不對應;
7.1.2 java報錯:Exception in thread "main" java.lang.UnsatisfiedLinkError: Unable to load library 'D:\vs workplace\X86InvokeTest\Release\X86CPPDlls': Native library (win32-x86/D:\vs workplace\X86InvokeTest\Release\X86CPPDlls.dll) not found in resource path ([file:/G:/My%20Eclipse%20workplace/InvokeCSharpX86Test/bin/, file:/G:/My%20Eclipse%20workplace/InvokeCSharpX86Test/Lib/jna-4.4.0.jar, file:/G:/My%20Eclipse%20workplace/InvokeCSharpX86Test/Lib/jna-platform-4.4.0.jar])
1、c++dll路徑不正确,建議做test時用絕對路徑,這樣你在c++項目編譯過後不用拷貝便可以在java程式裡面直接調用;
2、jdk的平台和c++項目的平台不比對,jdk是32位那麼c++dll一定也是32位的,64位也同樣;
7.1.3 windows64位下編譯的32位dll測試失敗,暫時不清楚是不是64位系統的原因,由于我電腦虛拟機沒有裝上,就沒有去32位系統上測試了。
================================2018-1-3 17:15:54 更新========================================================
1、提供給測試項目開源位址:
Github測試代碼連接配接 Github測試代碼連接配接22、怎麼确定c#的dll是不是成功複制到jdk的bin目錄呢?換言之怎麼确定自己的bin目錄在哪裡呢?可以在eclipse中運作的時候通過控制台看到。
黑夜給了我黑色的眼睛,我卻用它尋找光明