从Java调用C#dll方法

时间:2014-05-15 10:17:44

标签: java c# jna

有没有人知道我在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)

1 个答案:

答案 0 :(得分:1)

嗯,经过另一天的研究和"试验和错误"我找到了问题的原因和解决方案。

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

使用带有依赖关系的C#dll的解决方案是创建另一个具有 no 依赖关系的dll,并使用反射在此适配器中加载原始dll。在Java中,您应该使用 jna 加载适配器dll并调用任何导出的方法。我不仅可以从适配器执行方法,还可以使用反射和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文件。

请原谅我的[非常可能错误的]用C#做事的方式,我不熟悉这种语言。