windows API Help - IPC (clicking toolbar button)
Hoping there are one or two windows developer gurus around that can help me with some IPC.
Currently developing an automation tool to test new desktop OS builds at work. Need to be able to open menu items on toolbarwindow32 windows (main menu of explorer windows). Now this requires retrieving information of all the buttons on the toolbar, obtaining the button ID of desired menu and sending a WM_COMMAND message with the ID. I'm having trouble retrieving button information.
Now im using openprocess, virtualallocex to allocate virtual memory in the target process, and then sending the TB_GETBUTTON message with a pointer to the allocated memory then reading out the result. Allocation and the send message works fine but reading the processmemory back out is failing but not returning an error code, getlastwin32error returns nothing either.
Sorry for vb code, not my choice
Declarations
Code:
<StructLayout(LayoutKind.Sequential)> _
Public Structure TBBUTTON
Public iBitmap As Integer
Public idCommand As Integer
Public fsState As Byte
Public fsStyle As Byte
Public bReserved1 As Byte
Public bReserved2 As Byte
'Public bReserved2() As Byte
Public dwData As Integer
Public iString As Integer
End Structure
<DllImport("kernel32.dll", CharSet:=CharSet.Auto, SetLastError:=True)> _
Private Shared Function VirtualAllocEx(ByVal hProcess As IntPtr, ByVal lpAddress As IntPtr, _
ByVal dwSize As IntPtr, ByVal flAllocationType As VMemAllocType, ByVal flProtect As MemProtection) As IntPtr
End Function
<DllImport("kernel32.dll", CharSet:=CharSet.Auto, SetLastError:=True)> _
Private Shared Function VirtualFreeEx(ByVal hProcess As IntPtr, ByVal lpAddress As IntPtr, _
ByVal dwSize As IntPtr, ByVal dwFreeType As UInteger) As <MarshalAs(UnmanagedType.Bool)> Boolean
End Function
<DllImport("kernel32.dll", CharSet:=CharSet.Auto, SetLastError:=True)> _
Public Shared Function WriteProcessMemory(ByVal hProcess As IntPtr, ByVal lpBaseAddress As IntPtr, ByVal lpBuffer As IntPtr, ByVal nSize As IntPtr, ByVal lpNumberOfBytesWritten As IntPtr) As Integer
End Function
<DllImport("kernel32.dll", CharSet:=CharSet.Auto, SetLastError:=True)> _
Public Shared Function ReadProcessMemory(ByVal hProcess As IntPtr, ByVal lpBaseAddress As Integer, ByVal lpBuffer As Integer, _
ByVal iSize As IntPtr, ByVal lpNumberOfBytesRead As IntPtr) As Boolean
End Function
<DllImport("kernel32.dll", CharSet:=CharSet.Auto, SetLastError:=True)> _
Public Shared Function ReadProcessMemory(ByVal hProcess As IntPtr, ByVal lpBaseAddress As IntPtr, ByVal lpBuffer As Byte(), _
ByVal iSize As IntPtr, ByVal lpNumberOfBytesRead As IntPtr) As Boolean
End Function
<DllImport("kernel32.dll", CharSet:=CharSet.Auto, SetLastError:=True)> _
Public Shared Function OpenProcess(ByVal dwDesiredAccess As ProcessFlags, ByVal bInheritHandle As Boolean, ByVal dwProcessId As UInteger) As IntPtr
End Function
<DllImport("user32.dll", CharSet:=CharSet.Auto, SetLastError:=True)> _
Private Shared Function GetWindowThreadProcessId(ByVal hwnd As IntPtr, ByRef lpdwProcessId As UInteger) As UInteger
End Function
Function
Code:
Dim bInfo As TBBUTTON
Dim localPtr As Integer
Dim remotePtr As Integer
Dim bInfoSize As Integer
Dim res As Integer
Dim hwndProcess As IntPtr
Dim pid As UInteger
Dim bText As String
Dim tbCount As Integer
Dim lret As Integer
'ReDim bInfo.bReserved2(1)
tbCount = SendMessageSync(hwnd, Methods.TB_BUTTONCOUNT, IntPtr.Zero, IntPtr.Zero)
bInfoSize = Marshal.SizeOf(bInfo)
If tbCount > 0 Then
GetWindowThreadProcessId(hwnd, pid)
hwndProcess = OpenProcess(ProcessFlags.PROC_VM_OPREADWRITE, False, pid)
remotePtr = VirtualAllocEx(hwndProcess, Nothing, bInfoSize, VMemAllocType.MEM_RESERVECOMMIT, MemProtection.PAGE_READWRITE)
For tbIndex As Integer = 0 To tbCount - 1
Dim hret As Integer = SendMessageSync(hwnd, Methods.TB_GETBUTTON, tbIndex, remotePtr)
gcInfo = GCHandle.Alloc(bInfo, GCHandleType.Pinned)
localPtr = gcInfo.AddrOfPinnedObject().ToInt32()
gcInfo.Free()
res = ReadProcessMemory(hwndProcess, remotePtr, localPtr, Len(bInfo), IntPtr.Zero)
Dim str As String = Marshal.GetLastWin32Error()
VirtualFreeEx(hwnd, remotePtr, Marshal.SizeOf(remotePtr), &H8000UI)
CloseHandle(hwndProcess)
End If
As mentioned above, the read process memoery call fails, the return value is -1 :confused: The documentation states it returns 0 for fail, non zero for pass.
Wondering if the problem lies in incorrect use of types in pinvoke calls or the struct tbbutton is incorrect, or my use of a pinned pointer is wrong, or whether I need to use adjusttokenpriviledges to ensure I have rights. Though I'm running under admin and examples similar to this don't use it.
Relevant documentation
Virtualallocex
Read Process Memory
TBBUTTON Struct
Re: windows API Help - IPC (clicking toolbar button)
Quote:
Originally Posted by Mythrandil
As mentioned above, the read process memoery call fails, the return value is -1 :confused: The documentation states it returns 0 for fail, non zero for pass.
Why do you think the read process memory call failed? A -1 would indicate success.
Re: windows API Help - IPC (clicking toolbar button)
Because after the readprocessmemory call all bInfo's members are still default values (0's)
I assumed read memory was failing as send message returns 1 indicating success.
Maybe my memory allocation is therefore at fault? - The call does return a memory address though.
Re: windows API Help - IPC (clicking toolbar button)
Quote:
gcInfo = GCHandle.Alloc(bInfo, GCHandleType.Pinned)
localPtr = gcInfo.AddrOfPinnedObject().ToInt32()
gcInfo.Free()
res = ReadProcessMemory(hwndProcess, remotePtr, localPtr, Len(bInfo), IntPtr.Zero)
Try moving gcInfo.Free() to after your call to ReadProcessMemory. Also, why use Len(bInfo) instead of bInfoSize in your ReadProcessMemory call?
Re: windows API Help - IPC (clicking toolbar button)
Quote:
Originally Posted by michaelerice
Try moving gcInfo.Free() to after your call to ReadProcessMemory. Also, why use Len(bInfo) instead of bInfoSize in your ReadProcessMemory call?
Tried moving free afterwards and same behaviour. Returns -1 and bInfo is still all zero's.
I tried len(bInfo) just to compare whether it returned same size as marshal.sizeof, it does and I just forgot to change it back.
Re: windows API Help - IPC (clicking toolbar button)
A couple of items in your signature for ReadProcessMemory may be incorrect. Consider the following...
Code:
<DllImport("kernel32.dll", CharSet:=CharSet.Auto, SetLastError=true)>
Public Shared Function ReadProcessMemory( _
ByVal hProcess As IntPtr, _
ByVal lpBaseAddress As IntPtr, _
<Out()>ByVal lpBuffer() As Byte, _
ByVal dwSize as Integer, _
ByRef lpNumberOfBytesRead as Integer
) As Boolean
End Function
Re: windows API Help - IPC (clicking toolbar button)
Quote:
Originally Posted by michaelerice
A couple of items in your signature for ReadProcessMemory may be incorrect. Consider the following...
Code:
<DllImport("kernel32.dll", CharSet:=CharSet.Auto, SetLastError=true)>
Public Shared Function ReadProcessMemory( _
ByVal hProcess As IntPtr, _
ByVal lpBaseAddress As IntPtr, _
<Out()>ByVal lpBuffer() As Byte, _
ByVal dwSize as Integer, _
ByRef lpNumberOfBytesRead as Integer
) As Boolean
End Function
Thanks will give that a shot, how would I modify the use of local pointer to accomodate a byte array?
Re: windows API Help - IPC (clicking toolbar button)
I have fixed the problem now but still having trouble reaching my original target of clicking a toolbarwindow32 menu (File, edit etc)
TB_Buttonpressed just causes the buttons gfx to change and not trigger the context menu to appear, wm_command does nothing!
Any ideas?
Re: windows API Help - IPC (clicking toolbar button)
Are you sure that you absolutely have to 'test' the new desktop builds this way? I mean, are you sure there is no managed .NET code that can do what you want instead of having to use all these APIs just to click one button..
Also, from what I can see, this is pretty much all API stuff so you probably should of posted it in the API part of the forum ;)
Re: windows API Help - IPC (clicking toolbar button)
Quote:
Originally Posted by
chris128
Are you sure that you absolutely have to 'test' the new desktop builds this way? I mean, are you sure there is no managed .NET code that can do what you want instead of having to use all these APIs just to click one button..
Also, from what I can see, this is pretty much all API stuff so you probably should of posted it in the API part of the forum ;)
Lol . In other words 'I dont know how to answer your question'.
Here is a technique i am using to click on 'File' menus and items found in 3rd party programs i am trying to control. This method however may not work with 'Toolbar' controls. The code is for a test program i developed so its not been made specifically for your question im afraid.
Code:
Public Class Form1
Public Declare Function FindWindow Lib "user32" Alias "FindWindowA" (ByVal lpClassName As String, ByVal lpWindowName As String) As Integer
Public Declare Function SendMessage Lib "user32.dll" Alias "SendMessageA" (ByVal hWnd As Integer, ByVal wMsg As Integer, ByVal wParam As Integer, ByVal lParam As String) As Integer
Public Declare Function GetMenu Lib "user32" Alias "GetMenu" (ByVal hwnd As Integer) As Integer
Public Declare Function GetSubMenu Lib "user32" Alias "GetSubMenu" (ByVal hMenu As Integer, ByVal nPos As Integer) As Integer
Public Declare Function GetMenuItemID Lib "user32" Alias "GetMenuItemID" (ByVal hMenu As Integer, ByVal nPos As Integer) As Integer
' The GetMenuItemID function retrieves the menu item identifier of a menu item located at the specified position in a menu.
Public Declare Function GetMenuItemCount Lib "User32.Dll" (ByVal hMenu As IntPtr) As Integer
' The GetMenuItemCount function determines the number of items in the specified menu.
Private Declare Auto Function PostMessage Lib "user32.dll" (ByVal hwnd As IntPtr, ByVal wMsg As Integer, ByVal wParam As Integer, ByVal lParam As Integer) As Integer
' The PostMessage function places (posts) a message in the message queue associated with the thread that created the
' specified window and returns without waiting for the thread to process the message.
Private Const WM_COMMAND = &H111
Private Const WM_MENUSELECT = &H11F
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
Dim lhWnd As IntPtr, lMenu As IntPtr, submenu As IntPtr, lID As Integer
Dim total As Integer
Dim windowname As String
windowname = TextBox1.Text
lhWnd = FindWindow(vbNullString, windowname)
lMenu = GetMenu(lhWnd)
submenu = GetSubMenu(lMenu, 0)
lID = GetMenuItemID(submenu, 0)
total = GetMenuItemCount(submenu)
Label1.Text = "Parent Handle :" & lhWnd.ToString
Label2.Text = "Menu Handle :" & lMenu.ToString
Label3.Text = "SubMenu Handle :" & submenu.ToString
Label4.Text = "MenuItem ID :" & lID.ToString
Label5.Text = "Amount Of Menu Items : " & total
'SendMessage(lhWnd, WM_COMMAND, lID, 0&)
PostMessage(lhWnd, WM_COMMAND, lID, 0&)
End Sub
End Class
Re: windows API Help - IPC (clicking toolbar button)
Just to throw another option your way, are these new OS builds Vista, or are you still using an older operating system like XP?
Vista and up has a complete UI automation API that would likely make this much easier
http://msdn.microsoft.com/en-us/library/ms747327.aspx
Re: windows API Help - IPC (clicking toolbar button)
Quote:
Originally Posted by
appolospb
Lol . In other words 'I dont know how to answer your question'.
No I dont know how to do it via API, I rarely use APIs. How terrible of me.