如何将任何应用程序的选定文本放入 Windows 窗体应用程序

Posted

技术标签:

【中文标题】如何将任何应用程序的选定文本放入 Windows 窗体应用程序【英文标题】:How to get selected text of any application into a windows form application 【发布时间】:2014-02-23 00:26:57 【问题描述】:

这就是我想要做的,

当用户通过双击鼠标选择任何正在运行的应用程序的任何单词(文本)时,应将特定突出显示的单词插入到已经运行的 Windows 应用程序中。

到目前为止,我已经使用Global Keystroke 实现了逻辑,其中用户必须触发 CRT+ C 键盘组合键才能将所选单词复制到 win 表单应用程序中。

我想知道有没有什么方法可以让这些选定的文本进入应用程序而无需按下键盘的任何按钮按键?

【问题讨论】:

有些程序可以记录鼠标移动,但这似乎离发现任何给定应用程序的底层控件被 dblclicked 相距甚远。例如。如果您在窗口标签或桌面图标标签或自定义应用程序上单击会怎样?老实说,您似乎不太可能制作一个可以找出任何和所有被双击的控件的应用程序。我可能会离开这里,但我有一种预感,它无法完成。如果可以的话,我很想知道怎么做。 但是我看到一些应用程序已经实现了这个。当我们点击单词时,它会在他们的应用程序中显示该单词的含义? 是的,但是每个可以执行此操作的应用程序都有处理捕获和处理这些点击事件的代码,以获取您所说的这种“字典”行为。 Afaik 没有操作系统范围的方法可以做到这一点,甚至你可以让 Windows 以这种方式运行,每个应用程序也必须实现这一点。 如果你还有包含组合键的代码,你能把它贴出来吗?这正是我需要的 @Djeroen :对不起,我现在没有代码,但会尝试找到并与您分享 【参考方案1】:

上面的两个答案有些难以理解不详细。无论如何,非常感谢他们,因为它帮助我实现了这个功能,经过多次尝试。

现在是 2021 年!让我们深入了解我的详细简单和最新的方法。

    从 Nuget 安装 MousekeyHook,以前是 globalmousekeyhook。

    使用您的函数注册鼠标事件(MouseDoubleClick 事件和 MouseDragFinished 事件,在这种情况下您可能选择了一些文本)。

    保存剪贴板的当前内容以便以后恢复。 (可选)

    清除剪贴板以确定您是否稍后选择了某些文本。

    通过System.Windows.Forms.SendKeys.SendWait("^c");发送Ctrl+C命令简单

    获取剪贴板的内容。

    如果您选择了一些文本,请通过System.Windows.Clipboard.GetText()简单地获取它,然后做任何您想做的事情。

    如果没有,请恢复剪贴板。 (可选)

这些是我的代码:

private IKeyboardMouseEvents globalMouseHook;

public MainWindow()

    // Note: for the application hook, use the Hook.AppEvents() instead.
    globalMouseHook = Hook.GlobalEvents();

    // Bind MouseDoubleClick event with a function named MouseDoubleClicked.
    globalMouseHook.MouseDoubleClick += MouseDoubleClicked;

    // Bind DragFinished event with a function.
    // Same as double click, so I didn't write here.
    globalMouseHook.MouseDragFinished += MouseDragFinished;


// I make the function async to avoid GUI lags.
private async void MouseDoubleClicked(object sender, System.Windows.Forms.MouseEventArgs e)

    // Save clipboard's current content to restore it later.
    IDataObject tmpClipboard = System.Windows.Clipboard.GetDataObject();

    System.Windows.Clipboard.Clear();

    // I think a small delay will be more safe.
    // You could remove it, but be careful.
    await Task.Delay(50);

    // Send Ctrl+C, which is "copy"
    System.Windows.Forms.SendKeys.SendWait("^c");

    // Same as above. But this is more important.
    // In some softwares like Word, the mouse double click will not select the word you clicked immediately.
    // If you remove it, you will not get the text you selected.
    await Task.Delay(50);

    if (System.Windows.Clipboard.ContainsText())
    
        string text = System.Windows.Clipboard.GetText();
        
        // Your code

    
    else
    
        // Restore the Clipboard.
        System.Windows.Clipboard.SetDataObject(tmpClipboard);
    

【讨论】:

【参考方案2】:

经过一番阅读,我找到了方法:

    使用globalmousekeyhook.codeplex.com 之类的方法挂钩双击事件 (可选)保存剪贴板的当前状态 用GetCursorPosuser32.dll获取当前鼠标位置

    使用WindowFromPoint 从光标位置获取窗口 user32.dll

    [DllImport("user32.dll")]
    public static extern IntPtr WindowFromPoint(Point lpPoint);
    
    [DllImport("user32.dll")]
    public static extern bool GetCursorPos(out Point lpPoint);
    
    public static IntPtr GetWindowUnderCursor()
    
       Point ptCursor = new Point();
    
       if (!(PInvoke.GetCursorPos(out ptCursor)))
          return IntPtr.Zero;
    
       return WindowFromPoint(ptCursor);
    
    

    SendMessage 形式发送复制命令user32.dll(见 Using User32.dll SendMessage To Send Keys With ALT Modifier)

    您的代码 (可选)恢复步骤 2 中保存的剪贴板内容

【讨论】:

选项 1 很好。现在很有用。 选项 1 是什么?这是应该遵循的一系列步骤:P【参考方案3】:

我实现了属于我的this 项目。好的,我该如何处理,让我解释一下。

应该考虑两个主要的事情。

如何在任何窗口中获取文本? 我应该把它存放在哪里?

所以,@jcrada 的回答包含一个优点,即选项 1。

根据上述方法,步骤必须是:

从 Nuget 添加 globalmousekeyhook。 通过 Usr32.dll 注册 ClipboardContainsText 事件 注册鼠标右键事件 开始聆听

首先,创建包含剪贴板事件的 Win32 帮助类。

/// <summary>
///     This static class holds the Win32 function declarations and constants needed by
///     this sample application.
/// </summary>
internal static class Win32

    /// <summary>
    ///     The WM_DRAWCLIPBOARD message notifies a clipboard viewer window that
    ///     the content of the clipboard has changed.
    /// </summary>
    internal const int WmDrawclipboard = 0x0308;

    /// <summary>
    ///     A clipboard viewer window receives the WM_CHANGECBCHAIN message when
    ///     another window is removing itself from the clipboard viewer chain.
    /// </summary>
    internal const int WmChangecbchain = 0x030D;

    [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
    internal static extern IntPtr SetClipboardViewer(IntPtr hWndNewViewer);

    [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
    internal static extern bool ChangeClipboardChain(IntPtr hWndRemove, IntPtr hWndNewNext);

    [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
    internal static extern IntPtr SendMessage(IntPtr hWnd, int msg, IntPtr wParam, IntPtr lParam);

其次,注册鼠标和剪贴板事件,

public void Initialize()

        var wih = new WindowInteropHelper(this.mainWindow);
        this.hWndSource = HwndSource.FromHwnd(wih.Handle);
        this.globalMouseHook = Hook.GlobalEvents();
        this.mainWindow.CancellationTokenSource = new CancellationTokenSource();
        var source = this.hWndSource;
        if (source != null)
        
            source.AddHook(this.WinProc); // start processing window messages
            this.hWndNextViewer = Win32.SetClipboardViewer(source.Handle); // set this window as a viewer
        
        this.SubscribeLocalevents();
        this.growlNotifications.Top = SystemParameters.WorkArea.Top + this.startupConfiguration.TopOffset;
        this.growlNotifications.Left = SystemParameters.WorkArea.Left + SystemParameters.WorkArea.Width - this.startupConfiguration.LeftOffset;
        this.IsInitialized = true;

鼠标事件;

private void SubscribeLocalevents()

        this.globalMouseHook.MouseDoubleClick += async (o, args) => await this.MouseDoubleClicked(o, args);
        this.globalMouseHook.MouseDown += async (o, args) => await this.MouseDown(o, args);
        this.globalMouseHook.MouseUp += async (o, args) => await this.MouseUp(o, args);



private async Task MouseUp(object sender, MouseEventArgs e)

        this.mouseSecondPoint = e.Location;

        if (this.isMouseDown && !this.mouseSecondPoint.Equals(this.mouseFirstPoint))
        
            await Task.Run(() =>
            
                if (this.mainWindow.CancellationTokenSource.Token.IsCancellationRequested)
                    return;

                SendKeys.SendWait("^c");
            );
            this.isMouseDown = false;
        
        this.isMouseDown = false;


private async Task MouseDown(object sender, MouseEventArgs e)

        await Task.Run(() =>
        
            if (this.mainWindow.CancellationTokenSource.Token.IsCancellationRequested)
                return;

            this.mouseFirstPoint = e.Location;
            this.isMouseDown = true;
        );


private async Task MouseDoubleClicked(object sender, MouseEventArgs e)

        this.isMouseDown = false;
        await Task.Run(() =>
        
            if (this.mainWindow.CancellationTokenSource.Token.IsCancellationRequested)
                return;

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

最后一部分,当我们被抓住时我们会怎么做,

private IntPtr WinProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)

        switch (msg)
        
            case Win32.WmChangecbchain:
                if (wParam == this.hWndNextViewer)
                    this.hWndNextViewer = lParam; //clipboard viewer chain changed, need to fix it.
                else if (this.hWndNextViewer != IntPtr.Zero)
                    Win32.SendMessage(this.hWndNextViewer, msg, wParam, lParam); //pass the message to the next viewer.

                break;
            case Win32.WmDrawclipboard:
                Win32.SendMessage(this.hWndNextViewer, msg, wParam, lParam); //pass the message to the next viewer //clipboard content changed
                if (Clipboard.ContainsText() && !string.IsNullOrEmpty(Clipboard.GetText().Trim()))
                
                    Application.Current.Dispatcher.Invoke(
                        DispatcherPriority.Background,
                        (Action)
                            delegate
                            
                                var currentText = Clipboard.GetText().RemoveSpecialCharacters();

                                if (!string.IsNullOrEmpty(currentText))
                                
                                    //In this section, we are doing something, because TEXT IS CAPTURED.
                                    Task.Run(
                                        async () =>
                                        
                                            if (this.mainWindow.CancellationTokenSource.Token.IsCancellationRequested)
                                                return;

                                            await
                                                this.WhenClipboardContainsTextEventHandler.InvokeSafelyAsync(this,
                                                    new WhenClipboardContainsTextEventArgs  CurrentString = currentText );
                                        );
                                
                            );
                
                break;
        

        return IntPtr.Zero;

诀窍是在另一方面向窗口或操作系统发送复制命令 Control+C 命令,所以SendKeys.SendWait("^c"); 这样做。

【讨论】:

以上是关于如何将任何应用程序的选定文本放入 Windows 窗体应用程序的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 Powershell 将 ComboBox 选定文件附加到 TextBox?

将样式应用于选定文本未选定框

Qt:如何编写 Windows 桌面实用程序?

如何从表格视图中的选定单元格中获取文本?

如何通过辅助功能 API 获取当前选定文本的全局屏幕坐标。

Wordpress - PHP:如何将表单中的选定数据放入文本区域?