Glenn Linderman wrote:
Pre-canning of messages for common controls would be a good thing. It
would require lots of work (maybe not that much actually - some
analysis should be required).
Complexity and performance considerations may mean that frequently used
operations should still be coded in C. But let's hope not, because a
single mechanism would be less code to support. Hopefully pre-canning
would remove the complexity part, for frequently used messages.
Mechanisms to simplify the interfaces to remote SendMessage calls would
certainly be welcomed by most users, to the extent that they can be
invented.
You know, when I was thinking about the implementation of Interprocess
SendMessage for Win32::GuiTest I first come to an idea that there should be a
separate function called, say, SendIPMessage (Send Interprocess message), which
should take as a parameter a window handle, message id, WPARAM, LPARAM. In
addition a planned to pass as parameters information like - how much data we
send in the buffer pointed by WPARAM/LPARAM, how much data we expect to receive
in this buffer. This "flexibility" made the function so complex (mainly due to
analysis of several possible combinations of input parameters) that I decided
that it would be easier, if the user will be given a chance to allocate external
memory him/herself and do all necessary copying and then calling an ordinary
SendMessage. Another argument for this solution was, that sometimes (for example
for LVITEM for ListView) you had to allocate additional memory, which was then
pointed by LVITEM member. So you know what was my reasoning behind making user
aware of other process memory space problems. But I would be pretty happy to see
a solution, which would transparently and inteligently handle this problem,
freeing user from worring about complex windows process's related issues.
As for sending frequently used messages we can go this way: In each Win32::GUI
function, which actually does need marshaling, we can check if a window to which
we send a message, is in the same process which is calling SendMessage. You can
use GetCurrentProcessId and GetWindowThreadProcessId API to check it. If there
is interprocess call, we take the path with virtuall memory allocation. If
window is owned by a process, which sends a message, that the current code is
executed. We could start the implemenation from implementing in all these
methods (those which involve marshaling) a check if we do an interprocess
SendMessage. If such a call is detected, the function whould die. That way, we
would have TESTING application dying before making forbidden SendMessage rather
than allowing it to call SendMessage, which would kill the TESTED application.
We could then start to systematically add interprocess marshaling in all places
where it is required.
Let me give you an example:
Currently, the Win32::GUI function for getting an item from ListView looks like
this:
void
GetItem(handle,item, subitem=0)
HWND handle
int item
int subitem
ALIAS:
Win32::GUI::ListView::ItemInfo = 1
PREINIT:
LV_ITEM lv_item;
char pszText[1024];
PPCODE:
ZeroMemory(&lv_item, sizeof(LV_ITEM));
lv_item.iItem = item;
lv_item.mask = LVIF_IMAGE
| LVIF_PARAM
| LVIF_TEXT | LVIF_STATE;
lv_item.pszText = pszText;
lv_item.cchTextMax = 1024;
lv_item.iSubItem = subitem;
if(ListView_GetItem(handle, &lv_item)) {
EXTEND(SP, 6);
XST_mPV(0, "-text");
XST_mPV(1, lv_item.pszText);
XST_mPV(2, "-image");
XST_mIV(3, lv_item.iImage);
XST_mPV(4, "-state");
XST_mIV(5, lv_item.state);
XSRETURN(6);
} else {
XSRETURN_UNDEF;
}
Initially I would add something like this:
void
GetItem(handle,item, subitem=0)
HWND handle
int item
int subitem
ALIAS:
Win32::GUI::ListView::ItemInfo = 1
PREINIT:
LV_ITEM lv_item;
char pszText[1024];
PPCODE:
-> int thisproc = GetCurrentProcessId();
-> int calledproc = 0;
-> GetWindowThreadProcessId( handle, &calledproc );
-> if( thisproc != calledproc ){
-> die( "You are making interprocess call, which is not allowed for this
-> message" );
-> }
ZeroMemory(&lv_item, sizeof(LV_ITEM));
lv_item.iItem = item;
lv_item.mask = LVIF_IMAGE
| LVIF_PARAM
| LVIF_TEXT | LVIF_STATE;
lv_item.pszText = pszText;
lv_item.cchTextMax = 1024;
lv_item.iSubItem = subitem;
if(ListView_GetItem(handle, &lv_item)) {
EXTEND(SP, 6);
XST_mPV(0, "-text");
XST_mPV(1, lv_item.pszText);
XST_mPV(2, "-image");
XST_mIV(3, lv_item.iImage);
XST_mPV(4, "-state");
XST_mIV(5, lv_item.state);
XSRETURN(6);
} else {
XSRETURN_UNDEF;
}
And finally, to make function work with windows owned by other process:
void
GetItem(handle,item, subitem=0)
HWND handle
int item
int subitem
ALIAS:
Win32::GUI::ListView::ItemInfo = 1
PREINIT:
LV_ITEM lv_item;
char pszText[1024];
PPCODE:
-> int thisproc = GetCurrentProcessId();
-> int calledproc = 0;
-> GetWindowThreadProcessId( handle, &calledproc );
ZeroMemory(&lv_item, sizeof(LV_ITEM));
lv_item.iItem = item;
lv_item.mask = LVIF_IMAGE
| LVIF_PARAM
| LVIF_TEXT | LVIF_STATE;
lv_item.pszText = pszText;
lv_item.cchTextMax = 1024;
lv_item.iSubItem = subitem;
-> if( thisproc != calledproc ){
-> //Allocate memory in called process address space and copy lv_item
there
-> ListView_GetItem( handle, pointer to buffer in other process address
space);
-> //Copy results back to lvitem
-> //Free memory in called process address space
-> }else{ //Standard way for calling window belonging to this process
-> ListView_GetItem(handle, &lv_item);
-> }
{
EXTEND(SP, 6);
XST_mPV(0, "-text");
XST_mPV(1, lv_item.pszText);
XST_mPV(2, "-image");
XST_mIV(3, lv_item.iImage);
XST_mPV(4, "-state");
XST_mIV(5, lv_item.state);
XSRETURN(6);
} else {
XSRETURN_UNDEF;
}
I am curious what do you think about it.
--
Piotr Kaluski
"It is the commitment of the individuals to excellence,
their mastery of the tools of their crafts, and their
ability to work together that makes the product, not rules."
("Testing Computer Software" by Cem Kaner, Jack Falk, Hung Quoc Nguyen)