【開發者筆記】java 利用jna調用c#的dll


 

 

 

 

 

 

 

一、需求闡述:

  如果我們的項目利用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測試代碼連接2

2、怎么確定c#的dll是不是成功復制到jdk的bin目錄呢?換言之怎么確定自己的bin目錄在哪里呢?可以在eclipse中運行的時候通過控制台看到。

 

 

2018-12-25 10:36:44 補充。發現問問題的網友有點多,一一回復不來,大家進群聊吧。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM