-
Notifications
You must be signed in to change notification settings - Fork 1.7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Addition win32 api : SendMessage, GetActiveWindow, COPYDATASTRUCT and a few constants + a demo application #774
Merged
Merged
Changes from 1 commit
Commits
Show all changes
8 commits
Select commit
Hold shift + click to select a range
825f9be
Added a few mapping of win32 api : SendMessage and GetActiveWindow.
cnico 3aeed55
Corrections done to take into account, the comment of the pull reques…
cnico 3fbf4d2
Merge branch 'master' into master
cnico 051135a
Addition of forgotten test class : User32WindowMessagesTest.java
cnico 9c7e808
Merge branch 'master' of https://github.com/cnico/jna
cnico 4f2fae4
Correction of compilation error of unit test/com/sun/jna/platform/win…
cnico 5fc088e
Merge remote-tracking branch 'upstream/master'
cnico bda6938
Merged from jna master to be up to date with 4.4 release.
cnico File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
367 changes: 367 additions & 0 deletions
367
contrib/native_window_msg/src/com/sun/jna/platform/win32/Win32WindowMessagesDemo.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,367 @@ | ||
/* Copyright (c) 2012 Tobias Wolf, All Rights Reserved | ||
* | ||
* The contents of this file is dual-licensed under 2 | ||
* alternative Open Source/Free licenses: LGPL 2.1 or later and | ||
* Apache License 2.0. (starting with JNA version 4.0.0). | ||
* | ||
* You can freely decide which license you want to apply to | ||
* the project. | ||
* | ||
* You may obtain a copy of the LGPL License at: | ||
* | ||
* http://www.gnu.org/licenses/licenses.html | ||
* | ||
* A copy is also included in the downloadable source code package | ||
* containing JNA, in file "LGPL2.1". | ||
* | ||
* You may obtain a copy of the Apache License at: | ||
* | ||
* http://www.apache.org/licenses/ | ||
* | ||
* A copy is also included in the downloadable source code package | ||
* containing JNA, in file "AL2.0". | ||
*/ | ||
|
||
package com.sun.jna.platform.win32; | ||
|
||
import java.text.SimpleDateFormat; | ||
import java.util.Arrays; | ||
import java.util.Date; | ||
import java.util.List; | ||
|
||
import com.sun.jna.Native; | ||
import com.sun.jna.Pointer; | ||
import com.sun.jna.Structure; | ||
import com.sun.jna.platform.win32.BaseTSD.ULONG_PTR; | ||
import com.sun.jna.platform.win32.WinDef.DWORD; | ||
import com.sun.jna.platform.win32.WinDef.HINSTANCE; | ||
import com.sun.jna.platform.win32.WinDef.HMODULE; | ||
import com.sun.jna.platform.win32.WinDef.HWND; | ||
import com.sun.jna.platform.win32.WinDef.LPARAM; | ||
import com.sun.jna.platform.win32.WinDef.LRESULT; | ||
import com.sun.jna.platform.win32.WinDef.WPARAM; | ||
import com.sun.jna.platform.win32.WinUser.COPYDATASTRUCT; | ||
import com.sun.jna.platform.win32.WinUser.HHOOK; | ||
import com.sun.jna.platform.win32.WinUser.HOOKPROC; | ||
import com.sun.jna.platform.win32.WinUser.MSG; | ||
import com.sun.jna.platform.win32.WinUser.WNDCLASSEX; | ||
import com.sun.jna.platform.win32.WinUser.WNDENUMPROC; | ||
import com.sun.jna.platform.win32.WinUser.WindowProc; | ||
|
||
public class Win32WindowMessagesDemo { | ||
|
||
public static final int MSG_CODE = 101; | ||
|
||
public static final int DATA_STRUCT_CODE = 129; | ||
|
||
public static final int MSG_CODE_HOOKED = 711; | ||
|
||
/** | ||
* The main method. | ||
* | ||
* @param args | ||
* the arguments | ||
*/ | ||
public static void main(String[] args) { | ||
new Win32WindowMessagesDemo(); | ||
} | ||
|
||
/** | ||
* Instantiates 2 windows and make them communicate through windows messages, even complex ones throught | ||
* WM_COPYDATA. | ||
*/ | ||
public Win32WindowMessagesDemo() { | ||
// Create window 1 named "ping" | ||
createWindow("ping"); | ||
|
||
// let windows create the window before searching its handle. | ||
sleepCurrThread(4000); | ||
// Retrieves the created window's handle. | ||
HWND hwndPing = determineHWNDFromWindowClass("ping"); | ||
|
||
// DEMO 1 : sends a simple message to ping window with code MSG_CODE and value 123456. | ||
LRESULT result = User32.INSTANCE.SendMessage(hwndPing, WinUser.WM_USER, new WPARAM(MSG_CODE), new LPARAM(123456)); | ||
log("User Message sent to " + hwndPing + ", result = " + result); | ||
|
||
// DEMO 2 : send of structured message. | ||
COPYDATASTRUCT copyDataStruct = createStructuredMessage(); | ||
result = User32.INSTANCE.SendMessage(hwndPing, WinUser.WM_COPYDATA, null /* No current hwnd for this demo */, | ||
new LPARAM(copyDataStruct.getPointer().getLong(0))); | ||
log("COPYDATASTRUCT sent message to " + hwndPing + "(size=" + copyDataStruct.size() + ") code =" | ||
+ copyDataStruct.dwData); | ||
|
||
// DEMO 3 : hook winproc then send a message to the hooked proc. | ||
HHOOK hook = hookwinProc(hwndPing); | ||
result = User32.INSTANCE.SendMessage(hwndPing, WinUser.WM_USER, new WPARAM(MSG_CODE_HOOKED), new LPARAM(654321)); | ||
log("User Message sent to hooked proc " + hwndPing + ", result = " + result); | ||
|
||
// Waits e few moment before shutdown message. | ||
sleepCurrThread(3000); | ||
|
||
User32.INSTANCE.PostMessage(hwndPing, WinUser.WM_CLOSE, null, null); | ||
log("WM_CLOSE posted to " + hwndPing); | ||
|
||
//Remember to unhook the win proc. | ||
User32.INSTANCE.UnhookWindowsHookEx(hook); | ||
log("Unhook done correctly"); | ||
} | ||
|
||
private COPYDATASTRUCT createStructuredMessage() { | ||
MsgStruct myData = new MsgStruct(); | ||
myData.number = 5; | ||
myData.message = "Sending a structured message :)"; | ||
myData.write(); // writes to native memory the data structure otherwise nothing is sent... | ||
|
||
// log("Prepared structured content to send : " + myData.toString(true)); | ||
|
||
COPYDATASTRUCT copyDataStruct = new COPYDATASTRUCT(); | ||
copyDataStruct.dwData = new ULONG_PTR(DATA_STRUCT_CODE); | ||
copyDataStruct.cbData = new DWORD(myData.size()); | ||
copyDataStruct.lpData = myData.getPointer(); | ||
copyDataStruct.write(); // writes to native memory the data structure otherwise nothing is sent... | ||
return copyDataStruct; | ||
} | ||
|
||
/** | ||
* Example of message sent in the copydatastruct. | ||
*/ | ||
public class MsgStruct extends Structure { | ||
|
||
public MsgStruct() { | ||
super(); | ||
} | ||
|
||
public MsgStruct(Pointer p) { | ||
super(p); | ||
// reads memory effectively. | ||
read(); | ||
} | ||
|
||
public int number; | ||
public String message; | ||
|
||
protected List<String> getFieldOrder() { | ||
return Arrays.asList(new String[] { "number", "message" }); | ||
} | ||
} | ||
|
||
public void createWindow(final String windowClass) { | ||
//Runs it in a specific thread because the main thread is blocked in infinite loop otherwise. | ||
new Thread(new Runnable() { | ||
@Override | ||
public void run() { | ||
createWindowAndLoop(windowClass); | ||
} | ||
}).start(); | ||
log("Window " + windowClass + " created."); | ||
} | ||
|
||
public void createWindowAndLoop(String windowClass) { | ||
// define new window class | ||
HMODULE hInst = Kernel32.INSTANCE.GetModuleHandle(""); | ||
|
||
WNDCLASSEX wClass = new WNDCLASSEX(); | ||
wClass.hInstance = hInst; | ||
wClass.lpfnWndProc = new WindowProc() { | ||
|
||
@Override | ||
public LRESULT callback(HWND hwnd, int uMsg, WPARAM wParam, LPARAM lParam) { | ||
// log(hwnd + " - received a message : " + uMsg); | ||
switch (uMsg) { | ||
case WinUser.WM_CREATE: { | ||
log(hwnd + " - onCreate: WM_CREATE"); | ||
return new LRESULT(0); | ||
} | ||
case WinUser.WM_CLOSE: | ||
log(hwnd + " WM_CLOSE"); | ||
User32.INSTANCE.DestroyWindow(hwnd); | ||
return new LRESULT(0); | ||
case WinUser.WM_DESTROY: { | ||
log(hwnd + " - on Destroy."); | ||
User32.INSTANCE.PostQuitMessage(0); | ||
return new LRESULT(0); | ||
} | ||
case WinUser.WM_USER: { | ||
log(hwnd + " - received a WM_USER message with code : '" + wParam + "' and value : '" + lParam | ||
+ "'"); | ||
return new LRESULT(0); | ||
} | ||
case WinUser.WM_COPYDATA: { | ||
|
||
COPYDATASTRUCT copyDataStruct = new COPYDATASTRUCT(new Pointer(lParam.longValue())); | ||
ULONG_PTR uMsg1 = copyDataStruct.dwData; | ||
Pointer lParam1 = copyDataStruct.lpData; | ||
DWORD wParam1 = copyDataStruct.cbData; | ||
log(hwnd + " - received a WM_COPYDATA message with code : '" + uMsg1 + "' of size : '" + wParam1 | ||
+ "'"); | ||
|
||
switch (uMsg1.intValue()) { | ||
case DATA_STRUCT_CODE: { | ||
MsgStruct msg = new MsgStruct(lParam1); | ||
// log(hwnd + " - received structured content : " + msg.toString(true)); | ||
log(hwnd + " - message is of type MsgStruct with number = " + msg.number + " and message = '" | ||
+ msg.message + "'"); | ||
assert msg.number == DATA_STRUCT_CODE; | ||
} | ||
} | ||
return new LRESULT(0); | ||
} | ||
default: | ||
return User32.INSTANCE.DefWindowProc(hwnd, uMsg, wParam, lParam); | ||
} | ||
} | ||
}; | ||
wClass.lpszClassName = windowClass; | ||
|
||
// register window class | ||
User32.INSTANCE.RegisterClassEx(wClass); | ||
getLastError(); | ||
|
||
// create new window | ||
HWND hWnd = User32.INSTANCE.CreateWindowEx(User32.WS_EX_TOPMOST, windowClass, | ||
"My hidden helper window, used only to catch the windows events", 0, 0, 0, 0, 0, null, null, hInst, | ||
null); | ||
|
||
getLastError(); | ||
log("window sucessfully created! window hwnd: " + hWnd.getPointer().toString()); | ||
|
||
MSG msg = new MSG(); | ||
while (User32.INSTANCE.GetMessage(msg, hWnd, 0, 0) > 0) { | ||
User32.INSTANCE.TranslateMessage(msg); | ||
User32.INSTANCE.DispatchMessage(msg); | ||
} | ||
|
||
User32.INSTANCE.UnregisterClass(windowClass, hInst); | ||
User32.INSTANCE.DestroyWindow(hWnd); | ||
|
||
log("program exit!"); | ||
} | ||
|
||
public HWND determineHWNDFromWindowClass(String windowClass) { | ||
CallBackFindWindowHandleByWindowclass cb = new CallBackFindWindowHandleByWindowclass(windowClass); | ||
User32.INSTANCE.EnumWindows(cb, null); | ||
return cb.getFoundHwnd(); | ||
|
||
} | ||
|
||
private static class CallBackFindWindowHandleByWindowclass implements WNDENUMPROC { | ||
|
||
private HWND found; | ||
|
||
private String windowClass; | ||
|
||
public CallBackFindWindowHandleByWindowclass(String windowClass) { | ||
this.windowClass = windowClass; | ||
} | ||
|
||
@Override | ||
public boolean callback(HWND hWnd, Pointer data) { | ||
|
||
char[] windowText = new char[512]; | ||
User32.INSTANCE.GetClassName(hWnd, windowText, 512); | ||
String className = Native.toString(windowText); | ||
|
||
if (windowClass.equalsIgnoreCase(className)) { | ||
// Found handle. No determine root window... | ||
HWND hWndAncestor = User32.INSTANCE.GetAncestor(hWnd, User32.GA_ROOTOWNER); | ||
found = hWndAncestor; | ||
return false; | ||
} | ||
return true; | ||
} | ||
|
||
public HWND getFoundHwnd() { | ||
return this.found; | ||
} | ||
|
||
} | ||
|
||
private HHOOK hookwinProc(HWND hwndToHook) { | ||
HOOKPROC hookProc = new HOOKPROC() { | ||
|
||
/** | ||
* Callback method. cf : https://msdn.microsoft.com/en-us/library/windows/desktop/ms644975(v=vs.85).aspx | ||
* | ||
* nCode [in] : Specifies whether the hook procedure must process the message. If nCode is HC_ACTION, the | ||
* hook procedure must process the message. If nCode is less than zero, the hook procedure must pass the | ||
* message to the CallNextHookEx function without further processing and must return the value returned by | ||
* CallNextHookEx. wParam [in] : Specifies whether the message was sent by the current thread. If the | ||
* message was sent by the current thread, it is nonzero; otherwise, it is zero. lParam [in] : A pointer to | ||
* a CWPSTRUCT structure that contains details about the message. | ||
* | ||
*/ | ||
@SuppressWarnings("unused") //used by introspection from jna. | ||
public LRESULT callback(int nCode, WPARAM wParam, LPARAM lParam) { | ||
|
||
if (nCode < 0) { | ||
return User32.INSTANCE.CallNextHookEx(null, nCode, wParam, lParam); | ||
} | ||
|
||
try { | ||
WinUser.CWPSTRUCT cwp = new WinUser.CWPSTRUCT(new Pointer(lParam.longValue())); | ||
//log(" - received a message in hooked winproc : " + cwp.message.intValue()); | ||
|
||
switch (cwp.message.intValue()) { | ||
case WinUser.WM_USER: { | ||
|
||
HWND hWndSource = new HWND(new Pointer(cwp.wParam.longValue())); | ||
|
||
log(cwp.hwnd + " - Received a message from " + hWndSource + " hooked proc : code= " + cwp.wParam | ||
+ ", value = " + cwp.lParam); | ||
assert cwp.wParam.intValue() == MSG_CODE_HOOKED; | ||
|
||
return new LRESULT(0); | ||
} | ||
// Note : in more complex cases, the message could be structured with copydatastruct containing an | ||
// effective struct. | ||
// Read is like so : | ||
// COPYDATASTRUCT copyDataStruct = new COPYDATASTRUCT(new Pointer(cwp.lParam.longValue())); | ||
// then read the copy data then the message struct as above... | ||
} | ||
|
||
// Send message to next hook. | ||
return User32.INSTANCE.CallNextHookEx(null, nCode, wParam, lParam); | ||
} catch (Throwable t) { | ||
t.printStackTrace(); | ||
return new LRESULT(0); | ||
} | ||
} | ||
}; | ||
|
||
HINSTANCE hInst = Kernel32.INSTANCE.GetModuleHandle(null); | ||
int threadtoHook = User32.INSTANCE.GetWindowThreadProcessId(hwndToHook, null); | ||
// Hook of the wndProc | ||
return User32.INSTANCE.SetWindowsHookEx(WinUser.WH_CALLWNDPROC, hookProc, hInst, threadtoHook); | ||
} | ||
|
||
/** | ||
* Gets the last error. | ||
* | ||
* @return the last error | ||
*/ | ||
public int getLastError() { | ||
int rc = Kernel32.INSTANCE.GetLastError(); | ||
|
||
if (rc != 0) | ||
log("error: " + rc); | ||
|
||
return rc; | ||
} | ||
|
||
private void sleepCurrThread(int durationMs) { | ||
try { | ||
Thread.sleep(durationMs); | ||
} catch (InterruptedException e) { | ||
e.printStackTrace(); | ||
} | ||
} | ||
|
||
private void log(String message) { | ||
String currThread = Thread.currentThread().getName(); | ||
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS"); | ||
String currTime = sdf.format(new Date()); | ||
System.out.println(currTime + " [" + currThread + "] " + message); | ||
|
||
} | ||
|
||
} |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
the
copyDataStruct.getPointer().getLong(0)
looks wrong. I asumePointer.nativeValue(copyDataStruct.getPointer())
is what you mean. Also it might be worth mentioning in the comments, that copyDataStruct must be held strongly on the java side. In the sample this is archived by reading from the structure (line 92), but if that would not present, copyDataStruct becomes eligible for GC potentially to early. This leads to difficult to track down errors.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Corrected and commented with new commit.