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