简体   繁体   English

从Java调用C#dll方法

[英]C# dll method call from Java

Has anyone an idea about what is wrong with my attempt to call a method from a C# dll in my Java code? 有没有人知道我试图从我的Java代码中的C#dll调用方法有什么问题?

Here is my example: 这是我的例子:

Java code: 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);
}
} 

And in C#: 在C#中:

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

The Unmanaged Exports nuget should be enough for me to call this method (in theory) but I have some strange error: 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)

Well, after another day of research and "trial and error" I have found the cause of my problem and a solution. 好吧,经过另一天的研究和“试错”,我找到了问题的原因和解决方案。

The cause was that my C# dll had a dependency on log4net.dll . 原因是我的C#dll依赖于log4net.dll For running a static method from a standalone C# dll the code from the question is all you need. 要从独立的C#dll运行静态方法,问题中的代码就是您所需要的。

The solution for using C# dll with dependencies is to create another dll with no dependency and to load the original dll in this adapter with reflection. 使用带有依赖关系的C#dll的解决方案是创建另一个没有依赖关系的dll,并使用反射在此适配器中加载原始dll。 In Java you should load the adapter dll with jna and call any exported method. 在Java中,您应该使用jna加载适配器dll并调用任何导出的方法。 I was able not only to execute methods from the adapter but also to configure log4net with reflection and Java 我不仅可以从适配器执行方法,还可以使用反射和Java配置log4net

Here is my code: (C#) 这是我的代码:(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;
}
}

In the main method just use something like this: 在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");

In C# I have the desired methods on the original dll: 在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;
    }

and all the magic is in the DLL Adapter : 所有的魔法都在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);
    }
}

} }

Running this code will provide all the logged messages from the original C# DLL to the Java console output (and to a file if configured). 运行此代码将提供从原始C#DLL到Java控制台输出 (如果已配置)的所有已记录消息 In a similar way, we can load any other needed dll files for runtime. 以类似的方式,我们可以为运行时加载任何其他所需的dll文件。

Please forgive my [ very probable wrong ] way of doing things in C# with reflection, I'm new to this language. 请原谅我用反射做C#做事的[ 非常可能的错误 ]方式,我是这种语言的新手。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM