I am currently trying to translate a few WIN32API functions to Java using JNI. One of those functions is RegisterClassEx. Usually, I think you would specify a different callback function for every window class you register, but since I'm translating the callback to Java as well this doesn't work.
So the current plan is to append a jobject (defined as _jobject*) to the window class and work with that in the callback. Problem is: you can only change data attached to a window class using a HWND. The MSDN documentation to the best of my knowledge doesn't specify a function that can modify a window class using only a window class ATOM or the name.
Thus my question: Is there a way to change a window class (using something like SetClassLongPtr), without having to use a valid HWND?
Java side (I'll eventually add a public function that does what I actually need done):
public class c_winjni implements i_jnisystem {
public interface i_wnd_proc {
public int wnd_proc(long il_hwnd, int im_message, long im_wparam, long im_lparam);
}
private class c_wndclassex {
public int im_style = 0;
public i_wnd_proc ds_wnd_proc = null;
public int im_cls_extra = 0;
public int im_wnd_extra = 0;
public long il_instance = 0L;
public long il_icon = 0L;
public long il_small_icon = 0L;
public long il_cursor = 0L;
public long il_background = 0L;
public String str_menu_name = null;
public String str_class_name = null;
}
private static native short registerClassEx(c_wndclassex ds_wcx, int[] imr_error);
}
C++ side:
LRESULT CALLBACK default_window_callback_proc(HWND ds_hwnd, UINT im_message, WPARAM im_w_param, LPARAM im_l_param) {
return DefWindowProc(ds_hwnd, im_message, im_w_param, im_l_param);
}
/*
* Class: c_winjni
* Method: registerClassEx
* Signature: (Lc_winjni/c_wndclassex;[I)S
*/
JNIEXPORT_EX jshort JNICALL Java_c_1winjni_registerClassEx
(JNIEnv *ads_env, jclass /*jds_class*/, jobject jds_wcx, jintArray jimr_error)
JNI_CPPEXCEPTION_TRAP_BEGIN {
c_jnienv jds_env(ads_env);
jint *aim_error = NULL;
if (jimr_error && jds_env.get_array_length(jimr_error) > 0) {
aim_error = jds_env.get_array_elements(jimr_error, NULL);
}
WNDCLASSEX ds_wcx;
ds_wcx.cbSize = sizeof(WNDCLASSEX);
ds_wcx.style = jds_env.get_int_field(jds_wcx, "im_style");
// Imagine I'm checking whether field ds_wnd_proc in object jds_wcx is null.
// If it is, use the default callback (as shown below).
// If it isn't, set ds_wcx.lpfnWndProc to some other callback that reads
// custom class data and calls a Java function of the object attached to the window class.
ds_wcx.lpfnWndProc = default_window_callback_proc;
ds_wcx.cbClsExtra = jds_env.get_int_field(jds_wcx, "im_cls_extra") + sizeof(LONG_PTR);
ds_wcx.cbWndExtra = jds_env.get_int_field(jds_wcx, "im_wnd_extra");
ds_wcx.hInstance = (HINSTANCE) jds_env.get_long_field(jds_wcx, "il_instance");
ds_wcx.hIcon = (HICON) jds_env.get_long_field(jds_wcx, "il_icon");
ds_wcx.hIconSm = (HICON) jds_env.get_long_field(jds_wcx, "il_small_icon");
ds_wcx.hCursor = (HCURSOR) jds_env.get_long_field(jds_wcx, "il_cursor");
ds_wcx.hbrBackground = (HBRUSH) jds_env.get_long_field(jds_wcx, "il_background");
ct_jstring<TCHAR, 256> str_menu_name(ads_env, (jstring) jds_env.get_string_field(jds_wcx, "str_menu_name"));
ds_wcx.lpszMenuName = str_menu_name.get_data();
ct_jstring<TCHAR, 256> str_class_name(ads_env, (jstring) jds_env.get_string_field(jds_wcx, "str_class_name"));
ds_wcx.lpszClassName = str_class_name.get_data();
jshort result = RegisterClassEx(&ds_wcx);
if (result == NULL && aim_error) {
*aim_error = GetLastError();
}
// commit changes and invalidate pointer
if (aim_error) {
jds_env.release_array_elements(jimr_error, aim_error, 0);
}
return result;
} JNI_CPPEXCEPTION_TRAP_END2(ads_env, 0)
The simple answer to your question is that it is only possible to modify the extra class memory using SetClassLongPtr. For which you need a valid window handle.
Interesting problem... A WNDCLASSEX is a class, and HWND is a handle to a window. You may think of the first as a OO class, the second as a "pointer" or "reference" an object (instance).
Yet to modify the class, you seem to have to go through an instance of that class... Weird :) I can imagine this is the most common scenario (having the HWND) (BTW, why you don't have a HWND?)
An idea: would it be acceptable to create a window (hiddend) using the ATOM, and use the returned HWND as a "reference" for SetClassLongPtr?
As to get a valid HWND for registered class ATOM, you can use the FindWindow function.
By passing NULL for the lpWindowName parameter you'll get a handle to the first window that matches the class. Of course you'll fail if there is no window instance present. But in this it's probably good to assume that the corresponding window class isn't registered yet.
Related
I Basically want to change the state of a component, for that, I was doing something like this
void desenharpilha2(TImage *b1, TImage *b2, TImage *b3,TImage *b4, TImage *b5)
{
b1->Visible = False;
b2->Visible = False;
b3->Visible = False;
b4->Visible = False;
b5->Visible = False;
}
Basically, passing all the components that I need to change to the function, but I'm sure that there's a better way of doing that.
In short, I need a way to make the function have acess to all the components of the FMX without having to pass all the objects that I'm gonna change, because if I need to change for example, 15 objects, the code will be a mess.
Make desenharpilha2() be a member of the FMX Form class that owns the controls you are interested in, eg:
class TMyForm : public TForm
{
__published:
TImage *b1;
TImage *b2;
TImage *b3;
TImage *b4;
TImage *b5;
...
public:
...
void desenharpilha2();
};
...
void TMyForm::desenharpilha2()
{
b1->Visible = False;
b2->Visible = False;
b3->Visible = False;
b4->Visible = False;
b5->Visible = False;
}
And then you can call desenharpilha2() on the desired Form object at runtime when needed, eg:
TMyForm *someForm = ...;
...
someForm->desenharpilha2();
I have generated a Visual MFC project with a CMultiDocTemplate.
New and File open is already implemented. But I want now to open a database and query the data.
Under CDocument::OnOpenDocument I could read that it would be possible to write an application for that. But I have no idea how to open a document with an attached view without open an file.
What I need is a function OnOpenDB(LPCTSTR tableName) which creates the document and the view but not trying to open a file and extent the table name.
Google did not help me. I could not find any useful documentation.
#Adrian,
is there maybe another solution?
Finally I want to use more than one template for different functions.
In your solution I am missing a possibility to send the table name to the doc for doing the database query and provide the data to the view.
Can I do it in override the template class?
In your code, is dynamic_cast<CMyDoc *> ... ceating the view?
So finally I would need a public function which allows me to send a request for a new document with the table name.
Is this possible?
Update:
I tried to override CMultiDocTemplate::OpenDocumentFile(...), but it does not work. It seem that this function is not virtual.
Therefore I created a OpenDocumentFile(...) inside the override of the CMultiDocTemplate
CDocument* CStreamAuswertungMDT::OpenDocumentFile(LPCTSTR lpszPathName, BOOL bAddToMRU, BOOL bMakeVisible)
{
CDocument* pDoc = CreateNewDocument();
if (pDoc == NULL)
{
TRACE(traceAppMsg, 0, "CDocTemplate::CreateNewDocument returned NULL.\n");
AfxMessageBox(AFX_IDP_FAILED_TO_CREATE_DOC);
return NULL;
}
ASSERT_VALID(pDoc);
BOOL bAutoDelete = pDoc->m_bAutoDelete;
pDoc->m_bAutoDelete = FALSE; // don't destroy if something goes wrong
CFrameWnd* pFrame = CreateNewFrame(pDoc, NULL);
pDoc->m_bAutoDelete = bAutoDelete;
if (pFrame == NULL)
{
AfxMessageBox(AFX_IDP_FAILED_TO_CREATE_DOC);
delete pDoc; // explicit delete on error
return NULL;
}
ASSERT_VALID(pFrame);
// avoid creating temporary compound file when starting up invisible
if (!bMakeVisible)
pDoc->m_bEmbedded = TRUE;
if (!pDoc->OnNewDocument())
{
// user has be alerted to what failed in OnNewDocument
TRACE(traceAppMsg, 0, "CDocument::OnNewDocument returned FALSE.\n");
pFrame->DestroyWindow();
return NULL;
}
// it worked, now bump untitled count
pDoc->SetTitle(lpszPathName);
pDoc->OnDocumentEvent(CDocument::onAfterOpenDocument);
InitialUpdateFrame(pFrame, pDoc);
return pDoc;
}
This is now working for me!
You have to be a bit careful but you can do this by creating a local (static) 'copy' of your CMultiDocTemplate object and then manually calling OpenDocumentFile() on that with a nullptr argument:
static CMultiDocTemplate MY_tmplate(IDR_MYDOC, RUNTIME_CLASS(CMyDoc), RUNTIME_CLASS(CMDIChild), RUNTIME_CLASS(CMyView));
void OnOpenDB(LPCTSTR tableName)
{
CMyDoc* pDoc = dynamic_cast<CMyDoc *>(MY_tmplate.OpenDocumentFile(nullptr));
//... do stuff with pDoc (using `tableName` to probe the database, maybe)
//
}
You will need to be careful when initializing any required data members and any limits, et cetera, in the pDoc object and its associated view(s).
You can set the document's filename/path at any stage, using the SetPathName() base class function; or you can just change the displayed title (that shown in the view's frame windows) with pDoc->SetTitle().
Alternatively, rather than using a local (copy) document template, you could add a member to your application class to run through its list of 'installed' templates, looking for a match to a given string identifier:
CMultiDocTemplate *MyApp::GetDocTemplate(CString name)
{
POSITION dtPos = GetFirstDocTemplatePosition(); CString dtName;
while (dtPos != nullptr) {
CMultiDocTemplate *mdTmp = dynamic_cast<CMultiDocTemplate *>(GetNextDocTemplate(dtPos));
if ((mdTmp == nullptr) || !mdTmp->GetDocString(dtName, CDocTemplate::docName)) break;
if (dtName == name) return mdTmp;
}
return nullptr;
}
Then, if that function returns a non-NULL pointer, use that to call OpenDocumentFile. For a list of the various string components you can use to match the relevant template, see the CDocTemplate documentation.
I am trying to pass back a string from a Java method called from C++. I am not able to find out what JNI function should I call to access the method and be returned a jstring value.
My code follows:
C++ part
main() {
jclass cls;
jmethodID mid;
jstring rv;
/** ... omitted code ... */
cls = env->FindClass("ClassifierWrapper");
mid = env->GetMethodID(cls, "getString","()Ljava/lang/String");
rv = env->CallStatic<TYPE>Method(cls, mid, 0);
const char *strReturn = env->GetStringUTFChars(env, rv, 0);
env->ReleaseStringUTFChars(rv, strReturn);
}
Java Code
public class ClassifierWrapper {
public String getString() { return "TEST";}
}
The Method Signature (from "javap -s Class")
public java.lang.String getString();
Signature: ()Ljava/lang/String;
You should have
cls = env->FindClass("ClassifierWrapper");
Then you need to invoke the constructor to get a new object:
jmethodID classifierConstructor = env->GetMethodID(cls,"<init>", "()V");
if (classifierConstructor == NULL) {
return NULL; /* exception thrown */
}
jobject classifierObj = env->NewObject( cls, classifierConstructor);
You are getting static method (even though the method name is wrong). But you need to get the instance method since getString() is not static.
jmethodID getStringMethod = env->GetMethodID(cls, "getString", "()Ljava/lang/String;");
Now invoke the method:
rv = env->CallObjectMethod(classifierObj, getStringMethod, 0);
const char *strReturn = env->GetStringUTFChars(env, rv, 0);
The complete working solution is as below:
Java Side
public class ClassifierWrapper {
public ClassifierWrapper(){}
public String getString() { return "TEST";}
}
Native Side
jclass cls;
jmethodID mid;
jstring rv;
cls = jniEnv->FindClass("ClassifierWrapper"); //plase also consider your package name as package\name\classname
jmethodID classifierConstructor = jniEnv->GetMethodID(cls,"<init>", "()V");
if (classifierConstructor == NULL) {
return NULL; /* exception thrown */
}
jobject classifierObj = jniEnv->NewObject( cls, classifierConstructor);
jmethodID getStringMethod = jniEnv->GetMethodID(cls, "getString", "()Ljava/lang/String;");
rv = (jstring)(jniEnv->CallObjectMethod(classifierObj, getStringMethod));
const char *strReturn = jniEnv->GetStringUTFChars( rv, 0);
jniEnv->ReleaseStringUTFChars(rv, strReturn);
The signature ()Ljava/lang/String is wrong, due that a class name into JVM must terminate with ;, then in this case signature must be ()Ljava/lang/String;
The first problem is that ClassifierWrapper.getString() is not static. You will need to make it static or instantiate ClassifierWrapper.
The second problem is that you are using GetMethodId instead of GetStaticMethodId.
To invoke a method that returns an Object (such as a String) you would call CallStaticObjectMethod(). That will return a jobject local reference to the String that the method returned. You can safely cast the jobject to a jstring (see http://java.sun.com/docs/books/jni/html/types.html) and use GetStringUTFChars to retrieve the characters and GetStringUTFLength to get the number of characters.
JNI is very tricky. You need to check the error code for everything (use ExceptionCheck() when there is no error code). If you don't check for errors it will fail silently in most cases and usually not at the point where the actual bug is.
You also need to understand the difference between local and global references (and what methods generate new references) in order to not leak memory and run into the reference limit. For instance, FindClass returns a local reference to a class object, but GetMethodId returns a MethodID.
Good luck
Status quo:
I have a custom widget (MyWidget) with an event window.
Problem: if I create, show and then, later, hide and destroy the widget I get the following message from the application:
Gdk-WARNING **: losing last reference to undestroyed window
What I've found out: I've had a look in gdkwindow.c file and this message is reported when GDK_WINDOW_DESTROYED(window) == FALSE. So the thing I do not understand is how I should destroy my window correctly so that eventually gdk_window_destroy() function is called. I thought that the best place to call it was the Gdk::~Window() destructor. But it's empty. And moreover gdk_window_destroy() is absent in gdkwindow.cc file at all.
The on_realize() and on_unrealize() call-backs are below.
class MyWidget : public Gtk::Widget
{
...
private:
Glib::RefPtr<Gdk::Window> _event_window;
...
};
void Gtk::MyWidget::on_realize()
{
GdkWindowAttr attributes;
const Allocation & allocation = get_allocation();
attributes.event_mask = GDK_BUTTON_PRESS_MASK;
attributes.x = allocation.get_x();
attributes.y = allocation.get_y();
attributes.width = allocation.get_width();
attributes.height = allocation.get_height();
attributes.wclass = GDK_INPUT_ONLY;
attributes.window_type = GDK_WINDOW_CHILD;
_event_window = Gdk::Window::create(get_parent_window(), &attributes, GDK_WA_X | GDK_WA_Y);
_event_window->set_user_data(Widget::gobj());
set_window(get_parent_window());
set_realized();
}
void Gtk::MyWidget::on_unrealize()
{
_event_window->set_user_data(NULL);
_event_window.reset();
set_realized(false);
}
It's turned out that the correctest way to destroy the GDK window you have created with Gdk::Window::create() is.. guess what? to call Gtk::Widget::unrealize() in on_unrealize() method of your custom widget because apart from other things this base method calls gdk_window_destroy() for the widget's GDK window. And for that your widget has to be "windowful" (i.e you should call set_has_window(true); in the constructor and set_window(<your allocated GDK window>); in on_realize() callback. Very obvious approach, isn't it?
I should also say something about Gtk::Widget::realize(). Unlike Gtk::Widget::unrealize() you should call Gtk::Widget::realize() only if your widget doesn't have a GDK window (the method assumes that as an assertion).
Unfortunately I didn't have time and wish to get to the very bottom of it and endevour to understand why it has been done so and what reasons and consequences this approach has. Otherwise I would provide more detailed explanation.
You can find an official example from GTK's tutorial on custom widgets
here.
Also the code of my widget now looks like that:
class MyWidget : public Gtk::Widget
{
...
private:
Glib::RefPtr<Gdk::Window> _event_window;
...
};
void Gtk::MyWidget::on_realize()
{
GdkWindowAttr attributes;
const Allocation & allocation = get_allocation();
attributes.event_mask = GDK_BUTTON_PRESS_MASK | GDK_EXPOSURE;
attributes.x = allocation.get_x();
attributes.y = allocation.get_y();
attributes.width = allocation.get_width();
attributes.height = allocation.get_height();
attributes.wclass = GDK_INPUT_OUTPUT;
attributes.window_type = GDK_WINDOW_CHILD;
_event_window = Gdk::Window::create(get_parent_window(), &attributes, GDK_WA_X | GDK_WA_Y);
_event_window->set_user_data(Widget::gobj());
set_window(_event_window);
set_realized();
}
void Gtk::MyWidget::on_unrealize()
{
_event_window->set_user_data(NULL);
_event_window.reset();
Widget::unrealize();
// it will call gdk_destroy_window() and
// set_realized(false);
}
I am looking for a way to register non static methods of my MyWidget class as python methods.
MyWidget class is a QWidget class and it's on the main window. So I want to initialize
python when the app is launched then I want to retrive information from MyTableWidget (QTableWidget)
within MyWidget class. I like to register itemCount non-static method as python method. I could not do it
since non static object registration gives error on myPythonCommands below.
I checked boost::python but could not find a way to do it. It's doc cover to create a python clas within
C++ but not his one as far as I know.
I was wondering if any of you know a way and point me to right direction?
class MyTableWidget
{
};
class MyWidget
{
MyWidget()
{
Py_Initialize();
Py_InitModule("myApp", myPythonCommands);
}
~MyWidget()
{
Py_Finalize();
}
int itemCount()
{
return table.itemCount(); //return item count (in table) to python
}
MyTableWidget table;
};
static PyMethodDef myPythonCommands[] = {
{"itemCount", itemCount, METH_NOARGS, ""}, // here is the register part
{NULL, NULL, 0, NULL}
};