There is a dialog box, the handle of which I caught, also found a handle belonging to ToolbarWindow32, I can’t click on one of 4 buttons in the toolbar.

private void Button_Click(object sender, RoutedEventArgs e) { IntPtr ptr = WinAPI.FindWindow(null, "Выберите файл"); IntPtr hWndtoolBar = FindWindowByIndex(ptr, 1, "ToolbarWindow32"); // тут пытаюсь достучаться до toolbar var but = WinAPI.SendMessage(hWndtoolBar, (int)WinAPI.GetWindow_Cmd.TB_BUTTONCOUNT, 0, 0); // сообщение принято but = 4 TBBUTTON Button = new TBBUTTON(); WinAPI.SendMessage(hWndtoolBar, (int)WinAPI.GetWindow_Cmd.TB_GETBUTTON, 1, Button); // так пытался заполнить структуру WinAPI.SendMessage(hWndtoolBar, (int)(WinAPI.GetWindow_Cmd.WM_USER + 3), 2, 0); // так пытался нажать на кнопку } public struct TBBUTTON { public long iBitmap; public long idCommand; public byte fsState; public byte fsStyle; public long dwData; public long iString; } 

Alas, the buttons refuse to be pressed. I found some material on VBA, but I can't transfer the code to C #. It was possible to transfer only a part. Found information here: 1 source , 2 source
Unfortunately, I just can’t figure out how I can get the button pressed using the C # code. If anyone can tell, I will be grateful! UDP:

I tried clicking on the button like this:

 public static void MouseClick(IntPtr hWnd, MouseButton btn, int x, int y) { int xyPoint = NativeMacros.MAKELONG(x, y); int wParam = GetWParamFromButton(btn); SendMessage(hWnd, (int) btn, wParam, xyPoint); SendMessage(hWnd, (int) btn + 1, wParam, xyPoint); } private static int GetWParamFromButton(MouseButton btn) { switch (btn) { case MouseButton.Left: return 0x0001; case MouseButton.Middle: return 0x0010; case MouseButton.Right: return 0x0002; default: throw new Win32Exception("Невозможно преобразовать значение!"); } } public static int MAKELONG(int x, int y) { return (x & 0xffff) | ((y & 0xffff) << 16); } WinAPI.MouseClick((IntPtr)ptr, MouseButton.Left, 47, 441);// добавил в код 

Of course I can mess up with the coordinates because I added an image in paint and looked at the numbers of the pixels, but not the same .. I tried to press the usual buttons in this way, nothing happened. It was possible to press the button not from the toolbar only by catching the handle of the button itself. Tulbarovsky all can not be.

  • You mean to track the coordinates of the buttons and send a mouse click on these coordinates? I actually thought about this option, but did not try hoping to do it via TB_PRESSBUTTON. The question is not closed yet, but I still want to realize it in the first way ... UPD: I looked, I found out that I had sent a mouse click, but to the toolbar, but I didn’t say a reply ... - Ja_Dim
  • Okay, I'll try - Ja_Dim
  • I supplemented the information on attempts, maybe I didn’t use your advice correctly. If so, please tell me where to dig .. - Ja_Dim
  • Why I immediately called the heaps of code, indicated the coordinates, in the end if I specify the handle of the window, then I don’t get to any button coordinates, and even if I hit it it doesn’t work, the situation with the handle of the button itself is different, everything works fine, but it does not solve the problem. The last line was informative, it did not work. - Ja_Dim

2 answers 2

It is much easier to do the same with UI Automation .

 using System.Windows.Automation; //.... var runWindow = AutomationElement.FromHandle(hWndtoolBar); var list = runWindow.FindAll(TreeScope.Subtree, new PropertyCondition(AutomationElement.ControlTypeProperty, ControlType.Button)); if (list.Count > 1) { var button = list[1]; // выбираем вторую кнопку if (button.Current.IsEnabled) { var invokePattern = button.GetCurrentPattern(InvokePattern.Pattern) as InvokePattern; invokePattern.Invoke(); } } 

Do not forget to connect to the project Reference: UIAutomationClient, UIAutomationTypes.


If you use your approach, you must first obtain the coordinates of the button inside the toolbar window. For this, there is a TB_GETITEMRECT message. It requires an input pointer. But you can't just pass an ordinary pointer, because are in the address space of another process. You need to allocate memory in the address space of the target process.

Then send WM_LBUTTONDOWN and WM_LBUTTONUP (emulate a click). You can still try TB_PRESSBUTTON, but it works since Vista.

  [DllImport("user32.dll")] public static extern int GetWindowThreadProcessId(IntPtr handle, out int processId); [DllImport("kernel32.dll")] public static extern IntPtr OpenProcess( ProcessAccessFlags processAccess, bool bInheritHandle, int processId ); [DllImport("user32.dll")] public static extern int SendMessage(IntPtr hWnd, int wMsg, IntPtr wParam, IntPtr lParam); [DllImport("user32.dll", SetLastError = true)] static extern bool PostMessage(IntPtr hWnd, int Msg, IntPtr wParam, IntPtr lParam); [DllImport("kernel32.dll", SetLastError = true)] static extern Int32 ReadProcessMemory(IntPtr hProcess, IntPtr lpBaseAddress, [Out] byte[] buffer, UInt32 size, out IntPtr lpNumberOfBytesRead); [DllImport("kernel32.dll", SetLastError = true, ExactSpelling = true)] static extern IntPtr VirtualAllocEx(IntPtr hProcess, IntPtr lpAddress, uint dwSize, AllocationType flAllocationType, MemoryProtection flProtect); [DllImport("kernel32.dll")] public static extern bool VirtualFreeEx(IntPtr hProcess, IntPtr lpAddress, uint dwSize, uint dwFreeType); [DllImport("kernel32.dll")] public static extern bool CloseHandle(IntPtr handle); public enum VirtualFreeExTypes : uint { MEM_DECOMMIT = 0x4000, MEM_RELEASE = 0x8000 } [Flags] public enum AllocationType { Commit = 0x1000, Reserve = 0x2000, Decommit = 0x4000, Release = 0x8000, Reset = 0x80000, Physical = 0x400000, TopDown = 0x100000, WriteWatch = 0x200000, LargePages = 0x20000000 } [Flags] public enum MemoryProtection { Execute = 0x10, ExecuteRead = 0x20, ExecuteReadWrite = 0x40, ExecuteWriteCopy = 0x80, NoAccess = 0x01, ReadOnly = 0x02, ReadWrite = 0x04, WriteCopy = 0x08, GuardModifierflag = 0x100, NoCacheModifierflag = 0x200, WriteCombineModifierflag = 0x400 } [Flags] public enum ProcessAccessFlags : uint { All = 0x001F0FFF, Terminate = 0x00000001, CreateThread = 0x00000002, VirtualMemoryOperation = 0x00000008, VirtualMemoryRead = 0x00000010, VirtualMemoryWrite = 0x00000020, DuplicateHandle = 0x00000040, CreateProcess = 0x000000080, SetQuota = 0x00000100, SetInformation = 0x00000200, QueryInformation = 0x00000400, QueryLimitedInformation = 0x00001000, Synchronize = 0x00100000 } [StructLayout(LayoutKind.Sequential)] public struct RECT { public int Left, Top, Right, Bottom; } const int WM_USER = 0x0400; const int TB_GETITEMRECT = (WM_USER + 29); const int WM_LBUTTONDOWN = 0x0201; const int WM_LBUTTONUP = 0x0202; public static int MAKELONG(int x, int y) { return (x & 0xffff) | ((y & 0xffff) << 16); } public static RECT ByteArrayToRect(byte[] bytes) { GCHandle handle = GCHandle.Alloc(bytes, GCHandleType.Pinned); RECT stuff = (RECT)Marshal.PtrToStructure( handle.AddrOfPinnedObject(), typeof(RECT)); handle.Free(); return stuff; } static void Main(string[] args) { IntPtr ptr = WinAPI.FindWindow(null, "Выберите файл"); IntPtr window = FindWindowByIndex(ptr, 1, "ToolbarWindow32"); IntPtr nNumberOfBytesRead; int dwProcessID; GetWindowThreadProcessId(window, out dwProcessID); IntPtr hProcess = OpenProcess((ProcessAccessFlags.VirtualMemoryOperation | ProcessAccessFlags.VirtualMemoryRead | ProcessAccessFlags.VirtualMemoryWrite), false, dwProcessID); IntPtr Pointer = VirtualAllocEx(hProcess, (IntPtr)0, 4096, AllocationType.Reserve | AllocationType.Commit, MemoryProtection.ReadWrite); RECT bi; SendMessage(window, TB_GETITEMRECT, (IntPtr)1 /* индекс кнопки*/, (IntPtr)Pointer); byte[] buffer = new byte[16]; ReadProcessMemory(hProcess, (Pointer), buffer, (uint)buffer.Length, out nNumberOfBytesRead); bi = ByteArrayToRect(buffer); PostMessage(window, WM_LBUTTONDOWN, (IntPtr)0, (IntPtr)MAKELONG(bi.Left, bi.Top)); PostMessage(window, WM_LBUTTONUP, (IntPtr)0, (IntPtr)MAKELONG(bi.Left, bi.Top)); VirtualFreeEx(hProcess, Pointer, 0, (uint)VirtualFreeExTypes.MEM_RELEASE); CloseHandle(hProcess); } 
  • Wow! - VladD
  • @VladD what exactly caused your surprise? - zenden2k
  • Well, for example, working with the memory of another process :) Now in C #! (I kind of expected the first part of the answer, but not the second.) - VladD
  • @VladD the most difficult thing was to translate 10 lines from C ++ to C #, where every structure and function of Winapi should be declared. True, not enough error checking, but I think the author will cope. gist.github.com/zenden2k/3d577f72627fe2430940 - zenden2k
  • @ zenden2k Thank you very much for the information, it is a pity that the answer was received only yesterday. In fact, I found answers related to UI Automation and explanations, and interaction code with another process, but could not translate it into C #. I solved the task by sending messages with pressing the keys "Tab", "Space", "Return", "Up", "Down", found the handle from the ToolBar and SystemListView. Soon I will post as one of the solutions. - Ja_Dim

In general, the solution turned out to be simpler than implementing calls to another process and, of course, spent less time than rewriting the existing code in the UI Automation format. The solution, as I already wrote, is as follows: sending messages with pressing the function keys.

 IntPtr ptr = WinAPI.FindWindow(null, "Выберите файл"); IntPtr hWndtoolBar = FindWindowByIndex(ptr, 1, "ToolbarWindow32"); // потребовалось установить фокус в тулбар (также и с любым другим элементом) WinAPI.SendMessage(hWndtoolBar, (int)(WinAPI.GetWindow_Cmd.WM_SETFOCUS), 0, 0); Thread.Sleep(2500); //приходится приостанавливать главный поток, ибо пока не произойдут //изменения в UI, бесполезно посылать какие либо сообщения. // вот такая обертка для SysListView32 попалась мне IntPtr hWndSHELLDLL = FindWindowByIndex(ptr, 1, "SHELLDLL_DefView"); IntPtr hWndListView = FindWindowByIndex(hWndSHELLDLL, 1, "SysListView32"); Thread.Sleep(2500); WinAPI.SendMessage(hWndListView, (int)(WinAPI.GetWindow_Cmd.WM_SETFOCUS), 0, 0); WinAPI.SendMessage(hWndListView, (int)(WinAPI.GetWindow_Cmd.WM_KEYDOWN), 40, 0); 

further in the same vein.
Commands:

 public enum GetWindow_Cmd : uint { WM_KEYDOWN = 256, WM_KEYUP = 257, WM_SETFOCUS = 7 } 

I hope it will be useful to someone)
PS I used Window Detective to view components, it seemed to me more convenient than Spy ++.