How to iterate through window elements using JNA? - java

Using JNA, my ultimate goal is to read a message that was sent using Windows NET SEND or MSG.EXE, which appears as a Windows pop-up message window on the receiving machine.
I am already able to search for this specific message window and get the hWnd handle using the code below. My problem now is how to I iterate through all the elements of this window to find the actual message text, read the message, and also click the OK button?
My research tells me I need to use FindWindowEx (to go through the elements) and PostMessage (to click the OK button) but I am struggling to make it work.
package democode;
import com.sun.jna.Pointer;
import com.sun.jna.Native;
import com.sun.jna.win32.StdCallLibrary;
public class JNA_Main {
// Equivalent JNA mappings
public interface User32 extends StdCallLibrary {
User32 INSTANCE = (User32) Native.loadLibrary("user32", User32.class);
interface WNDENUMPROC extends StdCallCallback {
boolean callback(Pointer hWnd, Pointer arg);
}
boolean EnumWindows(WNDENUMPROC lpEnumFunc, Pointer arg);
boolean PostMessage(Pointer hwndParent, String msg, String wParam, String lParam);
Pointer FindWindowEx(Pointer hwndParent, String hwndChildAfter, String lpszClass, String lpszWindow);
int GetWindowTextA(Pointer hWnd, byte[] lpString, int nMaxCount);
}
public static void main(String[] args) {
final User32 user32 = User32.INSTANCE;
user32.EnumWindows(new User32.WNDENUMPROC() {
int count;
public boolean callback(Pointer hWnd, Pointer userData) {
byte[] windowText = new byte[512];
user32.GetWindowTextA(hWnd, windowText, 512);
String wText = Native.toString(windowText);
wText = (wText.isEmpty()) ? "" : "; text: " + wText;
if (wText.contains("My Window Name")){
System.out.println("Found window " + hWnd + ", total " + ++count + wText);
//**************************************************//
//NEED CODE HERE TO ITERATE THROUGH ELEMENTS OF THIS PARTICULAR WINDOW, READ THE MESSAGE TEXT AND CLICK OK BUTTON.
//**************************************************//
}
return true;
}
}, null);
}
}

The logical choice via the MSDN is to call EnumChildWindows using the hWnd pointer that you've received from the callback method above.

Related

JNA ClientToScreen?

Having issues figuring out how to use the ClientToScreen winapi function with JNA.
I'm still getting 0, 0 output for the coordinates of the window handle.
Am referencing this but im sure im not doing it right https://msdn.microsoft.com/en-us/library/windows/desktop/dd183434(v=vs.85).aspx
public interface User32Ex extends W32APIOptions {
User32Ex instance = (User32Ex) Native.loadLibrary("user32", User32Ex.class, DEFAULT_OPTIONS);
boolean GetCursorPos(long[] lpPoint);
WinDef.HWND WindowFromPoint(long point);
boolean GetClientRect(WinDef.HWND hWnd, WinDef.RECT rect);
boolean ClientToScreen(WinDef.HWND hWnd, int pt);
}
public void debug() throws InterruptedException {
while (true) {
long[] getPos = new long[1];
User32Ex.instance.GetCursorPos(getPos);
WinDef.HWND hwnd = User32Ex.instance.WindowFromPoint(getPos[0]);
WinDef.RECT rect = new WinDef.RECT();
User32Ex.instance.GetClientRect(hwnd, rect);
User32Ex.instance.ClientToScreen(hwnd, rect.left);
User32Ex.instance.ClientToScreen(hwnd, rect.right);
System.out.println(rect.toRectangle().toString());
Thread.sleep(1500);
}
}
#technomage is right. You need to use WinDef.POINT instead of int in your ClientToScreen() rect parameter.
If anyone looking for a working solution for the question, it's below.
In every 3rd sec, it's logging the window inner client coordinates desktop position.
Explanation.
In the User32ForClientRect interface, we load user32.dll with JNA and define a few methods.
The reason behind we not using the already implemented User32 methods from the JNA interface is simple. The methods that we need in our case are not implemented in JNA.
Methods:
FindWindow - it's included in JNA
Retreives the window handle HWND
GetClientRect
Retreives the window widht, height. to rect.right, rect.bottom
ClientToScreen
Retreives the window inner client desktop position. Left/Top corner coordinates
Example:
package application.playground;
import com.sun.jna.Native;
import com.sun.jna.platform.win32.WinDef;
import com.sun.jna.win32.StdCallLibrary;
import com.sun.jna.win32.W32APIOptions;
import java.awt.*;
public class ClientRectExample {
interface User32ForClientRect extends StdCallLibrary {
User32ForClientRect INSTANCE = Native.loadLibrary("user32", User32ForClientRect.class,
W32APIOptions.DEFAULT_OPTIONS);
WinDef.HWND FindWindow(String lpClassName, String lpWindowName);
boolean GetClientRect(WinDef.HWND hWnd, WinDef.RECT rect);
boolean ClientToScreen(WinDef.HWND hWnd, WinDef.POINT lpPoint);
}
public static void main(String[] args) {
while (true) {
try {
Rectangle rectangle = ClientRectExample.getClientRect("WindowName");
System.out.println(rectangle);
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public static Rectangle getClientRect(String startOfWindowName) {
WinDef.HWND hWnd = User32ForClientRect.INSTANCE.FindWindow(null, startOfWindowName);
WinDef.POINT getPos = new WinDef.POINT();
WinDef.RECT rect = new WinDef.RECT();
User32ForClientRect.INSTANCE.GetClientRect(hWnd, rect);
User32ForClientRect.INSTANCE.ClientToScreen(hWnd, getPos);
return new Rectangle(getPos.x, getPos.y, rect.right, rect.bottom);
}
}
Instead of an int, you need to pass in LPPOINT to ClientToScreen(), or more specifically a pointer to a POINT structure (WinDef.POINT in JNA).

JavaFX Minimizing Undecorated Stage

I have an undecorated JavaFX Stage, and my own minimize, maximize & close buttons. But unfortunately clicking the taskbar icon in Windows 7 does not automatically minimize the stage - compared to the decorated behaviour.
Is there a way to minimize an undecorated stage with pure Java code, by clicking the taskbar icon? If not how can I do this with, say, JNA?
EDIT:
OK, I've been trying to solve this with JNA, but having done next to none C/C++/JNA, I have a bit trouble setting this up. I'd be grateful if someone helped me to put the pieces together..
Here's my code so far:
public final class Utils {
static {
if (PlatformUtil.isWin7OrLater()) {
Native.register("shell32");
Native.register("user32");
}
}
// Apparently, this is the event I am after
public static final int WM_ACTIVATEAPP = 0x1C;
public static void registerMinimizeHandler(Stage stage) {
// Hacky way to get a pointer to JavaFX Window
Pointer pointer = getWindowPointer(stage);
WinDef.HWND hwnd = new WinDef.HWND(pointer);
// Here's my minimize/activate handler
WinUser.WindowProc windowProc = new MinimizeHandler(stage);
Pointer magicPointer = ... set this to point to windowProc?
// This.. apparently, re-sets the WndProc? But how do I get the "magicPointer" that is "attached" to the windowProc?
User32.INSTANCE.SetWindowLong(hwnd, User32.GWL_WNDPROC, magicPointer);
}
}
private static class MinimizeHandler implements WinUser.WindowProc {
private Stage stage;
private MinimizeHandler(Stage stage) {
this.stage = stage;
}
#Override
public WinDef.LRESULT callback(WinDef.HWND hWnd, int uMsg, WinDef.WPARAM wParam, WinDef.LPARAM lParam) {
if (uMsg == WM_ACTIVATEAPP) {
System.out.println("ACTIVATE");
}
return User32.INSTANCE.DefWindowProc(hWnd, uMsg, wParam, lParam);
}
}
private static Pointer getWindowPointer(Stage stage) {
try {
TKStage tkStage = stage.impl_getPeer();
Method getPlatformWindow = tkStage.getClass().getDeclaredMethod("getPlatformWindow" );
getPlatformWindow.setAccessible(true);
Object platformWindow = getPlatformWindow.invoke(tkStage);
Method getNativeHandle = platformWindow.getClass().getMethod( "getNativeHandle" );
getNativeHandle.setAccessible(true);
Object nativeHandle = getNativeHandle.invoke(platformWindow);
return new Pointer((Long) nativeHandle);
} catch (Throwable e) {
System.err.println("Error getting Window Pointer");
return null;
}
}
EDIT 2: I eventually got further on with this one, but as soon as I re-set the WNDPROC, my undecorated window didn't respond to any events.. I'm offering a bounty of 100 reputation for a self-contained example with a working solution. Windows (7+) only is OK, I do not even know how this behaves on other platforms.
EDIT 3:
Well, I kind of gave up with this one.. I got everything set up correctly, and received the events, but had problems figuring out the correct event to listen for..
Since there's been some interest in the question, if anyone wants to attempt to continue with this, here's my final code (it hopefully should "work" out-of-box):
public final class Utils {
static interface ExtUser32 extends StdCallLibrary, User32 {
ExtUser32 INSTANCE = (ExtUser32) Native.loadLibrary(
"user32",
ExtUser32.class,
W32APIOptions.DEFAULT_OPTIONS);
WinDef.LRESULT CallWindowProcW(
Pointer lpWndProc,
Pointer hWnd,
int msg,
WinDef.WPARAM wParam,
WinDef.LPARAM lParam);
int SetWindowLong(HWND hWnd, int nIndex, com.sun.jna.Callback wndProc) throws LastErrorException;
}
// Some possible event types
public static final int WM_ACTIVATE = 0x0006;
public static final int WM_ACTIVATEAPP = 0x1C;
public static final int WM_NCACTIVATE = 0x0086;
public static void registerMinimizeHandler(Stage stage) {
Pointer pointer = getWindowPointer(stage);
WinDef.HWND hwnd = new WinDef.HWND(pointer);
long old = ExtUser32.INSTANCE.GetWindowLong(hwnd, User32.GWL_WNDPROC);
MinimizeHandler handler = new MinimizeHandler(stage, old);
ExtUser32.INSTANCE.SetWindowLong(hwnd, User32.GWL_WNDPROC, handler);
}
private static Pointer getWindowPointer(Stage stage) {
try {
TKStage tkStage = stage.impl_getPeer();
Method getPlatformWindow = tkStage.getClass().getDeclaredMethod("getPlatformWindow" );
getPlatformWindow.setAccessible(true);
Object platformWindow = getPlatformWindow.invoke(tkStage);
Method getNativeHandle = platformWindow.getClass().getMethod( "getNativeHandle" );
getNativeHandle.setAccessible(true);
Object nativeHandle = getNativeHandle.invoke(platformWindow);
return new Pointer((Long) nativeHandle);
} catch (Throwable e) {
System.err.println("Error getting Window Pointer");
return null;
}
}
private static class MinimizeHandler implements WinUser.WindowProc, StdCallLibrary.StdCallCallback {
private Pointer mPrevWndProc32;
private Stage stage;
private MinimizeHandler(Stage stage, long oldPtr) {
this.stage = stage;
mPrevWndProc32 = new Pointer(oldPtr);
// Set up an event pump to deliver messages for JavaFX to handle
Thread thread = new Thread() {
#Override
public void run() {
int result;
WinUser.MSG msg = new WinUser.MSG();
while ((result = User32.INSTANCE.GetMessage(msg, null, 0, 0)) != 0) {
if (result == -1) {
System.err.println("error in get message");
break;
}
else {
System.out.println("got message: " + result);
User32.INSTANCE.TranslateMessage(msg);
User32.INSTANCE.DispatchMessage(msg);
}
}
}
};
thread.start();
}
#Override
public WinDef.LRESULT callback(WinDef.HWND hWnd, int uMsg, WinDef.WPARAM wParam, WinDef.LPARAM lParam) {
if (uMsg == WM_ACTIVATEAPP) {
// Window deactivated (wParam == 0)... Here's where I got stuck and gave up,
// this is probably not the best event to listen to.. the app
// does indeed get iconified now by pressing the task-bar button, but it
// instantly restores afterwards..
if (wParam.intValue() == 0) {
stage.setIconified(true);
}
return new WinDef.LRESULT(0);
}
// Let JavaFX handle other events
return ExtUser32.INSTANCE.CallWindowProcW(
mPrevWndProc32,
hWnd.getPointer(),
uMsg,
wParam,
lParam);
}
}
}
You can just set the appropriate window style. It works in XP but should be ok in windows 7 32 bit.
I think (but can't test) if you use 64 bit then change to the Ptr windows functions, ie. GetWindowLongPtr.
import com.sun.jna.Native;
import com.sun.jna.Pointer;
import com.sun.jna.platform.win32.User32;
import com.sun.jna.platform.win32.WinDef.HWND;
import com.sun.jna.platform.win32.WinUser;
import static com.sun.jna.platform.win32.WinUser.GWL_STYLE;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.TextArea;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
public class JNATest extends Application {
public static void main(String[] args) { launch(args); }
#Override
public void start(Stage stage) {
TextArea ta = new TextArea("output\n");
VBox root = new VBox(5,ta);
Scene scene = new Scene(root,800,200);
stage.setTitle("Find this window");
stage.setScene(scene);
stage.show();
//gets this window (stage)
long lhwnd = com.sun.glass.ui.Window.getWindows().get(0).getNativeWindow();
Pointer lpVoid = new Pointer(lhwnd);
//gets the foreground (focused) window
final User32 user32 = User32.INSTANCE;
char[] windowText = new char[512];
HWND hwnd = user32.GetForegroundWindow();
//see what the title is
user32.GetWindowText(hwnd, windowText, 512);
//user32.GetWindowText(new HWND(lpVoid), windowText, 512);//to use the hwnd from stage
String text=(Native.toString(windowText));
//see if it's the same pointer
ta.appendText("HWND java:" + lpVoid + " HWND user32:"+hwnd+" text:"+text+"\n");
//change the window style if it's the right title
if (text.equals(stage.getTitle())){
//the style to change
int WS_DLGFRAME = 0x00400000;//s/b long I think
//not the same constant here??
ta.appendText("windows api:"+WS_DLGFRAME+" JNA: "+WinUser.SM_CXDLGFRAME);
int oldStyle = user32.GetWindowLong(hwnd, GWL_STYLE);
int newStyle = oldStyle & ~0x00400000; //bitwise not WS_DLGFRAME means remove the style
newStyle = newStyle & ~0x00040000;//WS_THICKFRAME
user32.SetWindowLong(hwnd, GWL_STYLE, newStyle);
}
}
}
My guess is you replace the last 3 lines with
long oldStyleLong = user32.GetWindowLongPtr(hwnd, GWL_STYLE).longValue();
long newStyleLong = oldStyleLong & ~ 0x00400000l;
user32.SetWindowLongPtr(hwnd, GWL_STYLE, new BaseTSD.LONG_PTR(newStyleLong));
for 64 bit. I think I don't have those functions in my User32.dll, so I can't test it. There's lots of extraneous code in there, mainly for testing or teaching. Remove the unused lines once you figure out what you want to do.
ps. Don't add newStyle = newStyle & ~0x00020000;//WS_MINIMIZEBOX. That's one of the style flags JavaFX doesn't use for undecorated. That's why the minimize isn't available. Maybe if you try setting stage undecorated and adding (using |, not &~) the minimize box flag, you'll get the same result. There are tools to look up all the style flags from any window.
Here's the simplest amount of code that just changes an undecorated stage using the stage's HWND.
public void start(Stage stage) {
Scene scene = new Scene(new Pane(new Label("Hello World")));
stage.initStyle(StageStyle.UNDECORATED);
stage.setTitle("Find this window");
stage.setScene(scene);
stage.show();
long lhwnd = com.sun.glass.ui.Window.getWindows().get(0).getNativeWindow();
Pointer lpVoid = new Pointer(lhwnd);
HWND hwnd = new HWND(lpVoid);
final User32 user32 = User32.INSTANCE;
int oldStyle = user32.GetWindowLong(hwnd, GWL_STYLE);
System.out.println(Integer.toBinaryString(oldStyle));
int newStyle = oldStyle | 0x00020000;//WS_MINIMIZEBOX
System.out.println(Integer.toBinaryString(newStyle));
user32.SetWindowLong(hwnd, GWL_STYLE, newStyle);
}
It prints out the style flags before and after so you can look up what styles are set.
Two things to note here.
First, it doesn't look like these libraries are in the latest version of JNA, 5.50 as of now, adding from the Maven repository. I had to add the 4.2.1 library instead.
Second, you may encounter this exception, like I did on Windows 10 and Java 11:
Error with package com.sun.glass.ui while learning Java Native Access
The solution is go to your VM options in your IDE (Run -> Edit Configurations..., in IntelliJ) and add this:
--add-exports
javafx.graphics/com.sun.glass.ui=ALL-UNNAMED
It should work after that.
I would like to see someone implement the native Windows animations for minimizing and un-minimizing an undecorated window, but I haven't searched too thoroughly yet to see if this has already been discussed. I'll update this if I come across a solution.
Edit:
Upon further research on the Windows animations, it looks like a solution could be hacked together, but I gave up at trying to implement this C# hack below. It seems to be more of an OS issue and not just JavaFX.
I was able to get the initial window to stay undecorated while minimizing and with the animation by modifying this in start():
int newStyle = oldStyle | 0x00020000 | 0x00C00000;
But, after minimizing and reopening, the Windows border appears oddly enough.
Then, I tried to use a ChangeListener to swap Windows styles when iconifying.
stage.iconifiedProperty().addListener(new ChangeListener<Boolean>() {
#Override
public void changed(ObservableValue<? extends Boolean> ov, Boolean t, Boolean t1) {
if (t1.booleanValue() == true) {
int newStyle = oldStyle | 0x00020000 | 0x00C00000;
user32.SetWindowLong(hwnd, GWL_STYLE, newStyle);
} else if (t1.booleanValue() == false) {
int newStyle = oldStyle | 0x00020000;
user32.SetWindowLong(hwnd, GWL_STYLE, newStyle);
}
}
});
This successfully gets the windows un-minimize animation to work fine consistently, while leaving the (visible) stage borderless.
It looks like I can get minimization animations working once I find out the best way to re-apply:
int newStyle = oldStyle | 0x00020000 | 0x00C00000;
user32.SetWindowLong(hwnd, GWL_STYLE, newStyle);
just before the stage is iconified, and the border isn't visible to the user. Once implemented, this might work similarly to the C# solution in the first link below. Basically, what the above ChangeListener does in reverse.
Links to do with solving borderless/undecorated animations:
Use windows animations on borderless form
https://exceptionshub.com/borderless-window-using-areo-snap-shadow-minimize-animation-and-shake.html
http://pinvoke.net/default.aspx/Constants/Window%20styles.html
JavaFX Minimizing & Maximing undecorated stage with animations
Didn't get your question properly..but here's the solution
#FXML private void minimize()
{
Stage stage = (Stage) minimize.getScene().getWindow();
stage.setIconified(true);
}

Sending object, int and String via socket, serialisation

so I have a PrintWriter and a ObjectOutputStream trying to send an int, String and a Color[] from the client when the user clicks the list, where 'out' is the PrintWriter and outObject is the ObjectOutputStream.
private class MyMouseListener implements MouseListener{
public void mouseExited(MouseEvent e){}
public void mouseEntered(MouseEvent e){}
public void mouseClicked(MouseEvent e){
if(e.getSource() == list){
int index = list.locationToIndex(e.getPoint()); ///get the game that the player has selected
try{
out.println(index);
out.println("hel");
Color[] colors = new Color[5];
outObject.writeObject(colors)
}
}
Server, where 'in' is a scanner, 'objectIn' is an objectInputStream :
while(true)
if(in.nextLine().equals("hel")){
System.out.println("fee");
}
else if(in.hasNextInt()){
System.out.println("dee");
}
Object o = objectIn.readObject();
if(o instanceof Color[]){
System.out.println("ree");
}
If i try to send all three, on the first click the server only registers the String, and then all clicks after the server only registers the int, never registering the Color[], if i send the int first, then the Color[] and then the int like :`
out.println(index);
Color[] colors = new Color[5];
outObject.writeObject(colors);
out.println("hel");
Only the int and Object are registered first, on the second click only the String and then subsequent clicks only the int.
`
Object o = objectIn.readObject();
if(in.nextLine().equals("hel")){
System.out.println("fee");
}
//Object o = objectIn.readObject();
else if(in.hasNextInt()){
System.out.println("dee");
}
else if(o instanceof Color[]){
System.out.println("ree");
o = null;
}
If I use the above series of if statements nothing is registered
Using the first series of if statements, if i try to send the Color[] on it's own it's not registered, but if I send it with an int both are registered.
If i try to send the String and the Color[], sending the String first like so :
out.println("hel");
Color[] a = new Color[5];
outObject.writeObject(a);
both are registered once and then never again, also in the opposite way than was expected i.e the Color[] was registered first and then the String; if i send both but the Color[] first and then the String neither are registered.
I assume your out object (which is a PrintWriter) wraps the outObject object, which is the ObjectOutputStream. You should not use both a PrintWriter and a ObjectOutputStream for the same underlying connection. See this question for more details.

Null Pointer Exception Java

I'm just starting out with learning Java, and have run into a problem. When trying to run an Android application, I get a NullPointerException when the method makeResponse is called.
Code extract (full code appended at the end of this post):
private String makeResponse(String input){
//This doesn't work yet. I keep getting null pointer exceptions on the line beginning "int id" line, but can't tell why.
String response = "You picked "+input;
if (sw==null){
response+=" and sw is null"; //This doesn't activate
}
if (input==null){
response+=" and input is null"; //This doesn't activate
}
int id = sw.getIdFromName(input); //If this line (and the following one) are commented out, the method runs with no problem, but neither of the if clauses above trigger.
response+=", that has id "+String.valueOf(id);
return response;
}
(sw is a field of the parent class, set in another method. sw is an instance of a self-made class - full code at the end)
The exception is thrown at the line beginning "int id ="
My initial searching for NullPointerException told me that it was "thrown when an application attempts to use null in a case where an object is required." - hence the two "if" clauses in my code above, to try to find which object was unexpectedly null. Since neither of these are, I concluded that sw.getIdFromName must be returning a null of type Integer (as in this similar problem: Java: null pointer exception when unboxing Integer?). However, I don't see how this is possible in sw.getIdFromName as shown below (nameLookup is a String array, a field of sw):
public int getIdFromName(String name){
for (int i=0;i<267;i++){
if (nameLookup[i].equals(name)){
return i;
}
}
return -1;
}
(Incidentally, if there is a better way of searching a String array for a search term, I would be grateful if someone could tell me - binarySearch doesn't appear to be defined on String arrays).
Following the advice of the top commenter in the question linked above, I tried replacing "int id" with "Integer id" in makeResponse, but with no effect - the same exception is thrown at the same place.
Any advice would be gratefully appreciated.
Judging from the comments in the stackoverflow question linked above, providing a stack trace would not provide any new information, but I'd be happy to do so if asked.
P.s. this is my first question here, so apologies if I make some breach of etiquette or inane mistake.
Full code listings:
ConversationActivity.java:
package com.example.Conversation;
import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.ArrayAdapter;
import android.widget.AutoCompleteTextView;
import android.widget.TextView;
public class ConversationActivity extends Activity {
/** Called when the activity is first created. */
StationsWrapper sw;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
/**Setting the adapter for the AutoComplete*/
final AutoCompleteTextView textView = (AutoCompleteTextView) findViewById(R.id.ACTextView1);
String[] stationsArray = getResources().getStringArray(R.array.stations);
ArrayAdapter<String> adapter = new ArrayAdapter<String>(this, R.layout.list_item, stationsArray);
textView.setAdapter(adapter);
/**Code below grabs the data from stations.xml and puts it in a readable object */
this.sw = new StationsWrapper();
/** Code below is to set a click function for the AutoComplete*/
OnItemClickListener ACListener = new OnItemClickListener(){
public void onItemClick(AdapterView<?> parent, View v, int position,
long id) {
TextView reply = (TextView) findViewById(R.id.reply);
reply.setText("working...");
String ChosenStation = (String) parent.getItemAtPosition(position);
reply.setText(makeResponse(ChosenStation));
//Toast.makeText(ConversationActivity.this, "You clicked "+parent.getItemAtPosition(position), Toast.LENGTH_SHORT).show();
textView.setText("");
}
};
textView.setOnItemClickListener(ACListener);
}
private String makeResponse(String input){
//This doesn't work yet. I keep getting null pointer exceptions on the line beginning "int id" line, but can't tell why.
String response = "You picked "+input;
if (sw==null){
response+=" and sw is null"; //This doesn't activate
}
if (input==null){
response+=" and input is null"; //This doesn't activate
}
int id = sw.getIdFromName(input); //If this line (and the following one) are commented out, the method runs with no problem, but neither of the if clauses above trigger.
response+=", that has id "+String.valueOf(id);
return response;
}
}
StationsWrapper.java:
package com.example.Conversation;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;
public class StationsWrapper {
private int[][] stats;
private String[] nameLookup;
public StationsWrapper(){
//Constructor. Grabs data from XML, and whacks it into relevant arrays.
//stats is an integer array, indexed first by station id (1-267), and then by datatype (0 for line, 1 for zone)
final int[][] stats = new int[267][2];
final String[] nameLookup = new String[267];
try {
SAXParserFactory factory = SAXParserFactory.newInstance();
SAXParser saxParser = factory.newSAXParser();
DefaultHandler handler = new DefaultHandler() {
boolean bline = false;
boolean bzone= false;
String curStation;
int curID;
String curLine;
String curZone;
public void startElement(String uri, String localName,String qName,
Attributes attributes) throws SAXException {
if (qName.equalsIgnoreCase("STATION")){
curStation=attributes.getValue(0);
curID=Integer.parseInt(attributes.getValue(1));
}
if (qName.equalsIgnoreCase("LINE")) {
bline = true;
}
if (qName.equalsIgnoreCase("ZONE")) {
bzone = true;
}
}
public void endElement(String uri, String localName,
String qName) throws SAXException {
if (qName.equalsIgnoreCase("Station")){
nameLookup[curID-1]=curStation;
int intLine=(convLineToInt(curLine));
stats[curID-1][0]=intLine;
int intZone=(convZoneToInt(curZone));
stats[curID-1][1]=intZone;
}
}
public void characters(char ch[], int start, int length) throws SAXException {
if (bline) {
//System.out.println("Line : " + new String(ch, start, length));
curLine=new String(ch, start, length);
bline = false;
}
if (bzone) {
//System.out.println("Zone : " + new String(ch, start, length));
curZone=new String(ch, start, length);
bzone = false;
}
}
};
saxParser.parse("c:\\Users\\Jack Jackson\\Coding\\Java\\stations.xml", handler);
} catch (Exception e) {
e.printStackTrace();
}
this.stats=stats;
this.nameLookup=nameLookup;
}
public static void main(String[] args){
//Nothing to see here, move it along folks.
}
public String[] getNameLookup(){
return nameLookup;
}
public int getIdFromName(String name){
for (int i=0;i<nameLookup.length;i++){
if (nameLookup[i].equals(name)){
return i;
}
}
return -1;
}
public int returnData(int id, int datapoint){
return stats[id][datapoint];
}
public void displayStats(){
for (int i=0;i<267;i++){
for (int j=0;j<2;j++){
System.out.print(stats[i][j]);
System.out.print(" ");
}
System.out.println("");
}
}
}
Without running your code, it seems very likely that one of nameLookup array entries is null, so trying to call nameLookup[i].equals() throws a NullPointerException.
If elements of nameLookup can legitimately be null, one way to handle this is to reverse the order of the comparison in getIdFromName():
if (name.equals(nameLookup[i])) {
In any case, I'd recommend that you make sure that both nameLookup itself and its elements are fully initialized.

JAVA JNA WindowProc implementation

I'm trying to write a simple application in Java that will communicate with an USB device. The USB device is made by me using a Microchip Microcontroller. The communication is rather simple, since the USB device is from the HID Class, arrays of 64 bytes are exchanged between the computer and the device.
My program finds the device based on the product ID and the vendor ID, can write and read 64 bytes, but now I would like to detect when the device is connected or disconnected from the computer.
As I've seen in a C# program provided by Microchip as an example application, the WndProc method is overriden and the WM_DEVICECHANGE message is handled. My question is how can this be done in Java using JNA, how can I override the WindowProc Method and handle messages, if this is possible at all :), but I hope it is :D
Thanks in advance for the answers.
Gabor.
I finally managed to solve the problem :) And I found the following solution:
First extend the User32 interface in the following way
public interface MyUser32 extends User32 {
public static final MyUser32 MYINSTANCE = (MyUser32) Native.loadLibrary("user32", MyUser32.class, W32APIOptions.UNICODE_OPTIONS);
/**
* Sets a new address for the window procedure (value to be set).
*/
public static final int GWLP_WNDPROC = -4;
/**
* Changes an attribute of the specified window
* #param hWnd A handle to the window
* #param nIndex The zero-based offset to the value to be set.
* #param callback The callback function for the value to be set.
*/
public int SetWindowLong(WinDef.HWND hWnd, int nIndex, Callback callback);
}
Then extend the WinUser interface with the Windows Message code that you need, in my case this is the WM_DEVICECHANGE, because I want to check I the USB Device was attached or detached from the computer.
public interface MyWinUser extends WinUser {
/**
* Notifies an application of a change to the hardware configuration of a device or the computer.
*/
public static final int WM_DEVICECHANGE = 0x0219;
}
Then create an interface with the callback function, which will actually be my WndProc function.
//Create the callback interface
public interface MyListener extends StdCallCallback {
public LRESULT callback(HWND hWnd, int uMsg, WPARAM uParam, LPARAM lParam);
}
public MyListener listener = new MyListener()
{
public LRESULT callback(HWND hWnd, int uMsg, WPARAM uParam, LPARAM lParam)
{
if (uMsg == MyWinUser.WM_DEVICECHANGE)
{
// TODO Check If my device was attached or detached
return new LRESULT(1);
}
return new LRESULT(0);
}
};
And then somewhere in the code of the JFrame where you initialize things add the new address for the window procedure with the SetWindowLong function:
// Get Handle to current window
HWND hWnd = new HWND();
hWnd.setPointer(Native.getWindowPointer(this));
MyUser32.MYINSTANCE.SetWindowLong(hWnd, MyUser32.GWLP_WNDPROC, listener);
This code works nicely, but I have some doubts regarding one thing. I'm not sure if the return value of the callback function is correct. I've read in the MSDN that after handling a WM_DEVICECHANGE message the callback function should return true and I'm not sure that the value i'm currently returning is the one expected by the system, so any suggestions are welcome.
If anyone is interested in the whole code I've written for the HID communication just ask, I would be more than happy to help :)
Cheers,
Gabor.
If you don't have an existing window handle you have to create your own window first. And when you create a new window you also have to manage its message pump. Here's an example on how you can do this. JNA's own example code could also be very useful.
Thread thread;
HWND hWnd;
static final int WM_NCCREATE = 0x0081;
void start() {
thread = new Thread(this::myThread);
thread.start();
}
void stop() {
User32.INSTANCE.PostMessage(hWnd, User32.WM_QUIT, null, null);
}
WindowProc callback = new WindowProc() {
#Override
public LRESULT callback(HWND hWnd, int uMsg, WPARAM wParam, LPARAM lParam) {
switch (uMsg) {
case WM_NCCREATE:
return new LRESULT(1);
case User32.WM_DEVICECHANGE:
return new LRESULT(1);
default:
return new LRESULT(0);
}
}
};
void myThread() {
WString className = new WString("myclass");
WNDCLASSEX wx = new WNDCLASSEX();
wx.clear();
wx.lpszClassName = className;
wx.lpfnWndProc = callback;
if (User32.INSTANCE.RegisterClassEx(wx).intValue() != 0) {
hWnd = User32.INSTANCE.CreateWindowEx(0, className, null, 0, 0, 0, 0, 0, null, null, null, null);
WinUser.MSG msg = new WinUser.MSG();
msg.clear();
while (User32.INSTANCE.GetMessage(msg, hWnd, 0, 0) > 0) {
User32.INSTANCE.TranslateMessage(msg);
User32.INSTANCE.DispatchMessage(msg);
}
}
}
You can create COM DLL or OCX of your C# program and use it in the java code. If you create application.
Use JACOB OR JCOM
It will be a bridge between Java and COM Object. Other option is you can use JNI to communicate with DLL and OCX.
The solution I posted previously has some problems, unfortunately :(
Since it overrides the WndProc of the window, the controls I added to my Frame weren't working (not surprisingly, because no paint, repaint, etc. messages were handled). Then I realised that instead of returning LRESULT(1) I should call the default window proc (as it is used in Win32 C++ programs), but this still didn't solve the problem, the frame was painted but the buttons weren't working, although I was able to update labels... So I had to abandon this solution too.
After searching some more on the internet I've found a great article here (edit: link is dead, original article can be found here), where a static hidden window is created to handle windows messages. I managed to code it for my application and it works great. (I had to further extend the classes from JNA because several functions were not included. I can post my code if someone is interested.)
Hope this helps.

Categories

Resources