Closed Thread Icon

Topic awaiting preservation: A -hard challenge, Windows hooks (Page 1 of 1) Pages that link to <a href="http://ozoneasylum.com/backlink?for=27567" title="Pages that link to Topic awaiting preservation: A -hard challenge, Windows hooks (Page 1 of 1)" rel="nofollow" >Topic awaiting preservation: A -hard challenge, Windows hooks <span class="small">(Page 1 of 1)</span>\

 
_Mauro
Bipolar (III) Inmate

From:
Insane since: Jul 2005

posted posted 02-28-2006 15:50

Ok, this may, or may not be the place to ask such a question.
Basically, I could ask it *anywhere* else, in Java forums and such, but...

The sun java forums already contain the question, and no answer.

So why not challenging the pros, the people who make js do raytracing, virtual black pills that drag in even time travellers, and such..
...

Context: I am running a Windows app.. from within Java. Meaning, I have used a "Runtime" object to "exec" the Windows app,
and I now have a handle to that Windows app as a Java "Process" object.
No biggie, the Java Process object is rather limited, it can be used to input data to the process, get the output, wait for the process to finish, etc.

This windows application starts with a configuration Window.
Which is a pain in the arse, I want to configure that from within Java and get to the real stuff.
...

And I am stuck.
I know that OS are based on "messages", triggered by events.
I know it is possible, in C/C++ to "hook" these messages.. I know only a few details about so called Windows hooks, but they seem to be what I
am looking for.

So? Could I do it in C/C++, I'd build myself a dll and would pack this in my Java application.

But I can't seem to... say capture a user menu selection in a given, running Windows application.
Any, and I mean any hint would be greatly appreciated.

(Hell, Python even has a dedicated pyHook library, how comes I can't find interesting Java investigations on the topic?)

_Mauro
Bipolar (III) Inmate

From:
Insane since: Jul 2005

posted posted 02-28-2006 15:59

Wether this topic sounds "off-topic" or not, it will become more and more relevant over time, as this does not involve only Java/Windows,
but <any scripting language>/Windows communication.

Proof of concept: a very interesting O'Reilly article about Perl/Windows communication.

hyperbole
Paranoid (IV) Inmate

From: Madison, Indiana, USA
Insane since: Aug 2000

posted posted 02-28-2006 19:18

I don't know Java, however, I worked on a database project several years ago where one of the other developers was writing Java code and getting the Java to invoke C++ programs and getting C++ programs to invoke Java, so I know it's possible.

What I remember is that he used a couple of specialized modules for doing the communication between the two languages. You might start looking through the module lists for something to help you out.

.



-- not necessarily stoned... just beautiful.

Tyberius Prime
Maniac (V) Mad Scientist with Finglongers

From: Germany
Insane since: Sep 2001

posted posted 02-28-2006 20:22

last time I looked there was something called java native interface or such (JNI) that could be used to call libraries, or dlls.

You should be able to leverage that to get at the PostMessage/Sendmessage function - all Windows messages are well documented in the MSDN,
but sending keystrokes isn't simple - but not impossible either. If in doubt, see the source for Autohotkey.

I don't know to what degree you can do callbacks with JNI... you need them for some windows functions, but not most of the messaging ones.

So long,

->Tyberius Prime

_Mauro
Bipolar (III) Inmate

From:
Insane since: Jul 2005

posted posted 02-28-2006 20:41

Ok, this stuff rocks. And I do rock, too.

I've managed to do it!
What you're talking about, hyperbole, is now called JNI. In 2 words, it is a Java/Native binding.

You can write a method, or "function", in a Java program, and have it's body in a Cpp program.
So your java app can call homebrewed, platform dependent software.

Cool.

But what I wanted was being able to "drive" any Windows process, not only my applications, from inside a Java thing.

~~~

So, I have built a dll which recovers the pid of a given process, given the corresponding Window name (!), and passes it to a Java app.
Eg. (technical terminology): getting the Window process handle or hwnd of a given Windows application in Java.

Cool.

What am I supposed to be able to do with that?

Using Windows message queuing, and Windows messages, through my Java compliant dll, I am supposed to be able to script *any* Windows application,
service, process, daemon, you name it...
And this includes everything that is not meant to be scriptable.

~cough.
All this to? Create a little automation gem which I will release as soon as possible.

---------------------------------------

The hopes? As I said, being able to fully script Windows and all it's applications.
Something like:

WindowsController.getWindow("Adobe Photoshop CS").getComponent("Filters").getSub("Category").getSub("Filter").getButton("Ok").Click

_Mauro
Bipolar (III) Inmate

From:
Insane since: Jul 2005

posted posted 02-28-2006 20:48

Actually, I just noticed one of the pages where I got docs for this little thing holds a great tool, great demonstration of the concept:
record macros of ~anything~ Windows and bind them to hotkeys:
http://autohotkey.com/

And TP, my exact conclusions. Here are the links from "autohotkey", interesting stuff:
http://autohotkey.com/docs/commands/PostMessage.htm
http://autohotkey.com/docs/misc/SendMessageList.htm

(Edited by _Mauro on 02-28-2006 20:50)

_Mauro
Bipolar (III) Inmate

From:
Insane since: Jul 2005

posted posted 03-01-2006 07:30

Yaaawwnn... me thinks this thing is really fascinating. For further reference, I'll post some code and a quick/dirty "how-to".

And I also have questions: if anybody knows how to get a proper window handle (hwnd) from a given pid, I have my ways, but
they don't work in all cases.

*0) Pre-requisites: gcc recent builds for Windows (Mingw32 toolset), jdk most recent version. I am using the terrible dev cpp
and the fantastrippic eclipse as IDEs. A good java / c / cpp background is recommended, but the concept is simple:
put the definition of a function on the Java side, and through JNI, implement the code on the c / cpp / native binary stuff (assembler, delphi, they all will do the trick).

1) Create the java class. Mine looks like this:

code:
package scanner;

public class Scanner {

    

    public native int getWindowProc(String WindowName); // gets hwnd by window name
    public native int shellExecute(String ProgName); // launches a process and returns the pid
    public native int getHWNDfromPid(int pid);
    public native void toForeground(int WindowProc);
    public native void tabControls(int WindowProc);
    public native void pressButton(int WindowProc);
    

    /**
     * @param args
     */
    public Scanner() {
        // TODO Auto-generated method stub
        Runtime rt = Runtime.getRuntime();
        Process proc = null;
        try{
            
            //proc = rt.exec("./fr-031__faded_memories_by_visualice__chaos__vic/fr-031.exe");
            //System.out.println(proc.getClass());
            //if(proc.waitFor()==0){
              // System.out.println("Program terminated succesfully");
            
            //
            //
            //}
        }
        catch(Exception g){
            System.out.println("Error occured : " +g.getMessage());
 
        }
        test();
        
        
        
    }
    
    public static void main(String[] args){
        Scanner mine = new Scanner();
    }

    private String getAbsolutePath(String path){
        String fullPath = this.getClass().getClassLoader().getResource(path).getPath();
        fullPath = fullPath.substring(1);
        fullPath = fullPath.replace("%20", " ");
        return fullPath;
    }
    
    private void test(){
        System.loadLibrary("injectmessage");

        //String args = " -full";
        String path = getAbsolutePath("./fr-031__faded_memories_by_visualice__chaos__vic/fr-031.exe");
        
        //String path = getAbsolutePath("./dozen/dozen.exe");
        //System.out.println("");
        int pid = shellExecute(path);
        
        
        System.out.println("Absolute path: " + path);
        
        
        
        System.out.println("Pid: " + pid);
        
        int hwnd = getHWNDfromPid(pid);
        //getWindowProc("Werkkzeug");
        System.out.println("HWND: "+hwnd);
        
        toForeground(hwnd);
        //tabControls(hwnd);
        //
        //
        
        //tabControls(hwnd);
        pressButton(hwnd);

        //System.out.println(getHWNDfromPid(pid));
    }
    
}



It's located in a package/subfolder of the project called "scanner".

2) Use "javah" to extract the c++ header file (.h) from your class.
Like:
javah -jni scanner.Scanner

3) Create the cpp file, the implementation. Mine looks like this:

code:
//#include <jni.h>
#include "scanner_Scanner.h"
#include <windows.h>
#include <stdio.h>
//#include <iostream>
//#include <string>
//#define MOUSEEVENTF_LEFTDOWN 0x0002 /* left button down */
//#define MOUSEEVENTF_LEFTUP 0x0004 /* left button up */ 

//using namespace std;

//int ExecuteProcess(string &FullPathToExe, string &Parameters, int SecondsToWait);
//string getExeName(string strFullPathToExe);

JNIEXPORT jint JNICALL Java_scanner_Scanner_getWindowProc(JNIEnv *env, jclass obj, jstring title){
    HWND hwnd = NULL;
    const char *str = NULL;

    str = env->GetStringUTFChars(title, 0);
    hwnd = FindWindow(NULL,str);
    env->ReleaseStringUTFChars(title, str);
   return (jint) hwnd;
}

JNIEXPORT jint JNICALL Java_scanner_Scanner_shellExecute(JNIEnv *env, jclass obj, jstring fileName){
	STARTUPINFO si = { sizeof(si) }; 
	PROCESS_INFORMATION pi; 
	char *str = NULL; 
	str = (char *)env->GetStringUTFChars(fileName, 0);
	env->ReleaseStringUTFChars(fileName, str);
	int pid = 0;

	if(CreateProcess(0, str, 0, 0, FALSE, 0, 0, 0, &si, &pi)) { // optionally wait for process to finish 
		//WaitForSingleObject(pi.hProcess, INFINITE); 
		CloseHandle(pi.hProcess);
		CloseHandle(pi.hThread);
		return (jint)pi.dwProcessId; 
	}
	
	return 0;
}

JNIEXPORT void JNICALL Java_scanner_Scanner_tabControls(JNIEnv *, jclass obj, jint handle){
	PostMessage((HWND)handle, WM_NEXTDLGCTL, 0, 0) ;
}


JNIEXPORT void JNICALL Java_scanner_Scanner_pressButton(JNIEnv *, jclass obj, jint handle){
	SetFocus((HWND)handle);
	//PostMessage((HWND)handle, WM_NEXTDLGCTL, 0, 0) ;
    	keybd_event( VK_RETURN, 0x45, KEYEVENTF_EXTENDEDKEY, 0 );
    	keybd_event( VK_RETURN, 0x45, KEYEVENTF_EXTENDEDKEY | KEYEVENTF_KEYUP, 0 );
}

JNIEXPORT void JNICALL Java_scanner_Scanner_toForeground(JNIEnv *, jclass obj, jint handle){
	SetWindowPos((HWND) handle,HWND_TOPMOST,0,0,0,0,SWP_NOMOVE|SWP_NOSIZE); 
}

JNIEXPORT jint JNICALL Java_scanner_Scanner_getHWNDfromPid(JNIEnv *env, jclass obj, jint pid){
	HWND hwnd = NULL; 
	DWORD test_pid = 0;
	long test_thread;
 	printf("original pid:%d \n", pid);
	DWORD target_pid = (DWORD) pid;
 	printf("dworded pid:%d \n", target_pid);
	

// Get the first window handle. 
	hwnd = FindWindow(NULL, NULL);
	//printf("%d",hwnd);
	// ' Loop until we find the target or we run out ' of windows. 
	while (hwnd != 0){
	// ' See if this window has a parent. If not, ' it is a top-level window. 
		//if(!GetParent(hwnd)){	
	//		// ' This is a top-level window. See if ' it has the target instance handle. 
			GetWindowThreadProcessId(hwnd, &test_pid);
			printf("Pid for currently processed window: %d\n", test_pid);
			if(test_pid == pid){
//					//' This is the target. 
//					return (jint) hwnd;
//					//	printf("Nailed IT! %d %d\n",pid,test_thread);
			}

			// Examine the next window. 
			test_pid = 0;
		//}
			hwnd = GetWindow(hwnd, GW_HWNDNEXT);
    }

	return 0;
}



Basically, functions implemented in this file mirror the following java code:

code:
public native int getWindowProc(String WindowName); // gets hwnd by window name
    public native int shellExecute(String ProgName); // launches a process and returns the pid
    public native int getHWNDfromPid(int pid);
    public native void toForeground(int WindowProc);
    public native void tabControls(int WindowProc);
    public native void pressButton(int WindowProc);



3) Compile your cpp as a dll.
For this, you need to pass references to the jni.h header when calling gcc.
For example, for WinXP, I use the following batch file:

code:
gcc -I"C:/Dev-Cpp/include" -I"C:/Program Files/Java/jdk1.5.0_05/include"  -I"C:/Program Files/Java/jdk1.5.0_05/include/win32" -shared -o injectmessage.dll injectmessage.cpp injectmessage.def
pause



/!\ I've had huuuge problems with gcc exporting "bad" symbols for my dll, and have used "impdef.exe" to workaround this issue.
I first compile my cpp code using the commands above, then use "impdef" to extract a "def" file, with the real symbols.
I then equate each real symbol to it's "short name (same as in .h file)" in the def file,
and recompile.

The sample above works, in part, and has drawbacks and issues, for now.
The pressbutton function simply doesn't work.
The other events do work, like "tabbing" controls in a remote application.

This little snippet has issues when launched from a batch file.
It also has issues with retrieving the hwnd reference for the target application: if the target is launched before my app, no problemo.
If the target is launched from my app, then the pid is discovered, but the hwnd can't be retrieved.

My guess is that it is a matter of synchronisation.

It will take some time to tweak the features to my exact needs, and enhance this, but I have investigated quite a few tracks already,
and it wasn't really thoroughly documented.

** off to watch a movie and get some sleep.

_Mauro
Paranoid (IV) Inmate

From:
Insane since: Jul 2005

posted posted 03-02-2006 16:45

I DID IT!! Oh joy.
I hardly understand how I did that right now, but it works.

Sample app: this launches a farbrausch demo automatically, bypassing the demo configuration screen.
I recommend running it using run.bat. Your monitor will switch to fullscreen and will stay black for a couple of seconds.
http://www.beyondwonderland.com/data/java/DemoPlayer/DemoPlayer.zip

.............. Technical details
Basically, the problem was a synchronisation problem.
I don't know why I am reporting this and documenting this.

Oh yeah, there is at least one excellent reason: this terribly lacked clear docs... it's simple, why the hell was it so hard to google specific information about it?

The key was adding this:
java.awt.Toolkit.getDefaultToolkit().getSystemEventQueue().peekEvent();

After all calls to my native methods.

The message loop in Windows is a blocking method, it locks things for a given thread until it has translated and dispatched the latest system message.
I can't explain, and barely understand the underlying reasons, but calling native windows procedures from a java app caused that latest message not to be processed
until the java application's death.

So basically, the first system call worked, and all subsequent calls were locked.

Adding a java call to the "peekevent" method pops the latest event off of the message queue, thus restoring normal activity.
I am still cheating a bit on another, tiny issue, so if your system is utterly slow or low in memory, it could fail. But you would have to be running a pentium II or III
............................................


.............. Real world applications

Basically, I am telling Windows two things here, from inside a Java app.
"Launch a program I chose" and "Tell that program the user has pressed enter".

...there are infinite possibilities. Anything a human, or virtual "Windows role" can do with a comp can now be "macroed" and reproduced with my snippet. Including turning off the monitor, the comp,
and much, much more, almost no restrictions.

...

All I wanted to do was a demo player btw, I am tired of pressing buttons and configuring settings
when watching demos. I'll talk about other nifty features in another post: I now have the core engine to my player,
I'll build it up, test it up, and will publish it shortly.

Please please plase report bugs

I crave for bug reports, they help a lot: this is the first time I write a dll of my own, let alone a dll which talks to Java apps.
Please try to be precise and descriptive when reporting bugs, too short is misleading: an application progresses instruction by instruction,
so the more details about it's exact state when it crashed, the happier the coder.

There is no "obvious" reason for it to crash though, but you never know.

Vicne
Obsessive-Compulsive (I) Inmate

From:
Insane since: Mar 2006

posted posted 03-06-2006 23:30

Hi, Mauro

First let me congratulate you because you seemed to pass many hurdles nearly alone and I see it's not obvious at all - and absolutely not documented as you say.

Well, in fact, I'm following the exact same goal as you (sending WM_ messages to a Windows app from Java) although it a completely different context (off topic but take a look here if you like :-) ).

I started from your Java code (with peekMessage, from your zip file) and it compiles, then I can extract the C header, implement functions in C like you do and it also compiles and I get the dll. Great. But when starting the java program, it complains with an java.lang.UnsatisfiedLinkError for the C functions.

So I guess the missing step is fiddling around with function names with impdef...

However, many references on the net (e.g. the one on this page) talk about a version which is nowhere to be found. The only impdef.exe I can find is in the free Borland Command-Line Utils... Is that the one you're talking about ?
When running this version, the def file contains exports in the form

code:
_Z56Java_org_tlcdcemu_plugin_winjector_WInjector_sendMessageP7JNIEnv_P7_jclassllll @3   ;
 _Z56Java_org_tlcdcemu_plugin_winjector_WInjector_sendMessageP7JNIEnv_P7_jclassllll


And... er... I don't know what to do next :-)

My c code implements functions as declared in the generated include file, for example :

code:
JNIEXPORT jint Java_org_tlcdcemu_plugin_winjector_WInjector_sendMessage(
    JNIEnv *, jclass obj, jint handle, jint msg, jint wparam, jint lparam)



Can you give me a hint ?

Thanks again for your unvaluable explanations.

Best regards,

Vicne

(Edited by Vicne on 03-06-2006 23:37)

_Mauro
Paranoid (IV) Inmate

From:
Insane since: Jul 2005

posted posted 03-07-2006 08:38

Your guess is right. I don't know the exact details and have just reinstalled my pc, which crashed, but
for instance, when my dll is first compiled, the compiler only exports "mucked up" references, like
"functionName@32xx", and stuff like that.

I then do a functionName = functionName@32xx in the extracted def file, and recompile the dll with this new set of exports.
Which works for me.

Will try to provide some more details when I am through with restoring my pc.

Vicne
Obsessive-Compulsive (I) Inmate

From:
Insane since: Mar 2006

posted posted 03-07-2006 08:59

Thank you for your quick reply and sorry for your PC :-(. Hope the crash isn't related to JNI :-)

Just a few questions more when you have a little time :
- where did you get the first .DEF file from ? Did you write it by hand ?
- for my part, I hadn't one, so I just didn't specify it on the gcc line and it built the dll without it - but maybe it makes the functions kind of "private" (non-exported) ? Does it make sense ?
- do you know if the Borland impdef is suitable ? If it's not the one you used, do you remember where you got yours from ?
- when you say "functionName = functionName@32xx", do you mean in my case it should be

code:
Java_org_tlcdcemu_plugin_winjector_WInjector_sendMessage = 
_Z56Java_org_tlcdcemu_plugin_winjector_WInjector_sendMessageP7JNIEnv_P7_jclassllll @3


or

code:
sendMessage = 
_Z56Java_org_tlcdcemu_plugin_winjector_WInjector_sendMessageP7JNIEnv_P7_jclassllll @3


?
- and one last thing : where do you put this "alias" in the DEF file ?

Any information is welcome, and thanks again for your great help

Vicne

_Mauro
Paranoid (IV) Inmate

From:
Insane since: Jul 2005

posted posted 03-07-2006 09:58

Basically, I first compile without any def file.
Then, I use impdef, and yes, I think it's the very same impdef as in Borland. Don't remember where I got it though (googled
impdef and found one copy).

Impdef creates the def file which looks like:
EXPORTS
Weird_name@weird_cypher

I correct the def file to look like your first example.
For instance, in my case, the function JNIEXPORT jint JNICALL Java_scanner_Scanner_getWindowProc
Is mapped, after an impdef, to something like Java_scanner_Scanner_getWindowProc@stupidSymbolsxxxx

So I correct my def file, and while keeping the original EXPORTS list in it, I add the line:
Java_scanner_Scanner_getWindowProc = Java_scanner_Scanner_getWindowProc@stupidSymbolsxxxx

So, the final def file looks like:

code:
EXPORTS
Java_scanner_Scanner_getWindowProc@stupidSymbolsxxxx
Java_scanner_Scanner_getWindowProc = Java_scanner_Scanner_getWindowProc@stupidSymbolsxxxx
...



And recompiling my dll this time with the def file produces the final result, and fixes the "Unsatisified linker error".

My pc crash, for the record, is due to an overheating pc and a physical shock
And you are welcome, let me know how it works for you.

Vicne
Obsessive-Compulsive (I) Inmate

From:
Insane since: Mar 2006

posted posted 03-07-2006 14:36

I see. I'll test that tonight and will let you know how it goes.
Thanks very much for all this information.

Vicne

Vicne
Obsessive-Compulsive (I) Inmate

From:
Insane since: Mar 2006

posted posted 03-07-2006 22:45

Hey, you know what ? It works :-)

Mauro, you rule !

I only tested your toForeground function on Notepad and it just works.

I can't understand why and how function names are transformed, because some times, identical prototypes end up with very different exports...

Now I'm struggling to pass correct arguments to sendMessage :-)

Thanks a thousand times again.

(Edited by Vicne on 03-07-2006 22:48)

_Mauro
Paranoid (IV) Inmate

From:
Insane since: Jul 2005

posted posted 03-07-2006 23:47

You are welcome.
In several ways.

And.

You are right on the "I rule" bit.
Stick around, as I am just getting warm, I got new skills since my early days in this place, and a couple of unsuspected gems up my sleeve.

* mu-hu-hahahaha.. *

(Edited by _Mauro on 03-07-2006 23:48)

Vicne
Obsessive-Compulsive (I) Inmate

From:
Insane since: Mar 2006

posted posted 03-07-2006 23:49

Hey, maybe I found the reason for these weird names in the exported .def file : typecasts.

In my exported .h file, I had definitions such as :

code:
JNIEXPORT void JNICALL Java_org_tlcdcemu_plugin_winjector_WInjector_toForeground(JNIEnv *, jobject, jint);


while in the c implementation, its signature is :

code:
JNIEXPORT void JNICALL Java_org_tlcdcemu_plugin_winjector_WInjector_toForeground(JNIEnv *, jclass obj, jint handle){


Notice the type of the second parameter : jobject != jclass

Once I change the implementation to expect a jobject obj, the .def file contains the correct declaration.

Although, what I don't understand is that I still need the second compilation with the untouched .def file to get a working dll (binary different from the one of the first pass)...

_Mauro
Paranoid (IV) Inmate

From:
Insane since: Jul 2005

posted posted 03-07-2006 23:57

Hey? Stupid me, I had noticed the difference in signatures, just... didn't bother. Congrats on having spotted that one.
If you got a correct def file, you shouldn't need a second pass, you should be able to use the def directly... odd. Fiddle some more.

Prior to my comp crash, I had gotten to the point where I can get a window handle given any pid, send mouse moves, mouse clicks,
position the mouse, and capture window/screen coordinates.

I was able to, and building snippets for each and every demo config you could think of, first as java calls to my instructions and later on,
as a "very high level/simple language" for batching demos execution.

So let's play around and share.
Ask away if you have more questions anyway, and keep us posted, me likes these Windows commands.

Btw, something to think about: does a given Window proc take in account the originator of a message before processing it ?-) This is important,
it leads to another way to have fun with system messages. ~cough.

And I had also gotten to track events precisely, this time using "Windows hooks" for real, to capture events such as the death of a given window (by pid, by window name, you name it).

Vicne
Obsessive-Compulsive (I) Inmate

From:
Insane since: Mar 2006

posted posted 03-08-2006 00:13

Yeah, the 2-pass compilation still is a mystery, maybe a missing option in gcc or something...

Very interesting project indeed, to have a collection of utilities in a dll you can call from Java...

quote:
Btw, something to think about: does a given Window proc take in account the originator of a message before processing it ?-)



AFAIK, absolutely not. Message contents matters, not originator...

I'm currently playing around with the WM_COPYDATA message which is designed for inter-process communication. It's not working yet but I'll keep you posted in case it does :-)

(Edited by Vicne on 03-08-2006 00:15)

Vicne
Nervous Wreck (II) Inmate

From:
Insane since: Mar 2006

posted posted 03-09-2006 02:05

Just for your information : the dll I built doesn't seem to be able to send messages (at least WM_COPYDATA) to other apps. I copied the code to a simple "main()" program and compiled it. No complaint on compile or run, but no message gets sent.
Then in this code, I changed the console mode "main()" into a windows mode "WinMain()" and specified the '-Wl,--subsystem,windows -mwindows' linker options, and it works.
But then back to the dll, even if I compile with the same options, it doesn't seem to change anything, so maybe that is dependant on the host app calling the DLL, that is java.exe which *is* in console mode :-(

Well, for now, I resorted to compiling a windows-mode exe and calling it via Runtime.exec(). Ugly but well...

Although, if you have an idea, I'm still motivated to try other things...

Best regards,

Vicne

Tyberius Prime
Maniac (V) Mad Scientist with Finglongers

From: Germany
Insane since: Sep 2001

posted posted 03-09-2006 07:49

hm... you might need to have your own message loop (and your own window) for wm_copydata.

On the other hand... how were you sharing the memory you were passing around in your COPYDATASTRUCT?

_Mauro
Maniac (V) Inmate

From:
Insane since: Jul 2005

posted posted 03-09-2006 08:08

I don't understand how copydata should be different of other Windows messages, when I finally get my pc fully fixed (I hate installing tons of software), I'll have a look. I know that all the system messages I used (a limited subset of input devices events) work from the simple JNI dll, which doesn't contain any kind of main: it doesn't need an entry point, it's not an app, it's merely an api.

Odd. Will let you know what I think as soon as my compilers work.

Vicne
Nervous Wreck (II) Inmate

From:
Insane since: Mar 2006

posted posted 03-09-2006 13:25
quote:

Tyberius Prime said:
hm... you might need to have your own message loop (and your own window) for wm_copydata.


Mmh... In a DllMain or something ?

quote:
On the other hand... how were you sharing the memory you were passing around in your COPYDATASTRUCT?


I wasn't.
Java basically just passes a int for hWnd (gotten by a previous working JNI call to findWindow) and a String to be used as a message. The C code of the called function allocates a COPYDATASTRUCT, fills it with information to pass the String and sends it via SendMessage with the given hWnd.

Vicne
Nervous Wreck (II) Inmate

From:
Insane since: Mar 2006

posted posted 03-09-2006 13:30
quote:

_Mauro said:

I don't understand how copydata should be different of other Windows messages,


I don't either.

quote:

I know that all the system messages I used (a limited subset of input devices events) work from the simple JNI dll, which doesn't contain any kind of main: it doesn't need an entry point, it's not an app, it's merely an api.


Yes, I just built a main with the same code to determine if the error lied on the JNI side or on the C one, and that's what made me discover the subtle difference between a console and a windows mode. Now I'm wondering if the same difference applies for dlls, or if only the mode of the calling app matters.

But you're absolutely right for the first point : the most puzzling is why do some messages work and others not...

_Mauro
Maniac (V) Inmate

From:
Insane since: Jul 2005

posted posted 03-09-2006 14:34

Let's be fully honest: from the main/winmain investigations, I think you are lacking C/Cpp/Win32 expertise to work around this issue.
I don't think Windows is exposing "unexpected behavior", my guess is that, if some buffer cannot be read or written too,
it is "locked up" by something else.

Might be the JVM using a system-wise buffer, or something of that kind instead of the C/Cpp side of things, so
your google keywords should be something like JNI-COPYDATASTRUCT... more oriented towards JNI-Java compromising normal Windows
behavior.

My 2 cents.
No offense meant of course.

Tyberius Prime
Maniac (V) Mad Scientist with Finglongers

From: Germany
Insane since: Sep 2001

posted posted 03-09-2006 19:28

WM_COPYDATA is not like other windows messages.
You are passing around a pointer, and windows transparently copies the data into the target process's memory,
have to make sure that the receiver can actually read the data.

This definatly requires special handling within windows.

Then again, I have no clue where your actual problem is.

Vicne
Nervous Wreck (II) Inmate

From:
Insane since: Mar 2006

posted posted 03-10-2006 10:14

Well, first, let me say there's no emergency anymore as I worked around the problem by calling a stand-alone "messagesender" exe with Runtime.exec(), passing Strings to be sent on the command-line. It works, so users should be happy... but I'm not :-).

quote:

_Mauro said:
Let's be fully honest: from the main/winmain investigations, I think you are lacking C/Cpp/Win32 expertise to work around this issue.


I surely am :-)
To be more precise, I once had some expertise in C/Win32/MFC programming but that was long ago (I worked with an OS called "Windows 95" or something) but I admit I left that universe for 10 years to develop buisness systems in Java. So let's say I have a blackout :-)

quote:

I don't think Windows is exposing "unexpected behavior", my guess is that, if some buffer cannot be read or written too,it is "locked up" by something else.Might be the JVM using a system-wise buffer, or something of that kind instead of the C/Cpp side of things, so your google keywords should be something like JNI-COPYDATASTRUCT... more oriented towards JNI-Java compromising normal Windowsbehavior.My 2 cents.No offense meant of course.


No problem.
I don't have much time these days, but I just followed your advice and came across this very interesting thread. (Sage TV is a media center seemingly written in Java and this guy has written a Winamp plugin for it). I'll ask if he's willing to share some code...

Thanks for your input, it was helpful as usual.

(Edited by Vicne on 03-10-2006 10:17)

Vicne
Nervous Wreck (II) Inmate

From:
Insane since: Mar 2006

posted posted 03-10-2006 14:19

I think I found all the code I need here (and hopefully the week-end is coming :-) )

According to this file, it looks like buffer copying and length calculation should be slighlty more complicated than GetStringUTFChars + lstrlen...

Vicne
Nervous Wreck (II) Inmate

From:
Insane since: Mar 2006

posted posted 03-10-2006 14:43

(sorry for the spam)
More info that could explain the strange EXPORT and the 2-pass compilation here
He advises compiling with the following option :

quote:
--add-stdcall-alias
If given, symbols with a stdcall suffix (@nn) will be
exported as-is and also with the suffix stripped.
Vicne
Bipolar (III) Inmate

From:
Insane since: Mar 2006

posted posted 03-10-2006 19:50

Confirming : Using the following syntax

code:
gcc -Wall -mno-cygwin -shared -Wl,--add-stdcall-alias -I "c:\Program Files\Java\jdk1.5.0\include" -I "c:\Program Files\Java\jdk1.5.0\include\win32" -o <target.dll> <source.c>


The dll is compiled correctly in one step.

Vicne
Bipolar (III) Inmate

From:
Insane since: Mar 2006

posted posted 03-11-2006 12:57
quote:
WM_COPYDATA is not like other windows messages.
You are passing around a pointer, and windows transparently copies the data into the target process's memory,
have to make sure that the receiver can actually read the data.


Good guess, T P :-)

Once I copy the data parameter (a string) to a local buffer, it works :-)

code:
JNIEXPORT jint JNICALL Java_org_tlcdcemu_plugin_winjector_WInjector_sendCommand(JNIEnv *env, jobject obj, jint hWnd, jstring message) {

       char buf[255];

       const char * str = (*env)->GetStringUTFChars(env, message, 0);

       lstrcpy(buf, str);

       COPYDATASTRUCT data;
       data.dwData = 1;
       data.lpData = buf;
       data.cbData = lstrlen(buf);

       return (jint)SendMessage((HWND)hWnd, WM_COPYDATA, 0, (long) &data);

}



Thank you for your insightful help.

(Edited by Vicne on 03-11-2006 12:59)

« BackwardsOnwards »

Show Forum Drop Down Menu