Remote Buffer
Read and write process memory
Download                 Documentation
[module] RemoteBuffer 2.0
Started by
majkinetor
, Sep 01 2006 10:08 AM
25 replies to this topic
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)
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.
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)
;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 [color=red];open remote buffer[/color] bufID := RemoteBuf_Open(hwEx, 128) if bufID < 0 MsgBox Can not open remote buffer (code %bufID%) bufAdr := RemoteBuf_GetAdr(bufID) ;[color=red]get root handle[/color] TVM_GETNEXTITEM = 0x110A TVGN_ROOT = 0 SendMessage 0x110A, 0, 0, ,ahk_id %hwTV% root = %ErrorLevel% [color=red];set TVITEM struct[/color] 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 [color=red];copy the TVITEM structure to explorer address space [/color] r_tvi := RemoteBuf_Open(hwEx, 40) r_adr := RemoteBuf_GetAdr(r_tvi) RemoteBuf_Write(r_tvi, sTV, 40) [color=red];send tv_getitem message[/color] SendMessage 0x110C, 0, r_adr ,, ahk_id %hwTV% [color=red];read the text from the remote buffer[/color] [color=blue] VarSetCapacity(txt, 128, 1)[/color] RemoteBuf_Read(bufID, txt, 128 ) [color=red];close the buffers[/color] 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.
#2
-
Posted 01 September 2006 - 11:57 AM
Well, I just tested with these values at 0 on WinXP Pro SP2, and it worked fine, showing "Bureau"...
#3
-
Posted 01 September 2006 - 12:42 PM
vPhiLho := RegExReplace("Philippe Lhoste", "^(\w{3})\w*\s+\b(\w{3})\w*$", "$1$2")
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.
#4
-
Posted 01 September 2006 - 12:53 PM
Thx
To get currently selected item change one 0 to 9 in:
2 Philho
I don't know. I encounter this in several API functions so far... on XP SP1 + up to date hotfixes
To get currently selected item change one 0 to 9 in:
;get root handle TVM_GETNEXTITEM = 0x110A TVGN_ROOT = 0 SendMessage 0x110A, [color=blue]9[/color], 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
#5
-
Posted 01 September 2006 - 01:01 PM
if I take the example of majkinetor 01.09.2006, http://www.autohotke...5346.html#75346, I get the error
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.
#7
-
Posted 08 January 2008 - 04:19 PM
So why did you take it ? I removed it.
There is no such function any more. Instead GetSize and GetAdr are united into Get:
There is no such function any more. Instead GetSize and GetAdr are united into Get:
;---------------------------------------------------- ;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 ;
#8
-
Posted 08 January 2008 - 05:05 PM
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
#9
-
Posted 05 July 2008 - 09:58 AM
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:
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:
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:
RemoteBuf_Close(bufID) RemoteBuf_idx=0If 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:
RemoteBuf_Close(p_hRemote,ResetIndex=false) { Global ... ... if ResetIndex RemoteBuf_idx=0 return }Them be my thoughts. Thank you for your consideration.
#10
-
Posted 01 November 2008 - 03:03 PM
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.
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.
#11
-
Posted 02 November 2008 - 07:08 PM
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:
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:
Now, above peace of code in Open fun can be changed as:
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.
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:
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:
RemoteBuf_%RemoteBuf_idx%_handle := 0
Now, above peace of code in Open fun can be changed as:
;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.
#12
-
Posted 03 November 2008 - 02:39 PM
This will work. I've made these change (with of couple of modifications) in my copy of the RemoteBuf library.
Thanks.
Thanks.
#13
-
Posted 03 November 2008 - 07:24 PM
The module is updated by infogulch to not use globals.
Error reports changed.
The documentation updated
Error reports changed.
The documentation updated
#14
-
Posted 25 December 2008 - 10:57 AM