3

In my c# .Net application, I've been trying to be able to retrieve the currently selected text in the currently focused window. (Note that it can be any window open in windows, such as word, or safari).

I'm able to retrieve the handle to the currently focused control. (Using a couple of interop calls to user32.dll, and kernel32.dll).

However, I've been unable to consistently be able to get back the selected text.

I've tried using SENDMESSAGE and GET_TEXT. However this only seems to work for some applications (works for simple applications like wordpad, doesn't work for more complex applications like firefox, or word).

I've tried using SENDMESSAGE and WM_COPY. However, again this only seems to work on some controls. (I would think that WM_COPY, would cause the exact same behaviour as manually pressing CTRL-C, but it doesn't).

I've tried using SENDMESSAGE and WM_KEYUP+WM_KEYDOWN to manually stimulate a copy command. BUt this doesn't constantly work either. (Perhaps of an overlap with the actual hotkey pressed by a user to invoke my applications).

Any ideas on consistently being able to retrieve the currently selected text ? (on any application).

Joel Coehoorn
  • 399,467
  • 113
  • 570
  • 794
vicsz
  • 9,552
  • 16
  • 69
  • 101

7 Answers7

4

I got this working by a combination of a couple of things. So:

  1. Wait for whatever modifiers are currently held down to be released.
  2. Send control+c (using this answer Trigger OS to copy (ctrl+c or Ctrl-x) programmatically)

            bool stillHeld = true;
            int timeSlept = 0;
    
            do
            {
                // wait until our hotkey is released
                if ((Keyboard.Modifiers & ModifierKeys.Control) > 0 ||
                    (Keyboard.Modifiers & ModifierKeys.Alt) > 0 ||
                    (Keyboard.Modifiers & ModifierKeys.Shift) > 0)
                {
                    timeSlept += 50;
                    System.Threading.Thread.Sleep(timeSlept);
                }
                else
                {
                    stillHeld = false;
                }
            } while (stillHeld && timeSlept < 1000);
    
            Keyboard.SimulateKeyStroke('c', ctrl: true);
    

I'm using WPF so Keyboard.Modifiers is System.Windows.Input.Keyboard, whereas Keyboard.SimulateKeyStroke is from Chris Schmick's answer.

Note, timeSlept is my max time to wait for the user to let go of the key before continuing on its merry way.

Community
  • 1
  • 1
Travis Quirk
  • 106
  • 6
2

I managed to get text for wordpad/notepad and anything that supports UI automation.

The code below may work for you in some cases. I'm going to get a start on using Reflector to see how windows does it for textboxes in the TextBoxBase.SelectedText property.

public static string SelectedText
{
    get
    {
        AutomationElement focusedElement = AutomationElement.FocusedElement;

        object currentPattern = null;

        if (focusedElement.TryGetCurrentPattern(TextPattern.Pattern, out currentPattern))
        {
            TextPattern textPattern = (TextPattern)currentPattern;
            TextPatternRange[] textPatternRanges = textPattern.GetSelection();
            if (textPatternRanges.Length > 0)
            {
                string textSelection = textPatternRanges[0].GetText(-1);
                return textSelection;
            }
        }
        return string.Empty;
    }
    set
    {
        AutomationElement focusedElement = AutomationElement.FocusedElement;
        IntPtr windowHandle = new IntPtr(focusedElement.Current.NativeWindowHandle);
        NativeMethods.SendMessage(windowHandle, NativeMethods.EM_REPLACESEL, true, value);
    }
}
AndrewVos
  • 1,297
  • 2
  • 14
  • 25
  • Nice idea, although on my machine (Win 7) .. it doesn't work on .Net 3.5 WPF apps .. the windowHandle returned is always zero. – vicsz Jan 29 '10 at 17:04
1

I don't believe that it is possible, the currently focused may not contain any selected text. (It may not even contain any text at all). Or the current selection could be an icon, or an image.

Perhaps requiring the user to copy the selected text to the clipboard first may be a solution.

Craig T
  • 2,761
  • 5
  • 25
  • 33
1

I've possibly misunderstood the question, but could you just send Ctrl+c? If you know the window is always foremost and the text to be copied is selected?

SendKeys.SendWait("^c");

Once copied to the clipboard, it's not tricky to programatically retrieve the contents (you could even check it's actually text at that point).

Matt Brindley
  • 9,739
  • 7
  • 47
  • 51
  • I tried this along with SENDMESSAGE KEYUP/KEYDOWN. It doesn't always work either. I think its an overlap of the keys. (i.e. user pressed down on some key combination to activate my program say ctrl-shift-p, ctrl-c is invoked by program, and keys up on ctrl-shift-p, and ctrlc isn't understood). – vicsz Mar 17 '09 at 14:00
0

Try the GetWindowText() API on controls for which the other methods do not work.

Sandeep Datta
  • 28,607
  • 15
  • 70
  • 90
0

I think creating clipboard monitor is a good option. I suggest you to look at Klipper (KDE clipboard module), it copied everything you select in the clipboard list, either content is file, folder or some text.

More details can be found here.

Sharique
  • 4,199
  • 6
  • 36
  • 54
0

Hm... you find it to be easy? How come?

The best alternative to SendKeys.SendWait("^c"); I found was this one:

http://www.c-sharpcorner.com/Forums/ShowMessages.aspx?ThreadID=46203

However, it only works for a few apps, like notepad. For web browsers, it just crashes.

Anyone got anything better?