Я пытался следовать примерам, которые нашел здесь, но не могу заставить его работать. Это мой класс Java
package jniTester;
public class JNITester {
static {
System.load("D:\\\\VisualStudio_Cpp_2017\\SkriptumTeil5\\Debug\\HelloWorldJNI.dll");
}
public static native String welcome(String name);
}
Из этого я создал с помощью javah файл jniTester.h
Это мой класс C#
namespace HelloWorldJNI
{
public static class HelloWorldJNI
{
public static String Welcome(String name)
{
return "Hello " + name + "! This is your C# buddy.";
}
}
}
Из этого я создал HelloWorldJNI.netmodule
Вот мой класс cpp
#include "stdafx.h"
#include <jni.h>
#include <string>
#include "jniTester.h"
#using "D:\VisualStudio_C#_2017\SkriptumTeil5\HelloWorldJNI\HelloWorldJNI.netmodule"
using namespace std;
JNIEXPORT jstring JNICALL Java_jniTester_JNITester_welcome(JNIEnv *env, jclass thisclass, jstring inJNIStr) {
// Step 1: Convert the JNI String (jstring) into C-String (char*)
const char *inCStr = env->GetStringUTFChars(inJNIStr, NULL);
if (NULL == inCStr) return NULL;
// Step 2: Convert the C++ string to C-string, then to JNI String (jstring) and return
//string outCppStr = "Hello " + std::string(inCStr) + ". Greetings from your C++ buddy";
//env->ReleaseStringUTFChars(inJNIStr, inCStr); // release resources
//return env->NewStringUTF(outCppStr.c_str());
//// Alternate Step 2:
System::String^ outStr = HelloWorldJNI::HelloWorldJNI::Welcome(gcnew System::String(inCStr));
env->ReleaseStringUTFChars(inJNIStr, inCStr); // release resources
char* converted = static_cast<char*>((System::Runtime::InteropServices::Marshal::StringToHGlobalAnsi(outStr)).ToPointer());
return env->NewStringUTF(converted);
}
Код шага 2 работает. Однако это не вызов моего метода C#. Реализация на альтернативном шаге 2 не работает с
# A fatal error has been detected by the Java Runtime Environment:
#
# Internal Error (0xe0434352), pid=37224, tid=0x00003350
Я не эксперт по cpp, поэтому я полностью в неведении. Что здесь не так?
Спасибо за быстрый ответ. Однако я не могу использовать оператор экспорта. Он вызывает синтаксические ошибки уже в Visual Studio.
Проверьте свою библиотеку классов. Смотрите также
Извините, но я не знаю, что где проверить
Забудьте про export "C". Во-первых, это extern, а не export. Во-вторых, он уже должен быть в заголовке, иначе вы вообще не сможете вызвать функцию. Вы пробовали вызывать функцию C# без аргументов? Вы пробовали использовать автономный исполняемый файл C++? Маловероятно, что это проблема JNI.
Я пробовал использовать функцию C# без аргументов. Тоже не работает. Я не пробовал автономный исполняемый файл C++ из-за отсутствия навыков C++
Я думаю, что это второй ответ на вопрос, связанный с @Victor. IIUC директива #using работает только в управляемом C++. Чтобы иметь возможность использовать свой метод C# в неуправляемом коде (которым, безусловно, является ваша JRE), вы должны объявить для него [DllExport].
Я добавил атрибут [DllExport] к своим методам C#. Но результат все тот же.




Прежде всего вам нужно создать сборочную DLL на C# с COM-интерфейсом.
Затем вы можете создать собственную (неуправляемую) библиотеку-оболочку DLL, которую можно использовать с JNI.
Вы можете следить за этим руководство по вызову управляемых COM-объектов NET из неуправляемого кода C
Также вы можете проверить Руководство JNI
И небольшой пример:
Код C# Managed.cs
using System;
using System.Reflection;
using System.Runtime.InteropServices;
[assembly: ComVisible(true)]
[assembly: AssemblyDelaySign(false)]
[assembly:AssemblyKeyFileAttribute("Managed.snk")]
[assembly: AssemblyVersion("1.0.0.0")]
namespace ManagedClassLibrary {
[Guid("1CE4ECCB-EB78-4A50-8781-444052C7AEAE")]
[ComVisible(true)]
public interface IArithmetic {
int sum(int lsh, int rhs);
int subtract(int lsh, int rhs);
}
[Guid("30F078F9-F161-4112-B61A-B3BD6B63CB4C"),
ClassInterface(ClassInterfaceType.None),
ComSourceInterfaces(typeof(IArithmetic))
]
[ComVisible(true)]
public class ArithmeticImpl:IArithmetic {
public int sum(int lsh, int rhs)
{
return lsh + rhs;
}
public int subtract(int lsh, int rhs)
{
return lsh - rhs;
}
}
}
Код оболочки C++ dll dotnet_arithmetic.cpp
#include <windows.h>
#include <Objbase.h>
#include <jni.h>
#import "Managed.tlb" named_guids raw_interfaces_only
using namespace Managed;
class DNArithmetic
{
DNArithmetic(const DNArithmetic&) = delete;
DNArithmetic& operator=(const DNArithmetic&) = delete;
private:
DNArithmetic() noexcept
{
mi_.CreateInstance(CLSID_ArithmeticImpl);
}
public:
const DNArithmetic* instanse() {
if (!_co_init) {
::CoInitialize(NULL);
_co_init = true;
}
static DNArithmetic _ret;
return &_ret;
}
int sum(int lsh, int rhs) const {
int ret;
mi_->sum(lsh, rhs, &ret);
return ret;
}
int subtract(int lsh, int rhs) const {
int ret;
mi_->subtract(lsh, rhs, &ret);
return ret;
}
~DNArithmetic() noexcept
{
::CoUninitialize();
}
private:
IArithmeticPtr mi_;
static bool _co_init;
};
bool DNArithmetic::_co_init = false;
extern "C" {
JNIEXPORT jint JNICALL Java_Arithmetic_dotnet_1sum(JNIEnv *evn, jclass clazz, jint lsh, jint rhs)
{
return DNArithmetic::instance()->sum(lsh, rhs);
}
JNIEXPORT jint JNICALL Java_Arithmetic_dotnet_1subtract(JNIEnv *evn, jclass clazz, jint lsh, jint rhs)
{
return DNArithmetic::instance()->subtract(lsh, rhs);
}
BOOL WINAPI DllMain(::HMODULE hprocess,::DWORD fdwReason,::LPVOID lpvReserved)
{
switch (fdwReason) {
case DLL_PROCESS_ATTACH:
break;
case DLL_PROCESS_DETACH:
break;
case DLL_THREAD_ATTACH:
break;
case DLL_THREAD_DETACH:
break;
}
return TRUE; // successful
}
}
Код Java Arithmetic.java
public class Arithmetic {
private static native int dotnet_sum(int lsh, int rhs);
private static native int dotnet_subtract(int lsh, int rhs);
private Arithmetic() {
try {
System.loadLibrary("dotnet_arithmetic.dll");
} catch (UnsatisfiedLinkError e) {
throw new IllegalStateException(e);
}
}
private int sum(int lsh, int rhs) {
return dotnet_sum(lsh, rhs);
}
private int subtract(int lsh, int rhs) {
return dotnet_subtract(lsh,rhs);
}
public static void main(String[] args) {
System.setProperty("java.library.path", System.getProperty("user.dir") );
System.out.println("About to call C# functions from Java over the MS COM");
Arithmetic instance = new Arithmetic();
System.out.println("C# 1 + 2 = " + instance.sum(1,2) );
System.out.println("C# 2 - 1 = " + instance.subtract(2,1) );
}
}
И файл сборки build.cmd (простой файл bat для демонстрации)
@echo off
rem make sure you have JAVA_HOME system variable set
rem Please set your VS root dir, should be somewhere in C:\Program Files
SET VS_HOME=
echo %VS_HOME%
echo Take Visual studio command line tools
call %VS_HOME%\VC\Auxiliary\Build\vcvars64.bat
echo Build C# class library
sn /k Managed.snk
csc /optimize /target:library /out:Managed.DLL Managed.cs
RegAsm Managed.DLL /tlb:Managed.tlb
gacutil /i Managed.DLL
echo Build C++ wrapper JNI DLL
cl /c /nologo /GL /Zl /std:c++latest /I%JAVA_HOME%\include /I%JAVA_HOME%\include\win32 /Fodotnet_arithmetic.obj dotnet_arithmetic.cpp
link /DLL /LTCG /LIBPATH:%JAVA_HOME%\lib /OUT:dotnet_arithmetic.dll msvcrt.lib kernel32.lib Ole32.lib jvm.lib dotnet_arithmetic.obj
echo Build Java
javac Arithmetic.java
echo Running the Demo
java Arithmetic
pause
ПРИМЕЧАНИЕ. Это решение только для ОС Windows. И не должен работать для Mono в Unix.
Большое спасибо, Виктор, за этот подробный пример. Однако у меня есть ошибка компиляции в файле .cpp с IArithmeticPtr и CLSID_ArithmeticImpl (неизвестный тип данных).
Решено. Имя пространства имен - Managed.
Добавьте
export "C" { /* my JNI functions */ }в свои реализации функций JNI. Поскольку вы используете C++ для обертывания C#. Также проверьте - как вызов управляемого кода из собственного кода