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).
Related
How would I go about capturing an image for use in OpenCV from the standard camera module v2 plugged into a Raspberry Pi 3B? I've been trying to get high frame-rate video working, but using OpenCV's VideoCapture always gives me an empty Mat when I try to index device 0, and this and this both produced 1 frame per second or worse when I tried. I've also looked into spawning a raspivid process, but I don't see a way to get OpenCV's VideoCapture to read from the output of that process.
How would I get high FPS frame capture in Java? Is there a way I can get OpenCV to read from an OutputStream that I grab from another process?
EDIT: a simplified version of my code is below. I want to know how to populate the startRecording() function in this class
abstract class Main {
public static Mat currentCapture = null;
public static final double FOV_HEIGHT = 48.8;
public static final int WIDTH = 480;
public static final int HEIGHT = 384;
public static final int VIDEO_WIDTH = WIDTH * 2;
public static final int VIDEO_HEIGHT = HEIGHT * 2;
static {
System.loadLibrary(org.opencv.core.Core.NATIVE_LIBRARY_NAME);
}
public static void main(String[] args) {
client = new VisionClientTable("10.55.6.4", 5506);
new Thread(new VisionThread(VisionThread.Mode.VIDEO)).start();
new Thread(new VisionThread(VisionThread.Mode.TARGETING)).start();
}
private static class VisionThread implements Runnable {
private final Mode mode;
enum Mode {
VIDEO,
TARGETING
}
public VisionThread(Mode mode) {
this.mode = mode;
}
#Override
public void run() {
if (mode == Mode.TARGETING)
startTracking();
else if (mode == Mode.VIDEO)
startRecording();
}
}
public static void startTracking() {
/* this thread repeatedly captures currentCapture and processes it */
}
// this is where I need help
public static void startRecording() {
try {
VideoWriter video = new VideoWriter("/home/pi/vision/videos/video-" + Files.list(Paths.get("/home/pi/vision/captures")).count() + ".mp4", VideoWriter.fourcc('X', '2', '6', '4'), 30, new Size(VIDEO_WIDTH, VIDEO_HEIGHT), true);
VideoCapture capture = new VideoCapture(0);
capture.set(Videoio.CAP_PROP_FRAME_WIDTH, VIDEO_WIDTH);
capture.set(Videoio.CAP_PROP_FRAME_HEIGHT, VIDEO_HEIGHT);
Thread.sleep(2000);
while (true) {
long start = System.currentTimeMillis();
Mat mat = new Mat();
capture.read(mat);
if (!mat.empty()) { // mat is always empty
Mat downscaled = new Mat();
Imgproc.resize(mat, downscaled, new Size(WIDTH, HEIGHT), 0, 0, Imgproc.INTER_NEAREST);
currentCapture = downscaled;
}
video.write(mat);
long end = System.currentTimeMillis();
if (end - start < 1000 / 60)
Thread.sleep(1000 / 60 - (end - start));
}
} catch (Exception e) {
e.printStackTrace();
}
}
public static void log(String text) {
System.out.println(text);
}
}
I feel like the best way to do this might be to set raspivid to output to a file, and somehow feed the output of it into OpenCV, but I'm not sure how to do this.
EDIT 2: So far, I have tried using Runtime.getRuntime().exec() to run a raspivid command that outputs to a file, which works, but then when I try to open that file with OpenCV, capture.isOpened() remains false, even as the program repeatedly tries to open the file.
not getting much idea about your code from your question.
following may help you.
http://bigdinotech.com/tutorials/beaglebone-black-tutorials/building-opencv-on-the-beaglebone-black-or-raspberry-pi/
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);
}
Based on Peter Brinkmann's sample class, I am running libpd and processing in Eclipse. But I don't seem to completely understand how to get the audio input from the Android microphone into Pure Data.
when I run it on an actual or virtual device, I get a bunch of errors saying:
E/AudioRecord(1079): Could not get audio input for record source 1
E/AudioRecord-JNI(1079): Error creating AudioRecord instance: initialization check failed.
Here's the main Class:
package com.noisepages.nettoyeur.processing.sample;
import org.puredata.android.io.AudioParameters;
import org.puredata.android.processing.PureDataP5Android;
import processing.core.PApplet;
/**
* #author Peter Brinkmann (peter.brinkmann#gmail.com)
*/
public class PdP5Sample extends PApplet {
PureDataP5Android pd;
int zipId = com.noisepages.nettoyeur.processing.sample.R.raw.patch; // Processing masks R
int ins = AudioParameters.suggestInputChannels();
int sampleRate = AudioParameters.suggestSampleRate();
public void setup() {
pd = new PureDataP5Android(this, sampleRate, ins, 2);
pd.unpackAndOpenPatch(zipId, "audiotest.pd");
pd.start();
}
public void draw() {
background(0);
fill(mouseY, mouseX, 0);
stroke(mouseY, mouseX, 0);
ellipseMode(CENTER);
ellipse(mouseX, mouseY, 100, 100);
}
public void stop() {
pd.release();
super.stop();
}
/*
// Implement methods like the following if you want to receive messages from Pd.
// You'll also need to subscribe to receive symbols you're interested if you want
// to receive messages.
public void pdPrint(String s) {
// Handle string s, printed by Pd
}
public void receiveBang(String source) {
// Handle bang sent to symbol source in Pd
}
public void receiveFloat(String source, float x) {
// Handle float x sent to symbol source in Pd
}
public void receiveSymbol(String source, String sym) {
// Handle symbol sym sent to symbol source in Pd
}
*/
// boilerplate
public int sketchWidth() { return this.screenWidth; }
public int sketchHeight() { return this.screenHeight; }
public String sketchRenderer() { return PApplet.OPENGL; }
}
did you add this to AndroidManifest.xml?
<uses-permission android:name="android.permission.RECORD_AUDIO" />
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.
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.