Question

This is what am trying to do,

When user select any word(text) of any running application by double clicking the mouse particular highlighted word should be inserted into a windows application which is already running.

So far I have implemented the logic using Global Keystroke where user has to trigger CRT+ C keyboard key combination to copy the selected word into win form application.

What i want to know is there any way to get those selected text into the application without having any button key press of the keyboard?

Was it helpful?

Solution

After some reading, I have found the way:

  1. Hook the double click event using something like globalmousekeyhook.codeplex.com
  2. (Optional) Save the current state of the clipboard
  3. Get The current mouse position with GetCursorPos from user32.dll
  4. Get windows based on cursor position with WindowFromPoint from 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);
    }
    
  5. Send copy command with SendMessage form user32.dll (see Using User32.dll SendMessage To Send Keys With ALT Modifier)

  6. Your Code
  7. (Optional) Restore the clipboard content saved in step 2

OTHER TIPS

I implemented it this project that belongs to me. Ok, how can i handle this, let me explain.

Two major things should be consider.

  • How can i get text inside any window ?
  • Where should i store it ?

So, @jcrada's answer contains one good point which is option 1.

The steps must be, under the light of above approachs:

  • Add globalmousekeyhook from Nuget.
  • Register ClipboardContainsText event via Usr32.dll
  • Register right events for mouse
  • And start listening

Firstly, create Win32 helper class that contains clipboard event.

/// <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);
}

Seconly, Register mouse and clipboard events,

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;
}

Mouse Events;

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");
        });
}

And last part, What will we do when we caught,

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;
}

The trick is sending copy command to the window or Operating System on the other hand Control+C command,so SendKeys.SendWait("^c"); doing this.

The two answers above are somewhat difficult to understand and not detailed. Anyway, big thanks to them cause it helped me to achieve this function, after trying many times.

It's 2021 now! Let's dive into my detailed and simple and latest method.

  1. Install MousekeyHook, which was globalmousekeyhook before, from Nuget.

  2. Register mouse events (MouseDoubleClick event and MouseDragFinished event, in which case you might have selected some text) with your function.

  3. Save clipboard's current content to restore it later. (Optinal)

  4. Clear the clipboard to determine whether you have selected some text later.

  5. Send Ctrl+C command simply by System.Windows.Forms.SendKeys.SendWait("^c");

  6. Get the clipboard's content.

    If you have selected some text, get it simply by System.Windows.Clipboard.GetText() and do anything you want then.

    If haven't, restore the clipboard. (Optional)

And these are my code:

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);
    }
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top