从Java调用C#dll方法

有没有人知道我试图从我的Java代码中的C#dll调用方法有什么问题?

这是我的例子:

Java代码:

public class CsDllHandler { public interface IKeywordRun extends Library { public String KeywordRun(String action, String xpath, String inputData, String verifyData); } private static IKeywordRun jnaInstance = null; public void runDllMethod(String action, String xpath, String inputData, String verifyData) { NativeLibrary.addSearchPath(${projectDllName}, "${projectPath}/bin/x64/Debug"); jnaInstance = (IKeywordRun) Native.loadLibrary( ${projectDllName}, IKeywordRun.class); String csResult = jnaInstance.KeywordRun(action, xpath, inputData, verifyData); System.out.println(csResult); } } 

在C#中:

  [RGiesecke.DllExport.DllExport] public static string KeywordRun(string action, string xpath, string inputData, string verifyData) { return "C# here"; } 

Unmanaged Exports nuget应该足以让我调用这个方法(理论上),但我有一些奇怪的错误:

 Exception in thread "main" java.lang.Error: Invalid memory access at com.sun.jna.Native.invokePointer(Native Method) at com.sun.jna.Function.invokePointer(Function.java:470) at com.sun.jna.Function.invokeString(Function.java:651) at com.sun.jna.Function.invoke(Function.java:395) at com.sun.jna.Function.invoke(Function.java:315) at com.sun.jna.Library$Handler.invoke(Library.java:212) at com.sun.proxy.$Proxy0.KeywordRun(Unknown Source) at auto.test.keywords.utils.CsDllHandler.runDllMethod(CsDllHandler.java:34) at auto.test.keywords.runner.MainClass.main(MainClass.java:24) 

那么,经过另一天的研究和“反复试验”,我找到了问题的原因和解决方案。

原因是我的C#dll依赖于log4net.dll 。 要从独立的C#dll运行静态方法,问题中的代码就是您所需要的。

使用带有依赖关系的C#dll的解决方案是创建另一个没有依赖关系的dll,并使用reflection在此适配器中加载原始dll。 在Java中,您应该使用jna加载适配器dll并调用任何导出的方法。 我不仅可以从适配器执行方法,还可以使用reflection和Java配置log4net

这是我的代码:(C#)

 public class CSharpDllHandler { private static Logger log = Logger.getLogger(CSharpDllHandler.class); public interface IFrameworkAdapter extends Library { public String runKeyword(String action, String xpath, String inputData, String verifyData); public String configureLog4net(String log4netConfigPath); public String loadAssemblies(String frameworkDllPath, String log4netDllPath); } private static IFrameworkAdapter jnaAdapterInstance = null; private String jnaSearchPath = null; public CSharpDllHandler(String searchPath) { this.jnaSearchPath = searchPath; // add to JNA search path System.setProperty("jna.library.path", jnaSearchPath); // load attempt jnaAdapterInstance = (IFrameworkAdapter) Native.loadLibrary( "FrameworkAdapter", IFrameworkAdapter.class); } public String loadAssemblies(String frameworkDllPath, String log4netDllPath) { String csResult = jnaAdapterInstance.loadAssemblies(frameworkDllPath, log4netDllPath); log.debug(csResult); return csResult; } public String runKeyword(String action, String xpath, String inputData, String verifyData) { String csResult = jnaAdapterInstance.runKeyword(action, xpath, inputData, verifyData); log.debug(csResult); return csResult; } public String configureLogging(String log4netConfigPath) { String csResult = jnaAdapterInstance .configureLog4net(log4netConfigPath); log.debug(csResult); return csResult; } public String getJnaSearchPath() { return jnaSearchPath; } } 

在main方法中,只需使用以下内容:

  CSharpDllHandler dllHandler = new CSharpDllHandler( ${yourFrameworkAdapterDllLocation}); dllHandler.loadAssemblies( ${yourOriginalDllPath},${pathToTheUsedLog4netDllFile}); dllHandler.configureLogging(${log4net.config file path}); dllHandler.runKeyword("JAVA Action", "JAVA Xpath", "JAVA INPUT", "JAVA VERIFY"); dllHandler.runKeyword("JAVA Action2", "JAVA Xpath2", "JAVA INPUT2", "JAVA VERIFY2"); 

在C#中,我在原始dll上有所需的方法:

  public static string KeywordRun(string action, string xpath, string inputData, string verifyData) { log.Debug("Action = " + action); log.Debug("Xpath = " + xpath); log.Debug("InputData = " + inputData); log.Debug("VerifyData = " + verifyData); return "C# UserActions result: "+ action+" "+xpath+" "+inputData+" "+verifyData; } 

所有的魔法都在DLL Adapter中

 namespace FrameworkAdapter { [ComVisible(true)] public class FwAdapter { private const String OK="OK"; private const String frameworkEntryClassName = "${nameOfTheDllClass with method to run }"; private const String log4netConfiguratorClassName = "log4net.Config.XmlConfigurator"; private static Assembly frameworkDll = null; private static Type frameworkEntryClass = null; private static MethodInfo keywordRunMethod = null; private static Assembly logDll = null; private static Type logEntryClass = null; private static MethodInfo logConfigureMethod = null; private static String errorMessage = "OK"; [RGiesecke.DllExport.DllExport] public static string loadAssemblies(string frameworkDllPath, string log4netDllPath) { try { errorMessage = LoadFrameworkDll(frameworkDllPath, frameworkEntryClassName); LoadFrameworkMethods("KeywordRun", "Setup", "TearDown"); errorMessage = LoadLogAssembly(log4netDllPath, log4netConfiguratorClassName); if (errorMessage.CompareTo(OK) == 0) errorMessage = LoadLogMethods("Configure"); } catch (Exception e) { return e.Message; } return errorMessage; } [RGiesecke.DllExport.DllExport] public static string configureLog4net(string log4netConfigPath) { if (errorMessage.CompareTo("OK") == 0) { StringBuilder sb = new StringBuilder(); sb.AppendLine("Try to configure Log4Net"); try { FileInfo logConfig = new FileInfo(log4netConfigPath); logConfigureMethod.Invoke(null, new object[] { logConfig }); sb.AppendLine("Log4Net configured"); } catch (Exception e) { sb.AppendLine(e.InnerException.Message); } return sb.ToString(); } return errorMessage; } [RGiesecke.DllExport.DllExport] public static string runKeyword(string action, string xpath, string inputData, string verifyData) { StringBuilder sb = new StringBuilder(); object result = null; try { result = keywordRunMethod.Invoke(null, new object[] { action, xpath, inputData, verifyData }); sb.AppendLine(result.ToString()); } catch (Exception e) { sb.AppendLine(e.InnerException.Message); } return sb.ToString(); } private static String LoadFrameworkDll(String dllFolderPath, String entryClassName) { try { frameworkDll = Assembly.LoadFrom(dllFolderPath); Type[] dllTypes = frameworkDll.GetExportedTypes(); foreach (Type t in dllTypes) if (t.FullName.Equals(entryClassName)) { frameworkEntryClass = t; break; } } catch (Exception e) { return e.InnerException.Message; } return OK; } private static String LoadLogAssembly(String dllFolderPath, String entryClassName) { try { logDll = Assembly.LoadFrom(dllFolderPath); Type[] dllTypes = logDll.GetExportedTypes(); foreach (Type t in dllTypes) if (t.FullName.Equals(entryClassName)) { logEntryClass = t; break; } } catch (Exception e) { return e.InnerException.Message; } return OK; } private static String LoadLogMethods(String logMethodName) { try { logConfigureMethod = logEntryClass.GetMethod(logMethodName, new Type[] { typeof(FileInfo) }); } catch (Exception e) { return e.Message; } return OK; } private static void LoadFrameworkMethods(String keywordRunName, String scenarioSetupName, String scenarioTearDownName) { ///TODO load the rest of the desired methods here keywordRunMethod = frameworkEntryClass.GetMethod(keywordRunName); } } 

}

运行此代码将提供从原始C#DLL到Java控制台输出 (如果已配置)的所有已记录消息 。 以类似的方式,我们可以为运行时加载任何其他所需的dll文件。

请原谅我用reflection做C#做事的[ 非常可能的错误 ]方式,我是这种语言的新手。