/* * $Id: GuiTest.xs,v 1.2 2008/10/01 11:27:45 int32 Exp $ * * The SendKeys function is based on the Delphi sourcecode * published by Al Williams * in Dr.Dobbs * * Copyright (c) 1998-2002 by Ernesto Guisado * Copyright (c) 2004 by Dennis K. Paulsen * * You may distribute under the terms of either the GNU General Public * License or the Artistic License. * */ #define WIN32_LEAN_AND_MEAN #define _WIN32_IE 0x0500 #ifndef SIZE_T # define SIZE_T DWORD #endif #include #include #include "dibsect.h" #ifdef __cplusplus //extern "C" { #endif #include "EXTERN.h" #include "perl.h" #include "XSUB.h" #ifdef __cplusplus //} #endif #define MAX_DATA_BUF 1024 #define NUL '\0' HINSTANCE g_hDLL = NULL; #pragma data_seg(".shared") // Used by hooking/injected routines HWND g_hWnd = 0; HHOOK g_hHook = NULL; HWND g_popup = 0; //Hold's popup menu's handle BOOL g_bRetVal = 0; char g_szBuffer[MAX_DATA_BUF+1] = {NUL}; UINT WM_LV_GETTEXT = 0; UINT WM_LV_SELBYINDEX = 0; UINT WM_LV_SELBYTEXT = 0; UINT WM_LV_ISSEL = 0; UINT WM_TC_GETTEXT = 0; UINT WM_TC_SELBYINDEX = 0; UINT WM_TC_SELBYTEXT = 0; UINT WM_TC_ISSEL = 0; UINT WM_TV_SELBYPATH = 0; UINT WM_TV_GETSELPATH = 0; UINT WM_INITMENUPOPUPX = WM_INITMENUPOPUP; //Only needed to conform with SetHook()'s calling convention BOOL unicode_semantics = 0; #pragma data_seg() #pragma comment(linker, "/SECTION:.shared,RWS") BOOL APIENTRY DllMain(HANDLE hModule, DWORD ul_reason_for_call, LPVOID lpReserved) { switch (ul_reason_for_call) { case DLL_PROCESS_ATTACH: // Value used by SetWindowsHookEx, etc. g_hDLL = (HINSTANCE)hModule; break; case DLL_THREAD_ATTACH: case DLL_THREAD_DETACH: case DLL_PROCESS_DETACH: break; } return TRUE; } // Gets a treeview item handle by name HTREEITEM GetTVItemByName(HWND hWnd, HTREEITEM hItem, char *lpItemName) { // If hItem is NULL, start search from root item. if (hItem == NULL) hItem = (HTREEITEM)SendMessage(hWnd, TVM_GETNEXTITEM, TVGN_ROOT, 0); while (hItem != NULL) { char szBuffer[MAX_DATA_BUF+1]; TV_ITEM item; item.hItem = hItem; item.mask = TVIF_TEXT | TVIF_CHILDREN; item.pszText = szBuffer; item.cchTextMax = MAX_DATA_BUF; SendMessage(hWnd, TVM_GETITEM, 0, (LPARAM)&item); // Did we find it? if (lstrcmpi(szBuffer, lpItemName) == 0) return hItem; // Check whether we have child items. if (item.cChildren) { // Recursively traverse child items. HTREEITEM hItemFound, hItemChild; hItemChild = (HTREEITEM)SendMessage(hWnd, TVM_GETNEXTITEM, TVGN_CHILD, (LPARAM)hItem); hItemFound = GetTVItemByName(hWnd, hItemChild, lpItemName); // Did we find it? if (hItemFound != NULL) return hItemFound; } // Go to next sibling item. hItem = (HTREEITEM)SendMessage(hWnd, TVM_GETNEXTITEM, TVGN_NEXT, (LPARAM)hItem); } // Not found. return NULL; } int TabCtrl_GetItemText(HWND hwnd, int iItem, char *lpString, size_t sizeStr) { TCITEM tcItem; tcItem.pszText = lpString; tcItem.cchTextMax = sizeStr; tcItem.mask = TCIF_TEXT; assert(lpString != NULL); *lpString = NUL; TabCtrl_GetItem(g_hWnd, iItem, &tcItem); return (int)strlen(lpString); } // Hook procedure, does most of the work for various 32bit custom control // routines #define pCW ((CWPSTRUCT*)lParam) LRESULT HookProc (int code, WPARAM wParam, LPARAM lParam) { //// List Views //// if (pCW->message == WM_LV_GETTEXT) { *g_szBuffer = NUL; int iItem = pCW->wParam; int iSubItem = pCW->lParam; ListView_GetItemText(g_hWnd, iItem, iSubItem, g_szBuffer, MAX_DATA_BUF); UnhookWindowsHookEx(g_hHook); } else if (pCW->message == WM_LV_SELBYINDEX) { int iCount = ListView_GetItemCount(g_hWnd); int iSel = pCW->wParam; BOOL bMulti = pCW->lParam; // Clear out any previous selections if needed if (!bMulti && ListView_GetSelectedCount(g_hWnd) > 0) { for (int i = 0; i < iCount; i++) { ListView_SetItemState(g_hWnd, i, 0, LVIS_SELECTED); } } // Select item ListView_SetItemState(g_hWnd, iSel, LVIS_SELECTED | LVIS_FOCUSED, LVIS_SELECTED | LVIS_FOCUSED); g_bRetVal = ListView_EnsureVisible(g_hWnd, iSel, FALSE); UnhookWindowsHookEx(g_hHook); } else if (pCW->message == WM_LV_SELBYTEXT) { char szItem[MAX_DATA_BUF+1] = ""; int iCount = ListView_GetItemCount(g_hWnd); BOOL bMulti = pCW->lParam; // Clear out any previous selections if needed if (!bMulti && ListView_GetSelectedCount(g_hWnd) > 0) { for (int i = 0; i < iCount; i++) { ListView_SetItemState(g_hWnd, i, 0, LVIS_SELECTED); } } // Look for item for (int i = 0; i < iCount; i++) { ListView_GetItemText(g_hWnd, i, 0, szItem, MAX_DATA_BUF); if (lstrcmpi(g_szBuffer, szItem) == 0) { // Found it, select it ListView_SetItemState(g_hWnd, i, LVIS_SELECTED | LVIS_FOCUSED, LVIS_SELECTED | LVIS_FOCUSED); g_bRetVal = ListView_EnsureVisible(g_hWnd, i, FALSE); break; } } UnhookWindowsHookEx(g_hHook); } else if (pCW->message == WM_LV_ISSEL) { char szItem[MAX_DATA_BUF+1] = ""; int iCount = ListView_GetItemCount(g_hWnd); g_bRetVal = FALSE; // Assume false // Are there any selected? if (ListView_GetSelectedCount(g_hWnd) > 0) { // Look for item for (int i = 0; i < iCount; i++) { ListView_GetItemText(g_hWnd, i, 0, szItem, MAX_DATA_BUF); if (lstrcmpi(g_szBuffer, szItem) == 0) { // Found it, determine if currently selected if (ListView_GetItemState(g_hWnd, i, LVIS_SELECTED) & LVIS_SELECTED) { g_bRetVal = TRUE; } } } } UnhookWindowsHookEx(g_hHook); } else if (pCW->message == WM_TV_SELBYPATH) { //// Tree Views //// char szName[MAX_DATA_BUF+1] = ""; size_t pos = 0, len = 0; HTREEITEM hItem = NULL; g_bRetVal = FALSE; // Assume failure len = strlen(g_szBuffer); // Move through supplied tree view path, updating hItem appropriately for (size_t x = 0; x < len; x++) { if (g_szBuffer[x] == '|') { if (*szName) { hItem = GetTVItemByName(g_hWnd, hItem, szName); memset(&szName, 0, MAX_DATA_BUF); pos = 0; } } else { szName[pos++] = g_szBuffer[x]; } } if (*szName) { // Just a root item, no path delimiters (|) // OR a trailing child item? hItem = GetTVItemByName(g_hWnd, hItem, szName); } // Select Item if handle obtained g_bRetVal = hItem ? (BOOL)TreeView_SelectItem(g_hWnd, hItem) : FALSE; TreeView_EnsureVisible(g_hWnd, hItem); UnhookWindowsHookEx(g_hHook); } else if (pCW->message == WM_TV_GETSELPATH) { char szText[MAX_DATA_BUF+1] = ""; char szTmp[MAX_DATA_BUF+1] = ""; TVITEM tvItem = {NUL}; HTREEITEM hItem = TreeView_GetSelection(g_hWnd); *g_szBuffer = NUL; tvItem.mask = TVIF_TEXT; tvItem.pszText = szText; tvItem.cchTextMax = MAX_DATA_BUF; do { tvItem.hItem = hItem; TreeView_GetItem(g_hWnd, &tvItem); // Add in child path text if any if (*szTmp) lstrcat(szText, szTmp); hItem = TreeView_GetParent(g_hWnd, hItem); if (hItem) { // Has parent, so store delimiter and path text thus far sprintf(szTmp, "|%s", szText); } else { // No parent, so store complete path thus far lstrcpy(szTmp, szText); } } while (hItem); lstrcpy(g_szBuffer, szTmp); UnhookWindowsHookEx(g_hHook); } else if (pCW->message == WM_TC_GETTEXT) { //// Tab Control //// int iItem = pCW->wParam; g_bRetVal = (BOOL)TabCtrl_GetItemText(g_hWnd, iItem, g_szBuffer, MAX_DATA_BUF); UnhookWindowsHookEx(g_hHook); } else if (pCW->message == WM_TC_SELBYINDEX) { int iItem = pCW->wParam; g_bRetVal = FALSE; // Assume failure if (iItem < TabCtrl_GetItemCount(g_hWnd)) { TabCtrl_SetCurFocus(g_hWnd, iItem); g_bRetVal = TRUE; } UnhookWindowsHookEx(g_hHook); } else if (pCW->message == WM_TC_SELBYTEXT) { char szName[MAX_DATA_BUF+1] = ""; int iCount = TabCtrl_GetItemCount(g_hWnd); for (int i = 0; i < iCount; i++) { TabCtrl_GetItemText(g_hWnd, i, szName, MAX_DATA_BUF); // Is Tab item we want? if (lstrcmpi(g_szBuffer, szName) == 0) { // ... then set focus to it TabCtrl_SetCurFocus(g_hWnd, i); break; } } UnhookWindowsHookEx(g_hHook); } else if (pCW->message == WM_TC_ISSEL) { char szName[MAX_DATA_BUF+1] = ""; int iItem = TabCtrl_GetCurFocus(g_hWnd); g_bRetVal = FALSE; // Assume false TabCtrl_GetItemText(g_hWnd, iItem, szName, MAX_DATA_BUF); if (lstrcmpi(g_szBuffer, szName) == 0) { // Yes, selected g_bRetVal = TRUE; } UnhookWindowsHookEx(g_hHook); } else if (pCW->message == WM_INITMENUPOPUP) { g_popup = (HWND) pCW->wParam; UnhookWindowsHookEx(g_hHook); } return CallNextHookEx(g_hHook, code, wParam, lParam); } // Sets up the hook, global control/hook handles, and registers appropriate // window message. HHOOK SetHook(HWND hWnd, UINT &uMsg, char *lpMsgId) { g_hWnd = hWnd; // Give up rest of time slice, so g_hHook assignment and // SetWindowsHook will process. Sleep(0); // Hook the thread, that "owns" our control g_hHook = SetWindowsHookEx(WH_CALLWNDPROC, (HOOKPROC)HookProc, g_hDLL, GetWindowThreadProcessId(hWnd, NULL)); if (uMsg == 0) uMsg = RegisterWindowMessage(lpMsgId); return g_hHook; } // The following several routines all inject "ourself" into a remote process // and performs some work. int GetLVItemText(HWND hWnd, int iItem, int iSubItem, char *lpString) { if (SetHook(hWnd, WM_LV_GETTEXT, "WM_LV_GETTEXT_RM") == NULL) { *lpString = NUL; return 0; } // By the time SendMessage returns, // g_szBuffer already contains the text. SendMessage(hWnd, WM_LV_GETTEXT, iItem, iSubItem ); lstrcpy(lpString, g_szBuffer); return (int)strlen(lpString); } BOOL SelLVItem(HWND hWnd, int iItem, BOOL bMulti) { if (SetHook(hWnd, WM_LV_SELBYINDEX, "WM_LV_SELBYINDEX_RM") == NULL) return FALSE; SendMessage(hWnd, WM_LV_SELBYINDEX, iItem, bMulti); return g_bRetVal; } BOOL SelLVItemText(HWND hWnd, char *lpItem, BOOL bMulti) { if (SetHook(hWnd, WM_LV_SELBYTEXT, "WM_LV_SELBYTEXT_RM") == NULL) return FALSE; lstrcpy(g_szBuffer, lpItem); SendMessage(hWnd, WM_LV_SELBYTEXT, 0, bMulti); return g_bRetVal; } BOOL IsLVItemSel(HWND hWnd, char *lpItem) { if (SetHook(hWnd, WM_LV_ISSEL, "WM_LV_ISSEL_RM") == NULL) return FALSE; lstrcpy(g_szBuffer, lpItem); SendMessage(hWnd, WM_LV_ISSEL, 0, 0); return g_bRetVal; } BOOL SelTVItemPath(HWND hWnd, char *lpPath) { if (SetHook(hWnd, WM_TV_SELBYPATH, "WM_TV_SELBYPATH_RM") == NULL) return FALSE; lstrcpy(g_szBuffer, lpPath); SendMessage(hWnd, WM_TV_SELBYPATH, 0, 0); return g_bRetVal; } int GetTVSelPath(HWND hWnd, char *lpPath) { if (SetHook(hWnd, WM_TV_GETSELPATH, "WM_TV_GETSELPATH_RM") == NULL) return FALSE; SendMessage(hWnd, WM_TV_GETSELPATH, 0, 0); lstrcpy(lpPath, g_szBuffer); return (int)strlen(lpPath); } int GetTCItemText(HWND hWnd, int iItem, char *lpString) { if (SetHook(hWnd, WM_TC_GETTEXT, "WM_TC_GETTEXT_RM") == NULL) { *lpString = NUL; return 0; } SendMessage(hWnd, WM_TC_GETTEXT, iItem, 0); lstrcpy(lpString, g_szBuffer); return (int)strlen(lpString); } BOOL SelTCItem(HWND hWnd, int iItem) { if (SetHook(hWnd, WM_TC_SELBYINDEX, "WM_TC_SELBYINDEX_RM") == NULL) return FALSE; SendMessage(hWnd, WM_TC_SELBYINDEX, iItem, 0); return g_bRetVal; } BOOL SelTCItemText(HWND hWnd, char *szText) { if (SetHook(hWnd, WM_TC_SELBYTEXT, "WM_TC_SELBYTEXT_RM") == NULL) return FALSE; lstrcpy(g_szBuffer, szText); SendMessage(hWnd, WM_TC_SELBYTEXT, 0, 0); return g_bRetVal; } BOOL IsTCItemSel(HWND hWnd, char *lpItem) { if (SetHook(hWnd, WM_TC_ISSEL, "WM_TC_ISSEL_RM") == NULL) return FALSE; lstrcpy(g_szBuffer, lpItem); SendMessage(hWnd, WM_TC_ISSEL, 0, 0); return g_bRetVal; } int GetTCItemCount(HWND hWnd) { return TabCtrl_GetItemCount(hWnd); } /* * Piotr Kaluski * * WaitForWindowInputIdle is a wrapper for WaitForInputIdle Win32 function. * The function waits until the application is ready to accept input * (keyboard keys, mouse clicks). It is useful, for actions, which take a long * time to complete. Instead of putting sleeps of arbitrary length, we can just * wait until the application is ready to respond. Original function takes * a process handle as an input. However, in GUI tests we more often operate * on windows then on applications. * NOTE: Unfortunatelly, this function not always works, so before using * it, check that is works in your environment * */ DWORD WaitForWindowInputIdle( HWND hwnd, DWORD milliseconds ) { DWORD pid = 0; DWORD dwThreadId = GetWindowThreadProcessId( hwnd, &pid ); HANDLE hProcess = OpenProcess( PROCESS_ALL_ACCESS, TRUE, pid ); if( hProcess == NULL ){ LPVOID lpMsgBuf; DWORD dw = GetLastError(); FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, NULL, dw, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR) &lpMsgBuf, 0, NULL ); warn( "OpenProcess failed with error %d: %s", dw, lpMsgBuf ); } //printf( "Calling WaitForInputIdle for pid %ld\n", pid ); DWORD result = WaitForInputIdle( hProcess, milliseconds ); if( result == WAIT_FAILED ){ LPVOID lpMsgBuf; DWORD dw = GetLastError(); FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, NULL, dw, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR) &lpMsgBuf, 0, NULL ); warn( "WaitForInputIdle failed with error %d: %s", dw, lpMsgBuf ); }else{ if( result == WAIT_TIMEOUT ){ // printf( "WaitForInputIdle returned after TIME OUT" ); } if( result == 0 ){ // printf( "WaitForInputIdle returned after wait was satisfied" ); } } return result; } /* Wrapper around kebyd_event */ void KeyUp(UINT vk) { BYTE scan = MapVirtualKey(vk, 0); keybd_event(vk, scan, KEYEVENTF_KEYUP, 0); } void KeyDown(UINT vk) { BYTE scan=MapVirtualKey(vk, 0); keybd_event(vk, scan, 0, 0); } typedef struct windowtable { int size; HWND* windows/*[1024]*/; } windowtable; BOOL CALLBACK AddWindowChild( HWND hwnd, // handle to child window LPARAM lParam // application-defined value ) { HWND* grow; windowtable* children = (windowtable*)lParam; /* Need to grow the table to make space for the next entry */ if (children->windows) grow = (HWND*)saferealloc(children->windows, (children->size+1)*sizeof(HWND)); else grow = (HWND*)safemalloc((children->size+1)*sizeof(HWND)); if (grow == 0) return FALSE; children->windows = grow; children->size++; children->windows[children->size-1] = hwnd; return TRUE; } /* Phill Wolf Although mouse_event is documented to take a unit of "pixels" when moving to an absolute location, and "mickeys" when moving relatively, on my system I can see that it takes "mickeys" in both cases. Giving mouse_event an absolute (x,y) position in pixels results in the cursor going much closer to the top-left of the screen than is intended. Here is the function I have used in my own Perl modules to convert from screen coordinates to mickeys. */ #define SCREEN_TO_MICKEY(COORD,val) MulDiv((val)+1, 0x10000, GetSystemMetrics(SM_C ## COORD ## SCREEN))-1 void ScreenToMouseplane(POINT *p) { p->x = SCREEN_TO_MICKEY(X,p->x); p->y = SCREEN_TO_MICKEY(Y,p->y); } /* Same as mouse_event but without wheel and with time-out. */ VOID simple_mouse( DWORD dwFlags, // flags specifying various motion/click variants DWORD dx, // horizontal mouse position or position change DWORD dy // vertical mouse position or position change ) { char dstr[256]; sprintf(dstr, "simple_mouse(%d, %d, %d)\n", dwFlags, dx, dy); OutputDebugString(dstr); mouse_event(dwFlags, dx, dy, 0, 0); Sleep (10); } /* JJ Utilities for thread-specific window functions */ BOOL AttachWin(HWND hwnd, BOOL fAttach) { DWORD dwThreadId = GetWindowThreadProcessId(hwnd, NULL); DWORD dwMyThread = GetCurrentThreadId(); return AttachThreadInput(dwMyThread, dwThreadId, fAttach); } SV* GetTextHelper(HWND hwnd, int index, UINT lenmsg, UINT textmsg) { SV* sv = 0; int len = SendMessage(hwnd, lenmsg, index, 0L); char* text = (char*)safemalloc(len+1); if (text != 0) { SendMessage(hwnd, textmsg, index, (LPARAM)text); sv = newSVpv(text, len); safefree(text); } return sv; } /* * Piotr Kaluski * * OpenProcessForWindow opens a process, which is an owner of a window * identified by hWnd. * */ HANDLE OpenProcessForWindow( HWND hWnd ) { DWORD pid = 0; DWORD dwThreadId = GetWindowThreadProcessId( hWnd, &pid ); HANDLE hProcess = OpenProcess( PROCESS_ALL_ACCESS, TRUE, pid ); if( hProcess == NULL ){ LPVOID lpMsgBuf; DWORD dw = GetLastError(); FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, NULL, dw, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR) &lpMsgBuf, 0, NULL ); warn( "OpenProcess failed with error %d: %s", dw, lpMsgBuf ); } return hProcess; } //////////////////////////////////////////////////////////////////////////// HWND PopupHandleGet(HWND hWnd, int x, int y, int wait) { g_popup = 0; if (SetHook(hWnd, WM_INITMENUPOPUPX, "WM_INITMENUPOPUP_RM") == NULL) return 0; int mickey_x = SCREEN_TO_MICKEY(X,x); int mickey_y = SCREEN_TO_MICKEY(Y,y); simple_mouse(MOUSEEVENTF_MOVE|MOUSEEVENTF_ABSOLUTE, mickey_x, mickey_y); simple_mouse(MOUSEEVENTF_RIGHTDOWN, 0, 0); simple_mouse(MOUSEEVENTF_RIGHTUP, 0, 0); Sleep(wait); if (g_popup == 0) UnhookWindowsHookEx(g_hHook); return g_popup; } MODULE = Win32::GuiTest PACKAGE = Win32::GuiTest PROTOTYPES: DISABLE ###################################################################### # Allocates memory in address space of a process, which owns window # hWnd. # ###################################################################### void AllocateVirtualBufferImp( hWnd, memSize ) HWND hWnd SIZE_T memSize PPCODE: HANDLE hProcess = OpenProcessForWindow( hWnd ); LPVOID pBuffer = VirtualAllocEx( hProcess, NULL, memSize, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE); if( pBuffer == NULL ){ LPVOID lpMsgBuf; DWORD dw = GetLastError(); FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, NULL, dw, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR) &lpMsgBuf, 0, NULL ); die( "VirtualAllocEx failed with error %d: %s", dw, lpMsgBuf ); }else{ XPUSHs( sv_2mortal( newSVuv( ( LONG )pBuffer ) ) ); XPUSHs( sv_2mortal( newSVuv( ( LONG )hProcess ) ) ); } ###################################################################### # Frees memory allocated by AllocateVirtualBuffer. # ###################################################################### void FreeVirtualBufferImp( hProcess, pBuffer ) LONG hProcess LONG pBuffer PPCODE: BOOL result = VirtualFreeEx( ( HANDLE )hProcess, ( LPVOID )pBuffer, 0, 0x8000 ); if( !result ){ LPVOID lpMsgBuf; DWORD dw = GetLastError(); FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, NULL, dw, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR) &lpMsgBuf, 0, NULL ); die( "VirtualFreeEx failed with error %d: %s", dw, lpMsgBuf ); } ###################################################################### # Read from memory allocated by AllocateVirtualBuffer. # ###################################################################### void ReadFromVirtualBufferImp( hProcess, pVirtBuffer, memSize ) LONG hProcess LONG pVirtBuffer SIZE_T memSize PPCODE: SIZE_T copied = 0; char *pLocBuff = ( char *)safemalloc( memSize + 1 ); if( !ReadProcessMemory( ( HANDLE ) hProcess, ( LPVOID )pVirtBuffer, pLocBuff, memSize, &copied ) ) { LPVOID lpMsgBuf; DWORD dw = GetLastError(); FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, NULL, dw, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR) &lpMsgBuf, 0, NULL ); die( "ReadProcessMemory failed with error %d: %s", dw, lpMsgBuf ); }else{ XPUSHs( sv_2mortal( newSVpv( pLocBuff, memSize ) ) ); safefree( pLocBuff ); } ###################################################################### # Write to memory allocated by AllocateVirtualBuffer. # ###################################################################### void WriteToVirtualBufferImp( hProcess, pVirtBuffer, value ) LONG hProcess LONG pVirtBuffer SV* value PPCODE: SIZE_T copied = 0; STRLEN memSize = 0; char* pLocBuffer = SvPV( value, memSize ); if( !WriteProcessMemory( ( HANDLE )hProcess, ( LPVOID )pVirtBuffer, pLocBuffer, memSize, &copied ) ) { LPVOID lpMsgBuf; DWORD dw = GetLastError(); FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, NULL, dw, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR) &lpMsgBuf, 0, NULL ); die( "WriteProcessMemory failed with error %d: %s", dw, lpMsgBuf ); } SV* GetListViewItem(hWnd, iItem, iSubItem ) HWND hWnd int iItem int iSubItem CODE: char szItem[MAX_DATA_BUF+1] = ""; GetLVItemText(hWnd, iItem, iSubItem, szItem); RETVAL = newSVpv(szItem, 0); OUTPUT: RETVAL int GetListViewItemCount(hWnd) HWND hWnd CODE: RETVAL = ListView_GetItemCount(hWnd); OUTPUT: RETVAL BOOL SelListViewItem(hWnd, iItem, bMulti=FALSE) HWND hWnd int iItem BOOL bMulti CODE: RETVAL = SelLVItem(hWnd, iItem, bMulti); OUTPUT: RETVAL BOOL SelListViewItemText(hWnd, lpItem, bMulti=FALSE) HWND hWnd char *lpItem BOOL bMulti CODE: RETVAL = SelLVItemText(hWnd, lpItem, bMulti); OUTPUT: RETVAL BOOL IsListViewItemSel(hWnd, lpItem) HWND hWnd char *lpItem CODE: RETVAL = IsLVItemSel(hWnd, lpItem); OUTPUT: RETVAL HWND GetListViewHeader(hWnd) HWND hWnd CODE: RETVAL = ListView_GetHeader(hWnd); OUTPUT: RETVAL int GetHeaderColumnCount(hWnd) HWND hWnd CODE: RETVAL = Header_GetItemCount(hWnd); OUTPUT: RETVAL void GetTabItems(hWnd) HWND hWnd PPCODE: char szItem[MAX_DATA_BUF+1] = ""; int iCount = GetTCItemCount(hWnd); for (int i = 0; i < iCount; i++) { GetTCItemText(hWnd, i, szItem); XPUSHs(sv_2mortal(newSVpv(szItem, 0))); } BOOL SelTabItem(hWnd, iItem) HWND hWnd int iItem CODE: RETVAL = SelTCItem(hWnd, iItem); OUTPUT: RETVAL BOOL SelTabItemText(hWnd, lpItem) HWND hWnd char *lpItem CODE: RETVAL = SelTCItemText(hWnd, lpItem); OUTPUT: RETVAL BOOL IsTabItemSel(hWnd, lpItem) HWND hWnd char *lpItem CODE: RETVAL = IsTCItemSel(hWnd, lpItem); OUTPUT: RETVAL SV* GetTreeViewSelPath(hWnd) HWND hWnd CODE: char szPath[MAX_DATA_BUF+1] = ""; int len = GetTVSelPath(hWnd, szPath); RETVAL = newSVpv(szPath, len); OUTPUT: RETVAL void GetCursorPos() INIT: POINT pt; PPCODE: pt.x = pt.y = -1; GetCursorPos(&pt); XPUSHs(sv_2mortal(newSVnv(pt.x))); XPUSHs(sv_2mortal(newSVnv(pt.y))); void SendLButtonUp() CODE: simple_mouse(MOUSEEVENTF_LEFTUP, 0, 0); void SendLButtonDown() CODE: simple_mouse(MOUSEEVENTF_LEFTDOWN, 0, 0); void SendMButtonUp() CODE: simple_mouse(MOUSEEVENTF_MIDDLEUP, 0, 0); void SendMButtonDown() CODE: simple_mouse(MOUSEEVENTF_MIDDLEDOWN, 0, 0); void SendRButtonUp() CODE: simple_mouse(MOUSEEVENTF_RIGHTUP, 0, 0); void SendRButtonDown() CODE: simple_mouse(MOUSEEVENTF_RIGHTDOWN, 0, 0); void SendMouseMoveRel(x,y) int x; int y; CODE: simple_mouse(MOUSEEVENTF_MOVE, x, y); void SendMouseMoveAbs(x,y) int x; int y; CODE: simple_mouse(MOUSEEVENTF_MOVE|MOUSEEVENTF_ABSOLUTE, x, y); void MouseMoveAbsPix(x,y) int x; int y; PREINIT: int mickey_x = SCREEN_TO_MICKEY(X,x); int mickey_y = SCREEN_TO_MICKEY(Y,y); CODE: simple_mouse(MOUSEEVENTF_MOVE|MOUSEEVENTF_ABSOLUTE, mickey_x, mickey_y); #ifndef WHEEL_DELTA #define WHEEL_DELTA 120 #endif void MouseMoveWheel(dwChange) DWORD dwChange CODE: mouse_event(MOUSEEVENTF_WHEEL, 0, 0, (dwChange*WHEEL_DELTA), 0); HWND GetDesktopWindow() CODE: RETVAL = GetDesktopWindow(); OUTPUT: RETVAL HWND GetWindow(hwnd, uCmd) HWND hwnd UINT uCmd CODE: RETVAL = GetWindow(hwnd, uCmd); OUTPUT: RETVAL SV* GetWindowText(hwnd) HWND hwnd CODE: char text[512]; int r; if ( unicode_semantics) { WCHAR buf[256]; r = GetWindowTextW(hwnd, buf, 255); r = WideCharToMultiByte(CP_UTF8, 0, buf, r, text, 511, NULL, NULL); RETVAL = newSVpvn(text, r); SvUTF8_on( RETVAL); } else { r = GetWindowText(hwnd, text, 255); RETVAL = newSVpvn(text, r); } OUTPUT: RETVAL SV* GetClassName(hwnd) HWND hwnd CODE: // SV* sv; char text[255]; int r; r = GetClassName(hwnd, text, 255); RETVAL = newSVpv(text, r); OUTPUT: RETVAL HWND GetParent(hwnd) HWND hwnd CODE: RETVAL = GetParent(hwnd); OUTPUT: RETVAL long GetWindowLong(hwnd, index) HWND hwnd int index CODE: RETVAL = GetWindowLong(hwnd, index); OUTPUT: RETVAL BOOL SetForegroundWindow(hWnd) HWND hWnd CODE: RETVAL = SetForegroundWindow(hWnd); OUTPUT: RETVAL HWND SetFocus(hWnd) HWND hWnd CODE: AttachWin(hWnd, TRUE); RETVAL = SetFocus(hWnd); AttachWin(hWnd, FALSE); OUTPUT: RETVAL void GetChildWindows(hWnd) HWND hWnd; PREINIT: windowtable children; int i; PPCODE: children.size = 0; children.windows = 0; EnumChildWindows(hWnd, (WNDENUMPROC)AddWindowChild, (LPARAM)&children); for (i = 0; i < children.size; i++) { XPUSHs(sv_2mortal(newSViv((IV)children.windows[i]))); } safefree(children.windows); SV* WMGetText(hwnd) HWND hwnd CODE: // SV* sv; char* text; int len = SendMessage(hwnd, WM_GETTEXTLENGTH, 0, 0L); text = (char*)safemalloc(len+1); if (text != 0) { SendMessage(hwnd, WM_GETTEXT, (WPARAM)len + 1, (LPARAM)text); RETVAL = newSVpv(text, len); safefree(text); } else { RETVAL = 0; } OUTPUT: RETVAL int WMSetText(hwnd, text) HWND hwnd char * text CODE: RETVAL = SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) text); OUTPUT: RETVAL BOOL IsChild(hWndParent, hWnd) HWND hWndParent HWND hWnd CODE: RETVAL = IsChild(hWndParent, hWnd); OUTPUT: RETVAL DWORD GetChildDepth(hAncestor, hChild) HWND hAncestor HWND hChild PREINIT: DWORD depth = 1; CODE: while ((hChild = GetParent(hChild)) != 0) { depth++; if (hChild == hAncestor) { break; } } RETVAL = depth; OUTPUT: RETVAL int SendMessage(hwnd, msg, wParam, lParam) HWND hwnd UINT msg WPARAM wParam LPARAM lParam CODE: RETVAL = SendMessage(hwnd, msg, wParam, lParam); OUTPUT: RETVAL int PostMessage(hwnd, msg, wParam, lParam) HWND hwnd UINT msg WPARAM wParam LPARAM lParam CODE: RETVAL = PostMessage(hwnd, msg, wParam, lParam); OUTPUT: RETVAL void CheckButton(hwnd) HWND hwnd CODE: SendMessage(hwnd, BM_SETCHECK, BST_CHECKED, 0); void UnCheckButton(hwnd) HWND hwnd CODE: SendMessage(hwnd, BM_SETCHECK, BST_UNCHECKED, 0); void GrayOutButton(hwnd) HWND hwnd CODE: SendMessage(hwnd, BM_SETCHECK, BST_INDETERMINATE, 0); BOOL IsCheckedButton(hwnd) HWND hwnd CODE: RETVAL = SendMessage(hwnd, BM_GETCHECK, 0, 0) == BST_CHECKED; OUTPUT: RETVAL BOOL IsGrayedButton(hwnd) HWND hwnd CODE: RETVAL = SendMessage(hwnd, BM_GETCHECK, 0, 0) == BST_INDETERMINATE; OUTPUT: RETVAL BOOL IsWindow(hwnd) HWND hwnd CODE: RETVAL = IsWindow(hwnd); OUTPUT: RETVAL void ScreenToClient(hwnd, x, y) HWND hwnd int x int y INIT: POINT pt; PPCODE: pt.x = x; pt.y = y; if (ScreenToClient(hwnd, &pt)) { XPUSHs(sv_2mortal(newSViv((IV)pt.x))); XPUSHs(sv_2mortal(newSViv((IV)pt.y))); } void ClientToScreen(hwnd, x, y) HWND hwnd int x int y INIT: POINT pt; PPCODE: pt.x = x; pt.y = y; if (ClientToScreen(hwnd, &pt)) { XPUSHs(sv_2mortal(newSViv((IV)pt.x))); XPUSHs(sv_2mortal(newSViv((IV)pt.y))); } void GetCaretPos(hwnd) HWND hwnd INIT: POINT pt; PPCODE: AttachWin(hwnd, TRUE); pt.x = pt.y = -1; if (GetCaretPos(&pt)) { XPUSHs(sv_2mortal(newSVnv(pt.x))); XPUSHs(sv_2mortal(newSVnv(pt.y))); } AttachWin(hwnd, FALSE); HWND GetFocus(hwnd) HWND hwnd; CODE: AttachWin(hwnd, TRUE); RETVAL = GetFocus(); AttachWin(hwnd, FALSE); OUTPUT: RETVAL HWND GetActiveWindow(hwnd) HWND hwnd; CODE: AttachWin(hwnd, TRUE); RETVAL = GetActiveWindow(); AttachWin(hwnd, FALSE); OUTPUT: RETVAL HWND GetForegroundWindow() CODE: RETVAL = GetForegroundWindow(); OUTPUT: RETVAL HWND SetActiveWindow(hwnd) HWND hwnd; CODE: AttachWin(hwnd, TRUE); RETVAL = SetActiveWindow(hwnd); AttachWin(hwnd, FALSE); OUTPUT: RETVAL BOOL EnableWindow(hwnd, fEnable) HWND hwnd BOOL fEnable CODE: RETVAL = EnableWindow(hwnd, fEnable); OUTPUT: RETVAL BOOL IsWindowEnabled(hwnd) HWND hwnd CODE: RETVAL = IsWindowEnabled(hwnd); OUTPUT: RETVAL BOOL IsWindowVisible(hwnd) HWND hwnd CODE: RETVAL = IsWindowVisible(hwnd); OUTPUT: RETVAL BOOL ShowWindow(hwnd, nCmdShow) HWND hwnd int nCmdShow CODE: AttachWin(hwnd, TRUE); RETVAL = ShowWindow(hwnd, nCmdShow); AttachWin(hwnd, FALSE); OUTPUT: RETVAL BOOL UnicodeSemantics(...) CODE: switch( items) { case 0: break; case 1: unicode_semantics = SvTRUE( ST( 0)); break; default: croak("Format: UnicodeSemantics() or UnicodeSemantics(BOOL)"); } RETVAL = unicode_semantics; OUTPUT: RETVAL void ScreenToNorm(x,y) int x; int y; PPCODE: x = SCREEN_TO_MICKEY(X,x); y = SCREEN_TO_MICKEY(Y,y); XPUSHs(sv_2mortal(newSViv((IV)x))); XPUSHs(sv_2mortal(newSViv((IV)y))); void NormToScreen(x,y) int x; int y; PPCODE: x = MulDiv(x + 1, GetSystemMetrics(SM_CXSCREEN), 65536) - 1; y = MulDiv(y + 1, GetSystemMetrics(SM_CYSCREEN), 65536) - 1; XPUSHs(sv_2mortal(newSViv((IV)x))); XPUSHs(sv_2mortal(newSViv((IV)y))); void GetScreenRes() PREINIT: int hor,ver; PPCODE: hor = GetSystemMetrics(SM_CXSCREEN); ver = GetSystemMetrics(SM_CYSCREEN); XPUSHs(sv_2mortal(newSViv((IV)hor))); XPUSHs(sv_2mortal(newSViv((IV)ver))); void GetWindowRect(hWnd) HWND hWnd; PREINIT: RECT rect; PPCODE: GetWindowRect(hWnd,&rect); XPUSHs(sv_2mortal(newSViv((IV)rect.left))); XPUSHs(sv_2mortal(newSViv((IV)rect.top))); XPUSHs(sv_2mortal(newSViv((IV)rect.right))); XPUSHs(sv_2mortal(newSViv((IV)rect.bottom))); SV* GetComboText(hwnd, index) HWND hwnd; int index CODE: RETVAL = GetTextHelper(hwnd, index, CB_GETLBTEXTLEN, CB_GETLBTEXT); OUTPUT: RETVAL SV* GetListText(hwnd, index) HWND hwnd; int index CODE: RETVAL = GetTextHelper(hwnd, index, LB_GETTEXTLEN, LB_GETTEXT); OUTPUT: RETVAL void GetComboContents(hWnd) HWND hWnd; PPCODE: int nelems = SendMessage(hWnd, CB_GETCOUNT, 0, 0); int i; for (i = 0; i < nelems; i++) { XPUSHs(sv_2mortal(GetTextHelper(hWnd, i, CB_GETLBTEXTLEN, CB_GETLBTEXT))); } BOOL SelComboItem(hWnd, iItem) HWND hWnd; int iItem; CODE: RETVAL = (SendMessage(hWnd, CB_SETCURSEL, iItem, 0) != CB_ERR); OUTPUT: RETVAL BOOL SelComboItemText(hWnd, lpItem) HWND hWnd; char *lpItem; CODE: int nelems = SendMessage(hWnd, CB_GETCOUNT, 0, 0); int i; RETVAL = FALSE; for (i = 0; i < nelems; i++) { SV *sv = GetTextHelper(hWnd, i, CB_GETLBTEXTLEN, CB_GETLBTEXT); char *txt = sv_2pvbyte_nolen(sv); if (lstrcmpi(txt, lpItem) == 0) { RETVAL = (SendMessage(hWnd, CB_SETCURSEL, i, 0) != CB_ERR); break; } } OUTPUT: RETVAL ######################################################################### # Selects combo item, which starts with a given string # ######################################################################### DWORD SelComboString( hWnd, lpItem, start_idx = 0 ) HWND hWnd; char *lpItem; DWORD start_idx; CODE: int result = 0; result = SendMessage(hWnd, CB_SELECTSTRING, start_idx, LPARAM( lpItem ) ); if( result == CB_ERR ){ RETVAL = -1; }else{ RETVAL = result; } OUTPUT: RETVAL void GetListContents(hWnd) HWND hWnd; PPCODE: int nelems = SendMessage(hWnd, LB_GETCOUNT, 0, 0); int i; for (i = 0; i < nelems; i++) { XPUSHs(sv_2mortal(GetTextHelper(hWnd, i, LB_GETTEXTLEN, LB_GETTEXT))); } HMENU GetSubMenu(hMenu, nPos) HMENU hMenu; int nPos; CODE: RETVAL = GetSubMenu(hMenu, nPos); OUTPUT: RETVAL # experimental code by SZABGAB void GetMenuItemInfo(hMenu, uItem) HMENU hMenu; UINT uItem; INIT: MENUITEMINFO minfo; char buff[256] = ""; /* Menu Data Buffer */ PPCODE: memset(buff, 0, sizeof(buff)); minfo.cbSize = sizeof(MENUITEMINFO); minfo.fMask = MIIM_CHECKMARKS | MIIM_DATA | MIIM_TYPE | MIIM_STATE; minfo.dwTypeData = buff; minfo.cch = sizeof(buff); if (GetMenuItemInfo(hMenu, uItem, TRUE, &minfo)) { XPUSHs(sv_2mortal(newSVpv("type", 4))); if (minfo.fType == MFT_STRING) { XPUSHs(sv_2mortal(newSVpv("string", 6))); int r; r = strlen(minfo.dwTypeData); XPUSHs(sv_2mortal(newSVpv("text", 4))); XPUSHs(sv_2mortal(newSVpv(minfo.dwTypeData, r))); } else if (minfo.fType == MFT_SEPARATOR) { XPUSHs(sv_2mortal(newSVpv("separator", 9))); } else { XPUSHs(sv_2mortal(newSVpv("unknown", 7))); } XPUSHs(sv_2mortal(newSVpv("fstate", 6))); XPUSHs(sv_2mortal(newSViv(minfo.fState))); XPUSHs(sv_2mortal(newSVpv("ftype", 5))); XPUSHs(sv_2mortal(newSViv(minfo.fType))); } #void #getLW(hWnd) # HWND hWnd; #INIT: # CWnd myWnd; #PPCODE: # myWnd = CWnd::FromHandle(hWnd); # XPUSHs(sv_2mortal(newSVpv("type", 4))); int GetMenuItemCount(hMenu) HMENU hMenu; CODE: RETVAL = GetMenuItemCount(hMenu); OUTPUT: RETVAL int GetMenuItemIndex(hm, sitem) HMENU hm; char *sitem; CODE: int mi = 0; int mic = 0; MENUITEMINFO minfo; char buff[256] = ""; /* Menu Data Buffer */ BOOL found = FALSE; RETVAL = -1; mic = GetMenuItemCount(hm); if (mic != -1) { /* Look at each item to determine if it is the one we want */ for (mi = 0; mi < mic; mi++) { /* Refresh menu item info structure */ memset(buff, 0, sizeof(buff)); minfo.cbSize = sizeof(MENUITEMINFO); minfo.fMask = MIIM_DATA | MIIM_TYPE; minfo.dwTypeData = buff; minfo.cch = sizeof(buff); if (GetMenuItemInfo(hm, mi, TRUE, &minfo) && minfo.fType == MFT_STRING && minfo.dwTypeData != NULL && strnicmp(minfo.dwTypeData, sitem, strlen(sitem)) == 0) { /* Got what we came for, so return index. */ RETVAL = mi; break; } } } OUTPUT: RETVAL HMENU GetSystemMenu(hWnd, bRevert) HWND hWnd; BOOL bRevert; CODE: RETVAL = GetSystemMenu(hWnd, bRevert); OUTPUT: RETVAL UINT GetMenuItemID(hMenu, nPos) HMENU hMenu; int nPos; CODE: RETVAL = GetMenuItemID(hMenu, nPos); OUTPUT: RETVAL HMENU GetMenu(hWnd) HWND hWnd; CODE: RETVAL = GetMenu(hWnd); OUTPUT: RETVAL BOOL SetWindowPos(hWnd, hWndInsertAfter, X, Y, cx, cy, uFlags) HWND hWnd; HWND hWndInsertAfter; int X; int Y; int cx; int cy; UINT uFlags; CODE: RETVAL = SetWindowPos(hWnd, hWndInsertAfter, X, Y, cx, cy, uFlags); OUTPUT: RETVAL void TabCtrl_SetCurFocus(hWnd, item) HWND hWnd int item CODE: TabCtrl_SetCurFocus(hWnd, item); int TabCtrl_GetCurFocus(hWnd) HWND hWnd CODE: RETVAL = TabCtrl_GetCurFocus(hWnd); OUTPUT: RETVAL int TabCtrl_SetCurSel(hWnd, item) HWND hWnd int item CODE: RETVAL = TabCtrl_SetCurSel(hWnd, item); OUTPUT: RETVAL int TabCtrl_GetItemCount(hWnd) HWND hWnd CODE: RETVAL = TabCtrl_GetItemCount(hWnd); OUTPUT: RETVAL void SendRawKey(vk, flags) UINT vk; DWORD flags; CODE: BYTE scan = MapVirtualKey(vk, 0); keybd_event(vk, scan, flags, 0); int VkKeyScan(c) int c; CODE: RETVAL = VkKeyScan((char) c); OUTPUT: RETVAL int GetAsyncKeyState(c) int c; CODE: RETVAL = GetAsyncKeyState(c); OUTPUT: RETVAL HWND WindowFromPoint(x, y) int x; int y; PREINIT: POINT pt; CODE: pt.x = x; pt.y = y; RETVAL = WindowFromPoint(pt); OUTPUT: RETVAL ##################################################################### # Waits for input idle for the application, which owns the window # hWnd. It is a wrapper around WaitForInputIdle Win32 API. # Does not always work as expected, seams to be not that reliable # mechanism. But it's better then nothing and there are still some # cases when it works # ###################################################################### DWORD WaitForReady( hWnd, dwMilliseconds = 30000 ) HWND hWnd; DWORD dwMilliseconds; CODE: RETVAL = WaitForWindowInputIdle( hWnd, dwMilliseconds ); OUTPUT: RETVAL ############################################################################ HWND GetPopupHandle(hWnd, x, y, wait=50) HWND hWnd; int x; int y; int wait; CODE: RETVAL = PopupHandleGet(hWnd, x, y, wait); OUTPUT: RETVAL ############################################################################ MODULE = Win32::GuiTest PACKAGE = Win32::GuiTest::DibSect PROTOTYPES: DISABLE DibSect * DibSect::new() void DibSect::DESTROY() bool DibSect::CopyClient(hwnd, rect=0) HWND hwnd SV *rect CODE: RECT r, *pr = 0; if (rect) { if (!(SvROK(rect) && (rect = SvRV(rect)) && SvTYPE(rect) == SVt_PVAV)) croak("Second argument to CopyClient() must be a reference to array"); AV * av = (AV*) rect; int len = av_len(av) + 1; if (len != 4) croak("Rectangle requires 4 elements, not %d", len); pr = &r; SetRectEmpty(pr); LONG * p = (LONG *) pr; for(int i = 0 ; i < len; i++, p++) { SV ** psv = av_fetch(av, i, 0); if (psv) *p = SvIV(*psv); } } RETVAL = THIS->CopyWndClient(hwnd, pr) != 0; OUTPUT: RETVAL bool DibSect::CopyWindow(hwnd) HWND hwnd CODE: RECT r; GetWindowRect(hwnd, &r); RETVAL = THIS->CopyWndClient(GetDesktopWindow(), &r) != 0; OUTPUT: RETVAL bool DibSect::SaveAs(szFile) char * szFile CODE: RETVAL = THIS->SaveAs(szFile); OUTPUT: RETVAL bool DibSect::Invert() CODE: RETVAL = THIS->Invert(); OUTPUT: RETVAL bool DibSect::ToGrayScale() CODE: RETVAL = THIS->ToGrayScale(); OUTPUT: RETVAL bool DibSect::Destroy() CODE: RETVAL = THIS->Destroy(); OUTPUT: RETVAL bool DibSect::ToClipboard() CODE: RETVAL = THIS->ToClipboard(); OUTPUT: RETVAL