AutoHotkey Community

It is currently May 27th, 2012, 10:12 am

All times are UTC [ DST ]




Post new topic Reply to topic  [ 21 posts ]  Go to page 1, 2  Next
Author Message
PostPosted: September 1st, 2006, 11:08 am 
Offline

Joined: May 24th, 2006, 2:49 pm
Posts: 4511
Location: Belgrade

_________________
Image


Last edited by majkinetor on December 25th, 2008, 11:57 am, edited 6 times in total.

Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: September 1st, 2006, 12:57 pm 
Offline

Joined: May 24th, 2006, 2:49 pm
Posts: 4511
Location: Belgrade
This is more complicated example.

The goal is to read so called root item from the Explorers TreeView on the left (folders). This item is "Desktop" on all systems but you can not get the text of it using regular API functions. Example is a little complicated then shimanov's work for reading columns of ListView control in Explorer because it requires 2 remote buffers - one to receive the text, and one as an input to SendMessage function witch represents data of the item to be obtained (TVITEM structure in MSDN). If neither of those 2 buffers is local, the Windows will return nothing.

Run explorer, make sure you have folders open on the left and execute the script. You should get message containing word "Desktop" (or whatever localisation you have)

Code:
;Run Explorer
;WinWait ahk_class ExploreWClass

   GetHandles()   ;set XHandle & TreeHandle
   GetRoot()      ;display the text of root item
return


;------------------------------------------------------------------------------------------------
; Get the text of the root item (Desktop)
GetRoot()
{
   global
   local bufID, txt

   ;open remote buffer
   bufID  := RemoteBuf_Open(hwEx, 128)
   
   if bufID < 0
      MsgBox Can not open remote buffer (code %bufID%)
    bufAdr := RemoteBuf_GetAdr(bufID)


   ;get root handle   TVM_GETNEXTITEM = 0x110A  TVGN_ROOT = 0
   SendMessage 0x110A,   0, 0, ,ahk_id %hwTV%
   root = %ErrorLevel%

   ;set TVITEM struct
   VarSetCapacity(sTV, 40,  1)     ;10x4 = 40
   InsertInteger(0x011,   sTV, 0)    ;set mask to TVIF_TEXT | TVIF_HANDLE  = 0x001 | 0x0010 
   InsertInteger(root,    sTV, 4)    ;set itemid to root
   InsertInteger(bufAdr,  sTV, 16)  ;set txt pointer
   InsertInteger(127,     sTV, 20)  ;set txt size
   
   ;copy the TVITEM structure to explorer address space       
   r_tvi := RemoteBuf_Open(hwEx, 40)
   r_adr := RemoteBuf_GetAdr(r_tvi)
   RemoteBuf_Write(r_tvi, sTV, 40)

   ;send tv_getitem message
   SendMessage 0x110C, 0, r_adr ,, ahk_id %hwTV%

   ;read the text from the remote buffer
   VarSetCapacity(txt, 128, 1)
   RemoteBuf_Read(bufID, txt, 128 )

   ;close the buffers
   RemoteBuf_Close( bufID )
   RemoteBuf_Close( r_tvi )

   MsgBox %txt%
}

;------------------------------------------------------------------------------------------------
; Get Explorer and its TreeView handle
GetHandles()
{
   global
   ;get tree view handle
   hwEx   := WinExist("ahk_class ExploreWClass")

   hwTV   := FindWindowExId(hwEx, "BaseBar", 0)
   hwTV   := FindWindowExID(hwTV, "ReBarWindow32", 0)
   hwTV   := FindWindowExID(hwTV, "SysTreeView32", 100)
}

;------------------------------------------------------------------------------------------------
; Iterate through controls with the same class, find the one with ctrlID and return its handle
; Used for finding a specific control
;
FindWindowExID(dlg, className, ctrlId)
{
   local ctrl, id

   ctrl = 0
   Loop
   {
      ctrl := DllCall("FindWindowEx", "uint", dlg, "uint", ctrl, "str", className, "uint", 0 )
      if (ctrlId = "0")
      {
         return ctrl
      }

      if (ctrl != "0")
      {
         id := DllCall( "GetDlgCtrlID", "uint", ctrl )
         if (id = ctrlId)
            return ctrl            
      }
      else
         return 0
   }

}


InsertInteger(pInteger, ByRef pDest, pOffset = 0, pSize = 4)
; The caller must ensure that pDest has sufficient capacity.  To preserve any existing contents in pDest,
; only pSize number of bytes starting at pOffset are altered in it.
{
   Loop %pSize%  ; Copy each byte in the integer into the structure as raw binary data.
      DllCall("RtlFillMemory", "UInt", &pDest + pOffset + A_Index-1, "UInt", 1, "UChar", pInteger >> 8*(A_Index-1) & 0xFF)
}

#include RemoteBuf.ahk


One question remains here about the blue line: why I must initialize array of bytes to non-zero value. Script doesn't work if you set 0 instead of any positive value.

_________________
Image


Last edited by majkinetor on September 27th, 2010, 12:53 pm, edited 1 time in total.

Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: September 1st, 2006, 1:42 pm 
Offline

Joined: December 27th, 2005, 1:46 pm
Posts: 6837
Location: France (near Paris)
Well, I just tested with these values at 0 on WinXP Pro SP2, and it worked fine, showing "Bureau"...

_________________
Image vPhiLho := RegExReplace("Philippe Lhoste", "^(\w{3})\w*\s+\b(\w{3})\w*$", "$1$2")


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: September 1st, 2006, 1:53 pm 
Offline

Joined: March 2nd, 2004, 3:36 pm
Posts: 10720
Nice demonstration. Also, your RemoteBuf.ahk will make life easier and be a good foundation for future scripts. I can't explain why the non-zero value in VarSetCapacity(txt, 128, 1) is needed, but I didn't study it very much.


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: September 1st, 2006, 2:01 pm 
Offline

Joined: May 24th, 2006, 2:49 pm
Posts: 4511
Location: Belgrade
Thx

To get currently selected item change one 0 to 9 in:

Code:
   
   ;get root handle   TVM_GETNEXTITEM = 0x110A  TVGN_ROOT = 0
   SendMessage 0x110A,   9, 0, ,ahk_id %hwTV%
   root = %ErrorLevel%



2 Philho
I don't know. I encounter this in several API functions so far... on XP SP1 + up to date hotfixes

_________________
Image


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: January 1st, 2008, 8:25 pm 
Offline

Joined: May 24th, 2006, 2:49 pm
Posts: 4511
Location: Belgrade
Some small bug fixed. Documentation created.

_________________
Image


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: January 8th, 2008, 5:19 pm 
if I take the example of majkinetor 01.09.2006, http://www.autohotkey.com/forum/post-75346.html#75346, I get the error
Quote:
Error: Call to nonexistent function.

Specifically: RemoteBuf_GetAdr(bufID)

Line#
005: GetHandles()
006: GetRoot()
007: Return
013: {
018: bufID := RemoteBuf_Open(hwEx, 128)
020: if bufID < 0
021: MsgBox,Can not open remote buffer (code %bufID%)
---> 022: bufAdr := RemoteBuf_GetAdr(bufID)
026: SendMessage,0x110A,0,0,,ahk_id %hwTV%
027: root = %ErrorLevel%
030: VarSetCapacity(sTV, 40, 1)
031: InsertInteger(0x011, sTV, 0)
032: InsertInteger(root, sTV, 4)
033: InsertInteger(bufAdr, sTV, 16)
034: InsertInteger(127, sTV, 20)

The program will exit.



Report this post
Top
  
Reply with quote  
 Post subject:
PostPosted: January 8th, 2008, 6:05 pm 
Offline

Joined: May 24th, 2006, 2:49 pm
Posts: 4511
Location: Belgrade
So why did you take it ? I removed it.

There is no such function any more. Instead GetSize and GetAdr are united into Get:

Code:
;----------------------------------------------------
;Function:   Get
;         Get address or size of the remote buffer
;
;Parameters:
;         p_hRemote   - Remote buffer handle
;         p_info      - Set to "adr" to get address (default) or to "size" to get size.
;
;Returns:
;         Address or size of the remote buffer
;

_________________
Image


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: July 5th, 2008, 10:58 am 
is it supposed to read/write from/to external process's memory area? like when you're trying to cheat games and such. i think it's a little confusedness. could you demonstrate a simple and basic example? Thanks for sharing anyway. i like your libraries :)


Report this post
Top
  
Reply with quote  
 Post subject:
PostPosted: November 1st, 2008, 4:03 pm 
Offline

Joined: October 1st, 2005, 9:55 pm
Posts: 775
Location: Texas, USA
majkinetor,

Thanks for directing me to this post. Although somewhat obscure, I'm sure this library will be useful to a lot of users.

I noticed one bug when using this library to solve the EM_GETSELTEXT message problem. OK, maybe not a bug but an inefficiency.

The library uses a global variable to store the Remote Buffer index and 3 global variables for each index to hold the following information: process handle, buffer size, and buffer address. By the way the library is written, these global variables are a probably a necessary evil. Although the RemoteBuf_Close function clears the contents of these variables when the buffer is closed, the memory allocated to each variable is not actually freed because each variable contain less than 4K.

The problem I have is not with amount of memory wasted (trivial amount) but the number of global variables created. After doing just a few dozen tests with a function that uses the Remote Buffer library, I checked the list of variables and found more than a hundred RemoteBuf_* global variables.

There are a few workarounds....

One option is to open a remote buffer with an extra large buffer an reuse the address/space over and over again until you're ready to close the program. I hate this option but it will probably work for some commands/messages/etc.

A better solution might be for the developer to reset the Remote Buffer index after closing the last opened handle. Something like this:
Code:
RemoteBuf_Close(bufID)
RemoteBuf_idx=0

If the Remote Buffer library is called from within a function, the RemoteBuf_idx variable must be defined as a global variable.

Resetting the RemoteBuf_idx will not remove the global variables but it will insure that a minimum number of global variables are created. If only one remote buffer is opened at time, a total of 4 global variables will be created.

An even better solution is to add a "reset" parameter (default FALSE) to the RemoteBuf_Close function that if set to TRUE will reset the RemoteBuf_idx variable. Something like this:
Code:
RemoteBuf_Close(p_hRemote,ResetIndex=false)
    {
    Global
    ...
    ...
    if ResetIndex
        RemoteBuf_idx=0

    return
    }

Them be my thoughts. Thank you for your consideration.


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: November 2nd, 2008, 8:08 pm 
Offline

Joined: May 24th, 2006, 2:49 pm
Posts: 4511
Location: Belgrade
Hey jballi.

This library is written long time ago.

RemoteBuffer and for instance MMenu use large number of global variables in the form of array. At that time, I expected that arrays will be introduced soon enough (so I was bulding everything with pseudo-arrays) but I was obviously wrong.

Now this lib can be done to contain data within one global or even static.
Blank variables aren't that consuming by the way, u can safely have hundreeds of them from my experience.

It can also be design problem from your side. Instead creating new remote buffer each time you can probably create only 1 and assing it to running instance of remote process. You use static to keep handle, create it first time only, and afterwards just check if process is restarted to alocate new buffer for that process. I say this as you were probably opening and closing buffer on each function call.

I hope you solved your problem btw.

_________________
Image


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: November 3rd, 2008, 3:39 pm 
Offline

Joined: May 24th, 2006, 2:49 pm
Posts: 4511
Location: Belgrade
Btw, easy way to fix massive variable creation is to use another technique.

As I already said, I used pesudo-associative array creation. I expected that I will today have some way to create normal arrays or at least free empty variables for good. As that is not the case you can do it like this:

The problem is this peace of code in Open function:

Code:
RemoteBuf_idx += 1
RemoteBuf_%RemoteBuf_idx%_handle  := proc_hwnd
RemoteBuf_%RemoteBuf_idx%_size     := p_size
RemoteBuf_%RemoteBuf_idx%_adr     := bufAdr


You can see that idx is incremented everytime. To prevent this, Close functions must mark that index is deleted, we can use 0 as handle to mark closed buffer:
Code:
RemoteBuf_%RemoteBuf_idx%_handle := 0


Now, above peace of code in Open fun can be changed as:
Code:
;find first available index (use idx of closed buffer if exists)
loop
    if !RemoteBuf_%A_Index%_handle
          new_idx := A_Index

RemoteBuf_%new_idx%_handle  := proc_hwnd
RemoteBuf_%new_idx%_size     := p_size
RemoteBuf_%new_idx%_adr     := bufAdr


The construct if !RemoteBuf_%A_Index%_handle will pass if handle is both 0 (closed buffer) or "" (we need new index).

This will keep the number of variables to number of globaly opened buffers x 3. In your case, you will have only 3 vars all the time. You can also put all tree variables in 1 var, ultimatively, u can have number of variables equal to maximum created number of buffers in session. This is the fastest fix for this issue.

_________________
Image


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: November 3rd, 2008, 8:24 pm 
Offline

Joined: October 1st, 2005, 9:55 pm
Posts: 775
Location: Texas, USA
This will work. I've made these change (with of couple of modifications) in my copy of the RemoteBuf library.

Thanks. :)


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: December 25th, 2008, 11:57 am 
Offline

Joined: May 24th, 2006, 2:49 pm
Posts: 4511
Location: Belgrade
The module is updated by infogulch to not use globals.
Error reports changed.
The documentation updated

_________________
Image


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: December 31st, 2008, 6:29 pm 
Offline

Joined: July 30th, 2007, 11:32 pm
Posts: 581
Very nice wrapper majkinetor!


Report this post
Top
 Profile  
Reply with quote  
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 21 posts ]  Go to page 1, 2  Next

All times are UTC [ DST ]


Who is online

Users browsing this forum: specter333, XX0 and 24 guests


You can post new topics in this forum
You can reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot post attachments in this forum

Search for:
Powered by phpBB® Forum Software © phpBB Group