├── README.md ├── pom.xml └── src └── main └── java └── org └── tbworks └── auto_alipay ├── AlipayEditInputTyper.java ├── MESSAGE.java ├── TestTyper.java └── User324J.java /README.md: -------------------------------------------------------------------------------- 1 | # alipay_edit_typer 2 | Just a DEMO to demonstrate how to use JNA to type chars into alipay's password edit control automatically. 3 | 4 | For Chinese document [Click Here](http://blog.csdn.net/tbwood/article/details/44649109) 5 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 3 | 4.0.0 4 | 5 | org.tbworks 6 | auto_alipay 7 | 0.0.1-SNAPSHOT 8 | jar 9 | 10 | auto_alipay 11 | http://maven.apache.org 12 | 13 | 14 | UTF-8 15 | 16 | 17 | 18 | 19 | junit 20 | junit 21 | 3.8.1 22 | test 23 | 24 | 25 | net.java.dev.jna 26 | jna 27 | 4.1.0 28 | 29 | 30 | org.seleniumhq.selenium 31 | selenium-java 32 | 2.45.0 33 | 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /src/main/java/org/tbworks/auto_alipay/AlipayEditInputTyper.java: -------------------------------------------------------------------------------- 1 | package org.tbworks.auto_alipay; 2 | 3 | import java.util.ArrayList; 4 | import java.util.HashMap; 5 | import java.util.Iterator; 6 | import java.util.List; 7 | import java.util.Map; 8 | import java.util.concurrent.TimeUnit; 9 | 10 | import com.sun.jna.Native; 11 | import com.sun.jna.Pointer; 12 | import com.sun.jna.platform.win32.User32; 13 | import com.sun.jna.platform.win32.WinDef.HWND; 14 | import com.sun.jna.platform.win32.WinUser.WNDENUMPROC; 15 | import com.sun.jna.win32.W32APIOptions; 16 | 17 | /** 18 | * @author tangb Note: className means the class name of window (frame), you can 19 | * get it by spy++ 20 | */ 21 | public class AlipayEditInputTyper { 22 | 23 | // load the user32.dll 24 | final protected static User324J user32 = (User324J) Native.loadLibrary( 25 | "user32", User324J.class, W32APIOptions.DEFAULT_OPTIONS); 26 | 27 | 28 | /** 29 | * @author tangb 30 | * You can not modify a final variable in the anonymous class, but you can modify its member if the member was not final. 31 | */ 32 | private static final class finalHandleContainer 33 | { 34 | private HWND handle; 35 | } 36 | 37 | // -----------------------privates------------------------------- 38 | 39 | /** 40 | * get browser frame's window handle by class name and window title 41 | * 42 | * @param browserClassName 43 | * : browser window's class name, you can get it by spy++ 44 | * @param browserTitle 45 | * @return 46 | */ 47 | private static HWND getAlipayWindowHandle(String browserClassName, 48 | String browserTitle) { 49 | 50 | HWND deskTopHandle = user32.GetDesktopWindow(); 51 | 52 | char[] winClass = new char[100]; 53 | user32.GetClassName(deskTopHandle, winClass, 100); 54 | 55 | System.out.println(winClass); 56 | 57 | if( deskTopHandle == null ) 58 | throw new RuntimeException("Can not find the desktop's window handle!"); 59 | 60 | 61 | return getSpecifiedWindowHandle(deskTopHandle,browserClassName,browserTitle); 62 | } 63 | 64 | 65 | /** search the children windows of the father window specified by father-handle and find the child window with specified className and substring of title 66 | * @param fatherHandle handle of the father window 67 | * @param className className of the child window 68 | * @param title title of the child window 69 | * @return 70 | */ 71 | private static HWND getSpecifiedWindowHandle(HWND fatherHandle, final String className,final String title) 72 | { 73 | final finalHandleContainer alipayWindowHandleContainer = new finalHandleContainer(); 74 | 75 | boolean result = user32.EnumChildWindows(fatherHandle, new WNDENUMPROC() { 76 | @Override 77 | public boolean callback(HWND hWnd, Pointer data) { 78 | // TODO Auto-generated method stub 79 | 80 | char[] winClass = new char[100]; 81 | char[] winText = new char[200]; 82 | user32.GetClassName(hWnd, winClass, 100); 83 | user32.GetWindowText(hWnd, winText, 200); 84 | 85 | System.out.println("Class Name:"+Native.toString(winClass)+" Title:"+Native.toString(winText)); 86 | 87 | if(user32.IsWindowVisible(hWnd) && Native.toString(winClass).equals(className) && Native.toString(winText).contains(title)) 88 | { 89 | alipayWindowHandleContainer.handle = hWnd; 90 | return false;//false means stop 91 | } 92 | return true; 93 | } 94 | }, Pointer.NULL); 95 | 96 | return alipayWindowHandleContainer.handle; 97 | } 98 | 99 | 100 | /** get all children's class-names and handles under specified father windows, and store them in a map. 101 | * @param fatherHandle the handle of father window 102 | * @return a map stores every class name and its window handles, e.g., key="CLASSONE", value="12312" 103 | */ 104 | private static Map> getChildWindowClassHandleMap(HWND fatherHandle) 105 | { 106 | final finalHandleContainer alipayWindowHandleContainer = new finalHandleContainer(); 107 | 108 | final Map> resultMap = new HashMap>(); 109 | 110 | boolean result = user32.EnumChildWindows(fatherHandle, new WNDENUMPROC() { 111 | @Override 112 | public boolean callback(HWND hWnd, Pointer data) { 113 | // TODO Auto-generated method stub 114 | 115 | char[] winClass = new char[100]; 116 | user32.GetClassName(hWnd, winClass, 100); 117 | if(user32.IsWindowVisible(hWnd)) 118 | { 119 | String tempClassName = Native.toString(winClass); 120 | if(resultMap.containsKey(tempClassName)) 121 | { 122 | resultMap.get(tempClassName).add(hWnd); 123 | } 124 | else 125 | { 126 | List tempHWNDList = new ArrayList(); 127 | tempHWNDList.add(hWnd); 128 | resultMap.put(tempClassName, tempHWNDList); 129 | } 130 | } 131 | return true; 132 | } 133 | }, Pointer.NULL); 134 | 135 | return resultMap; 136 | } 137 | 138 | 139 | /** In term of alipay password edit control, its class name changes very time. And I come up with one solution: the window class name with only one instance may be the alipay edit, others are not definitely. But times always change, so maybe this is not a permanently valid way! Cautions! 140 | * Get several potential alipay edit window. As sending messages to all the sub windows are not efficient, so it is necessary to shrink the range. 141 | * @param browserWindowHandle 142 | * @return 143 | */ 144 | private static List getPotentialAlipayEditHandle(HWND browserWindowHandle) { 145 | 146 | Map> candidates = getChildWindowClassHandleMap(browserWindowHandle); 147 | List resultList = new ArrayList(); 148 | 149 | for(Iterator it =candidates.entrySet().iterator();it.hasNext();) 150 | { 151 | Map.Entry> next = (Map.Entry>) it.next(); 152 | List tempList = next.getValue(); 153 | if(tempList.size()==1) 154 | { 155 | resultList.add(tempList.get(0)); 156 | } 157 | } 158 | 159 | return resultList; 160 | } 161 | 162 | 163 | // -----------------------publics-------------------------------- 164 | /** 165 | * Set password to all potential alipay password edit controls embedded in the browser page 166 | * 167 | * @param browserClassName 168 | * : browser's class name. e.g., MozillaWindowClass for firefox 169 | * @param browserTitleOrSubTitle 170 | * : browser frame's title, or you can just specified a sub-title 171 | * which can mark the alipay window uniquely. E.g., frame's title 172 | * is "123456-scs**0s90", you can just utilize "234" if it is ok. 173 | * @param className 174 | * : alipay edit frame's class name. 175 | * @param title 176 | * : alipay edit frame's title. 177 | * @param password 178 | * : the password. 179 | * @return 180 | * @throws InterruptedException 181 | */ 182 | public static boolean setPassword(String browserClassName, 183 | String browserTitleOrSubTitle,String password) throws InterruptedException { 184 | HWND browserWindowHandle = getAlipayWindowHandle(browserClassName, 185 | browserTitleOrSubTitle); 186 | 187 | System.out.println("------------------------------------------------------------------------------------------------------"); 188 | 189 | List list = getPotentialAlipayEditHandle(browserWindowHandle); 190 | 191 | 192 | for (char c : password.toCharArray()) { 193 | TimeUnit.MILLISECONDS.sleep(500); 194 | for(HWND handle:list) 195 | user32.SendMessage(handle, MESSAGE.WM_CHAR.toInt(), (byte) c, 0); 196 | } 197 | 198 | 199 | return true; 200 | } 201 | } 202 | -------------------------------------------------------------------------------- /src/main/java/org/tbworks/auto_alipay/MESSAGE.java: -------------------------------------------------------------------------------- 1 | package org.tbworks.auto_alipay; 2 | 3 | public enum MESSAGE { 4 | 5 | WM_SETTEXT(0x000C),//输入文本 6 | WM_CHAR(0x0102), //输入字符 7 | BM_CLICK(0xF5),//点击事件,即按下和抬起两个动作 8 | KEYEVENTF_KEYUP(0x0002),//键盘按键抬起 9 | KEYEVENTF_KEYDOWN(0x0); //键盘按键按下 10 | 11 | private MESSAGE(int value) 12 | { 13 | this.value = value; 14 | } 15 | 16 | private int value; 17 | 18 | public String toString() 19 | { 20 | return String.valueOf(value); 21 | } 22 | 23 | public int toInt() 24 | { 25 | return value; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/main/java/org/tbworks/auto_alipay/TestTyper.java: -------------------------------------------------------------------------------- 1 | package org.tbworks.auto_alipay; 2 | 3 | public class TestTyper { 4 | 5 | 6 | public static void main(String[] args) throws InterruptedException { 7 | 8 | // firefox 9 | AlipayEditInputTyper.setPassword("MozillaWindowClass", "支付宝", "mypassword"); 10 | // chrome 11 | AlipayEditInputTyper.setPassword("Chrome_WidgetWin_1", "支付宝", "mypassword"); 12 | 13 | } 14 | 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/org/tbworks/auto_alipay/User324J.java: -------------------------------------------------------------------------------- 1 | package org.tbworks.auto_alipay; 2 | 3 | import com.sun.jna.Pointer; 4 | import com.sun.jna.platform.win32.User32; 5 | import com.sun.jna.platform.win32.WinUser; 6 | import com.sun.jna.platform.win32.WinDef.HWND; 7 | import com.sun.jna.platform.win32.WinUser.WNDENUMPROC; 8 | import com.sun.jna.win32.StdCallLibrary; 9 | 10 | /**Define the exported functions provided by user32.dll 11 | * @author tangb 12 | */ 13 | public interface User324J extends StdCallLibrary, WinUser { 14 | 15 | /** 16 | * Retrieves a handle to a window whose class name and window name match the 17 | * specified strings. The function searches child windows, beginning with 18 | * the one following the specified child window. This function does not 19 | * perform a case-sensitive search. 20 | * 21 | * @param lpParent 22 | * : its parent's handle 23 | * @param lpChild 24 | * : handle of the window before it 25 | * @param lpClassName 26 | * : class name 27 | * @param lpWindowName 28 | * : windows name 29 | * @return return the window handle if success, or return null 30 | */ 31 | HWND FindWindowEx(HWND lpParent, HWND lpChild, String lpClassName, 32 | String lpWindowName); 33 | 34 | /** 35 | * Retrieves a handle to the desktop window. The desktop window covers the 36 | * entire screen. The desktop window is the area on top of which other 37 | * windows are painted. 38 | * 39 | * @return is a handle to the desktop window. 40 | */ 41 | HWND GetDesktopWindow(); 42 | 43 | /** 44 | * Use to send text to a edit window 45 | * 46 | * @param hWnd 47 | * : the handle of the destination's window 48 | * @param Msg 49 | * : the message to be sent. more can be found in : 50 | * https://msdn.microsoft 51 | * .com/en-us/library/ms644927(v=vs.85).aspx#quequed_messages 52 | * @param wParam 53 | * : Additional message-specific information. In this context, 54 | * it's the char needed to be sent to the alipay edit. 55 | * @param lParam 56 | * : Additional message-specific information. 57 | * @return The return value specifies the result of the message processing; 58 | * it depends on the message sent. 59 | */ 60 | int SendMessage(HWND hWnd, int Msg, int wParam, int lParam); 61 | 62 | /** 63 | * Switches focus to the specified window and brings it to the foreground. 64 | * 65 | * @param hWnd 66 | * : A handle to the window. 67 | * @param fAltTab 68 | * : A TRUE for this parameter indicates that the window is being 69 | * switched to using the Alt/Ctl+Tab key sequence. This parameter 70 | * should be FALSE otherwise. 71 | */ 72 | void SwitchToThisWindow(HWND hWnd, boolean fAltTab); 73 | 74 | /** 75 | * The EnumChildWindows function enumerates the child windows that belong to 76 | * the specified parent window by passing the handle to each child window, 77 | * in turn, to an application-defined callback function. EnumChildWindows 78 | * continues until the last child window is enumerated or the callback 79 | * function returns FALSE. 80 | * 81 | * @param hWnd 82 | * Handle to the parent window whose child windows are to be 83 | * enumerated. If this parameter is NULL, this function is 84 | * equivalent to EnumWindows. 85 | * @param lpEnumFunc 86 | * Pointer to an application-defined callback function. 87 | * @param data 88 | * Specifies an application-defined value to be passed to the 89 | * callback function. 90 | * @return If the function succeeds, the return value is nonzero. If the 91 | * function fails, the return value is zero. To get extended error 92 | * information, call GetLastError. If EnumChildProc returns zero, 93 | * the return value is also zero. In this case, the callback 94 | * function should call SetLastError to obtain a meaningful error 95 | * code to be returned to the caller of EnumChildWindows. 96 | */ 97 | boolean EnumChildWindows(HWND hWnd, WNDENUMPROC lpEnumFunc, Pointer data); 98 | 99 | /** 100 | * This function retrieves the name of the class to which the specified 101 | * window belongs. 102 | * 103 | * @param hWnd 104 | * Handle to the window and, indirectly, the class to which the 105 | * window belongs. 106 | * @param lpClassName 107 | * Long pointer to the buffer that is to receive the class name 108 | * string. 109 | * @param nMaxCount 110 | * Specifies the length, in characters, of the buffer pointed to 111 | * by the lpClassName parameter. The class name string is 112 | * truncated if it is longer than the buffer. 113 | * @return The number of characters copied to the specified buffer indicates 114 | * success. Zero indicates failure. To get extended error 115 | * information, call GetLastError. 116 | */ 117 | int GetClassName(HWND hWnd, char[] lpClassName, int nMaxCount); 118 | 119 | /** 120 | * Determines the visibility state of the specified window. 121 | * 122 | * @param hWnd 123 | * A handle to the window to be tested. 124 | * 125 | * @return If the specified window, its parent window, its parent's parent 126 | * window, and so forth, have the WS_VISIBLE style, the return value 127 | * is nonzero. Otherwise, the return value is zero. 128 | * 129 | * Because the return value specifies whether the window has the 130 | * WS_VISIBLE style, it may be nonzero even if the window is totally 131 | * obscured by other windows. 132 | */ 133 | boolean IsWindowVisible(HWND hWnd); 134 | 135 | 136 | /** 137 | * This function copies the text of the specified window's title bar - if it has one - into a buffer. If 138 | * the specified window is a control, the text of the control is copied. 139 | * @param hWnd 140 | * Handle to the window or control containing the text. 141 | * @param lpString 142 | * Long pointer to the buffer that will receive the text. 143 | * @param nMaxCount 144 | * Specifies the maximum number of characters to copy to the buffer, including the NULL character. 145 | * If the text exceeds this limit, it is truncated. 146 | * @return 147 | * The length, in characters, of the copied string, not including the terminating null character, 148 | * indicates success. Zero indicates that the window has no title bar or text, if the title bar is 149 | * empty, or if the window or control handle is invalid. To get extended error information, call 150 | * GetLastError. This function cannot retrieve the text of an edit control in another application. 151 | */ 152 | int GetWindowText(HWND hWnd, char[] lpString, int nMaxCount); 153 | 154 | 155 | 156 | } 157 | --------------------------------------------------------------------------------