将 Ctrl+C 发送到上一个活动窗口

本文关键字:上一个 活动 窗口 Ctrl+C | 更新日期: 2024-11-09 02:04:07

我正在尝试编写一个位于系统托盘中的小应用程序。我已经注册了一个热键。当热键被触发并且应用程序被激活时,我想将 Ctrl+C 发送到最后一个活动窗口,以便我可以将突出显示的文本放入剪贴板。

这是我到目前为止得到的:

    //http://stackoverflow.com/questions/9468476/switch-to-last-active-application-like-alt-tab
    [DllImport("user32.dll")]
    [return: MarshalAs(UnmanagedType.Bool)]
    static extern bool IsWindowVisible(IntPtr hWnd);
    [DllImport("user32.dll")]
    static extern IntPtr GetLastActivePopup(IntPtr hWnd);
    [DllImport("user32.dll", ExactSpelling = true)]
    static extern IntPtr GetForegroundWindow();
    [DllImport("user32.dll")]
    [return: MarshalAs(UnmanagedType.Bool)]
    static extern bool SetForegroundWindow(IntPtr hWnd);
    const uint GA_PARENT = 1;
    const uint GA_ROOT = 2;
    const uint GA_ROOTOWNER = 3;
    public static IntPtr GetPreviousWindow()
    {
        IntPtr activeAppWindow = GetForegroundWindow();
        if (activeAppWindow == IntPtr.Zero)
            return IntPtr.Zero;
        IntPtr prevAppWindow = GetLastActivePopup(activeAppWindow);
        return IsWindowVisible(prevAppWindow) ? prevAppWindow : IntPtr.Zero;
    }
    public static void FocusToPreviousWindow()
    {
        IntPtr prevWindow = GetPreviousWindow();
        if (prevWindow != IntPtr.Zero)
            SetForegroundWindow(prevWindow);
    }
    ...
    private static void OnHotKeyFired()
    {
        FocusToPreviousWindow();
        SendKeys.SendWait("^(c)");
        _viewModel.Input = Clipboard.GetText();
        new UIWindow(_viewModel).ShowDialog();
    }

但是我无法让发送密钥工作。在大多数应用程序中,没有任何内容,这意味着不会触发 ctrl-c。在 Mss Word 中,当执行 SendWait 时,我的文档中插入了一个版权标志 (c)。

更新:

我已经尝试过WM_COPY和发送消息:

private static void OnHotKeyFired()
{
    IntPtr handle = GetPreviousWindow();
    SendMessage(handle, WM_COPY, IntPtr.Zero, IntPtr.Zero);
    _viewModel.Input = Clipboard.GetText();
    ...

它适用于Word,但不适用于Excel,记事本,Visual Studio。

将 Ctrl+C 发送到上一个活动窗口

    SendKeys.SendWait("^(c)");

这不会发送 Ctrl+C,括号也会发送。 您可能的意思是"^{c}",大括号而不是括号。 否则,Word 插入版权符号的原因会自动更正 (c) 为 © 。 修复:

    SendKeys.SendWait("^c");

如果您仍然遇到问题,您可能会遇到问题,然后阅读有关 SendKey 的 MSDN 文章。 它建议使用 .config 文件,以便使用不同的方式注入击键。 SendInput() 在更高版本的 Windows 版本上比日志钩子更好。

我认为

您的问题与时间有关。

要设置窗口焦点并实际复制到剪贴板,您需要等待

窗口获得焦点,并等待剪贴板更新。

有几种方法可以处理这些丑陋的 win32 事情。

对于剪贴板内容。 我将原始内容与当前内容进行比较。我将原始内容设置为 string.empty,如果它是图像或其他一些非文本数据。然后,我等待一个检查剪贴板是否有更改的函数。

对于 SetForegroundWindow,我目前在我的异步函数中添加了一个延迟。 您可能还会找到一个 win32 api 调用来等待此窗口正确置于前台。

我在异步函数中执行这两项操作并等待它,以便没有阻塞。

SendKeys 应该与 SendWait("^c") 一起使用。 SendWait("^(c)") 并不总是像其他答案中指出的那样工作。 但是,ctrl + c复制到剪贴板不会立即发生。

Point p;
if (GetCursorPos(out p))
{
    IntPtr ptr = WindowFromPoint(p);
    if (ptr != IntPtr.Zero)
    {                    
        SetForegroundWindow(ptr);
        //wait for window to get focus quick and ugly
        // probably a cleaner way to wait for windows to send a message
        // that it has updated the foreground window
        await Task.Delay(300);
        //try to copy text in the current window
        SendKeys.Send("^c");
        await WaitForClipboardToUpdate(originalClipboardText);
   }
}
}
    private static async Task WaitForClipboardToUpdate(string originalClipboardText)
    {
        Stopwatch sw = Stopwatch.StartNew();
        while (true)
        {
            if (await DoClipboardCheck(originalClipboardText)) return;
            if (sw.ElapsedMilliseconds >= 1500) throw new Exception("TIMED OUT WAITING FOR CLIPBOARD TO UPDATE.");
        }
    }
    private static async Task<bool> DoClipboardCheck(string originalClipboardText)
    {
        await Task.Delay(10);
        if (!Clipboard.ContainsText()) return false;
        var currentText = Clipboard.GetText();
        Debug.WriteLine("current text: " + currentText + " original text: " + originalClipboardText);
        return currentText != originalClipboardText;
    }

   [DllImport("user32.dll")]
    private static extern bool GetCursorPos(out Point pt);
    [DllImport("user32.dll", EntryPoint = "WindowFromPoint", CharSet = CharSet.Auto, ExactSpelling = true)]
    private static extern IntPtr WindowFromPoint(Point pt);
    // Activate an application window.
    [DllImport("USER32.DLL")]
    public static extern bool SetForegroundWindow(IntPtr hWnd);
    [DllImportAttribute("user32.dll", EntryPoint = "GetForegroundWindow")]
    public static extern IntPtr GetForegroundWindow();
    [DllImport("user32.dll", SetLastError = true)]
    static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint processId);

另外 - 快速验证/检查时间是否是您的问题的一种真正快速而肮脏的方法是,您可以在SetForegroundWindow和SendKeys.SendWait调用之后放置Thread.Sleep(1000)。

我知道

你正在编程一些东西,但如果你这样做是因为你没有找到更简单的解决方案......您可以尝试AutoHotKey,这是一个位于托盘中的小程序,可以执行各种热键/宏之类的操作...您需要为宏编写一个小脚本,如下所示:

#c::Send !{ESC}, ^c

这个简单的脚本意味着:

  • #c:: (定义热键 Windows+c)
  • 发送
  • (发送一些击键)
  • !{ESC}(Alt+Esc 切换到最后一个程序)
  • ^c (控制+c)

您可以使用此软件执行各种花哨的脚本,例如启动程序、正则表达式、线程、鼠标事件等......

我知道它不能回答你的问题,但这是一个简单的选择。