Beefy Boxes and Bandwidth Generously Provided by pair Networks
Clear questions and runnable code
get the best and fastest answer
 
PerlMonks  

Re: Automation Problem with Win32::GUI and Win32::GUITest

by talwyn (Monk)
on Jun 02, 2005 at 04:58 UTC ( [id://462756]=note: print w/replies, xml ) Need Help??


in reply to Automation Problem with Win32::GUI and Win32::GUITest

Well it turns out that a Solution in C was required. Windows automagically supplies shared memory for the original 16 bit controls.

Listboxes, however are 32 bit controls!!! So I had to implement the select function to allocate, use and deallocate memory inside the foreign process.

Here is the bare bones perl module and XS code I used. Sorry about the long time between updates ... but school and work interfere.

If you like this or use it and want to donate you can paypal me at Steven_swenson@sbcglobal.net. I can always use pizza/gas money :) I attached the code below:

perlmodule

package ListViewEx; use 5.006; use strict; use warnings; use Carp; require Exporter; require DynaLoader; use AutoLoader; our @ISA = qw(Exporter DynaLoader); # Items to export into callers namespace by default. Note: do not expo +rt # names by default without a very good reason. Use EXPORT_OK instead. # Do not simply export all your public functions/methods/constants. # This allows declaration use ListViewEx ':all'; # If you do not need this, moving things directly into @EXPORT or @EXP +ORT_OK # will save memory. our %EXPORT_TAGS = ( 'all' => [ qw( lvGetItem lvSelectItem ) ] ); our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } ); our @EXPORT = qw( ); our $VERSION = '0.01'; sub AUTOLOAD { # This AUTOLOAD is used to 'autoload' constants from the constant( +) # XS function. If a constant is not found then control is passed # to the AUTOLOAD in AutoLoader. my $constname; our $AUTOLOAD; ($constname = $AUTOLOAD) =~ s/.*:://; croak "& not defined" if $constname eq 'constant'; local $! = 0; my $val = constant($constname, @_ ? $_[0] : 0); if ($! != 0) { if ($! =~ /Invalid/ || $!{EINVAL}) { $AutoLoader::AUTOLOAD = $AUTOLOAD; goto &AutoLoader::AUTOLOAD; } else { croak "Your vendor has not defined ListViewEx macro $constname +"; } } { no strict 'refs'; # Fixed between 5.005_53 and 5.005_61 if ($] >= 5.00561) { *$AUTOLOAD = sub () { $val }; } else { *$AUTOLOAD = sub { $val }; } } goto &$AUTOLOAD; } bootstrap ListViewEx $VERSION; # Preloaded methods go here. # Autoload methods go after =cut, and are processed by the autosplit p +rogram. 1; __END__ # Below is stub documentation for your module. You better edit it! =head1 NAME ListViewEx - Perl extension for Accessing a ListView Object outside yo +ur own process space =head1 SYNOPSIS use ListViewEx; lvGetItem($listview_handle,$item,$subItem); lvSelectItem ($listview_handle, $item); =head1 DESCRIPTION ListViewEX provides two subroutines to access a listview control's tex +t and select that item for a listview that belongs to a different process than the one that Perl is running in. =head2 EXPORT None by default. =head1 AUTHOR Steven Swenson<lt>Steven_Swenson@sbcglobal.netE<gt> =head1 SEE ALSO L<perl>. =cut

The XS Code

#include "EXTERN.h" #include "perl.h" #include "XSUB.h" #include <windows.h> #include <commctrl.h> #include <string.h> HV* lvGetItem ( SV* handle, SV* item, SV* subitem ) { /* Var */ HWND ihandle; int iItem; int iSubItem; int textlength; BOOL Success; HANDLE hProcess; DWORD dwProcessId; LV_ITEM* plvi; LV_ITEM lvi; TCHAR text[100]; DWORD* bytes; SV* sv; HV* hv; /* Process Arguments, get handle */ ihandle = (HWND) SvIV(handle); iItem = SvIV ( item); iSubItem = SvIV ( subitem); #ifdef DEBUG_CHECK printf ("Made it to GetItem handle %d\n", ihandle); #endif /* Open a handle to the remote process's kernel object '*/ if ( GetWindowThreadProcessId( ihandle, &dwProcessId) == NULL ) { croak ("Got a NULL reference for the process or thread han +dles\n"); } hProcess = OpenProcess(PROCESS_VM_OPERATION | PROCESS_VM_READ | PR +OCESS_VM_WRITE, FALSE, dwProcessId); /* check that we got a valid process handle */ if ( hProcess == NULL ) { croak ("Got a NULL reference for the process handle\n"); } #ifdef DEBUG_CHECK printf ("Successfully Received a Process handle and readied the pr +ocess for a memory operation.\n"); #endif /* Allocate memory in the remote process's address space.'*/ plvi = (LV_ITEM*) VirtualAllocEx(hProcess, NULL, 4096, MEM_RESERVE + | MEM_COMMIT, PAGE_READWRITE); #ifdef DEBUG_CHECK printf ("Successfully Allocated Memory%d.\n",(long)plvi); #endif /* Initialize a local LV_ITEM structure */ lvi.mask = LVIF_TEXT; lvi.iItem = iItem; lvi.iSubItem = iSubItem; /* NOTE: The text data immediately follows the LV_ITEM structure * +/ /* in the memory block allocated in the remote process. */ lvi.pszText = (LPTSTR) (plvi + sizeof(lvi)+1); lvi.cchTextMax = 100; /* Write the local LV_ITEM structure to the remote memory block */ Success = WriteProcessMemory(hProcess, plvi, &lvi, sizeof(lvi), NU +LL); if ( ! Success ) { croak ("Did Not write request to memory\n"); } #ifdef DEBUG_CHECK else { printf ("Successfully copied local structure\n"); printf ("The size of an lvi struct is %d bytes \n", sizeof(lvi +)); } #endif /* Tell the ListView control to fill the remote LV_ITEM structure +*/ Success = ListView_GetItem( ihandle, plvi); if (!Success) { croak ("The remote process failed to fill the LV structure\n") +; } #ifdef DEBUG_CHECK else { printf("The remote Process has filled the LV_ITEM STRUCTURE\n" +); } #endif /* Get the Text */ memset(text, 0, 100); Success = ReadProcessMemory(hProcess, (plvi + sizeof(lvi) + 1) , t +ext, 100, &textlength ); if (!Success) { printf("Last Error was error:%d\n", GetLastError() ); croak ("ReadProcess Memory Failed!!!!\n"); } #ifdef DEBUG_CHECK printf ("Got '%s' of length %d for Item %d, %d\n", text,textlength +, iItem,iSubItem); #endif /* Free the memory in the remote process's address space'*/ Success = VirtualFreeEx (hProcess, plvi, 0, MEM_RELEASE); if (!Success) { printf("Last Error was error:%d\n", GetLastError() ); croak ("Free Memory Failed!!!!\n"); } #ifdef DEBUG_CHECK else { printf("Freed foreign process memory block\n"); } #endif /* Cleanup */ CloseHandle(hProcess); /* put result on perlstack */ textlength = strlen ( text); sv = newSVpvn(text,textlength); hv = newHV(); hv_store (hv,"-text",5,sv, 0); /* Need to return state and image index as well to be equive to W +in32::Gui version */ return (hv); } int lvSelectItem( SV * hw, SV * in){ /* SelectItem macro uses a pointer address so needs a cross-process + set-up */ /* Var */ HWND ihandle; int iItem; int textlength; BOOL Success; HANDLE hProcess; DWORD dwProcessId; LV_ITEM* plvi; LV_ITEM lvi; NM_LISTVIEW* plvnm; NM_LISTVIEW lvnm; TCHAR text[100]; DWORD* bytes; SV* sv; HV* hv; /* Process Arguments, get handle */ ihandle = (HWND) SvIV(hw); iItem = (int) SvIV ( in); #ifdef DEBUG_CHECK printf ("Made it to Select Item handle %d\n", ihandle); #endif /* Open a handle to the remote process's kernel object '*/ if ( GetWindowThreadProcessId( ihandle, &dwProcessId) == NULL ) { croak ("Got a NULL reference for the process or thread han +dles\n"); } hProcess = OpenProcess(PROCESS_VM_OPERATION | PROCESS_VM_READ | PR +OCESS_VM_WRITE, FALSE, dwProcessId); /* check that we got a valid process handle */ if ( hProcess == NULL ) { croak ("Got a NULL reference for the process handle\n"); } #ifdef DEBUG_CHECK printf ("Successfully Received a Process handle and readied the pr +ocess for a memory operation.\n"); #endif /* Allocate memory in the remote process's address space.'*/ plvi = (LV_ITEM*) VirtualAllocEx(hProcess, NULL, 4096, MEM_RESERVE + | MEM_COMMIT, PAGE_READWRITE); #ifdef DEBUG_CHECK printf ("Successfully Allocated Memory%x.\n",(long)plvi); #endif /* Initialize a local LV_ITEM structure */ lvi.mask = LVIF_STATE; lvi.state = LVIS_FOCUSED | LVIS_SELECTED; lvi.stateMask = LVIS_FOCUSED | LVIS_SELECTED; lvi.iItem = iItem; lvi.iSubItem = 0; /* NOTE: The text data immediately follows the LV_ITEM structure * +/ /* in the memory block allocated in the remote process. */ lvi.pszText = (LPTSTR) (plvi + sizeof(lvi)+1); lvi.cchTextMax = 100; /* Write the local LV_ITEM structure to the remote memory block */ Success = WriteProcessMemory(hProcess, plvi, &lvi, sizeof(lvi), NU +LL); if ( ! Success ) { croak ("Did Not write request to memory\n"); } #ifdef DEBUG_CHECK else { printf ("Successfully copied local structure\n"); printf ("The size of an lvi struct is %d bytes \n", sizeof(lvi +)); } #endif /* SetItemState(nItem, LVIS_FOCUSED | LVIS_SELECTED, LVIS_FOCUSED | LV +IS_SELECTED) /* Tell the ListView control to fill the remote LV_ITEM structure */ Success = SendMessage ( ihandle, LVM_SETITEMSTATE, iItem, plvi ); #ifdef DEBUG_CHECK if (Success) { printf("Selected Item %d \n",iItem); } else { printf("Failed to select Item %d\n",iItem); return (0); } #endif /* Free the memory in the remote process's address space'*/ Success = VirtualFreeEx (hProcess, plvi, 0, MEM_RELEASE); if (!Success) { printf("Last Error was error:%d\n", GetLastError() ); croak ("Free Memory Failed!!!!\n"); } #ifdef DEBUG_CHECK else { printf("Freed foreign process memory block\n"); } #endif /* At this point the item should display its new state... but we h +ave to tell mom about the change. */ /* Cleanup */ CloseHandle(hProcess); return (1); } MODULE = ListViewEx PACKAGE = ListViewEx HV* lvGetItem ( handle, item, subitem ) SV* handle SV* item SV* subitem OUTPUT: RETVAL int lvSelectItem( hw, in) SV * hw SV * in OUTPUT: RETVAL

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: note [id://462756]
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others wandering the Monastery: (5)
As of 2024-03-29 10:54 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found