-
Notifications
You must be signed in to change notification settings - Fork 53
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[jnienv-gen] fix p/invoke usage for .NET framework #460
[jnienv-gen] fix p/invoke usage for .NET framework #460
Conversation
I recently attempted to use Java.Interop from a full .NET framework console application on Windows. We don't currently build `java-interop.dll` for Windows, so I: * Took `C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\MSBuild\Xamarin\Android\libmono-android.release.dll` and just renamed it to `java-interop.dll`. * Since this is a 64-bit binary, I made the .NET framework project targeting `x64` only (it was *not* `AnyCPU`). * I added `java-interop.dll` as a `Content` build action. My console app was attempting to run the `main` method of `r8.jar`: var builder = new JreRuntimeOptions { JvmLibraryPath = @"C:\Users\jopepper\android-toolchain\jdk\jre\bin\server\jvm.dll", MarshalMemberBuilder = new ProxyMarshalMemberBuilder (), ObjectReferenceManager = new ProxyObjectReferenceManager (), ValueManager = new ProxyValueManager (), TypeManager = new ProxyTypeManager (), }; builder.ClassPath.Add (@"C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\MSBuild\Xamarin\Android\r8.jar"); using (var jre = builder.CreateJreVM ()) { var @string = new JniType ("java/lang/String"); var swissArmyKnife = new JniType ("com.android.tools.r8.SwissArmyKnife"); var main = swissArmyKnife.GetStaticMethod ("main", "([Ljava/lang/String;)V"); var help = JniEnvironment.Strings.NewString ("--help"); var args = JniEnvironment.Arrays.NewObjectArray (1, @string.PeerReference, help); var __args = stackalloc JniArgumentValue [1]; __args [0] = new JniArgumentValue (args); JniEnvironment.StaticMethods.CallStaticVoidMethod (swissArmyKnife.PeerReference, main, __args); } Unfortunately this code crashes at runtime with a cryptic error on any p/invoke using `JniArgumentValue*`: System.Runtime.InteropServices.MarshalDirectiveException: Cannot marshal 'parameter dotnet#5': Pointers cannot reference marshaled structures. Use ByRef instead. This seems like a limitation of .NET framework... However, it seems to work fine if we use `IntPtr` instead and just cast any `JniArgumentValue*` values to `IntPtr`. So for example, the p/invoke can change to: [DllImport (JavaInteropLib, CallingConvention=CallingConvention.Cdecl, CharSet=CharSet.Ansi)] internal static extern unsafe jobject java_interop_jnienv_call_object_method_a (IntPtr jnienv, out IntPtr thrown, jobject instance, IntPtr method, IntPtr args); `args` used to be a `JniArgumentValue*`. Other generated methods need a cast, such as: public static unsafe JniObjectReference CallObjectMethod (JniObjectReference instance, JniMethodInfo method, JniArgumentValue* args) { ... IntPtr thrown; var tmp = NativeMethods.java_interop_jnienv_call_object_method_a (JniEnvironment.EnvironmentPointer, out thrown, instance.Handle, method.ID, (IntPtr) args); ... } After this, my .NET framework console app was able to start, and it printed `r8 --help` output.
So looks like gendarme is reporting errors:
I'll check that out on macOS. I can also make |
Some parameters changed, so we have to update `gendarme-ignore.txt`.
As part of this PR, please update
That will implicitly update Additionally, please apply the following patch to diff --git a/tests/invocation-overhead/Makefile b/tests/invocation-overhead/Makefile
index 66f61d7..8f58b59 100644
--- a/tests/invocation-overhead/Makefile
+++ b/tests/invocation-overhead/Makefile
@@ -9,6 +9,7 @@ clean:
include ../../build-tools/scripts/mono.mk
include ../../build-tools/scripts/jdk.mk
+include ../../bin/BuildDebug/JdkInfo.mk
include ../../build-tools/scripts/msbuild.mk
$(JNIENV_GEN): |
There must be something else I need to fix here:
The line is: var __info = JniEnvironment.CurrentInfo;
__info.Invoker.CallStaticVoidMethodA (__info.EnvironmentPointer, type, method, (IntPtr) args); Looking into it. |
* `jnienv-gen` needs to declare `JniArgumentValue*` as `IntPtr` for delegate types, too. * Generated a new `jni.cs` -- which now compiles * Updated `Makefile`
I recently attempted to use Java.Interop from a full .NET framework
console application on Windows.
We don't currently build
java-interop.dll
for Windows, so I:C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\MSBuild\Xamarin\Android\libmono-android.release.dll
and just renamed it to
java-interop.dll
.targeting
x64
only (it was notAnyCPU
).java-interop.dll
as aContent
build action.My console app was attempting to run the
main
method ofr8.jar
:Unfortunately this code crashes at runtime with a cryptic error on any
p/invoke using
JniArgumentValue*
:This seems like a limitation of .NET framework...
However, it seems to work fine if we use
IntPtr
instead and justcast any
JniArgumentValue*
values toIntPtr
.So for example, the p/invoke can change to:
args
used to be aJniArgumentValue*
. Other generated methods needa cast, such as:
After this, my .NET framework console app was able to start, and it
printed
r8 --help
output.