I'm currently programming an interface between some C++ code and Java using JNI.
I'm getting some events in my GUI that I want to pass to a C++ event handler.
I therefore call a function that I wrote in Java.
public void sendToEventQueue( AWTEvent evt ) {
Mudkiptz.Main.fctC_sendEvent( evt );
}
This method is in an abstract class EventHdl that I used as a super class for keyEventHandler in which I overload the keyTyped( KeyEvent) to send the KeyEvent to my C++ event handler with the method previously declared.
My problem is that I want to get my keyEvent keyCode from the KeyEvent that I passed.
JNIEXPORT void JNICALL Java_Mudkiptz_Main_fctC_1sendEvent
(JNIEnv* env, jclass, jobject evt) {
// Obtenir les infos (Get information)
jclass keyEventClass = env->FindClass("java/awt/event/KeyEvent");
if( env->IsInstanceOf(evt, keyEventClass) ) {
jmethodID getKeyCode = env->GetMethodID(keyEventClass, "getKeyCode", "()I");
int keyCode = 0;
keyCode = env->CallIntMethod(evt, getKeyCode);
}
// getInstance()
Application::obtenirInstance()->getEventQueue()->push( evt );
}
But it doesn't not work... :( I'm not used to JNI so it may be a easy mistake nevertheless I would really appreciate help.
Edit: I should have been more precise. The method return but keyCode always equals zero when it should give the keyCode. thanks!
Thanks!
I finally found what I was doing wrong. It seems that getKeyCode wasn't the function I was searching for.
To debug, I went to the java call and print the event to string and keycode always equals 0 even in java. So, I check the dump and keyChar had the correct value of my input. So I changed the call to use getKeyChar instead and everything is working fine.
Thanks for your help!
Related
I'm working on a student project. It's a network card game. The solution contains 3 projects. Client's GUI using Windows Forms so it has managed classes. Static client's library in native C++. GUI's project has reference to it thus uses 'Mixed Rules'. Server is in native C++ as well. I use RPC middleware for communication. It works only with native C++. That is why I need the static library to hide there all the details of communication on client's side.
Since the server can at any moment change its state and that should be shown in client's GUI, I use callback approach to change Windows Forms' components. And here I found a problem because I need to change private members of managed class with the help of a native object.
There are probably different ways to do that. My idea is sending a pointer to instance of managed class into instance of native class and saving it there. So later I can call from that native object public member functions of that managed class to change components.
This is from my 'Mixed Rules' GUI project:
//Native class for changing window 'Lobby'
class LobbyI : public ClientLib::Lobby {
public:
LobbyI();
~LobbyI();
//Should change window due to current Server's state
void reDraw(const CommonLogic::ServerState&);
};
// Managed class implements GUI for window 'Lobby'
// generated by Visual Studio designer
public ref class LobbyGUI : public System::Windows::Forms::Form {
//My members
ClientLib::Mediator* mediatorPtr; // Is it correct?
LobbyI* lobbyPtr; // ?
public:
LobbyGUI(void) {
InitializeComponent();
mediatorPtr = new ClientLib::Mediator(); // Is it correct?
lobbyPtr = new LobbyI(); // ?
mediatorPtr->setCallback(lobbyPtr);
}
protected:
~LobbyGUI() {
if (components) { delete components; }
delete lobbyPtr; // Is it correct?
lobbyPtr = nullptr; // ?
delete mediatorPtr; // ?
mediatorPtr = nullptr; // ?
}
private: System::Windows::Forms::Button^ buttonLogIn;
//...
This is from native static library ClientLib:
class Lobby {
public:
virtual ~Lobby();
virtual void reDraw(const CommonLogic::ServerState&) = 0;
};
class Mediator {
CommonLogic::ServerState serverState;
Lobby* lobbyPtr;
public:
Mediator();
~Mediator();
void setCallback(Lobby* ptr) { lobbyPtr = ptr; }
void reDrawLobby() { lobbyPtr->reDraw(serverState); }
};
This code builds ok. The only thing I need now is that the member function reDraw() of native derived class LobbyI is able to change the window implemented by managed class LobbyGUI. Thus getting and keeping and using pointer to it. And then I think it all will work. How to do that?
Maybe it's not the nicest implementation in general. I would be happy to read other suggestion.
I'm also doubtful about the way I used pointers to native classes inside managed class. Is it correct? It didn't work correct until I inserted ptr=nullptr; after delete ptr; in destructor.
UPDATE: Now I see redundancy in my code. Abstract class Lobby is useless. I need only to implement reDraw() function in managed class which will have obviously access to components of the window. And then pass safe pointer to native class function which expects pointer to a function as a parameter.
Finally I've solved it!! Using this article. In the following code a native object stores provided pointer to a function of managed object. So this callback function can be invoked at any time. A delegate is used as a form of type-safe function pointer. Instance of GCHandle is used to prevent the delegate from being relocated by garbage collector.
Here is simple CLR Console Application which increments and prints some integer using callback function invoked from native object. Thus we can "change private members of managed object using a native one".
using namespace System;
using namespace System::Runtime::InteropServices;
typedef void(__stdcall *ANSWERCB)(); // define type of callback function
#pragma unmanaged
class NativeClass {
ANSWERCB cbFuncPtr = 0; // pointer to callback function
public:
void setCallback(ANSWERCB fptr) {
cbFuncPtr = fptr;
incAndPrint();
}
void incAndPrint() { cbFuncPtr(); } // invokes callback which increments and prints
};
#pragma managed
ref class ManagedClass {
public: delegate void Del();
private:
Int32 i;
NativeClass* nativePtr;
Del^ delHandle;
GCHandle gch;
public:
ManagedClass(Int32 ii) : i(ii) {
nativePtr = new NativeClass;
delHandle = gcnew Del(this, &ManagedClass::changeAndPrintInt);
gch = GCHandle::Alloc(delHandle);
IntPtr ip = Marshal::GetFunctionPointerForDelegate(delHandle);
ANSWERCB callbackPtr = static_cast<ANSWERCB>(ip.ToPointer());
nativePtr->setCallback(callbackPtr);
}
~ManagedClass() {
delete nativePtr;
nativePtr = __nullptr;
gch.Free();
}
private:
void changeAndPrintInt() // callback function
{
Console::WriteLine(++i);
}
};
int main(array<System::String ^> ^args)
{
ManagedClass mc(1);
return 0;
}
Folks,
In my Android Java code, I have a declaration as follows:
public class SurfacePanelNative extends SurfaceView implements SurfaceHolder.Callback {
...
private static native void native_render();
}
In my native code, I have the function declared as:
void native_render(JNIEnv *env, jobject javaSurface) {
ANativeWindow* window = ANativeWindow_fromSurface(env, javaSurface);
...
}
Looking at some examples on the net, it appears that the function should be declared as:
void native_render(JNIEnv *env, jclass clazz) {
...
}
I am wondering which declaration is the right one.
I am thinking the first one is the right one. Otherwise, I don't have enough information to obtain javaSurface.
I would appreciate it if someone can shed some light on this.
Thank you in advance for your help.
Regards,
Peter
It is jclass if the method is static, otherwise jobject. If you use javah, as the JNI designers intended, you will always get the right answer.
I've subclassed my Qthread so I can implement my code in run() method. I have to pass it some parameters,
I tried it like this, so what's wrong in here?
class QMyThread :
public QThread
{
public:
QMyThread();
~QMyThread(void);
virtual void start(FILE *data, int sock, int bits);
protected:
virtual void run(FILE *data, int sock, int bits);
};
run method;
void QMyThread::run(FILE *data, int sock, int bits)
{
//do stuff
}
start the thread:
QMyThread *thread;
thread->start(datafile, sockint, bitsint);
first it says the thread might not be initialized and then it crashes in the start() method with SIGSEGV error. Anyone can help me?
You shouldn't be subclassing the QThread class as this is no longer the recommended way of using QThread.
For more information http://qt-project.org/doc/qt-4.8/qthread.html
To answer your question, couldn't you just make those parameters members of your class and assign their values through setters or its contructor?
You should do this instead:
QMyThread thread;
thread.start(...)
You created a pointer to a thread and did not new it. I frankly see no reason for a pointer here, you can just create a normal variable and call a method on it.
If you do want a pointer, then use std::unique_ptr in C++11 or boost::unique_ptr
std::unique_ptr<QMyThread> thread;
thread->start(...);
EDIT:
You should really just create a QThread * thread = new QThread(this); as per the documentation.
How about using the QMetaObject class to pass the parameters to worker class. You can try like this:
QMetaObject::invokeMethod(worker, "methodName", Q_ARG(QString, "ParameterQStringValue");
Note this method will work if methodName is a slot and you use the new way of creating threads: https://mayaposch.wordpress.com/2011/11/01/how-to-really-truly-use-qthreads-the-full-explanation/
You can specify different parameters using Q_ARG macro up to 9 (http://doc.qt.io/qt-5/qmetaobject.html#details). If you need more parameters, then I suggest you to create the QVector with a structure and pass it to QMetaObject::invokeMethod as the parameter.
Right I created a new thread from a static function from the same class.
Inside the same class I try to call a delegate to update the GUI.
I get a compiler error saying:
Invalid delegate initializer - an object is needed in addition to a function.
At &MainUi::AddListItemMethod.
delegate void AddListItem(void);
public: void AddListItemMethod(String^ myString)
{
ListView1->Items->Add(myString);
}
private: static void SecondThread()
{
AddListItem^ del = gcnew AddListItem(&MainUI::AddListItemMethod);
del->Invoke("test");
}
I don't know why it doesn't work. I also tried this and still failed. Any help please?
Invoke(gcnew AddListItem(MainUI::&AddListItemMethod), "test");
Either You have to make Listview1 static to work or you should create an instance/object of MainUI class to access a non static method of that class.
Thank you and Happy coding.
I'd like to pass java class object to JNI method,
And I want to call few methods in JNI method like below.
Is there anyone who have some example like below?
class JavaClassParameter{
void javaMethodTobeCalledInJNI(){
...java source...
}
}
class MainJavaClass{
void somemethod(){
JavaClassParameter object = new JavaClassParameter();
JNIMethod(object);
}
native void JNIMethod(JavaClassParameter object);
}
// C++ code
void JNIMethod(object){
object->javaMethodTobeCalledInJNI();
}
Your method declaration:
class MainJavaClass {
native void JNIMethod(JavaClassParameter object);
}
means javah should generate a forward declaration like the following:
JNIEXPORT void JNICALL Java_MainJavaClass_JNIMethod(JNIEnv* env, jobject mainJavaClass);
In the implementation of that, you have a few things to do:
Find JavaClassParameter
Use FindClass, which takes a string name:
jclass cls = env->FindClass("JavaClassParameter");
Find javaMethodTobeCalledInJNI()
Use GetMethodID, which takes the class to check, the string name of the method, and its signature. Since this is a void function with no arguments, its signature is just ()V:
jmethodID method = env->GetMethodID(cls, "javaMethodTobeCalledInJNI", "()V");
Call javaMethodTobeCalledInJNI()
Use CallVoidMethod, which takes the object instance, the method ID, and any arguments (none in this case):
env->CallVoidMethod(mainJavaClass, method);
You should check for NULL results after each step; if you get a NULL back from one JNI function and pass it to another, you'll usually crash the JVM