You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

如何在C#中获取Windows任务栏指定Toolbar的屏幕坐标?

解决任务栏指定名称Toolbar的动态定位与点击问题

你遇到的这个痛点我太懂了——硬编码坐标在任务栏控件位置动态变化的场景下完全不靠谱,好在我们可以利用Windows提供的API来精准定位目标Toolbar,下面给你两个经过验证的可行方案,优先推荐C#实现(毕竟你对C++不太熟练):

方案一:使用UI Automation(稳定可靠,推荐)

之前你听说C#的UI Automation接口废弃是误解,现在System.Windows.Automation命名空间的库是完全可用的,它能通过控件的类型、名称等属性精准定位,步骤如下:

  • 定位任务栏主窗口(类名固定为Shell_TrayWnd
  • 遍历任务栏下所有ToolBar类型的子控件
  • 匹配目标Toolbar的名称属性
  • 获取控件的屏幕坐标并模拟点击

完整代码示例

using System;
using System.Windows.Automation;
using System.Runtime.InteropServices;

class TaskbarToolbarClicker
{
    // 模拟鼠标点击的Windows API
    [DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
    public static extern void mouse_event(uint dwFlags, uint dx, uint dy, uint cButtons, uint dwExtraInfo);
    private const uint MOUSEEVENTF_LEFTDOWN = 0x02;
    private const uint MOUSEEVENTF_LEFTUP = 0x04;

    static void Main(string[] args)
    {
        // 替换成你的映射Toolbar的准确名称(可通过Spy++查看Window Text属性)
        string targetToolbarName = "你的映射Toolbar名称";

        // 获取任务栏主窗口句柄
        IntPtr taskbarHandle = FindWindow("Shell_TrayWnd", null);
        if (taskbarHandle == IntPtr.Zero)
        {
            Console.WriteLine("无法找到任务栏窗口");
            return;
        }

        // 转换为UI Automation元素
        AutomationElement taskbarElement = AutomationElement.FromHandle(taskbarHandle);

        // 筛选所有ToolBar类型的控件
        Condition toolbarCondition = new PropertyCondition(AutomationElement.ControlTypeProperty, ControlType.ToolBar);
        AutomationElementCollection toolbars = taskbarElement.FindAll(TreeScope.Descendants, toolbarCondition);

        foreach (AutomationElement toolbar in toolbars)
        {
            string toolbarName = toolbar.Current.Name;
            // 忽略大小写匹配名称
            if (string.Equals(toolbarName, targetToolbarName, StringComparison.OrdinalIgnoreCase))
            {
                // 获取控件的屏幕边界
                Rect boundingRect = toolbar.Current.BoundingRectangle;
                // 计算控件中心作为点击位置
                int clickX = (int)(boundingRect.Left + boundingRect.Width / 2);
                int clickY = (int)(boundingRect.Top + boundingRect.Height / 2);

                // 移动鼠标并执行点击
                SetCursorPos(clickX, clickY);
                mouse_event(MOUSEEVENTF_LEFTDOWN | MOUSEEVENTF_LEFTUP, (uint)clickX, (uint)clickY, 0, 0);

                Console.WriteLine($"已成功点击目标Toolbar:{targetToolbarName}");
                return;
            }
        }

        Console.WriteLine($"未找到名称为「{targetToolbarName}」的Toolbar");
    }

    // 导入其他所需API
    [DllImport("user32.dll", SetLastError = true)]
    private static extern IntPtr FindWindow(string lpClassName, string lpWindowName);

    [DllImport("user32.dll")]
    private static extern bool SetCursorPos(int X, int Y);
}

注意事项

  • 项目需要添加System.Windows.Automation引用(可通过NuGet安装UIAutomationClient包)
  • 如果需要点击Toolbar内的特定按钮,可以在找到Toolbar后,继续枚举其下的ControlType.Button控件,匹配按钮的名称或其他属性后再点击

方案二:使用Windows原生API枚举子窗口(更底层,兼容特殊Toolbar)

如果UI Automation在某些第三方Toolbar上表现不佳,可以直接通过枚举任务栏的子窗口来定位,Spy++里看到的Toolbar类名通常是ToolbarWindow32,我们可以利用这个特征来筛选:

完整代码示例

using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Text;

class TaskbarToolbarFinder
{
    private delegate bool EnumWindowsProc(IntPtr hWnd, IntPtr lParam);

    [DllImport("user32.dll", SetLastError = true)]
    private static extern bool EnumChildWindows(IntPtr hWndParent, EnumWindowsProc lpEnumFunc, IntPtr lParam);

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

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

    [DllImport("user32.dll", SetLastError = true)]
    private static extern bool GetWindowRect(IntPtr hWnd, out RECT lpRect);

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

    [StructLayout(LayoutKind.Sequential)]
    private struct RECT
    {
        public int Left;
        public int Top;
        public int Right;
        public int Bottom;
    }

    static void Main(string[] args)
    {
        string targetToolbarName = "你的映射Toolbar名称";
        IntPtr taskbarHandle = FindWindow("Shell_TrayWnd", null);
        if (taskbarHandle == IntPtr.Zero)
        {
            Console.WriteLine("无法找到任务栏窗口");
            return;
        }

        // 枚举任务栏子窗口,找到目标Toolbar
        EnumChildWindows(taskbarHandle, (hWnd, lParam) =>
        {
            StringBuilder className = new StringBuilder(256);
            GetClassName(hWnd, className, className.Capacity);

            // 筛选ToolbarWindow32类的控件
            if (className.ToString() == "ToolbarWindow32")
            {
                int textLength = GetWindowTextLength(hWnd);
                if (textLength > 0)
                {
                    StringBuilder windowText = new StringBuilder(textLength + 1);
                    GetWindowText(hWnd, windowText, windowText.Capacity);

                    if (string.Equals(windowText.ToString(), targetToolbarName, StringComparison.OrdinalIgnoreCase))
                    {
                        if (GetWindowRect(hWnd, out RECT rect))
                        {
                            int clickX = rect.Left + (rect.Right - rect.Left) / 2;
                            int clickY = rect.Top + (rect.Bottom - rect.Top) / 2;
                            SetCursorPos(clickX, clickY);
                            mouse_event(MOUSEEVENTF_LEFTDOWN | MOUSEEVENTF_LEFTUP, (uint)clickX, (uint)clickY, 0, 0);
                            Console.WriteLine($"已成功点击目标Toolbar:{targetToolbarName}");
                            return false; // 找到后停止枚举
                        }
                    }
                }
            }
            return true;
        }, IntPtr.Zero);
    }

    // 导入模拟点击和窗口定位的API
    [DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
    public static extern void mouse_event(uint dwFlags, uint dx, uint dy, uint cButtons, uint dwExtraInfo);
    private const uint MOUSEEVENTF_LEFTDOWN = 0x02;
    private const uint MOUSEEVENTF_LEFTUP = 0x04;

    [DllImport("user32.dll", SetLastError = true)]
    private static extern IntPtr FindWindow(string lpClassName, string lpWindowName);

    [DllImport("user32.dll")]
    private static extern bool SetCursorPos(int X, int Y);
}

关键说明

  • 可以通过Spy++确认目标Toolbar的类名,如果不是ToolbarWindow32,直接替换代码里的类名字符串即可
  • 该方案不需要额外引用第三方库,仅依赖Windows原生API,兼容性更好

通用注意事项

  1. 名称准确性:一定要通过Spy++获取Toolbar的Window Text属性,确保匹配的名称完全一致(可以用忽略大小写匹配来容错)
  2. 权限问题:如果程序运行时无法定位控件,尝试以管理员权限启动
  3. 按钮精准点击:如果需要点击Toolbar内的小按钮,可以在找到Toolbar后,进一步枚举其下的子控件(方案一用UI Automation找Button,方案二用EnumChildWindows继续枚举Toolbar的子窗口)

内容的提问来源于stack exchange,提问作者Finch

火山引擎 最新活动