I would like to show a WinForms MessageBox centered on top of another application's (TortoiseGit) "sub"-form, i.e. a form (the TortoiseGit checkout form) on top of the main form (the TortoiseGit log window). I am able to successfully find the process (TortoiseGitProc) and from there I can successfully get a MainWindowHandle to the main form, but how do get from there to the "sub"-form (the TortoiseGit checkout form)?
List<IntPtr> allChildWindows = new WindowHandleInfo(parentMainWindowHandle).GetAllChildHandles();
for (int i = 0; i < allChildWindows.Count; i++)
StringBuilder sb2 = new StringBuilder(1024);
GetClassName(allChildWindows[i], sb2, sb2.Capacity);
sb2 = new StringBuilder(1024);
GetWindowText(allChildWindows[i], sb2, sb2.Capacity);
then I get mostly empty strings, but I do get some strings that appears to come from the main form's (not the "sub"-form's) components (buttons, labels, etc), e.g. "<all branches="">", "Help", "Refresh", "Walk Be&haviour", "&View", "OK", etc. What am I doing wrong, why do I get pointers to the main form's components and not to the "sub"-form?
So, it seems the Switch/Checkout window is not a child or a "sub" form of the Log Message window, they certainly seem to be on the same level (both are "sibling parents"?). So, I guess I'm back to square one. Does anybody know how I can find the Switch/Checkout window instead of the Log Message window? When I obtain a MainWindowHandle from the process name (TortoiseGitProc) it returns a pointer to the Log Message window. Does anybody know what I should do?
It would be ok if I can find both, but then I would need to somehow obtain the heading strings ("C:\DummyRepo - Log Messages - TortoiseGit" and "C:\DummyRepo - Switch/Checkout - TortoiseGit") so that I have a way to distinguish which window (or actually, dialog) is which.
Instead of using Process.MainWindowHandle (which only returns the "C:\DummyRepo - Log Messages - TortoiseGit" window) I guess I could iterate through all windows using the technique described in Winforms-How can I make MessageBox appear centered on MainForm? - Stack Overflow[^]. However, when I do so and log the window texts, I never find the strings "C:\DummyRepo - Log Messages - TortoiseGit" and "C:\DummyRepo - Switch/Checkout - TortoiseGit":
[DllImport("user32.dll", EntryPoint = "GetWindowText",
ExactSpelling = false, CharSet = CharSet.Auto, SetLastError = true)]
publicstaticexternint GetWindowText(IntPtr hWnd, StringBuilder lpWindowText, int nMaxCount);
privatebool checkWindow(IntPtr hWnd, IntPtr lp)
// Checks if <hWnd> is a dialog
StringBuilder sb = new StringBuilder(260);
GetClassName(hWnd, sb, sb.Capacity);
// My added code to log all window texts
StringBuilder strbTitle = new StringBuilder(255);
int nLength = GetWindowText(hWnd, strbTitle, strbTitle.Capacity + 1);
string strTitle = strbTitle.ToString();
Debug.WriteLine("The window text is: " + strTitle);
if (sb.ToString() != "#32770")
returntrue; // My modified code to check all windows
The strings I find appear to be much more low-level, e.g "Form1", "MSCTFIME UI", "Default IME", etc. Does anybody know which function I should use instead to obtain the window headings, "C:\DummyRepo - Log Messages - TortoiseGit" and "C:\DummyRepo - Switch/Checkout - TortoiseGit"?
returntrue; // My modified code to check all windows
on purpose because I wanted to log all window texts. My goals was to find the particular window that has the window text "C:\DummyRepo - Switch/Checkout - TortoiseGit", but all I found was windows having window texts such as "Form1", "MSCTFIME UI" and "Default IME".
because I can't find any window that has the heading "C:\DummyRepo - Switch/Checkout - TortoiseGit" using GetWindowText.
Now, on the other hand, the pure C# (no win32 pinvoke involved) property Process.MainWindowTitle seems to return the proper string, but I can only apply it to the de facto main window ("C:\DummyRepo - Log Messages - TortoiseGit"), not to the "secondary sibling"-window ("C:\DummyRepo - Switch/Checkout - TortoiseGit").
Yes, I stepped it. I first rewrote the method EnumerateProcessWindowHandles according to this:
privatestatic List<IntPtr> handles;
privatestatic IEnumerable<IntPtr> EnumerateProcessWindowHandles(int processId)
handles = new List<IntPtr>();
ProcessThreadCollection processThreadCollection = Process.GetProcessById(processId).Threads;
for (int i = 0; i < processThreadCollection.Count; i++)
ProcessThread thread; // If I put a breakpoint here, the debugger stops 3 times
thread = processThreadCollection[i];
EnumThreadWindows(thread.Id, myDelegate, IntPtr.Zero);
privatestaticbool myDelegate(IntPtr hWnd, IntPtr lParam)
handles.Add(hWnd); // If I put a breakpoint here, then the debugger never stopsreturntrue;
So, something seems to go wrong inside the call to EnumThreadWindows, but that's a Win32 function and I don't know how to put a breakpoint inside it or how to step through it.
It is finding the right bash.exe process (there is only 1 running) and it successfully finds its 3 threads. As you said, the filtering with the substrings happens later and by then there are no handles at all to filter.
Yes, I think it's some kind of command prompt that is standard for Unix systems, but it exists for Windows also. I've seen some of my colleagues (those that prefer typing over interacting with GUI:s) type
When I closed my bash.exe command prompt, I could see that the following processes disappeared: backgroundTaskHost, bash, conhost, git-bash, mintty and RuntimeBroker. When I tried mintty instead of bash, then it worked great! Again, thanks for your help!
Last Visit: 18-Sep-20 16:38 Last Update: 18-Sep-20 16:38