You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
[jnienv-gen] fix p/invoke usage for .NET framework
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.
0 commit comments