Android native method is invisible for Java - java

I am struggling with very annoying error in my code. I have this error appearing over and over again:
No implementation found for long com.oculus.gles3jni.GLES3JNILib.onCreate(android.app.Activity)
(tried Java_com_oculus_gles3jni_GLES3JNILib_onCreate and
Java_com_oculus_gles3jni_GLES3JNILib_onCreate__Landroid_app_Activity_2)
But in my file GLES3JNILib.java I have this:
package com.oculus.gles3jni;
import android.app.Activity;
import android.view.Surface;
// Wrapper for native library
public class GLES3JNILib
{
// Activity lifecycle
public static native long onCreate( Activity obj );
public static native void onStart( long handle );
public static native void onResume( long handle );
public static native void onPause( long handle );
public static native void onStop( long handle );
public static native void onDestroy( long handle );
// Surface lifecycle
public static native void onSurfaceCreated( long handle, Surface s );
public static native void onSurfaceChanged( long handle, Surface s );
public static native void onSurfaceDestroyed( long handle );
// Input
public static native void onKeyEvent( long handle, int keyCode, int action );
public static native void onTouchEvent( long handle, int action, float x, float y );
}
So I am not sure what is wrong. It is there but still I can't start my app. In my cpp code the implementation is:
jlong Java_com_oculus_gles3jni_GLES3JNILib_onCreate( JNIEnv * env, jobject obj, jobject activity )
{
...
}
Does someone see what I am missing, or doing wrong? Is it possible that this is because I don't have h file for my cpp?

You must have generated the C code and then changed the Java native method declaration to static without re-running javah.
Or you didn't run it at all and tried to wing it. Don't do that.
The correct signature has jclass for the second parameter, but don't take my word for it: rerun javah and adjust your .c file accordingly.
NB your .c file should #include your .h file.

Did you forget to load the library?
public class GLES3JNILib
{
static {
try {
System.loadLibrary("libGLES3JNILib");
} catch (UnsatisfiedLinkError e) {
// do something helpful here
}
}
...
}

Related

Not possible in C++ to retrieve the value of jlong from java context using QAndroidJniObject JNI. Exception java.lang.NoSuchMethodError

In the C++ context, it is not possible to retrieve the value of jlong when calling a function from the java context using the QAndroidJniObject JNI. Only the jstring value is retrieved. How to extract jlong? How can this be done? Maybe I need on JNI extern C to pass instead of QAndroidJniObject if it is possible in Qt?
//----------In Java context:
package com.sim.lib.operation.OperationData
public class OperationData {
private final String mID;
private Long mAmount;
public String getID() {
return this.mID;
}
public Long getAmount() {
return this.mAmount;
}
OperationData(){
this.mAmount = 55;
this.mID = "someid111";
}
}
//----------In Java context:
//other package
import com.sim.lib.operation.OperationData
public static native void approvedStatusSendToQt(java.lang.Object statusObj);
OperationData mOperationData = new OperationData();
approvedStatusSendToQt((Object)mOperationData);
//------------------------------------------------In C++ context:
void AndroidClass::registerNatives()
{
JNINativeMethod methods[] {
{"approvedStatusSendToQt", "(Ljava/lang/Object;)V",reinterpret_cast<void *>(onOperationStatusApprovedReceived)}
}
//.....register method
};
static void onOperationStatusApprovedReceived(JNIEnv *env, jobject /*thiz*/,jobject statusObj)
{
QAndroidJniObject callbackObj(statusObj);
qDebug()<<"mId"<<callbackObj.callObjectMethod<jstring>("getID").toString(); //Print "someid111" it's ok
qDebug()<<"mAmount"<< callbackObj.callMethod<jlong>("getAmount", "()J"); //Print exception System.err: java.lang.NoSuchMethodError: no non-static method "Lcom/sim/lib/operation/OperationData;.getAmount()J"
//qDebug()<<"mAmount"<< callbackObj.callMethod<jlong>("getAmount");//the same mistake
}

what is '#CalledByNative("...")'?

How do I use #CalledByNative("...")? I need a callback from a webrtc lib.
If you know the class PeerConnection on it:
PeerConnection.java
Here is a old version of PeerConnection but it's almost same now
I call the function addStream, but I can't get the callback from it.
Please explain how calledbynative works!
PeerConnection.java
/*
* Copyright 2013 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree. An additional intellectual property rights grant can be found
* in the file PATENTS. All contributing project authors may
* be found in the AUTHORS file in the root of the source tree.
*/
package org.webrtc;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
/**
* Java-land version of the PeerConnection APIs; wraps the C++ API
* http://www.webrtc.org/reference/native-apis, which in turn is inspired by the
* JS APIs: http://dev.w3.org/2011/webrtc/editor/webrtc.html and
* http://www.w3.org/TR/mediacapture-streams/
*/
public class PeerConnection {
static {
System.loadLibrary("jingle_peerconnection_so");
}
/** Tracks PeerConnectionInterface::IceGatheringState */
public enum IceGatheringState { NEW, GATHERING, COMPLETE }
/** Tracks PeerConnectionInterface::IceConnectionState */
public enum IceConnectionState {
NEW,
CHECKING,
CONNECTED,
COMPLETED,
FAILED,
DISCONNECTED,
CLOSED
}
/** Tracks PeerConnectionInterface::SignalingState */
public enum SignalingState {
STABLE,
HAVE_LOCAL_OFFER,
HAVE_LOCAL_PRANSWER,
HAVE_REMOTE_OFFER,
HAVE_REMOTE_PRANSWER,
CLOSED
}
/** Java version of PeerConnectionObserver. */
public static interface Observer {
/** Triggered when the SignalingState changes. */
public void onSignalingChange(SignalingState newState);
/** Triggered when the IceConnectionState changes. */
public void onIceConnectionChange(IceConnectionState newState);
/** Triggered when the ICE connection receiving status changes. */
public void onIceConnectionReceivingChange(boolean receiving);
/** Triggered when the IceGatheringState changes. */
public void onIceGatheringChange(IceGatheringState newState);
/** Triggered when a new ICE candidate has been found. */
public void onIceCandidate(IceCandidate candidate);
/** Triggered when some ICE candidates have been removed. */
public void onIceCandidatesRemoved(IceCandidate[] candidates);
/** Triggered when media is received on a new stream from remote peer. */
public void onAddStream(MediaStream stream);
/** Triggered when a remote peer close a stream. */
public void onRemoveStream(MediaStream stream);
/** Triggered when a remote peer opens a DataChannel. */
public void onDataChannel(DataChannel dataChannel);
/** Triggered when renegotiation is necessary. */
public void onRenegotiationNeeded();
}
/** Java version of PeerConnectionInterface.IceServer. */
public static class IceServer {
public final String uri;
public final String username;
public final String password;
/** Convenience constructor for STUN servers. */
public IceServer(String uri) {
this(uri, "", "");
}
public IceServer(String uri, String username, String password) {
this.uri = uri;
this.username = username;
this.password = password;
}
public String toString() {
return uri + "[" + username + ":" + password + "]";
}
}
/** Java version of PeerConnectionInterface.IceTransportsType */
public enum IceTransportsType { NONE, RELAY, NOHOST, ALL }
/** Java version of PeerConnectionInterface.BundlePolicy */
public enum BundlePolicy { BALANCED, MAXBUNDLE, MAXCOMPAT }
/** Java version of PeerConnectionInterface.RtcpMuxPolicy */
public enum RtcpMuxPolicy { NEGOTIATE, REQUIRE }
/** Java version of PeerConnectionInterface.TcpCandidatePolicy */
public enum TcpCandidatePolicy { ENABLED, DISABLED }
/** Java version of PeerConnectionInterface.CandidateNetworkPolicy */
public enum CandidateNetworkPolicy { ALL, LOW_COST }
/** Java version of rtc::KeyType */
public enum KeyType { RSA, ECDSA }
/** Java version of PeerConnectionInterface.ContinualGatheringPolicy */
public enum ContinualGatheringPolicy { GATHER_ONCE, GATHER_CONTINUALLY }
/** Java version of PeerConnectionInterface.RTCConfiguration */
public static class RTCConfiguration {
public IceTransportsType iceTransportsType;
public List<IceServer> iceServers;
public BundlePolicy bundlePolicy;
public RtcpMuxPolicy rtcpMuxPolicy;
public TcpCandidatePolicy tcpCandidatePolicy;
public CandidateNetworkPolicy candidateNetworkPolicy;
public int audioJitterBufferMaxPackets;
public boolean audioJitterBufferFastAccelerate;
public int iceConnectionReceivingTimeout;
public int iceBackupCandidatePairPingInterval;
public KeyType keyType;
public ContinualGatheringPolicy continualGatheringPolicy;
public int iceCandidatePoolSize;
public boolean pruneTurnPorts;
public boolean presumeWritableWhenFullyRelayed;
public RTCConfiguration(List<IceServer> iceServers) {
iceTransportsType = IceTransportsType.ALL;
bundlePolicy = BundlePolicy.BALANCED;
rtcpMuxPolicy = RtcpMuxPolicy.NEGOTIATE;
tcpCandidatePolicy = TcpCandidatePolicy.ENABLED;
candidateNetworkPolicy = candidateNetworkPolicy.ALL;
this.iceServers = iceServers;
audioJitterBufferMaxPackets = 50;
audioJitterBufferFastAccelerate = false;
iceConnectionReceivingTimeout = -1;
iceBackupCandidatePairPingInterval = -1;
keyType = KeyType.ECDSA;
continualGatheringPolicy = ContinualGatheringPolicy.GATHER_ONCE;
iceCandidatePoolSize = 0;
pruneTurnPorts = false;
presumeWritableWhenFullyRelayed = false;
}
};
private final List<MediaStream> localStreams;
private final long nativePeerConnection;
private final long nativeObserver;
private List<RtpSender> senders;
private List<RtpReceiver> receivers;
PeerConnection(long nativePeerConnection, long nativeObserver) {
this.nativePeerConnection = nativePeerConnection;
this.nativeObserver = nativeObserver;
localStreams = new LinkedList<MediaStream>();
senders = new LinkedList<RtpSender>();
receivers = new LinkedList<RtpReceiver>();
}
// JsepInterface.
public native SessionDescription getLocalDescription();
public native SessionDescription getRemoteDescription();
public native DataChannel createDataChannel(String label, DataChannel.Init init);
public native void createOffer(SdpObserver observer, MediaConstraints constraints);
public native void createAnswer(SdpObserver observer, MediaConstraints constraints);
public native void setLocalDescription(SdpObserver observer, SessionDescription sdp);
public native void setRemoteDescription(SdpObserver observer, SessionDescription sdp);
public native boolean setConfiguration(RTCConfiguration config);
public boolean addIceCandidate(IceCandidate candidate) {
return nativeAddIceCandidate(candidate.sdpMid, candidate.sdpMLineIndex, candidate.sdp);
}
public boolean removeIceCandidates(final IceCandidate[] candidates) {
return nativeRemoveIceCandidates(candidates);
}
public boolean addStream(MediaStream stream) {
boolean ret = nativeAddLocalStream(stream.nativeStream);
if (!ret) {
return false;
}
localStreams.add(stream);
return true;
}
public void removeStream(MediaStream stream) {
nativeRemoveLocalStream(stream.nativeStream);
localStreams.remove(stream);
}
public RtpSender createSender(String kind, String stream_id) {
RtpSender new_sender = nativeCreateSender(kind, stream_id);
if (new_sender != null) {
senders.add(new_sender);
}
return new_sender;
}
// Note that calling getSenders will dispose of the senders previously
// returned (and same goes for getReceivers).
public List<RtpSender> getSenders() {
for (RtpSender sender : senders) {
sender.dispose();
}
senders = nativeGetSenders();
return Collections.unmodifiableList(senders);
}
public List<RtpReceiver> getReceivers() {
for (RtpReceiver receiver : receivers) {
receiver.dispose();
}
receivers = nativeGetReceivers();
return Collections.unmodifiableList(receivers);
}
public boolean getStats(StatsObserver observer, MediaStreamTrack track) {
return nativeGetStats(observer, (track == null) ? 0 : track.nativeTrack);
}
// Starts recording an RTC event log. Ownership of the file is transfered to
// the native code. If an RTC event log is already being recorded, it will be
// stopped and a new one will start using the provided file. Logging will
// continue until the stopRtcEventLog function is called. The max_size_bytes
// argument is ignored, it is added for future use.
public boolean startRtcEventLog(int file_descriptor, int max_size_bytes) {
return nativeStartRtcEventLog(file_descriptor, max_size_bytes);
}
// Stops recording an RTC event log. If no RTC event log is currently being
// recorded, this call will have no effect.
public void stopRtcEventLog() {
nativeStopRtcEventLog();
}
// TODO(fischman): add support for DTMF-related methods once that API
// stabilizes.
public native SignalingState signalingState();
public native IceConnectionState iceConnectionState();
public native IceGatheringState iceGatheringState();
public native void close();
public void dispose() {
close();
for (MediaStream stream : localStreams) {
nativeRemoveLocalStream(stream.nativeStream);
stream.dispose();
}
localStreams.clear();
for (RtpSender sender : senders) {
sender.dispose();
}
senders.clear();
for (RtpReceiver receiver : receivers) {
receiver.dispose();
}
receivers.clear();
freePeerConnection(nativePeerConnection);
freeObserver(nativeObserver);
}
private static native void freePeerConnection(long nativePeerConnection);
private static native void freeObserver(long nativeObserver);
private native boolean nativeAddIceCandidate(
String sdpMid, int sdpMLineIndex, String iceCandidateSdp);
private native boolean nativeRemoveIceCandidates(final IceCandidate[] candidates);
private native boolean nativeAddLocalStream(long nativeStream);
private native void nativeRemoveLocalStream(long nativeStream);
private native boolean nativeGetStats(StatsObserver observer, long nativeTrack);
private native RtpSender nativeCreateSender(String kind, String stream_id);
private native List<RtpSender> nativeGetSenders();
private native List<RtpReceiver> nativeGetReceivers();
private native boolean nativeStartRtcEventLog(int file_descriptor, int max_size_bytes);
private native void nativeStopRtcEventLog();
}
#CallByNative is an annotation used by the Google WebRtc Development team to specify mappings between Java and Native (C/C++) Stacks of WebRtc.
As said on there official WebRtc Chromium Git representation such as:
#CalledByNative is used by the JNI generator to create the necessary
JNI bindings and expose this method to native code.
Now if there is a question that how is it happening? How WebRtc Java code is inter-linked with WebRtc Native Code?
For this, I will suggest looking into the Native Code of WebRtc will help you most. You will learn more informative and relevant stuff from the Comments inside the native stack. On the other hand, you should also have the basic knowledge of
How Java Native Interface (JNI) works
How WebRtc Java and Native Stack is interrelated
To get detailed intuition of Java Native Interface, you can visit these references:
Java Native Interface Specification Contents
Java Native Interface Tips
Here I am giving you an example from PeerConnection.java. Let's say, we have a method inside PeerConnection.java class such as:
#CalledByNative("IceGatheringState")
static IceGatheringState fromNativeIndex(int nativeIndex) {
return values()[nativeIndex];
}
Now this method is mapped inside src/sdk/android/src/jni/pc/peer_connection.cc class such as:
static ScopedJavaLocalRef<jobject> JNI_PeerConnection_IceGatheringState(
JNIEnv* env,
const JavaParamRef<jobject>& j_pc) {
return Java_IceGatheringState_fromNativeIndex(
env, ExtractNativePC(env, j_pc)->ice_gathering_state());
}
If you follow the same technique by looking into both Java and Native Class of PeerConnection, you must found some other linkages of that kind. Now, what #CallByNative did here?
If you look, after loading library, #CallByNative basically do these two things such as:
Tells which inner class the method belongs to
Expose this Java method to the Native Code
Make JNI Bindings with Java and Native Code
These types of annotations are provided by Google Developers to make JNI easy and understandable while working with Pre-built libraries or your own compiled libraries such as libwebrtc.aar.
I did some investigation on this topic lately and I would like to share my results. I realized that it might have to do something with #Override methods and the "super" keyword in Java. I suppose when we try to call e.g. the override method onIceCandidate or onAddStream, which is located in the PeerConnection.java file, from the MainActivity.java class we write:
public class MainActivity extends AppCompatActivity implements View.OnClickListener, SignallingClient.SignalingInterface {
...
private void createPeerConnection() {
...
#Override
public void onIceCandidate(IceCandidate iceCandidate) {
super.onIceCandidate(iceCandidate);
onIceCandidateReceived(iceCandidate);
}
#Override
public void onAddStream(MediaStream mediaStream) {
showToast("Received Remote stream");
super.onAddStream(mediaStream);
gotRemoteStream(mediaStream);
}
...
}
...
}
Furthermore, the onAddStream and onIceCandidate are both override methods. If we trace those methods back by the help of the super keyword we get into the following file CustomPeerConnectionObserver.java:
class CustomPeerConnectionObserver implements PeerConnection.Observer {
...
#Override
public void onIceCandidate(IceCandidate iceCandidate) {
Log.d(logTag, "onIceCandidate() called with: iceCandidate = [" + iceCandidate + "]");
}
...
#Override
public void onAddStream(MediaStream mediaStream) {
Log.d(logTag, "onAddStream() called with: mediaStream = [" + mediaStream + "]");
}
...
}
At this point when we take a look at the class we see that it "implements" the PeerConnection.Observer. If we keep investigating a bit further, we realize that we are guided to our file PeerConnection.java:
public class PeerConnection {
...
public interface Observer {
...
#CalledByNative("Observer")
void onIceCandidate(IceCandidate var1);
...
#CalledByNative("Observer")
void onAddStream(MediaStream var1);
...
}
...
}
We get into the "interface" called Observer. So far I took the example from GitHub and I tried to comprehend how the application works. It is for Android, but I suppose we use Java, so there should not be a huge difference, correct me if I am wrong.
In conclusion, I suppose it somehow gets called automatically when a special event occurs and/or when it is mentioned/written as a #Override method, where we need it to be called. I also assume that the #CalledByNative tag could have impact on the method calls, i.e. addStream, also respectively in the example I provided above:
onIceCandidate
onAddStream.
I hope I was able to at least give you some reference points about my investigation on this behaviour, where you could pick up the trace and make further research. I am also looking forward that an expert could answer this good question on how #CalledByNative works.

Convert a C++ callback to Java

I have the following code in C++ (Cocos2d) :
typedef void (CCObject::*SEL_CallFunc)();
CCCallFunc * CCCallFunc::actionWithTarget(CCObject* pSelectorTarget,
SEL_CallFunc selector) {
CCCallFunc *pRet = new CCCallFunc();
if (pRet && pRet->initWithTarget(pSelectorTarget)) {
pRet->m_pCallFunc = selector;
pRet->autorelease();
return pRet;
}
CC_SAFE_DELETE(pRet);
return NULL;
}
When converting with swig to java I get the following :
public static CCCallFunc actionWithTarget(CCObject pSelectorTarget, SWIGTYPE_m_CCObject__f___void selector) {
long cPtr = cocos2dxMappingJNI.CCCallFunc_actionWithTarget(CCObject.getCPtr(pSelectorTarget), pSelectorTarget,
SWIGTYPE_m_CCObject__f___void.getCMemberPtr(selector));
return (cPtr == 0) ? null : new CCCallFunc(cPtr, false);
}
Where SWIGTYPE_m_CCObject__f___void is just a pointer I can't use.
How do I implement this in the SWIG interface ?
I've looked into this solution stackoverflow but couldn't implement it for my case.
I don't believe SWIG supports member function pointers in any meaningful way. However, it's possible to get it done with JavaCPP. Given this C++ code in a file named MemberFunction.h:
class MyClass {
public:
virtual ~MyClass() { }
};
typedef void (MyClass::*MyFunction)(const char *str);
void callback(MyClass* cls, MyFunction fct, const char *str) {
(cls->*fct)(str);
}
We can define and use the callback this way in Java:
import org.bytedeco.javacpp.*;
import org.bytedeco.javacpp.annotation.*;
#Platform(include="MemberFunction.h")
public class MemberFunction {
static { Loader.load(); }
public static abstract class MyClass extends Pointer {
public MyClass() { allocate(); }
private native void allocate();
#Virtual public abstract void myCallback(String str);
#Virtual #MemberGetter #Name("myCallback")
public static native MyFunction getMyCallback();
}
#Namespace("MyClass")
public static class MyFunction extends FunctionPointer {
public native void call(MyClass cls, String str);
}
public static native void callback(MyClass cls, MyFunction fct, String str);
public static void main(String[] args) {
MyClass cls = new MyClass() {
public void myCallback(String str) {
System.out.println(str);
}
};
MyFunction fct = MyClass.getMyCallback();
callback(cls, fct, "Hello World");
}
}
Which builds fine and outputs the expected result:
$ javac -cp javacpp.jar MemberFunction.java
$ java -jar javacpp.jar MemberFunction
$ java -cp javacpp.jar MemberFunction
Hello World
You probably want to look at a typemap for "SEL_CallFunc selector". The typemap squirrels the original language callback, which is called via a tramploline function. There are python examples here and here. You'll find various similar questions for java on SO.

how do i pass pointer ref to native method in JNI [duplicate]

This is native method from ITLSSPProc.dll
NOMANGLE int CCONV OpenSSPComPort (SSP_COMMAND * cmd);
Here, SSP_COMMAND is structure in ITLSSPProc.dll which is in C++
Language.
struct SSP_COMMAND
{
unsigned long BaudRate;
unsigned char PortNumber;
};
So, I have to access OpenSSPComPort (SSP_COMMAND * cmd) in java using
JNI. Here is a code i have written,
public class Main {
public interface ITLSSPProc extends Library {
ITLSSPProc INSTANCE = (ITLSSPProc) Native.loadLibrary(
(Platform.isWindows() ? "ITLSSPProc" : "simpleDLLWindowsPort"), ITLSSPProc.class);
int OpenSSPComPort(Pointer param);
int CloseSSPComPort();
}
public static void main(String[] args)throws IOException {
ITLSSPProc sdll = ITLSSPProc.INSTANCE;
Memory intMem = new Memory(10); // allocating space
intMem.setLong(0,9600);
intMem.setString(1,"com7");
if(sdll.OpenSSPComPort(intMem)==1)
{// calling function with int parameter&result
System.out.println("connected");
}
else
{
System.out.println("failed");
}
}
}
Output : failed
Port number is COM7 on which we are working. So, when i run this
application and as i passing baud rate as manually as given in user
manual and if port number is correct it has to print "connected" on
console. So, anybody know where i am going wrong, i dont understand
where is actual problem..
JNA documentation for basic types (long, char).
JNA documentation for aggregate types (struct, struct *).
// tl;dr
class SSP_COMMAND extends Structure {
public NativeLong BaudRate;
public byte PortNumber;
}
int OpenSSPComPort(SSP_COMMAND param)

JNA Using macros

Is it possible to map The following Macro function with JNA?
int ListView_FindItem(
HWND hwnd,
int iStart,
const LPLVFINDINFO plvfi
);
I've tried to map this function with StdCallLibraryb, but that does not seem to work
(Function not found exception is thrown).
Basically i am trying find the index of a particular item in List view of desktop.
I have the name of the item i intend to find.
EDIT:
i have tried using the send message feature, i get the following exception
Exception in thread "main" java.lang.UnsatisfiedLinkError: Error looking up function
'GetMessage': The specified procedure could not be found.
at com.sun.jna.Function.<init>(Function.java:179)
at com.sun.jna.NativeLibrary.getFunction(NativeLibrary.java:347)
at com.sun.jna.NativeLibrary.getFunction(NativeLibrary.java:327)
at com.sun.jna.Library$Handler.invoke(Library.java:203)
at $Proxy0.GetMessage(Unknown Source)
at javaapplication4.Main.main(Main.java:43)
Java Result: 1
This is the code i used
public class Main {
public static class LVFINDINFO extends Structure {
public int flags =1002;
public String psz = "new folder3";
public LPARAM lParam ;
public POINT pt;
public int vkDirection;
}
public static class MSG extends Structure {
public HWND hWnd;
public int message;
public int wParam =-1;
public LVFINDINFO lParam1;
public int time;
public POINT pt;
public MSG(LVFINDINFO lParam) {
lParam1 = lParam;
}
}
public static void main(String[] args) {
User32 user32 = (User32) Native.loadLibrary("User32", User32.class);
LVFINDINFO i = new LVFINDINFO();
MSG m = new MSG(i);
user32.GetMessage(m, user32.GetDesktopWindow(), 0, 0);
System.out.println(user32.GetMessage(m, user32.GetDesktopWindow(), 0, 0));
}
}
Since a macro exists purely at compile-time, there's no way to call it using JNA.
You'll need to see what the macro actually does and do that instead. According to the documentation it sends the LVM_FINDITEM message. You need to find out how to send that message using JNA.

Categories