From: Piotr K. <pka...@pi...> - 2005-10-07 09:15:30
|
Hi, On Thu, 06 Oct 2005 22:08:13 +0100 Robert May <rm...@po...> wrote: > So, from a Win32:GUI point of view I would think that building > Win32::Gui::Test on top of the existing Win32::GUI framework makes a lot > of sense. From a Win32::GuiTest point of view I think there needs to be > some thought as to whether having it installable as a separate module, > without Win32::GUI makes sense - as has already been mentioned, > Win32::GUI is not a small download, and may be rather heavy-weight for > someone looking for a test framework to test Win32 paps built in other > environments. In some of my previous e-mails I have pointed some problems, which I see with a merge (i.e. resources to do the actual work and backward compatibility). But from the design point view I am leaning more dowards your suggestion of having gui test functions in Win32::GUI::Test domain. I feel it would nicely solve a dilemma, which I have with Win32::GuiTest - what else to implement in Win32::GuiTest and shall we implement it if its already in Win32::GUI. I do not consider heavy-weightness of Win32::GUI as a problem. Its size is nothing in comparizon with average GUI application. This is how I see the future, in case we consider merge feasible and reasonable: 1. Win32::GUI team has a look on what is implemented in Win32::GuiTest. Have a look on the manual. It is up to date: http://www.piotrkaluski.com/files/winguitest/docs/index.html . The source code is available at www.sourceforge.net/projects/winguitest. 2. We figure out what is already available and what has to be ported to Win32::GUI (remember about interprocess boundary problems, see the manual, "Advanced tutorial"). 3. Win32::GUI team ads new functions to their module. 4. If it works, the development of Win32::GuiTest will be limited to bug fixes. Any development of new functionality will be done in Win32::GUI::Test. What do you think? Just one more think. I believe this discussion is important, since it may have serious impact in both components. Therefore I suggest that we submit all posts to the per...@li... list and we avoid e-mails sent to individual people directly. It will help others to follow the progress of the discussion. And please do not post to per...@li... and in the same time to me (as CC). The result is that I am getting messages twice :-) --Piotr |
From: Robert M. <rm...@po...> - 2005-10-07 09:57:46
|
Piotr Kaluski wrote: > > 1. Win32::GUI team has a look on what is implemented in Win32::GuiTest. > Have a look on the manual. It is up to date: > http://www.piotrkaluski.com/files/winguitest/docs/index.html . > The source code is available at www.sourceforge.net/projects/winguitest. I'll certainly have a look, but it may be a couple of weeks before I get the time to do this. > 2. We figure out what is already available and what has to be ported to > Win32::GUI (remember about interprocess boundary problems, see the manual, > "Advanced tutorial"). Seems reasonable. > 3. Win32::GUI team ads new functions to their module. I think there is a decision to be made between steps 2 and 3. Another possibility would be for the Win32::GuiTest team to take on the responsibility for development of Win32::GUI::Test, possibly even as a separate project, and just use Win32::GUI. It sounds like you don't have much development effort in your group - I'm not sure that we have a whole lot here either. But let's wait and see how much work we think is involved. > Just one more think. I believe this discussion is important, since it may > have serious impact in both components. Therefore I suggest that we submit > all posts to the per...@li... list and we > avoid e-mails sent to individual people directly. It will help others to > follow the progress of the discussion. I would suggest moving this discussion to the hackers list which is much more suited for development issues (You will need to subscribe if you have not already). I have set 'Reply-To: per...@li...' Regards, Rob. |
From: Emmanuel E <emm...@gm...> - 2005-10-08 05:20:40
|
Hi Everyone, This discussion was very informative. Since there is talk of changing the module namespace anyway I was just wondering if the name could be changed to something more descriptive - like Win32::GUI::Automate or something like that? I had absolutely no idea on what GuiTest did until I read the documentation on Piotr's website. Excellent documentation btw. And we could leave the Win32::GUI::Test to the for the test scripts of Win32? Being able to automate GUIs from within Perl would be really cool. I've played aroud with VBScript and SendKeys - SendKeys dosent work on Terminal Server windows for some reason. Specifically it dosent work on Citrix terminal server sessions. However AutoIT from http://www.autoitscript.com/autoit3/index.php works on Citrix terminal server sessions. Maybe Win32::GuiTest could attempt to replicate some of the features in AutoIT. And finally if this could evolve to something like Internet Macros http://www.iopus.com/iim/ then we would have arrived! Warm regards, Emmanuel ----- Original Message ----- From: "Robert May" <rm...@po...> To: <per...@li...> Sent: Friday, October 07, 2005 3:27 PM Subject: [perl-win32-gui-users] Re: Merging Win32::GUI and Win32::GuiTest > Piotr Kaluski wrote: >> >> 1. Win32::GUI team has a look on what is implemented in Win32::GuiTest. >> Have a look on the manual. It is up to date: >> http://www.piotrkaluski.com/files/winguitest/docs/index.html . >> The source code is available at www.sourceforge.net/projects/winguitest. > > I'll certainly have a look, but it may be a couple of weeks before I get > the time to do this. > >> 2. We figure out what is already available and what has to be ported to >> Win32::GUI (remember about interprocess boundary problems, see the >> manual, >> "Advanced tutorial"). > > Seems reasonable. > >> 3. Win32::GUI team ads new functions to their module. > > I think there is a decision to be made between steps 2 and 3. Another > possibility would be for the Win32::GuiTest team to take on the > responsibility for development of Win32::GUI::Test, possibly even as a > separate project, and just use Win32::GUI. It sounds like you don't have > much development effort in your group - I'm not sure that we have a whole > lot here either. But let's wait and see how much work we think is > involved. > > > Just one more think. I believe this discussion is important, since it > may >> have serious impact in both components. Therefore I suggest that we >> submit >> all posts to the per...@li... list and we >> avoid e-mails sent to individual people directly. It will help others to >> follow the progress of the discussion. > > I would suggest moving this discussion to the hackers list which is much > more suited for development issues (You will need to subscribe if you have > not already). I have set 'Reply-To: > per...@li...' > > Regards, > Rob. > > > ------------------------------------------------------- > This SF.Net email is sponsored by: > Power Architecture Resource Center: Free content, downloads, discussions, > and more. http://solutions.newsforge.com/ibmarch.tmpl > _______________________________________________ > Perl-Win32-GUI-Users mailing list > Per...@li... > https://lists.sourceforge.net/lists/listinfo/perl-win32-gui-users > http://perl-win32-gui.sourceforge.net/ > |
From: Piotr K. <pka...@pi...> - 2005-10-08 07:56:59
|
Glenn Linderman wrote: > Is it impractical to code the injected DLL so that it implements generic > methods for memory allocation, and API invocation, that can be scripted? > For example, Win32::API can invoke "any old Win32 API" if you teach it > how... could the injected Win32::GUI::Test DLL somehow be similarly > scriptable to call any old SendMessage if you teach it how (by writing > interface descriptions in the Perl client code, many of which would be > "pre-canned" for known, frequently used messages for frequently used > controls)? Sounds reasonable. We have to think about it, maybe comming with some prototypes. We should have two goals in mind: 1. User should be able to easily perform opearions on M$ controls (like TreeView). 2. In the same time user should be able to deal with custom controls without the need to modify xs files. 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). But still a general mechanism should be available for custom controls. I am not sure if using Win32::API is a best solution. As for me it is slightly more error prone, since it is up to the user to check carefully what is being passed as parameters. Also debugging seems to be slightly more difficult. Using xs or even wrapper around Win32::API in pm file, will free the user from making the same errors again and again. > [I remembered to delete you, Piotr, from the "Reply-All" list this time, > but I can't guarantee to always do that. While you prefer not to get > duplicate messages, many others prefer to get the duplicates, because > the direct one often comes much faster than the one through the list. So > my general practice is to simply Reply-All to mailing list messages.] > OK. I understand the reason now. Go ahead with your standard way. After all it is not a big deal if I receive some duplicates. -- 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) |
From: Piotr K. <pka...@pi...> - 2005-10-08 22:34:06
|
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) |
From: Piotr K. <pka...@pi...> - 2005-10-09 06:55:16
|
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) |
From: Jeremy W. <jez...@ho...> - 2005-10-09 10:06:43
|
>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 ); Imagine this situation. A list view is created in a different thread from the 'main' thead. The main thread then calls GetItem for the listview that was created in the other thread. Would this still work? I guess what I'm trying to say is that if we do make all Win32::GUI function calls work across processes, they should also work across all threads within the same process that is running Win32::GUI. Cheers, jez. |
From: Piotr K. <pka...@pi...> - 2005-10-09 14:09:12
|
Jeremy White wrote: > >> 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 ); > > > Imagine this situation. A list view is created in a different thread > from the 'main' thead. The main thread then calls GetItem for the > listview that was created in the other thread. Would this still work? I > guess what I'm trying to say is that if we do make all Win32::GUI > function calls work across processes, they should also work across all > threads within the same process that is running Win32::GUI. > > Cheers, > > jez. > > I didn't test it but AFAIK it should work. -- 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) |
From: Piotr K. <pka...@pi...> - 2005-10-13 06:45:54
|
Glenn Linderman wrote: >> 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. > > It would also have some significant disadvantages... it wouldn't be > available for several years, and, since I'm only a casual user of > GuiTest, it might break compatibility with some significant features of > GuiTest. > > I'd be glad to discuss the idea, and help people understand it, if > anyone wants to understand it. Beyond discussion, I'm afraid it would > be several months, at least, before I might be able to start > contributing code. We would mark it as experimental. You know, coding is of course an actual implementation. But it is also a kind consistency check. When you design in your imagination or on paper you can still miss some important details. Coding forces us to specify everything so we can then verify if our concepts are OK. The fact that you will implement the method and post it to the group does not mean your code will make it to the code base. But everyone who is interested would be able to try it out. It would also be a good base for further discussion. > OK, so the Win32::GuiTest documentation states that both DLL injection > and allocation of memory in remote process are used by GuiTest. > > What I read in Richter, Advnaced Windows, 3rd edition, implies that DLL > injection is built using allocation of memory in remote process, > together with some other features. > Is "Advanced Window" the same as "Programming Applications for MS Windows"? I read 4th edition. I will reread it if I have time, to make sure if I am right, but this is how understand it: * There are several ways of DLL injections. * Not all of them require that you explicitly allocate memory in remote process address space * There is DLL injection method called "by remote threads" where you do need to allocate memory in other process address space. > I haven't read enough Win32::GuiTest documentation to figure out why > GuiTest uses each of the techniques. Is such documented, so that I > could read about it? Well, the reason for it isn't very scientific. Dennis Paulsen, one of contributors, implemented some ListView, TreeView related functions using DLL injections with Win32 hooks. In the same time I was strugling with some common controls and I thought it would be good to have a mechanism, which would allow experimenting without a need to modify the code in xs file. So I implemented remote process memory handling function. > > You mentioned that performance isn't a big deal, because GuiTest > programs generally have to wait for the tested program anyway, but the > more cycles consumed by GuiTest, the fewer are available for the tested > program... so performance shouldn't be totally ignored. While Windows > provides a number of APIs that can affect other processes, I have no > idea what the costs are to use those APIs. It would seem that any > interprocess activities would require a significant amount of > synchronization, and that would make such APIs costly, compared to local > actions. Hence, one of the purposes of my envisioned approach is to > reduce the number of cross-process APIs that are used. If benchmarking > shows that cross-process operations are nearly as fast as in-process > operations, then perhaps that concern is unwarranted. I agree but I would put it the other way around: If benchmarking shows that cross-process operations are performance bottle necks, we should then start thinking about optimisation. --Piotr |