【问题标题】:Switch between windows of a program在程序的窗口之间切换
【发布时间】:2016-03-24 16:14:42
【问题描述】:

我想要一个提供Alt + Tab 类似功能的工具,但我不想在所有打开的窗口之间切换,而是想将它们缩小到仅 1 个特定程序(例如 firefox.exe)。

我能想到的只是使用GetWindowText 来获取标题中包含“Mozilla Firefox”的窗口列表,然后使用ShowWindowAsync 来显示它们,但如果有的话似乎效果不佳其他打开的窗口的标题中也有“Mozilla Firefox”这句话。

有没有更好的解决方案? 代码如下:

 /// <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();
}

我就是这样使用它的:

 /// <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;
    }

【问题讨论】:

  • 一个应用程序的不同(顶级)窗口通常共享同一个窗口类。在您的 EnumWindowProc 回调中调用 GetClassName 以过滤类名。
  • 只有您可以决定您的标准。这些是什么?决定后,使用 EnumWindows 获取顶级窗口

标签: c# winforms winapi


【解决方案1】:

这就是为我做的:

 [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;
}

【讨论】:

    【解决方案2】:

    可以使用System.Diagnostics.Process.GetProcessesByName() 方法搜索进程。您可以在此处获取有关该功能的更多详细信息:https://msdn.microsoft.com/en-us/library/z3w4xdc9(v=vs.110).aspx

    下面的函数接受进程的名称,例如“firefox.exe”,并返回第一个匹配的具有窗口的进程的主窗口的窗口句柄。
    进程可以在没有主窗口的情况下运行,例如服务或控制台应用程序等......

    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;
    }
    

    可能已经打开了同一进程的多个实例,因此您可能希望更直接地利用生成的 ProcessList。此外,Process 类中内置了许多您可能会发现有用的函数。

    更新(根据您的评论问题):
    所有 Chrome 进程不等于不同的 Chrome 选项卡。 相反,每个 Chrome 进程实际上都是一个单独的网络应用程序、插件或 Tab 处理引擎。
    Google 将每个 Web 应用程序、插件和选项卡后台进程分离到自己的 Windows 进程中,以保护主进程以防插件崩溃。 这可以防止崩溃的插件影响主 Chrome 应用程序或任何其他选项卡。
    此外,它还具有让操作系统管理并行多任务处理的好处。
    您可以通过在 Chrome 中按“Shift-ESC”在 Chrome 任务管理器中查看每个。 您可以在此处阅读更多信息:http://blog.chromium.org/2008/09/multi-process-architecture.html

    Chrome 将其父进程中的每个选项卡窗口作为单独的顶级窗口进行管理。 最好的解决方案是浏览所有WINDOWS,而不是浏览所有PROCESS。 通过在下面的示例中调用 SwitchToAllChromeWinows(),它遍历所有窗口并切换到任何具有文本“Google Chrome”的窗口 但是,可能还有其他窗口在其标题中包含该文本。 此外,这会切换到所有这些,并一次将它们全部集中到一个焦点上。 如果您正在寻找一个特定的,您可以将您的搜索字符串限制为更具体。

    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);
            }
        }
    }
    

    更新:
    如果您只使用 Chrome 或想要更直接的方式,那么您可以使用 Google 的 Chrome API 直接管理标签:
    http://code.google.com/chrome/extensions/windows.html#method-getAll http://code.google.com/chrome/extensions/tabs.html#method-getAllInWindow

    【讨论】:

    • 一个进程可以有多个顶层窗口
    • 谢谢,这对我来说可能已经足够了。
    • 太好了,我希望如此。一旦获得有效的窗口句柄,如果您想要该进程中的顶部父窗口,则迭代父窗口链,直到获得顶部。如果您需要帮助,请告诉我。
    • 似乎列表中总是只有 1 个进程的句柄不为零,尽管我打开了几个 Chrome 窗口。我错过了什么?
    • 在 Chrome 中,MainWIndowHandle 设置为最近激活选项卡的窗口。例如,我刚刚在同一个窗口中打开了 2 个 Chrome 选项卡,在单独的窗口中打开了第三个选项卡。根据我最近激活的选项卡,前两个选项卡的 MainWindowHandle 相同,而最近激活的第三个选项卡则不同。然后我打电话给:[System.Runtime.InteropServices.DllImport("user32.dll")] public static extern void SwitchToThisWindow(IntPtr hWnd, bool fAltTab);,它切换得很好。如果您有不同的目标,请说明。
    猜你喜欢
    • 2020-12-02
    • 1970-01-01
    • 1970-01-01
    • 2020-01-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-03-30
    • 2017-07-11
    相关资源
    最近更新 更多