I have an app that sits as a trayicon in the system tray. I have registered a Hotkey that when pressed will capture the current text selection in any application, even in Web Browsers.
My aproach is to send the key combination {Ctlr + C} to copy the text. Then access the Clipboard and use the text in my own application.
I am programming in VB.NET but any help in C# or even C++ with Win32_Api would be highly appreciated.
I use AutoHotkey and there, I have a script which access the Clipboard text and works fine.
Pause::
clipboard =  ; Start off empty to allow ClipWait to detect when the text has arrived.
Send ^c
ClipWait, 2  ; Wait for the clipboard to contain text.
if ErrorLevel
{
    ;Do nothing after 2 seconds timeout
    return
}
Run https://translate.google.com/#auto/es/%clipboard%
return
As AutoHotkey is open source, I downloaded the code and try to replicate the behaviour of ClipWait as much as I could.
My code works most of the time but sometimes there is an important delay. I cannot access the Clipboard and the win32 function IsClipboardFormatAvailable() keeps returning False for a While. This happens when I am trying to copy from Google Chrome specially in editable TextBoxes.
I tried a lot of different things including using the .Net Framework Clipboard Class. I read the problem could be that the thread that was running the commands was not set as STA, so I did it. In my desperation I also put a timer but nothing solves the problem completely.
I read as well the option of putting a hook to monitor the Clipboard, but I would like to avoid this unless it is the only way of doing it.
Here is my VB.NET code:
Imports System.Runtime.InteropServices
Imports System.Text
Imports System.Threading
Imports Hotkeys
Public Class Form1
    Public m_HotKey As Keys = Keys.F6
    Private Sub RegisterHotkeys()
        Try
            Dim alreaydRegistered As Boolean = False
            ' set the hotkey:
            ''---------------------------------------------------
            ' add an event handler for hot key pressed (or could just use Handles)
            AddHandler CRegisterHotKey.HotKeyPressed, AddressOf hotKey_Pressed
            Dim hkGetText As HotKey = New HotKey("hkGetText",
                            HotKey.GetKeySinModificadores(m_HotKey),
                            HotKey.FormatModificadores(m_HotKey.ToString),
                            "hkGetText")
            Try
                CRegisterHotKey.HotKeys.Add(hkGetText)
            Catch ex As HotKeyAddException
                alreaydRegistered = True
            End Try
        Catch ex As Exception
            CLogFile.addError(ex)
        End Try
    End Sub
    Private Sub hotKey_Pressed(sender As Object, e As HotKeyPressedEventArgs)
        Try
            Timer1.Start()
        Catch ex As Exception
            CLogFile.addError(ex)
        End Try
    End Sub
    Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
        RegisterHotkeys()
    End Sub
    Function copyText() As String
        Dim result As String = String.Empty
        Clipboard.Clear()
        Console.WriteLine("Control + C")
        SendKeys.SendWait("^c")
        Dim Attempts As Integer = 100
        Do While Attempts > 0
            Try
                result = GetText()
                If result = String.Empty Then
                    Attempts -= 1
                    'Console.WriteLine("Attempts {0}", Attempts)
                    Thread.Sleep(100)
                Else
                    Attempts = 0
                End If
            Catch ex As Exception
                Attempts -= 1
                Console.WriteLine("Attempts Exception {0}", Attempts)
                Console.WriteLine(ex.ToString)
                Threading.Thread.Sleep(100)
            End Try
        Loop
        Return result
    End Function
#Region "Win32"
    <DllImport("User32.dll", SetLastError:=True)>
    Private Shared Function IsClipboardFormatAvailable(format As UInteger) As <MarshalAs(UnmanagedType.Bool)> Boolean
    End Function
    <DllImport("User32.dll", SetLastError:=True)>
    Private Shared Function GetClipboardData(uFormat As UInteger) As IntPtr
    End Function
    <DllImport("User32.dll", SetLastError:=True)>
    Private Shared Function OpenClipboard(hWndNewOwner As IntPtr) As <MarshalAs(UnmanagedType.Bool)> Boolean
    End Function
    <DllImport("User32.dll", SetLastError:=True)>
    Private Shared Function CloseClipboard() As <MarshalAs(UnmanagedType.Bool)> Boolean
    End Function
    <DllImport("Kernel32.dll", SetLastError:=True)>
    Private Shared Function GlobalLock(hMem As IntPtr) As IntPtr
    End Function
    <DllImport("Kernel32.dll", SetLastError:=True)>
    Private Shared Function GlobalUnlock(hMem As IntPtr) As <MarshalAs(UnmanagedType.Bool)> Boolean
    End Function
    <DllImport("Kernel32.dll", SetLastError:=True)>
    Private Shared Function GlobalSize(hMem As IntPtr) As Integer
    End Function
    Private Const CF_UNICODETEXT As UInteger = 13UI
    Private Const CF_TEXT As UInteger = 1UI
#End Region
    Public Shared Function GetText() As String
        If Not IsClipboardFormatAvailable(CF_UNICODETEXT) AndAlso Not IsClipboardFormatAvailable(CF_TEXT) Then
            Return Nothing
        End If
        Try
            If Not OpenClipboard(IntPtr.Zero) Then
                Return Nothing
            End If
            Dim handle As IntPtr = GetClipboardData(CF_UNICODETEXT)
            If handle = IntPtr.Zero Then
                Return Nothing
            End If
            Dim pointer As IntPtr = IntPtr.Zero
            Try
                pointer = GlobalLock(handle)
                If pointer = IntPtr.Zero Then
                    Return Nothing
                End If
                Dim size As Integer = GlobalSize(handle)
                Dim buff As Byte() = New Byte(size - 1) {}
                Marshal.Copy(pointer, buff, 0, size)
                Return Encoding.Unicode.GetString(buff).TrimEnd(ControlChars.NullChar)
            Finally
                If pointer <> IntPtr.Zero Then
                    GlobalUnlock(handle)
                End If
            End Try
        Finally
            CloseClipboard()
        End Try
    End Function
    Private Sub Timer1_Tick(sender As Object, e As EventArgs) Handles Timer1.Tick
        Try
            Timer1.Stop()
            Dim ThreadA As Thread
            ThreadA = New Thread(AddressOf Me.copyTextThread)
            ThreadA.SetApartmentState(ApartmentState.STA)
            ThreadA.Start()
        Catch ex As Exception
            CLogFile.addError(ex)
        End Try
    End Sub
    Sub copyTextThread()
        Dim result As String = copyText()
        If result <> String.Empty Then
            MsgBox(result)
        End If
    End Sub
End Class
I also searched in other similar questions without a final solution to my problem:
Send Ctrl+C to previous active window
How do I get the selected text from the focused window using native Win32 API?
 
     
     
    
