0

I want to have a tool that provide a Alt + Tab like function, but instead of switch between all of opening windows, I want to narrow them down to 1 specific program only (e.g. firefox.exe).

All I could think of is using GetWindowText to get a list of windows that contain "Mozilla Firefox" in their titles, and then ShowWindowAsync to show them up, but it doesn't seem to work well if some other opening windows also have this phrase "Mozilla Firefox" in their titles.

Is there any better solution? Here is the code:

 /// <summary>Contains functionality to get all the open windows.</summary>
public static class OpenWindowGetter
{
    /// <summary>Returns a dictionary that contains the handle and title of all the open windows.</summary>
    /// <returns>A dictionary that contains the handle and title of all the open windows.</returns>
    public static IDictionary<HWND, string> GetOpenWindows()
    {
        HWND shellWindow = GetShellWindow();
        Dictionary<HWND, string> windows = new Dictionary<HWND, string>();

        EnumWindows(delegate(HWND hWnd, int lParam)
        {
            if (hWnd == shellWindow) return true;
            if (!IsWindowVisible(hWnd)) return true;

            int length = GetWindowTextLength(hWnd);
            if (length == 0) return true;

            StringBuilder builder = new StringBuilder(length);
            GetWindowText(hWnd, builder, length + 1);

            windows[hWnd] = builder.ToString();
            return true;

        }, 0);

        return windows;
    }

    private delegate bool EnumWindowsProc(HWND hWnd, int lParam);

    [DllImport("USER32.DLL")]
    private static extern bool EnumWindows(EnumWindowsProc enumFunc, int lParam);

    [DllImport("USER32.DLL")]
    private static extern int GetWindowText(HWND hWnd, StringBuilder lpString, int nMaxCount);

    [DllImport("USER32.DLL")]
    private static extern int GetWindowTextLength(HWND hWnd);

    [DllImport("USER32.DLL")]
    private static extern bool IsWindowVisible(HWND hWnd);

    [DllImport("USER32.DLL")]
    private static extern IntPtr GetShellWindow();
}

This is how I use it:

 /// <summary>
    /// Get a window handle by a title
    /// </summary>
    /// <param name="windowTitle"></param>
    /// <param name="wildCard">match if window title contains input windowTitle</param>
    /// <returns></returns>
    public static int GetWindowHandle(string windowTitle, bool wildCard = false)
    {
        var processList = OpenWindowGetter.GetOpenWindows();
        foreach (var process in processList)
        {
            if ((wildCard && process.Value.ToLower().Contains(windowTitle.ToLower())) //Find window by wildcard
                || !wildCard && process.Value.Equals(windowTitle))  //Find window with exact name
            {
                int a = (int)process.Key;
                return a;
            }
        }

        return 0;
    }
FlySoFast
  • 1,854
  • 6
  • 26
  • 47
  • 2
    Different (top-level) windows of an application usually share the same window class. Call [GetClassName](https://msdn.microsoft.com/en-us/library/windows/desktop/ms633582.aspx) in your `EnumWindowProc` callback to filter on the class name. – IInspectable Dec 18 '15 at 02:16
  • Only you can decide on your criteria. What are they? Once you decide, use EnumWindows to get top level windows – David Heffernan Dec 18 '15 at 07:59

2 Answers2

1

Searching for processes can be done with the System.Diagnostics.Process.GetProcessesByName() method. You can get more details about that function here: https://msdn.microsoft.com/en-us/library/z3w4xdc9(v=vs.110).aspx

The function below accepts the name of a process, such as "firefox.exe" and returns the Window handle of the main window of the first matching process which has a window.
(A process can run without a main window, like services or console applications, etc...)

public static IntPtr GetMainWindowHandle(string ProcessName)
{
    Process[] ProcessList = Process.GetProcessesByName(ProcessName);

    foreach (Process ThisProcess in ProcessList)
    {
        if (ThisProcess.MainWindowHandle != IntPtr.Zero)
        {
            return ThisProcess.MainWindowHandle;
        }
    }

    return IntPtr.Zero;
}

There may be multiple instances of the same process already open, so you might want to utilize the resulting ProcessList more directly. Also, there are many functions built into the Process class that you may find helpful.

UPDATE (Per your comment questions):
All the Chrome process's do not equal a different Chrome tab. Instead each Chrome process is actually a separate web-app, plug-in or Tab processing engine.
Google separates each web-app, plug-in and tab background process into their own Windows process to protect the main process in case the plug-in crashes. This prevents a crashed plug-in from affecting the main Chrome application or any other tabs.
Also it has the benefit to let the OS manage parallel multitasking.
You can see each in the Chrome Task Manager by hitting "Shift-ESC" while in Chrome. You can read more about this here: http://blog.chromium.org/2008/09/multi-process-architecture.html

Chrome manages each tab window within it's parent process as separate Top Level windows. Instead of looking through all the PROCESS's you original idea is the best solution, to look through all WINDOWS. By calling SwitchToAllChromeWinows() in my example below, it iterates through all windows and switches to any the have the text "Google Chrome" However, there might be other windows that have that text in their title. Also, this switches to them all and brings them all to into focus one at a time. If you're looking for a specific one, you can limit your search string to be more specific.

public delegate bool Win32Callback(IntPtr hwnd, IntPtr lParam);

[DllImport("user32.dll")]
protected static extern bool EnumWindows(Win32Callback enumProc, IntPtr lParam);

private static bool EnumWindow(IntPtr handle, IntPtr pointer)
{
    List<IntPtr> pointers = GCHandle.FromIntPtr(pointer).Target as List<IntPtr>;
    pointers.Add(handle);
    return true;
}

private static List<IntPtr> GetAllWindows()
{
    Win32Callback enumCallback = new Win32Callback(EnumWindow);
    List<IntPtr> AllWindowPtrs = new List<IntPtr>();
    GCHandle listHandle = GCHandle.Alloc(AllWindowPtrs);
    try
    {
        EnumWindows(enumCallback, GCHandle.ToIntPtr(listHandle));
    }
    finally
    {
        if (listHandle.IsAllocated) 
            listHandle.Free();
    }
    return AllWindowPtrs;
}

[DllImport("User32", CharSet = CharSet.Auto, SetLastError = true)]
public static extern int GetWindowText(IntPtr windowHandle, StringBuilder stringBuilder, int nMaxCount);

[DllImport("user32.dll", EntryPoint = "GetWindowTextLength", SetLastError = true)]
internal static extern int GetWindowTextLength(IntPtr hwnd);
private static string GetTitle(IntPtr handle)
{
    int length = GetWindowTextLength(handle);
    StringBuilder sb = new StringBuilder(length + 1);
    GetWindowText(handle, sb, sb.Capacity);
    return sb.ToString();
}

[System.Runtime.InteropServices.DllImport("user32.dll")]
public static extern void SwitchToThisWindow(IntPtr hWnd, bool fAltTab);

private static void SwitchToAllChromeWinows()
{
    List<IntPtr> AllWindowsPtrs = GetAllWindows();
    foreach (IntPtr ThisWindowPtr in AllWindowsPtrs)
    {
        if (GetTitle(ThisWindowPtr).Contains("Google Chrome") == true)
        {
            SwitchToThisWindow(ThisWindowPtr, true);
        }
    }
}

UPDATE:
If you are working with Chrome only or want a more direct way, then you could use Google's Chrome API's to directly manage the Tabs:
http://code.google.com/chrome/extensions/windows.html#method-getAll http://code.google.com/chrome/extensions/tabs.html#method-getAllInWindow

Moon
  • 1,141
  • 2
  • 11
  • 25
  • 2
    A process can have multiple top level windows – David Heffernan Dec 18 '15 at 07:58
  • Thanks, this may be enough for my case. – FlySoFast Dec 19 '15 at 01:22
  • Great, I hope so. Once getting a valid windows handle, if you want the top parent window in that process, then iterate up the parent window chain until you get the top one. Let me know if you need help. – Moon Dec 19 '15 at 01:25
  • Seems like there's always only 1 process in the list with its handle is not zero, although I have several Chrome windows opened. What did I miss? – FlySoFast Dec 19 '15 at 10:17
  • In Chrome, the MainWIndowHandle is set to the window with the most recently activated tab. For example, I just opened 2 Chrome tabs in the same window and a third tab in a separate window. Depending on which tab I had most recently activated, the MainWindowHandle is the same for the first two and different when the third tab when it was the most recently activated. I then called: `[System.Runtime.InteropServices.DllImport("user32.dll")] public static extern void SwitchToThisWindow(IntPtr hWnd, bool fAltTab);` and it switched great. If you have a different goal, please specify. – Moon Dec 19 '15 at 13:33
  • If you're wanting a specific tab, then these other questions may help you better: [Getting the current tab's URL from Google Chrome using C#](http://stackoverflow.com/questions/18897070/getting-the-current-tabs-url-from-google-chrome-using-c-sharp) [How can I get URLs of open pages from Chrome and Firefox?](http://stackoverflow.com/questions/7814027/how-can-i-get-urls-of-open-pages-from-chrome-and-firefox) However, I think both have solutions that get the `MainWindowHandle` as I described in the answer above. – Moon Dec 19 '15 at 18:32
  • I only have 1 tab per window, and problem is I cannot get all of their handle to switch to. For instance: I opened about 5 windows of Chrome, with 1 tab each window, then in the GetProcessesByName function, I got 17 processes in the list, but only 1 of them has the MainWindowHandle different than 0. How can I get the handles of every opening Chrome window, so I can switch to each one of them? – FlySoFast Dec 20 '15 at 07:05
  • I updated the answer to include searching for chrome windows and NOT process's – Moon Dec 20 '15 at 16:56
  • See the most recent update to the answer, there are Google Chrome API's to directly manipulate their tabs. http://code.google.com/chrome/extensions/windows.html#method-getAll http://code.google.com/chrome/extensions/tabs.html#method-getAllInWindow – Moon Dec 21 '15 at 02:03
  • THANK YOU SO MUCH. However, I wasn't really into Chrome or any particular program. I updated an answer based on your answer, which is enough for my case. – FlySoFast Dec 21 '15 at 14:07
0

This is what did it for me:

 [DllImport("user32.dll")]
static extern bool EnumThreadWindows(int dwThreadId, EnumThreadDelegate lpfn, IntPtr lParam);
[DllImport("user32.dll")]
private static extern bool IsWindowVisible(int hWnd);
[DllImport("user32.dll", CharSet = CharSet.Auto)]
private static extern int GetWindowText(int hWnd, StringBuilder title, int size);


delegate bool EnumThreadDelegate(IntPtr hWnd, IntPtr lParam);

public List<IntPtr> GetMainWindowHandle()
{
    var list = Process.GetProcessesByName(ProcessName);

    List<IntPtr> windowList = new List<IntPtr>();

    foreach (Process process in list)
    {
        if (process.MainWindowHandle != IntPtr.Zero)
        //windowList.Add(ThisProcess.MainWindowHandle);
        {
            foreach (ProcessThread processThread in process.Threads)
            {
                EnumThreadWindows(processThread.Id,
                 (hWnd, lParam) =>
                 {
                     //Check if Window is Visible or not.
                     if (!IsWindowVisible((int)hWnd))
                         return true;

                     //Get the Window's Title.
                     StringBuilder title = new StringBuilder(256);
                     GetWindowText((int)hWnd, title, 256);

                     //Check if Window has Title.
                     if (title.Length == 0)
                         return true;

                     windowList.Add(hWnd);

                     return true;
                 }, IntPtr.Zero);
            }
        }
    }

    return windowList;
}
FlySoFast
  • 1,854
  • 6
  • 26
  • 47