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

时间:2015-03-10 16:28:35

标签: c# wpf window

我正在尝试编写一个小型应用程序,它位于系统托盘中。我注册了一个热键。当激活热键并激活应用程序时,我想将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();
    }

但是我无法让SendKeys工作。在大多数应用程序中没有任何好处,这意味着ctrl-c不会被解雇。在Ms Word中,当执行SendWait时,我的文档中插入了版权符号(c)。

更新:

我已尝试使用WM_COPY和SendMessage:

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

它适用于Word但不适用于Excel,Notepad,Visual Studio

3 个答案:

答案 0 :(得分:5)

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

不发送Ctrl + C,也会发送括号。你可能意味着"^{c}",花括号而不是括号。否则Word插入版权符号的原因,它会自动更正(c)到©。修正:

    SendKeys.SendWait("^c");

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

答案 1 :(得分:1)

我认为你的问题与时间有关。

要设置窗口焦点并将实际副本设置为剪贴板,您需要等待窗口获得焦点,并等待剪贴板更新。

有几种方法可以处理这些丑陋的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);

此外 - 快速验证/检查时间是否是您的问题的一种快速而又脏的方法是您可以放置​​一个Thread.Sleep(1000);在你的SetForegroundWindow和SendKeys.SendWait调用之后。

答案 2 :(得分:0)

我知道你正在编程,但如果你这样做,因为你找不到更简单的解决方案...... 你可以尝试AutoHotKey,它是一个小程序,放在你的托盘中,做各种热键/宏类的东西...... 您需要为宏编写一个小脚本,类似于:

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

这个简单的脚本意味着:

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

您可以使用此软件执行所有类似的花哨脚本,例如启动程序,正则表达式,线程,鼠标事件等等。

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