Quicker获取窗口标题、进程名、类名、句柄

Quicker 的“脚本动作”中使用 C# 脚本

“点击选窗 → 取标题 → 写入剪贴板”的脚本:

  • 运行后会先弹出提示,让你用鼠标左键点击目标窗口(标题栏或内容区都行);
  • 自动获取该窗口(沿父链/拥有者链提升到顶层)的标题;若为空会回退到进程主窗口标题
  • 把结果写入剪贴板,并弹窗确认。 兼容旧版 C# 语法:不要using Quicker.Public;,入口保持 Exec(object context)
  • 一次性返回:标题/路径(如有)、进程名、类名、句柄(十六进制)
using System;
using System.Text;
using System.Runtime.InteropServices;
using System.Diagnostics;
using System.Threading;

public class Script
{
    [StructLayout(LayoutKind.Sequential)]
    public struct POINT { public int X; public int Y; }

    [DllImport("user32.dll")] static extern short GetAsyncKeyState(int vKey);
    [DllImport("user32.dll")] static extern bool GetCursorPos(out POINT lpPoint);
    [DllImport("user32.dll")] static extern IntPtr WindowFromPoint(POINT Point);
    [DllImport("user32.dll")] static extern bool IsWindow(IntPtr hWnd);
    [DllImport("user32.dll")] static extern bool IsWindowVisible(IntPtr hWnd);
    [DllImport("user32.dll")] static extern IntPtr GetAncestor(IntPtr hWnd, uint gaFlags);
    [DllImport("user32.dll")] static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint processId);
    [DllImport("user32.dll", CharSet=CharSet.Unicode)] static extern int GetWindowText(IntPtr hWnd, StringBuilder text, int count);
    [DllImport("user32.dll", CharSet=CharSet.Unicode)] static extern int GetWindowTextLength(IntPtr hWnd);
    [DllImport("user32.dll", CharSet=CharSet.Unicode)] static extern int GetClassName(IntPtr hWnd, StringBuilder lpClassName, int nMaxCount);
    [DllImport("user32.dll", CharSet=CharSet.Unicode)] static extern int MessageBoxW(IntPtr hWnd, string text, string caption, uint type);

    // 剪贴板 WinAPI
    [DllImport("user32.dll")] static extern bool OpenClipboard(IntPtr hWndNewOwner);
    [DllImport("user32.dll")] static extern bool EmptyClipboard();
    [DllImport("user32.dll")] static extern IntPtr SetClipboardData(uint uFormat, IntPtr hMem);
    [DllImport("user32.dll")] static extern bool CloseClipboard();

    [DllImport("kernel32.dll")] static extern IntPtr GlobalAlloc(uint uFlags, UIntPtr dwBytes);
    [DllImport("kernel32.dll")] static extern IntPtr GlobalLock(IntPtr hMem);
    [DllImport("kernel32.dll")] static extern bool GlobalUnlock(IntPtr hMem);
    [DllImport("kernel32.dll")] static extern IntPtr GlobalFree(IntPtr hMem);

    const int VK_LBUTTON = 0x01;
    const uint GA_ROOT = 2;
    const uint GA_ROOTOWNER = 3;
    const uint GMEM_MOVEABLE = 0x0002;
    const uint CF_UNICODETEXT = 13;

    static string GetClassStr(IntPtr h)
    {
        StringBuilder cls = new StringBuilder(256);
        GetClassName(h, cls, cls.Capacity);
        return cls.ToString();
    }

    static string GetTitleStr(IntPtr h)
    {
        int len = GetWindowTextLength(h);
        StringBuilder sb = new StringBuilder(len > 0 ? len + 8 : 512);
        GetWindowText(h, sb, sb.Capacity);
        return sb.ToString();
    }

    static IntPtr PromoteToRootOwner(IntPtr h)
    {
        IntPtr r = GetAncestor(h, GA_ROOTOWNER);
        if (r == IntPtr.Zero) r = GetAncestor(h, GA_ROOT);
        return r == IntPtr.Zero ? h : r;
    }

    static string ProcessNameFromHwnd(IntPtr h)
    {
        uint pid;
        GetWindowThreadProcessId(h, out pid);
        try
        {
            Process p = Process.GetProcessById((int)pid);
            return p != null ? p.ProcessName : "";
        }
        catch { return ""; }
    }

    static string ProcessMainWindowTitleFromHwnd(IntPtr h)
    {
        uint pid;
        GetWindowThreadProcessId(h, out pid);
        try
        {
            Process p = Process.GetProcessById((int)pid);
            return p != null ? p.MainWindowTitle : "";
        }
        catch { return ""; }
    }

    // 带重试的剪贴板写入:最多 10 次,每次间隔 100ms
    static bool TrySetClipboardUnicodeText(string text, int maxRetry, int delayMs)
    {
        byte[] data = Encoding.Unicode.GetBytes(text + "\0");
        UIntPtr size = (UIntPtr)data.Length;

        for (int i = 0; i < maxRetry; i++)
        {
            IntPtr hGlobal = GlobalAlloc(GMEM_MOVEABLE, size);
            if (hGlobal == IntPtr.Zero) return false;

            IntPtr ptr = GlobalLock(hGlobal);
            if (ptr == IntPtr.Zero)
            {
                GlobalFree(hGlobal);
                return false;
            }

            try { Marshal.Copy(data, 0, ptr, data.Length); }
            finally { GlobalUnlock(hGlobal); }

            if (OpenClipboard(IntPtr.Zero))
            {
                try
                {
                    EmptyClipboard();
                    IntPtr setRes = SetClipboardData(CF_UNICODETEXT, hGlobal);
                    if (setRes != IntPtr.Zero)
                    {
                        // 成功后,内存归剪贴板所有,不可再 free
                        return true;
                    }
                    else
                    {
                        // 失败:我们回收内存
                        GlobalFree(hGlobal);
                    }
                }
                finally { CloseClipboard(); }
            }
            else
            {
                // 打不开剪贴板,回收内存,并稍后重试
                GlobalFree(hGlobal);
            }

            Thread.Sleep(delayMs);
        }
        return false;
    }

    public static object Exec(object context)
    {
        // 1) 提示点击
        MessageBoxW(IntPtr.Zero, "请用鼠标左键单击要获取标题的窗口(标题栏或内容区)。\n将复制:标题/路径、进程、类名、句柄。", "获取窗口信息 → 复制", 0);

        // 2) 等待一次左键按下(最多 5 秒)
        int waited = 0;
        while (waited < 5000)
        {
            short state = GetAsyncKeyState(VK_LBUTTON);
            if ((state & 0x8000) != 0) break;
            Thread.Sleep(10);
            waited += 10;
        }
        Thread.Sleep(120); // 等抬起稳定

        // 3) 通过点击点选窗
        POINT pt;
        if (!GetCursorPos(out pt)) { MessageBoxW(IntPtr.Zero, "无法获取鼠标位置", "获取窗口信息 → 复制", 0); return "无法获取鼠标位置"; }

        IntPtr h = WindowFromPoint(pt);
        if (h == IntPtr.Zero || !IsWindow(h) || !IsWindowVisible(h))
        { MessageBoxW(IntPtr.Zero, "未找到目标窗口", "获取窗口信息 → 复制", 0); return "未找到目标窗口"; }

        // 4) 提升到顶层拥有者窗口
        h = PromoteToRootOwner(h);

        // 5) 获取标题,必要时回退到进程主窗口标题
        string title = GetTitleStr(h);
        if (title == null) title = "";
        if (title.Length == 0)
        {
            string t2 = ProcessMainWindowTitleFromHwnd(h);
            if (!string.IsNullOrEmpty(t2)) title = t2;
        }
        if (title.Length == 0) title = "[空标题]";

        // 6) 组装你需要的一整行
        string proc = ProcessNameFromHwnd(h);
        string cls  = GetClassStr(h);
        string hwndHex = "0x" + h.ToInt64().ToString("X");

        string line = "title/path=" + title
                    + " | process=" + proc
                    + " | class=" + cls
                    + " | hwnd=" + hwndHex;

        // 7) 写入剪贴板(带重试)
        bool ok = TrySetClipboardUnicodeText(line, 10, 100);

        // 8) 提示并返回
        if (ok)
            MessageBoxW(IntPtr.Zero, "已复制到剪贴板:\n" + line, "获取窗口信息 → 复制", 0);
        else
            MessageBoxW(IntPtr.Zero, "复制失败(剪贴板可能被占用)。\n请重试或关闭占用剪贴板的程序。\n\n内容:\n" + line, "获取窗口信息 → 复制", 0);

        return line; // 也作为 Quicker 的“上一步结果”返回
    }
}