4

假设我打开了多个 chrome 窗口(不是选项卡),
我如何检查浏览器标题?

我尝试了以下方法:

Process[] p = Process.GetProcessesByName("chrome");

foreach (Process item in p)
{
    Console.WriteLine(item.MainWindowTitle);
}

但它只返回最后一个打开的窗口名称,所有其他都是空白..

4

4 回答 4

7

我不得不做这样的事情,但是涉及调用 Windows API 函数的过程非常繁琐。问题是 Chrome 似乎对多个窗口使用单个进程或其他一些奇怪的东西,这意味着简单的方法对我不起作用。

无论如何,试试这个,看看它是否有效。基本上,它使用 Chrome 窗口类名(可能是Chrome_WidgetWin_0Chrome_WidgetWin_1)来枚举具有该类名的所有窗口,并返回那些不为空的窗口标题。

请注意,这也总是返回一个"Chrome App Launcher"由于某种原因调用的窗口标题,因此您可能需要将其过滤掉。

注意: 您也可以使用“MozillaWindowClass”对 Firefox 执行此操作,使用“IEFrame”对 IE 执行此操作(尽管其中任何一个都可能随不同版本而改变)。

using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Runtime.InteropServices;
using System.Security;
using System.Text;

namespace Demo
{
    class WindowsByClassFinder
    {
        public delegate bool EnumWindowsDelegate(IntPtr hWnd, IntPtr lparam);

        [SuppressMessage("Microsoft.Security", "CA2118:ReviewSuppressUnmanagedCodeSecurityUsage"), SuppressUnmanagedCodeSecurity]
        [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
        public static extern int GetClassName(IntPtr hWnd, StringBuilder lpClassName, int nMaxCount);

        [SuppressMessage("Microsoft.Security", "CA2118:ReviewSuppressUnmanagedCodeSecurityUsage"), SuppressUnmanagedCodeSecurity]
        [DllImport("user32.dll")]
        [return: MarshalAs(UnmanagedType.Bool)]
        public extern static bool EnumWindows(EnumWindowsDelegate lpEnumFunc, IntPtr lparam);

        [SuppressMessage("Microsoft.Security", "CA2118:ReviewSuppressUnmanagedCodeSecurityUsage"), SuppressUnmanagedCodeSecurity]
        [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);


        /// <summary>Find the windows matching the specified class name.</summary>

        public static IEnumerable<IntPtr> WindowsMatching(string className)
        {
            return new WindowsByClassFinder(className)._result;
        }

        private WindowsByClassFinder(string className)
        {
            _className = className;
            EnumWindows(callback, IntPtr.Zero);
        }

        private bool callback(IntPtr hWnd, IntPtr lparam)
        {
            if (GetClassName(hWnd, _apiResult, _apiResult.Capacity) != 0)
            {
                if (string.CompareOrdinal(_apiResult.ToString(), _className) == 0)
                {
                    _result.Add(hWnd);
                }
            }

            return true; // Keep enumerating.
        }

        public static IEnumerable<string> WindowTitlesForClass(string className)
        {
            foreach (var windowHandle in WindowsMatchingClassName(className))
            {
                int length = GetWindowTextLength(windowHandle);
                StringBuilder sb = new StringBuilder(length + 1);
                GetWindowText(windowHandle, sb, sb.Capacity);
                yield return sb.ToString();
            }
        }

        public static IEnumerable<IntPtr> WindowsMatchingClassName(string className)
        {
            if (string.IsNullOrWhiteSpace(className))
                throw new ArgumentOutOfRangeException("className", className, "className can't be null or blank.");

            return WindowsMatching(className);
        }

        private readonly string _className;
        private readonly List<IntPtr> _result = new List<IntPtr>();
        private readonly StringBuilder _apiResult = new StringBuilder(1024);
    }

    class Program
    {
        void run()
        {
            ChromeWindowTitles().Print();
        }

        public IEnumerable<string> ChromeWindowTitles()
        {
            foreach (var title in WindowsByClassFinder.WindowTitlesForClass("Chrome_WidgetWin_0"))
                if (!string.IsNullOrWhiteSpace(title))
                    yield return title;

            foreach (var title in WindowsByClassFinder.WindowTitlesForClass("Chrome_WidgetWin_1"))
                if (!string.IsNullOrWhiteSpace(title))
                    yield return title;
        }

        static void Main()
        {
            new Program().run();
        }
    }

    static class DemoUtil
    {
        public static void Print(this object self)
        {
            Console.WriteLine(self);
        }

        public static void Print(this string self)
        {
            Console.WriteLine(self);
        }

        public static void Print<T>(this IEnumerable<T> self)
        {
            foreach (var item in self)
                Console.WriteLine(item);
        }
    }
}
于 2013-06-06T09:43:52.440 回答
5

我知道这已经得到解答,但我也提出了一个解决方案,它在一个线程中枚举所有 Windows。

它是根据 Matthew Watson 的解决方案构建的,因此有一些相似之处。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Diagnostics;
using System.Runtime.InteropServices;

namespace Chrome_Windows
{
    class Program
    {
        [DllImport("user32.dll")]
        private static extern bool EnumThreadWindows(uint dwThreadId, EnumThreadDelegate lpfn, IntPtr lParam);

        [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
        private static extern int GetClassName(IntPtr hWnd, StringBuilder lpClassName, int nMaxCount);

        [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 List<IntPtr> windowList;
        private static string _className;
        private static StringBuilder apiResult = new StringBuilder(256); //256 Is max class name length.
        private delegate bool EnumThreadDelegate(IntPtr hWnd, IntPtr lParam);

        static void Main(string[] args) 
        {
            List<IntPtr> ChromeWindows = WindowsFinder("Chrome_WidgetWin_1", "chrome");
            foreach (IntPtr windowHandle in ChromeWindows) 
            {
                int length = GetWindowTextLength(windowHandle);
                StringBuilder sb = new StringBuilder(length + 1);
                GetWindowText(windowHandle, sb, sb.Capacity);
                Console.WriteLine(sb.ToString());
            }
        }

        private static List<IntPtr> WindowsFinder(string className, string process)
        {
            _className = className;
            windowList = new List<IntPtr>();

            Process[] chromeList = Process.GetProcessesByName(process);

            if (chromeList.Length > 0)
            {
                foreach (Process chrome in chromeList)
                {
                    if (chrome.MainWindowHandle != IntPtr.Zero)
                    {
                        foreach (ProcessThread thread in chrome.Threads)
                        {
                            EnumThreadWindows((uint)thread.Id, new EnumThreadDelegate(EnumThreadCallback), IntPtr.Zero);
                        }
                    }
                }
            }

            return windowList;
        }

        static bool EnumThreadCallback(IntPtr hWnd, IntPtr lParam)
        {
            if (GetClassName(hWnd, apiResult, apiResult.Capacity) != 0)
            {
                if (string.CompareOrdinal(apiResult.ToString(), _className) == 0)
                {
                    windowList.Add(hWnd);
                }
            }
            return true;
        }   
    }
}
于 2014-08-15T13:54:39.217 回答
0

我知道这是一个旧线程,但我已经找到了答案,至少对于我的用例来说。我也想按标题查找所有打开的 chrome 窗口/选项卡,但就我而言,我想关闭我找到的包含 x Title 的窗口/选项卡。读完icbytesdor-cohen上面的帖子我意识到我可以通过多次调用 Process.GetProcessesByName() 来实现我所需要的。进行此调用时,您确实会获得所有正在运行的 chrome 进程的数组,但只有一个实例将包含 MainWindowTitle 的值。由于几个原因,这有点烦人。您可以使用“活动”“显示的选项卡”打开多个 chrome 会话,但该调用仍然只返回一个 chrome proc 数组,该数组中只有一个实例具有 MainWindowTitle 的值。同样,我的解决方案不一定是 OP 的意图,因为他只是想列出标题。我的解决方案想要关闭每个找到的标题。

我所做的如下:

一旦我找到第一个带有我正在寻找的标题的 chrome 进程,我就会在该进程上调用 CloseMainWindow()。不要调用 Kill() ,因为它会使浏览器完全崩溃。我只是在这里关闭活动或顶级窗口。我在下面发布我的代码。我希望这对其他人有帮助!谢谢!

        bool foundAll = false;
        do
        {
            bool foundOne = false;
            procs = Process.GetProcessesByName("chrome");

            foreach (Process p in procs)
            {
                if (p.MainWindowTitle.Length > 0)
                {
                    string t = p.MainWindowTitle.Replace(" - Google Chrome", "");
                    if (t.ToLower().Contains(this.BrowserTabText.ToLower()))
                    {
                        foundOne = true;
                        this.WriteEventLogEntry($"Found Tab Title: {this.BrowserTabText} with PID: {p.Id}.  \r\nWe will close it.", EventLogEntryType.Information);
                        p.CloseMainWindow();
                        break;
                    }
                }
            }
            if (!foundOne)
            {
                foundAll = true;
            }
        } while (!foundAll);
于 2020-08-28T13:09:31.273 回答
-2

您必须获得进程列表。

遍历列表,仅在名称为“chrome”的地方。

这将允许您获得所有头衔。

因为如果你有不止一个 chrome 进程,你的调用只会给你一个,因为你只调用一次。

它返回的可能是另一个问题。在你的情况下,它是最后一个。

于 2013-06-06T09:21:00.453 回答