Glenn Linderman wrote:

At a different level of detail related to this example, I guess I'm reading that ListView_GetItem is "just" a wrapper macro around SendMessage, filling in the SendMessage parameters that are appropriate for the LVM_GETITEM message. MSDN seems to confirm this, pointing out that SendMessage could be called directly instead.
Yes. All these are defined in commctrl.h file. For example ListView_GetItem
 looks like this (in VC++ 6.0):
#define ListView_GetItem(hwnd, pitem) \
    (BOOL)SNDMSG((hwnd), LVM_GETITEM, 0, (LPARAM)(LV_ITEM FAR*)(pitem))


This particular call seems to have a structure that contains a pointer to a buffer of 1024 bytes. If I understand correctly, that means that two buffers must be allocated in the remote process: 1) the 1024 byte buffer 2) the LV_ITEM structure.
Exactly


I don't know the expense of doing the allocation, or the exact capabilities of the injected DLL or how one communicates with it, but it would seem that messaging would be the expensive part, so that it would be best to bundle a number of operations together when possible. Most messaging would consist of 3 sub-steps: i) allocating and initializing remote memory ii) SendMessage (or a wrapper macro or function) iii) reading and discarding remote memory. I don't know if this requires 1 or 3 interactions with the injected DLL. I'll list it as 3 interactions, but if it is possible to make it one, that would likely have better performance.

I don't know all the actuall consequences of both methods (remote memory
and DLL injection). I prefer remote/virtual memory thing, because as for me,
it is easier to comprehend.
I wouldn't worry that much about the perfomance. My experience from gui test automation is that in most cases tested controls have a significant latency (because they need time to be created, to refresh themselves and so on), so in most cases you have to delay your testing code, so the tested application can catch up.

The envisioned design "somehow" passes a request buffer to the injected DLL in the other process. The request buffer would encode the following operations to be performed by a small interpreter on the remote side.

i) allocating and initializing memory

A) allocate 1024 bytes for remote_buffer_1
B) allocate sizeof(LV_ITEM) bytes for remote_buffer_2
C) fill remote_buffer_2 offset 0 with sizeof(LV_ITEM) bytes included in request... the request would include the max data and other structure elements following. D) set remote_buffer_2 offsetof(lv_item.pszText) to address of remote_buffer_1

ii) calling ListView_GetItem
- if this must be done from the controlling process, then the buffer addresses, at least for remote_buffer_2 in this case, must be returned to the controlling process, so that it can call ListView_GetItem with the remote handle, and the remote_buffer_2 address. - alternately, if this can be done in the controlled process, by the injected DLL, the request buffer should contain further information, and probably it would be easiest to bypass ListView_GetItem and work directly with SendMessage parameters... For this case, we add more items to our request buffer:

E) set SendMessage parameters from buffer (this is a fixed number of bytes, I didn't look it up). For this message, the LVM_GETITEM would be the message code, wParam would be 0, and lParam could start as 0, but see the next step
F) Set LPARAM to the adress of remote_buffer_2
G) Call SendMessage using the current parameters
H) Copy SendMessage status into return buffer

iii) reading buffers would be another, or a continued set of operations in a request buffer

I) copy remote_buffer_1 into return buffer
J) copy remote_buffer_2 into return buffer (probably don't need it for this operation, maybe would for others?)
K) deallocate remote_buffer_1
L) deallocate remote_buffer_2

If all the operations, A-L could be placed in a single request buffer, and a single return buffer contained all the results, this would seem to minimize the interactions with the remote process, which should give reasonable performance. And a selection of operations similar to those outlined for this message would seem to support most messages, whether known in advance or not.

Now the ones directly supported by Win32::GUI would have the type checking, marshalling, etc. all done implicitly. Those not directly supported by Win32::GUI would have to be defined, in terms of the operations made available in the request buffers, and type checking would be less stringent at that level of implementation, but the typical technique would be to code an message wrapper function that could do all the dirty work, in terms of the available operations and subfunctions that place operation parameters into the request buffer, and it shouldn't be too complex. And if the technique reduces the number of cross-process interactions, it should be reasonably fast.

Well, OK, so is this a pipe dream? Far-fetched? Far from the current reality of Win32::GuiTest? Totally inappropriate? Let the discussions begin!

It seams that you have a pretty complete and consistent vision on how to do it. IMHO, it would be beneficial if you convert it to the code. It would have 2 advantages:
1. You yourself would be able to verify if your concept is consistent
2. Others would be able to bettern understand your idea.

Just one note. Yout talk about DLL injection and allocation of memory in remote process together, which I think maybe confusing for some people. So let me make it clear. DLL injection is one thing and allocation of memory in other process address space is another. You can use either former or latter of them. You don't have to use both methods together (you can, but you don't have to).



--
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)



Reply via email to