From dcae6275271c1a4e2e77698cf71f06fe279f4489 Mon Sep 17 00:00:00 2001 From: Edward Jakubowski Date: Thu, 10 Apr 2014 21:48:28 -0400 Subject: [PATCH] Fixed WindowInfo class to work properly in Windows 32 bit OS Fixed WindowInfo class to work properly in Windows 32 bit OS. Added more unit tests. About dialog shows system information --- src/org/synthuse/Api.java | 2 + src/org/synthuse/SynthuseDlg.java | 16 +- src/org/synthuse/WindowInfo.java | 7 +- src/org/synthuse/test/WinApiTest.java | 138 ++++++++++++++++++ src/org/synthuse/test/WinFormsMockTestApp.exe | Bin 0 -> 20480 bytes src/org/synthuse/test/WpfBridgeTest.java | 94 +++++++++++- src/org/synthuse/test/WpfMockTestApp.exe | Bin 11776 -> 54784 bytes 7 files changed, 248 insertions(+), 9 deletions(-) create mode 100644 src/org/synthuse/test/WinApiTest.java create mode 100644 src/org/synthuse/test/WinFormsMockTestApp.exe diff --git a/src/org/synthuse/Api.java b/src/org/synthuse/Api.java index 469ddb8..4ab1a96 100644 --- a/src/org/synthuse/Api.java +++ b/src/org/synthuse/Api.java @@ -147,6 +147,8 @@ public class Api { int GetClassName(HWND hWnd, char[] buffer2, int i); int GetWindowModuleFileName(HWND hWnd, char[] buffer2, int i); int GetWindowThreadProcessId(HWND hWnd, PointerByReference pref); + //int GetWindowThreadProcessId(HWND hWnd, IntByReference lpdwProcessId); + boolean GetCursorPos(long[] lpPoint); //use macros POINT_X() and POINT_Y() on long lpPoint[0] diff --git a/src/org/synthuse/SynthuseDlg.java b/src/org/synthuse/SynthuseDlg.java index e5fa76f..674bd87 100644 --- a/src/org/synthuse/SynthuseDlg.java +++ b/src/org/synthuse/SynthuseDlg.java @@ -163,7 +163,6 @@ public class SynthuseDlg extends JFrame { btnRefresh = new JButton(" Refresh "); btnRefresh.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent arg0) { - //lblStatus.setText("Loading Windows Enumerated Xml..."); //WindowsEnumeratedXml wex = new WindowsEnumeratedXml(textPane, lblStatus); //wex.run(); WindowsEnumeratedXml.getXmlThreaded(textPane, lblStatus); @@ -199,7 +198,20 @@ public class SynthuseDlg extends JFrame { helpBtn.setIcon(new ImageIcon(SynthuseDlg.class.getResource(RES_STR_HELP_IMG))); helpBtn.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent arg0) { - JOptionPane.showMessageDialog(null, "Version " + VERSION_STR + " create by Edward Jakubowski ejakubowski7@gmail.com", "About", JOptionPane.QUESTION_MESSAGE); + String about = ""; + about += "Synthuse Version " + VERSION_STR + " create by Edward Jakubowski ejakubowski7@gmail.com\n\n"; + + about += "System information: \n"; + about += " Java version - " + System.getProperty("java.version") + "\n"; + about += " Java home - " + System.getProperty("java.home") + "\n"; + about += " OS Name - " + System.getProperty("os.name") + "\n"; + about += " OS Arch - " + System.getProperty("os.arch") + "\n"; + String jclasspath = System.getProperty("java.class.path"); + jclasspath = jclasspath.replaceAll(";", ";\n "); + if (!System.getProperty("os.name").startsWith("Windows")) + jclasspath = jclasspath.replaceAll(":", ":\n "); + about += " Java class path - " + jclasspath + "\n\n"; + JOptionPane.showMessageDialog(null, about , "About", JOptionPane.QUESTION_MESSAGE); } }); toolBar.add(helpBtn); diff --git a/src/org/synthuse/WindowInfo.java b/src/org/synthuse/WindowInfo.java index 53d2861..08169bf 100644 --- a/src/org/synthuse/WindowInfo.java +++ b/src/org/synthuse/WindowInfo.java @@ -56,15 +56,14 @@ public class WindowInfo { parentStr = Api.GetHandleAsString(parent); } else { - //User32.instance.GetWindowModuleFileName(hWnd, path, 512); + PointerByReference pointer = new PointerByReference(); User32.instance.GetWindowThreadProcessId(hWnd, pointer); - Pointer p = pointer.getPointer(); - pid = p.getLong(0); + pid = pointer.getPointer().getInt(0); Pointer process = Kernel32.instance.OpenProcess(Api.PROCESS_QUERY_INFORMATION | Api.PROCESS_VM_READ, false, pointer.getValue()); Psapi.instance.GetModuleBaseNameW(process, null, buffer2, 512); processName = Native.toString(buffer2); - //processName = Native.toString(path); + } this.hwnd = hWnd; hwndStr = Api.GetHandleAsString(hWnd); diff --git a/src/org/synthuse/test/WinApiTest.java b/src/org/synthuse/test/WinApiTest.java new file mode 100644 index 0000000..6b0d7d2 --- /dev/null +++ b/src/org/synthuse/test/WinApiTest.java @@ -0,0 +1,138 @@ +package org.synthuse.test; + + +import java.util.LinkedHashMap; +import java.util.Map; + + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import static org.junit.Assert.*; + +import org.synthuse.Api; +import org.synthuse.WindowInfo; +import org.synthuse.WindowsEnumeratedXml; +import org.synthuse.Api.Kernel32; +import org.synthuse.Api.Psapi; +import org.synthuse.Api.User32; + +import com.sun.jna.Native; +import com.sun.jna.Pointer; +import com.sun.jna.platform.win32.WinUser; +import com.sun.jna.platform.win32.WinDef.HWND; +import com.sun.jna.platform.win32.WinDef.RECT; +import com.sun.jna.ptr.PointerByReference; + +public class WinApiTest { + + @Before + public void setUp() throws Exception { + + } + + @After + public void tearDown() throws Exception { + + } + + public static void output(String val) { + System.out.println(val); + } + + //copied and modified slightly from WindowInfo class + public String getWindow32Information(HWND hWnd, boolean isChild) { + String hwndStr = ""; + HWND parent = null; + String parentStr = ""; + RECT rect; + String text; + String className = ""; + String processName = ""; + long pid = 0; + + byte[] buffer = new byte[1024]; + output("Calling GetWindowTextA"); + User32.instance.GetWindowTextA(hWnd, buffer, buffer.length); + text = Native.toString(buffer); + output("GetWindowTextA returned: " + text); + if (text.isEmpty()) { + output("Calling sendWmGetText"); + text = new Api().sendWmGetText(hWnd); + output("sendWmGetText returned: " + text); + } + + char[] buffer2 = new char[1026]; + output("Calling GetClassName"); + User32.instance.GetClassName(hWnd, buffer2, 1026); + className = Native.toString(buffer2); + output("GetClassName returned: " + className); + + rect = new RECT(); + output("Calling GetWindowRect"); + User32.instance.GetWindowRect(hWnd, rect); + output("GetWindowRect returned: " + rect.toString()); + + if (isChild) { + output("Calling GetParent"); + parent = User32.instance.GetParent(hWnd); + parentStr = Api.GetHandleAsString(parent); + output("GetParent returned: " + parentStr); + } + else { + //User32.instance.GetWindowModuleFileName(hWnd, path, 512); + //IntByReference pointer = new IntByReference(); + PointerByReference pointer = new PointerByReference(); + output("Calling GetWindowThreadProcessId"); + User32.instance.GetWindowThreadProcessId(hWnd, pointer); + pid = pointer.getPointer().getInt(0); + output("GetWindowThreadProcessId returned: " + pid); + Pointer process = Kernel32.instance.OpenProcess(Api.PROCESS_QUERY_INFORMATION | Api.PROCESS_VM_READ, false, pointer.getPointer()); + //output("OpenProcess returned: " + process.getLong(0)); + output("Calling GetModuleBaseNameW"); + Psapi.instance.GetModuleBaseNameW(process, null, buffer2, 512); + processName = Native.toString(buffer2); + output("GetModuleBaseNameW returned: " + processName); + //processName = Native.toString(path); + } + hwndStr = Api.GetHandleAsString(hWnd); + output("GetHandleAsString returned: " + hwndStr); + return hwndStr; + } + + @Test + public void enumerateParentWindowsTest() { + final Map infoList = new LinkedHashMap(); + + class ParentWindowCallback implements WinUser.WNDENUMPROC { + @Override + public boolean callback(HWND hWnd, Pointer lParam) { + output("Getting window info " + Api.GetHandleAsString(hWnd)); + //WindowInfo wi = new WindowInfo(hWnd, false); + infoList.put(getWindow32Information(hWnd, false), null); + //Api.User32.instance.EnumChildWindows(hWnd, new ChildWindowCallback(), new Pointer(0)); + return true; + } + } + Api.User32.instance.EnumWindows(new ParentWindowCallback(), 0); + output("enumerateParentWindowsTest size: " + infoList.size()); + assertTrue(infoList.size() > 0); + } + + @Test + public void getXmlTest() { + String xml = WindowsEnumeratedXml.getXml(); + output("getXmlTest len: " + xml.length()); + assertTrue(xml.length() > 500); + } + + public static void main(String[] args) { + output("WinApiTest"); + WinApiTest wat = new WinApiTest(); + wat.enumerateParentWindowsTest(); + wat.getXmlTest(); + output("done."); + + } + +} diff --git a/src/org/synthuse/test/WinFormsMockTestApp.exe b/src/org/synthuse/test/WinFormsMockTestApp.exe new file mode 100644 index 0000000000000000000000000000000000000000..64b76a949cd428ada3e76ec8fb489aa435495ac2 GIT binary patch literal 20480 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 P0A9(R>D2ZA&IA7mXJ{vi literal 0 HcmV?d00001 diff --git a/src/org/synthuse/test/WpfBridgeTest.java b/src/org/synthuse/test/WpfBridgeTest.java index f02f5d0..be15446 100644 --- a/src/org/synthuse/test/WpfBridgeTest.java +++ b/src/org/synthuse/test/WpfBridgeTest.java @@ -1,16 +1,48 @@ package org.synthuse.test; import static org.junit.Assert.*; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + import org.junit.After; +import org.junit.AfterClass; import org.junit.Before; +import org.junit.BeforeClass; import org.junit.Test; import org.synthuse.*; public class WpfBridgeTest { + static Process mockApp = null; + public static void RunMockWpfApp() { + String tempFilename = ExtractFileFromJar("/org/synthuse/test/WpfMockTestApp.exe"); + Runtime runtime = Runtime.getRuntime(); + try { + mockApp = runtime.exec(tempFilename); + } catch (IOException e) { + e.printStackTrace(); + } + } + + @BeforeClass + public static void setUpBeforeClass() { + //this runs only once for this class + RunMockWpfApp(); + } + + @AfterClass + public static void tearDownAfterClass() { + //this runs only once for this class + if (mockApp != null) + mockApp.destroy(); + } + @Before public void setUp() throws Exception { - + } @After @@ -41,7 +73,7 @@ public class WpfBridgeTest { System.out.println("wpf unfiltered countChildrenWindows: " + wb.countChildrenWindows()); } - @Test + //@Test public void countDescendantsWin32() { WpfBridge wb = new WpfBridge(); wb.setFrameworkId("Win32");//We should find some Win32 windows, maybe not WPF @@ -54,7 +86,7 @@ public class WpfBridgeTest { assertTrue(ufwin32Cnt >= win32Cnt); } - @Test + //@Test public void countDescendantsWpf() { WpfBridge wb = new WpfBridge(); wb.setFrameworkId("WPF");// maybe not WPF @@ -72,6 +104,9 @@ public class WpfBridgeTest { wb.setFrameworkId("WPF"); WinPtr wp = new WinPtr(api.user32.FindWindow(null, "MainWindow"));//find WpfMockTestApp.exe long handle = Long.parseLong(wp.hWndStr); + if (handle == 0) + return; + System.out.println("calling getRuntimeIdFromHandle: " + handle); String rid = wb.getRuntimeIdFromHandle(handle); System.out.println(wp.hWndStr + " getRuntimeIdFromHandle: " + rid); assertTrue(rid != null); @@ -91,6 +126,8 @@ public class WpfBridgeTest { wb.setTouchableOnly(false); WinPtr wp = new WinPtr(api.user32.FindWindow(null, "MainWindow"));//find WpfMockTestApp.exe long handle = Long.parseLong(wp.hWndStr); + if (handle == 0) + return; String rid = wb.getRuntimeIdFromHandle(handle); System.out.println(wp.hWndStr + " getRuntimeIdFromHandle: " + rid); if (rid == null) @@ -101,4 +138,55 @@ public class WpfBridgeTest { for(String w : wInfo) System.out.println(w); } + + public static String ExtractFileFromJar(String path) { + // Obtain filename from path + String[] parts = path.split("/"); + String filename = (parts.length > 1) ? parts[parts.length - 1] : null; + // Split filename to prexif and suffix (extension) + String prefix = ""; + String suffix = null; + if (filename != null) { + parts = filename.split("\\.", 2); + prefix = parts[0]; + suffix = (parts.length > 1) ? "."+parts[parts.length - 1] : null; + } + File temp = null; + try { + // Prepare temporary file + temp = File.createTempFile(prefix, suffix); + temp.deleteOnExit(); + } catch(Exception e) { + e.printStackTrace(); + } + if (!temp.exists()) { //some reason the temp file wasn't create so abort + System.out.println("File " + temp.getAbsolutePath() + " does not exist."); + return null; + } + + // Prepare buffer for data copying + byte[] buffer = new byte[1024]; + int readBytes; + // Open and check input stream + InputStream is = WpfBridgeTest.class.getResourceAsStream(path); + if (is == null) { //check if valid + System.out.println("File " + path + " was not found inside JAR."); + return null; + } + // Open output stream and copy data between source file in JAR and the temporary file + OutputStream os = null; + try { + os = new FileOutputStream(temp); + while ((readBytes = is.read(buffer)) != -1) { + os.write(buffer, 0, readBytes); + } + os.close(); + is.close(); + } catch(Exception e) { + e.printStackTrace(); + } + return temp.getAbsolutePath(); + // Finally, load the library + //System.load(temp.getAbsolutePath()); + } } diff --git a/src/org/synthuse/test/WpfMockTestApp.exe b/src/org/synthuse/test/WpfMockTestApp.exe index 05b1150bfb2d2f8032084489072f0cfbbcb9f772..c5f8e278d8f3ff639579960e6acdc963c869ac6f 100644 GIT binary patch literal 54784 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 delta 3077 zcmZWr4Nz3q6+ZXAw{L%dgS;|B~sb<3z^^Y`=R~CYk9S z&iBsw?m6e4ckaE9jlZeIH}8LOWyMf^D?J$3h_e)CBPs@ghaCTH1QbBpA@p!k=R~?R zG3K5$MkALiod@B*TJr5gW0sM^%+xqhsw6Ps6CJsJ(Z&`#?i7J3fE0_DSdqOQngLlP z-edXpI5ez4esPs8TyznEax_CYS;$sRbk9Yk!A6YHj#R%)L*{bv7{BLrDz=<51DtZ< zVj*%7-{}(V+TxfS^_DIqayZ;nT7qYI5rCw(MwQ8g(mNA=*|RLgTB%V*F84$wG1N;m z=pCLf!?{tqI2lQZ)oB|evdWb-hv8{^Bt5w>W#b7y3PD*UZmm!BBZ$gek zu0agsnFk5w0k|Hq6R{{-tcNm_ghLW|Cq2ZIM2aNP2R(EP@d!b)SroPCA(;?Fnk2BB zdMI`w$)HCpiqv}OWRgI=EQ-{6=xCD2kOWfep@T^R4QElL)7Z_qQ+y(o8+JK!#x$!{u_y9_7bhonmTdNzJ|y0kvDIK zENgoseLclk-|^j!-hn522fD*Q7@aZYu||ie*nTOY)tG_t**c#x&n7f8Xu`yMP}S{( ziOo=FSK|qIW4iXC8cd^kR8^5M<*`O*NgJ7=N?Dr;LB;<}Ez+v%YeZUFKCcdnsmTm{DEyVb&p8aelDS{4G+SpSD7fX+N?*8-Qp8xC9{a-+1bLE-fRh) zelsX~)63`UCi+9@Ht8mIg|LsL?VlmjDc*PaN~MWiAo5zmX?PB#jpGB^pE#Ar;TEeJ%hU_iFjaz!Uw)S)vI~eI}hqm{$&DOnv7+tf^ zk|}emXa}MTm`4oG8>-YzFX&|3a-hjR>831Go(Jt0h?5^Bc_1oN(DwN(S8UGyj&Ayc zLGkPCfI}WKHJd8p9*?TY_Y+Tp9`XmP#d447$eVLWgF`OM69+vFmb!qPzUrxU$Wx)> zoj5i=wm{T-D`a+4Z1D!l^5Dduk(HHL8D%+4RPJ7FZU?3<1Z+X{e z>1F{U z5Q-jM*V2Ti4JF*fSH1{~cEXkeCVRs3nr4^xwLC~Q&KsW7VWfWnLVG2KCbkLf?fCfo%Y#;X`~$g;~~&`F?^ z?pUrsPcfQ-rH14ijcxF0R@i4uYfd^~>@l2lOyP&%oiwFpuPZ%aybb>x>o&Bk%W9L$ zk6NFxit%?hes48jgZ=8_>w(+(M&MrlB=Bdv6Zjf`3V4ivANU780Q{H_0dFY0seESn zGhhr&qEnNxh1#>&0l&6O#NOM>?-|1O+p5@DAvS>i!QNrHDCUZ?uCt}uZdh`|w&T<= z+6m;e=l}E<6hER_C<`0gkEmB^2ir?W6dqIffx^=Y&nvv7@KcE~x~`0W1Ak1nfFs11 zOkr2(0*+E4aE!`;xl|2&o$8goT%jFNxzxfo8n=Mkjr&v$hFAw?v)DFZ3v`$|zJg!WLT3vk_Kqy}&wXsdX=nuqNoEN_WvZ>!)lKiEgo1 z*|_yCE2C-hu_}6=&hjeSM;C#a#DTfA1z1GS0V@>N&>rYZ72ilN@v0cDR1=#NcF{Pd zhq1F&w3ANqMiTS}euN#SFMvM>wy4Frz?ZP+fgM0OJEmq2D}Iu0fSFXxB$H9s*fjl% zT~oS^Ww6cc1iQ?xvsoq%9k10F;Bbg5$Dfs7Ms-Ku!exE`fqun05kC>&V(m$<_~c}p z=seY$*4NS5(-))_+ark^TF zDNmH14P~tRYGu~thLonG8@}i-Xn1s)7(BbisTuAPNExJ3GsKm%3l?&NTM9U{`Sb$D zoD#Sdwkzt28Zcb(m&LMk>leN5iXX8QkUM@<&N}7Os=0IAIb63gcV5!H zaK}FoU!Lo)KjV&{!F_hc&uazbv{;ewyp=ff!NIU{Ah+TC$Zn9V!{<##|5Ej|MFEhm$|UZ`O#y?)xIp{ka&-b^Q-ex>NZ8