4

I'm curious if it is possible to write a program that monitors my text selection. One possible use would be to write an editor/IDE agnostic code formatter:

  1. Application/Service, P, is launched and somehow hooks into windows such that it gets notified when text is selected in any window.
  2. Some other application, A, is launched.
  3. User selects text in A.
  4. P is notified with the text that is selected.

--> I'd be happy to get this far...

Aaron Anodide
  • 16,906
  • 15
  • 62
  • 121
  • Do you really want to be notified on *selection*, or is knowing when something is copied to the clipboard enough? – Bryan Oakley Mar 07 '12 at 17:32
  • 1
    This is not exactly the same thing but maybe close. You could monitor when data enters the clipboard. It's one extra step for the user but maybe it's all the same... see this: http://stackoverflow.com/questions/621577/clipboard-event-c-sharp – Brad Mar 07 '12 at 17:35
  • +1 Like the question, but I think that it should be more possible if the application have already launched. – Omar Mar 07 '12 at 17:46

3 Answers3

6

This is not possible without specific knowledge of each control/application that will be in use as they can all handle/treat it differently.

Deanna
  • 23,876
  • 7
  • 71
  • 156
3

I don't think you can register any sort of hook. I think you'll need to constantly poll the "focused" or selected window.

You can probably use the Windows Automation API to do this, which is as far as I am aware superceeded the older Accesibility API: http://msdn.microsoft.com/en-us/library/ms747327.aspx

I have used this API to automate GUI tests. I am a bit rusty with it so I don't know for sure, but I am reasonably confident you could use it for what you are trying to do. Basically the API allows you to traverse the tree of automation objects with the root at the desktop. Each automation element tends to be a windows control of some kind and different controls implement different patterns. You can also get at elements beneath the mouse cursor and possibly you can get straight to the currently selected/focused element.

After that I notice the TextPattern class for example has a GetSelection() method which is documented as "Retrieves a collection of disjoint text ranges associated with the current text selection or selections.". I bet the automation object for textboxes implement the TextPattern. http://msdn.microsoft.com/en-us/library/system.windows.automation.textpattern.aspx

anbob
  • 41
  • 1
3

This code help you to get focused control text in focused window, i hope that helps :

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Runtime.InteropServices;

namespace TextFocusedns 
{
    public partial class TextFocusedFrm : Form
    {
        #region APIs

        [DllImport("user32.dll")]
        public static extern bool GetCursorPos(out Point pt);

        [DllImport("user32.dll", EntryPoint = "WindowFromPoint", CharSet = CharSet.Auto, ExactSpelling = true)]
        public static extern IntPtr WindowFromPoint(Point pt);

        [DllImport("user32.dll", EntryPoint = "SendMessageW")]
        public static extern int SendMessageW([InAttribute] System.IntPtr hWnd, int Msg, int wParam, IntPtr lParam);
        public const int WM_GETTEXT = 13;

        [DllImport("user32.dll", CharSet = CharSet.Auto, ExactSpelling = true)]
        internal static extern IntPtr GetForegroundWindow();

        [DllImport("user32.dll", CharSet = CharSet.Auto, ExactSpelling = true)]
        internal static extern IntPtr GetFocus();

        [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        internal static extern int GetWindowThreadProcessId(int handle, out int processId);

        [DllImport("user32", CharSet = CharSet.Ansi, SetLastError = true, ExactSpelling = true)]
        internal static extern int AttachThreadInput(int idAttach, int idAttachTo, bool fAttach);
        [DllImport("kernel32.dll")]
        internal static extern int GetCurrentThreadId();

        [DllImport("user32", CharSet = CharSet.Auto, SetLastError = true)]
        internal static extern int GetWindowText(IntPtr hWnd, [Out, MarshalAs(UnmanagedType.LPTStr)] StringBuilder lpString, int nMaxCount);

        #endregion

        private System.Windows.Forms.Timer timer = new System.Windows.Forms.Timer() { Interval = 100, Enabled = true };

        public TextFocusedFrm()
        {
            InitializeComponent();
        }

        private void TextFocusedFrm_Load(object sender, EventArgs e)
        {
            timer.Tick += new EventHandler(timer_Tick);
            timer.Start();
        }

        void timer_Tick(object sender, EventArgs e)
        {
            try
            {
                MultiLineTextBox.Text = GetTextFromFocusedControl();
            }
            catch (Exception exp)
            {
                MultiLineTextBox.Text += exp.Message;
            }
        }

        //Get the text of the focused control
        private string GetTextFromFocusedControl()
        {
            try
            {
                int activeWinPtr = GetForegroundWindow().ToInt32();
                int activeThreadId = 0, processId;
                activeThreadId = GetWindowThreadProcessId(activeWinPtr, out processId);
                int currentThreadId = GetCurrentThreadId();
                if (activeThreadId != currentThreadId)
                    AttachThreadInput(activeThreadId, currentThreadId, true);
                IntPtr activeCtrlId = GetFocus();

                return GetText(activeCtrlId);
            }
            catch (Exception exp)
            {
                return exp.Message;
            }
        }

        //Get the text of the control at the mouse position
        private string GetTextFromControlAtMousePosition()
        {
            try
            {
                Point p;
                if (GetCursorPos(out p))
                {
                    IntPtr ptr = WindowFromPoint(p);
                    if (ptr != IntPtr.Zero)
                    {
                        return GetText(ptr);
                    }
                }
                return "";
            }
            catch (Exception exp)
            {
                return exp.Message;
            }
        }

        //Get the text of a control with its handle
        private string GetText(IntPtr handle)
        {
            int maxLength = 512;
            IntPtr buffer = Marshal.AllocHGlobal((maxLength + 1) * 2);
            SendMessageW(handle, WM_GETTEXT, maxLength, buffer);
            string w = Marshal.PtrToStringUni(buffer);
            Marshal.FreeHGlobal(buffer);
            return w;
        }
    }
}
Amen Ayach
  • 4,288
  • 1
  • 23
  • 23