Firstly I'll start with a high level description of my problem. What I would like to do is to manipulate "Edit" controls in native windows applications by getting and setting their text items. The two ways I can see of doing this are:
(a)
Calling:
GetDlgItemText http://msdn2.microsoft.com/en-us/library/ms645489(VS.85).aspx
SetDlgItemText http://msdn2.microsoft.com/en-us/library/ms645521(VS.85).aspx
from user32.dll
Tried this but I couldn't wrap my head around all the parameter conversions. Even after following these mappings:
http://www.teamdev.com/winpack/windowsTypes.jsf
I couldn't get or set the text
(b)
The simplest method seems to be to call sendMessageEx in the Wnd class. I would like to send it a WM_SETTEXT message but the parameters seem too restrictive. It takes int msg, long wParam, long lParam. If you have a look at the WM_SETTEXT message in MSDN
http://msdn2.microsoft.com/en-us/library/ms632644(VS.85).aspx
You are supposed to feed it the string via lparam...but how can you do this if it only takes int/long as a parameter. Similarly for the WM_GETTEXT message how do you get back the String as a return type?
Hi Don,
Yes, the existing Wnd.sendMessageEx() method is not quite convenient, because it expects LPARAM and WPARAM arguments as Java long values. But it's quite possible to send string values using even the current implementation and the following example demonstrates this technique:
Str string = new Str("String value");
Pointer stringPtr = new Pointer(string);
Pointer.Void stringHandle = new Pointer.Void();
stringPtr.castTo(stringHandle);
long lParam = stringHandle.getValue();
wnd.sendMessageEx(Msg.WM_SETTEXT, 0, lParam);
I undertand that such pointers cast is not obvious enough therefore I have created the following example, which contains the wrappers for SetDlgItemText/GetDlgItemText API functions as well:
import com.jniwrapper.*;
import com.jniwrapper.win32.FunctionName;
import com.jniwrapper.win32.IntPtr;
import com.jniwrapper.win32.Msg;
import com.jniwrapper.win32.process.Process;
import com.jniwrapper.win32.ui.User32;
import com.jniwrapper.win32.ui.Wnd;
/**
* This example demonstrates how to wrap SetDlgItemText, GetDlgItemText and SendMessage API functions.
*
* @author Serge Piletsky
*/
public class Sample {
public static final FunctionName FUNCTION_SetDlgItemText = new FunctionName("SetDlgItemText");
public static final FunctionName FUNCTION_GetDlgItemText = new FunctionName("GetDlgItemText");
public static final FunctionName FUNCTION_SendMessage = new FunctionName("SendMessage");
/**
* Sends a message to the specified window.
*
* @param wnd window handle
* @param msg message
* @param wParam WPARAM
* @param lParam LPARAM
* @return the return value meaning depends on the message.
*/
public static long sendMessage(Wnd wnd, int msg, Parameter wParam, Parameter lParam) {
Function sendMessage = User32.getInstance().getFunction(FUNCTION_SendMessage.toString());
IntPtr result = new IntPtr();
sendMessage.invoke(result,
wnd,
new UInt(msg),
wParam != null ? wParam : new IntPtr(),
lParam != null ? lParam : new IntPtr());
return result.getValue();
}
/**
* Sets the title or text of a control in a dialog box.
*
* @param wnd Handle to the dialog box that contains the control
* @param idDlgItem Specifies the control with a title or text to be set
* @param string String that contains the text to be copied to the control
* @return If the function succeeds, the return value is true
*/
public static boolean setDlgItemText(Wnd wnd, int idDlgItem, String string) {
Function sendMessage = User32.getInstance().getFunction(FUNCTION_SetDlgItemText.toString());
Bool result = new Bool();
sendMessage.invoke(result,
wnd,
new Int(idDlgItem),
new Pointer(new Str(string)));
return result.getValue();
}
/**
* Returns the title or text associated with a control in a dialog box.
*
* @param wnd Handle to the dialog box that contains the control
* @param idDlgItem Specifies the control with a title or text to be set
* @return return value is true
*/
public static String getDlgItemText(Wnd wnd, int idDlgItem) {
Function sendMessage = User32.getInstance().getFunction(FUNCTION_GetDlgItemText.toString());
final int maxCount = 512;
Bool result = new Bool();
Str resultString = new Str(maxCount);
sendMessage.invoke(result,
wnd,
new Int(idDlgItem),
new Pointer(resultString),
new Int(maxCount));
return resultString.getValue();
}
public static void main(String[] args) throws Exception {
// start Notepad application and wait 2 seconds
Process notepadProcess = new Process("notepad");
Thread.sleep(2000);
// find Notepad window
Wnd notepadWindow = Wnd.findWindowByName("Untitled - Notepad");
// send WM_SETTEXT message to change the caption of Notepad window
sendMessage(notepadWindow, Msg.WM_SETTEXT, null, new Pointer(new Str("Hello, World!")));
Thread.sleep(2000);
// send WM_CLOSE message to close the Notepad window
sendMessage(notepadWindow, Msg.WM_CLOSE, null, null);
notepadProcess.waitFor();
System.out.println("Done.");
}
}
Also, we will add these functions to Wnd class in the next version of WinPack.
-Serge
The examples work perfectly and I can now set the text of an edit
control. Although it may be obvious I haven't worked out how to
analogously get the text of the control. Tried a few things:
(a) Tried modifying your
wnd.sendMessageEx(Msg.WM_SETTEXT, 0, lParam) example...replaced WM_SETTEXT with WM_GETTEXT but can't work out how to get the return String
(b) Most obviously there is your getDlgItemText method. However, I can't work out what to put for the idDlgItem parameter. I've tried getProcessID() and getThreadID(), neither of which seem to work
(c) I also tried calling sendMessage directly. Again I replaced SETTEXT with GETTEXT and I tried feeding it all sorts of Strings. I tried feeding it blank Str, AnsiString, WideString, UnicodeString as well as trying to wrap them all in Pointer classes i.e. new Pointer(new Str()), new Pointer(new AnsiString()) etc etc. However, when the method is called the variables still seem to remain as blank.
My Win32 programming is very weak apologies if the solution is painfully obvious
Hi,
Here are examples that demonstrate how to get the resulting string value after invocation of sendMessage method with WM_GETTEXT message:
int maxNumberOfChars = 512;
Str result = new Str(maxNumberOfChars);
sendMessage(notepadWindow, Msg.WM_GETTEXT, new Int(maxNumberOfChars), new Pointer(result));
System.out.println("result = " + result);
Or
int maxNumberOfChars = 512;
Str result = new Str(maxNumberOfChars);
Pointer resultPtr = new Pointer(result);
Pointer.Void resultHandle = new Pointer.Void();
resultPtr.castTo(resultHandle);
notepadWindow.sendMessageEx(Msg.WM_GETTEXT, maxNumberOfChars, resultHandle.getValue());
System.out.println("result = " + result);
-Serge
Works perfectly thanks.