This is the Java class i'm accessing through JNIEnv in my monodroid application
package mypackage;
import android.util.Log;
public class JavaScriptInterface {
public String submitAns = "";
// The JNI in the original question uses a default constructor.
// Either provide one explicitly or use the implicit one...
public JavaScriptInterface()
{
}
public String getSelctd()
{
return submitAns;
}
}
I'm able to instantiate the class by the following statements:
Java.Lang.Object jclassWrp_;
IntPtr JavaScriptInterface_Class = JNIEnv.FindClass("mypackage.JavaScriptInterface");
IntPtr JavaScriptInterface_ctor = JNIEnv.GetMethodID(JavaScriptInterface_Class, "<init>", "()V"); //(Landroid/context/Context;)V
IntPtr jsInterfaceinstance_ = JNIEnv.NewObject(JavaScriptInterface_Class, JavaScriptInterface_ctor);
jclassWrp_ = new Java.Lang.Object(jsInterfaceinstance_, JniHandleOwnership.TransferGlobalRef);
But when i try to create the object to access the getSelctd() method:
IntPtr ipApid = JNIEnv.GetMethodID(jclassWrp_, "getSelctd", "()Ljava/lang/String;");
It throws NoSuchMethodExist Exception...
Please tell me whether i'm doing it the right way and what i'm missing here...
I'm able to instantiate the class by the following statements:
Java.Lang.Object jclassWrp_;
IntPtr JavaScriptInterface_Class = JNIEnv.FindClass("mypackage.JavaScriptInterface");
JNI use should use JNI conventions, thus mypackage/JavaScriptInterface (note / instead of .).
IntPtr JavaScriptInterface_ctor = JNIEnv.GetMethodID(JavaScriptInterface_Class, "<init>", "()V");
IntPtr jsInterfaceinstance_ = JNIEnv.NewObject(JavaScriptInterface_Class, JavaScriptInterface_ctor);
jclassWrp_ = new Java.Lang.Object(jsInterfaceinstance_, JniHandleOwnership.TransferGlobalRef);
JNIEnv.NewObject() returns a local ref, not a global ref, so you want JniHandleOwnership.TransferLocalRef.
But when i try to create the object to access the getSelctd() method:
IntPtr ipApid = JNIEnv.GetMethodID(jclassWrp_, "getSelctd", "()Ljava/lang/String;");
JNIEnv.GetMethodID() takes a class handle, not an instance. Firstly, the above shouldn't compile (Java.Lang.Object != IntPtr). Secondly, jclassWrp contains a mypackage.JavaScriptInterface instance, not the mypackage.JavaScriptInterface Class instance.
Instead, do:
IntPtr ipApid = JNIEnv.GetMethodID(JavaScriptInterface_Class, "getSelctd", "()Ljava/lang/String;");
Finally, don't forget to JNIEnv.DeleteGlobalRef(JavaScriptInterface_Class) when you don't need it anymore, otherwise you'll leak the gref.
Complete code:
// FindClass() returns a gref; must be freed (see below)
IntPtr JavaScriptInterface_Class = JNIEnv.FindClass("mypackage/JavaScriptInterface");
// MethodIDs do not need to be freed
IntPtr JavaScriptInterface_ctor = JNIEnv.GetMethodID(JavaScriptInterface_Class,
"<init>", "()V");
IntPtr JavaScriptInterface_getSelctd = JNIEnv.GetMethodID(JavaScriptInterface_Class,
"getSelctd", "()Ljava/lang/String;");
// JNIEnv.NewObject() & JNIEnv.CallObjectMethod() return lrefs; freed below
IntPtr lrefInstance = JNIEnv.NewObject(JavaScriptInterface_Class,
JavaScriptInterface_ctor);
IntPtr lrefSelectd = JNIEnv.CallObjectMethod(jsInterfaceinstance_, ipApid);
// JniHandleOwnership.TransferLocalRef causes lrefSelectd to be released for us
string selected = JNIEnv.GetString(lrefSelectd, JniHandleOwnership.TransferLocalRef);
// Resource cleanup
JNIEnv.DeleteLocalRef(lrefInstance);
JNIEnv.DeleteGlobalRef(JavaScriptInterface_Class);
Related
I've successfully wrapped a C DLL library using JNA.
As I'm not the owner of the C development part, I would like to hide
some parameters of a C function that I've wrapped on java side.
To be more precise my java code is as follows :
public interface IJNALibrary extends Library {
// INIT FUNCTION
public int initFunction(int firstValue, int secondValue, int thirdValue);
}
On the C side I have in the *.h file :
extern "C" CSAMPLE_API int initFunction (
unsigned firstValue,
unsigned secondValue,
unsigned thirdValue);
My purpose is to directly set secondValue and thirdValue parameters to 1 and thus hide those parameters to the java API user.
I don't want the user to know that he could change the values of those parameters.
In fact I would like to have something like :
public interface IJNALibrary extends Library {
// INIT FUNCTION
public int initFunction(int firstValue);
}
and initFunction(int firstValue) calls initFunction(int firstValue, int secondValue, int thirdValue) from the C DLL part.
But this has to be done inside the java Wrapper and not from the code which calls the java Wrapper.
I'm afraid that It cannot be possible, is it?
Unless I create another C DLL (with public int initFunction(int firstValue) function) which calls the first C DLL(which embed initFunction(int firstValue, int secondValue, int thirdValue).But I would rather do it on the java side in order not to have manage 2 C DLLs.
See also below the Sample.java file which calls the mapped method defined in IJNALibrary interface.
public class Sample {
static IJNALibrary IJNAFunctions;
public static void main(String[] args) throws IOException {
System.setProperty("jna.library.path", "./librayPath");
// LOADING LIBRARY
IJNAFunctions = (IJNALibrary) Native.load("c", IJNALibrary.class);
int firstValue = 1;
int secondValue = 2;
int thirdValue = 3;
int initReturn = IJNAFunctions.initFunction(firstValue, secondValue, thirdValue);
}
}
Thanx for your help.
It depends on what you want to archive. If you want to make it easier for users to call the init, this is an option (demonstrated using gethostname from libc), which uses a Java 8 feature, which allows adding default methods to interfaces:
public class TestDefaultMethod {
public static interface LibC extends Library {
LibC INSTANCE = Native.load("c", LibC.class);
// Original binding of method
int gethostname(byte[] name, int len);
// Helper method to make it easier to call gethostname
default String gethostname() {
byte[] result = new byte[255];
LibC.INSTANCE.gethostname(result, result.length);
return Native.toString(result);
}
}
public static void main(String[] args) {
// Usage
System.out.println(LibC.INSTANCE.gethostname());
}
}
Java developers normally don't arrays to functions, which fill them and a java developer would never pass the length of the array in a separate parameter. These are artifacts of the C nature of the function. In the wrapped function an array is allocated, the native call done and the array then unwrapped. All the ugly C specialties are hidden in the default method.
If you don't want to expose the method on java at all (be warned, if your users can access the JNA library, they can circumvent your protections!), you can use a function pointer directly:
public class TestDefaultMethod {
public static interface LibC extends Library {
NativeLibrary libc = NativeLibrary.getInstance("c");
LibC INSTANCE = Native.load("c", LibC.class);
default String gethostname() {
byte[] result = new byte[255];
libc.getFunction("gethostname").invokeInt(new Object[] {result, result.length});
return Native.toString(result);
}
}
public static void main(String[] args) {
System.out.println(LibC.INSTANCE.gethostname());
}
}
Same idea as above, the default method will hide the ugly parts. In this case though the function is not accessed through the managed INSTANCE, but access through the function pointer directly.
How can I use the iOS OSLog in Xamarin.iOS?
I did succeed in using NSLog as follows, but I see no way of setting the subsystem (to the bundle identifier) with NSLog so that I can use that to filter the logs in Console.app.
public class Logger
{
#if DEBUG
[DllImport(ObjCRuntime.Constants.FoundationLibrary)]
private extern static void NSLog(IntPtr message);
#endif
public void WriteLine(string line)
{
#if DEBUG
using (var nss = new NSString(line))
{
NSLog(nss.Handle);
}
#endif
}
}
In Xamarin.iOS, there is a static CoreFoundation.OSLog object called Default that one can use straight away which will log messages with the specified OSLogLevel argument such as OSLogLevel.Debug, OSLogLevel.Error etc.
You could have a method in your Xamarin.iOS code that logs a message at the Debug level:
using CoreFoundation;
// ...
public void Write(string message)
{
OSLog.Default.Log(OSLogLevel.Debug, message);
}
If you want to use the subsystem and category, you have to instantiate an instance of OSLog using the appropriate constructor and use that instance to log your messages. Presumably you'd want to hold a static reference to your created instance and use it like you'd use OSLog.Default.
public partial class AppDelegate : Xamarin.Forms.Platform.iOS.FormsApplicationDelegate
{
public static OSLog LoggerInstance;
public override bool FinishedLaunching(UIApplication app, NSDictionary launchOptions)
{
LoggerInstance = new OSLog(subsystem: "subsystem", category: "category");
//...
public class SomeClass
{
public void SomeMethod()
{
AppDelegate.LoggerInstance?.Log(OSLogLevel.Debug, "log message");
// ...
}
This will print a fairly nice message in the device log with a timestamp and showing the specified category.
OSLog is a ObjC struct (of two const chars) and a kernel method, os_log_create, is provided to allocate one.
Note: Refer to the os/log.h for details.
Define:
[DllImport("__Internal", EntryPoint = "os_log_create")]
public static extern IntPtr os_log_create(string subsystem, string category);
Usage:
var oslog = os_log_create("some.bundle.id", "StackOverflowCategory");
FYI: your NSLog should include a printf format string as a NSString
[DllImport (Constants.FoundationLibrary, EntryPoint = "NSLog")]
extern static void NSLog (IntPtr format, [MarshalAs (UnmanagedType.LPStr)] string s);
How to return nil value to Objective-C using DLLImport and MonoPInvokeCallback.
I'm trying to implement this solution iOS 7 UIWebView keyboard issue in Xamarin.iOs
I'm able to get callback using this approach How to port "method_getImplementation" and "method_setImplementation" to MonoTouch?
I'm trying something like this. Delegate is called but IntPtr.Zero value isn't returned to Objective-c.
delegate IntPtr CaptureDelegate(IntPtr block, IntPtr self, IntPtr uiView);
[MonoPInvokeCallback(typeof (CaptureDelegate))]
static IntPtr MyCapture(IntPtr block, IntPtr self, IntPtr uiView)
{
return IntPtr.Zero;
}
public void SetImplementation(UIView subview)
{
var method = class_getInstanceMethod(subview.ClassHandle, new Selector("inputAccessoryView").Handle);
var block_value = new BlockLiteral();
block_value.SetupBlock(MyCapture, null);
class_addMethod(subview.ClassHandle, new Selector("inputAccessoryView").Handle, imp_implementationWithBlock(ref block_value), IntPtr.Zero);
}
How to return nil to obj-c?
This is a follow-up question to How to write void pointer typedefs in vapi files?
I now have four almost identical [Compact] classes that represent handles allocated with unixODBCs SQLAllocHandle function.
The first one (for the ENV type handle) looks like this:
[CCode (cname = "void", free_function = "EnvironmentHandle.free")]
[Compact]
public class EnvironmentHandle {
[CCode (cname = "SQLAllocHandle")]
private static Return allocate_internal (HandleType type, void* nothing, out EnvironmentHandle output_handle);
public static Return allocate (out EnvironmentHandle output_handle) {
return allocate_internal (HandleType.ENV, null, out output_handle);
}
[CCode (cname = "SQLFreeHandle")]
private static Return free_internal (HandleType type, EnvironmentHandle handle);
public static Return free (EnvironmentHandle handle) {
return free_internal (HandleType.ENV, handle);
}
}
This doesn't compile.
Is it possible to use a static class method as the free_function?
If not, is there at least a way to write a custom free_function in the vapi file?
I need a custom function because the SQLFreeHandle function takes the handle type and the handle as an argument.
From the vapi users perspective all that is really important is:
[CCode (cname = "void")]
[Compact]
public class EnvironmentHandle {
public static Return allocate (out EnvironmentHandle output_handle);
}
The only other solution would be to use a [SimpleType] struct as suggested by apmasell in the original question. That would hide the fact that a SQLHANLDE is really a reference type.
The full code of my current implementation is available online:
https://github.com/antiochus/unixodbc-vala/tree/0486f54dc3f86d9c8bf31071980e4f171aca9591
No. The free_function is a C function, not a Vala function and it cannot take any context. You have two options:
Write a C macro in an extra header file to do what you want and bind the macro as the free function.
Bind the free function as a static method that takes an owned instance of the object:
[CCode (cname = "SQLFreeHandle")]
public static Return free(HandleType type, owned EnvironmentHandle handle);
EnvrionmentHandle foo = ...;
EnvironmentHandle.free(HandleType.ENV, (owned) foo);
public interface Kernel32 extends StdCallLibrary {
int GetComputerNameW(Memory lpBuffer, IntByReference lpnSize);
}
public class Kernel32Test {
private static final String THIS_PC_NAME = "tiangao-160";
private static Kernel32 kernel32;
#BeforeClass
public static void setUp() {
System.setProperty("jna.encoding", "GBK");
kernel32 = (Kernel32) Native.loadLibrary("kernel32", Kernel32.class);
}
#AfterClass
public static void tearDown() {
System.setProperty("jna.encoding", null);
}
#Test
public void testGetComputerNameW() {
final Memory lpBuffer = new Memory(1024);
final IntByReference lpnSize = new IntByReference();
final int result = kernel32.GetComputerNameW(lpBuffer, lpnSize);
if (result != 0) {
throw new IllegalStateException(
"calling 'GetComputerNameW(lpBuffer, lpnSize)'failed,errorcode:" + result);
}
final int bufferSize = lpnSize.getValue();
System.out.println("value of 'lpnSize':" + bufferSize);
Assert.assertEquals(THIS_PC_NAME.getBytes().length + 1, bufferSize);
final String name = lpBuffer.getString(0);
System.out.println("value of 'lpBuffer':" + name);
Assert.assertEquals(THIS_PC_NAME, name);
}
}
The offical instructions says use byte[]、char[]、Memory or NIO Buffer for mapping char pointer in c native function.But I tried all of above, and String、WString、StringArrays、class extends PointType etc, all of them are no use.
Out parameter 'lpnSize' can return the corret buffer size,but 'lpBuffer' return 'x>'(i think it's random memory) or nothing no matter I mapping any java type.If i wrote someting to the 'lpBuffer' memory first, it would read the same things after calling native function.
How can I solve the problem?
You need to use Pointer.getString(0, true) to extract the unicode string returned by GetComputerNameW.
EDIT
You'll also need to call GetComputerNameW again with the length parameter initialized before the function will fill in the result. Either pass back the same IntByReference to a second call, or initialize the IntByReference to the size of your Memory buffer to have the buffer written to in the first call.