【发布时间】:2016-02-24 13:02:57
【问题描述】:
我有一个应用程序,它作为托盘图标位于系统托盘中。我已经注册了一个热键,当按下它时,它将捕获任何应用程序中的当前文本选择,即使在 Web 浏览器中也是如此。
我的方法是发送组合键 {Ctlr + C} 来复制文本。然后访问剪贴板并在我自己的应用程序中使用文本。
我正在使用 VB.NET 进行编程,但对于 C# 甚至 C++ 与 Win32_Api 的任何帮助将不胜感激。
我使用 AutoHotkey,我有一个脚本可以访问剪贴板文本并且工作正常。
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
由于 AutoHotkey 是开源的,我下载了代码并尝试尽可能多地复制 ClipWait 的行为。
我的代码大部分时间都能正常工作,但有时会出现严重的延迟。我无法访问剪贴板,win32 函数 IsClipboardFormatAvailable() 在一段时间内一直返回 False。当我试图在可编辑的文本框中从谷歌浏览器复制时,就会发生这种情况。
我尝试了很多不同的方法,包括使用 .Net Framework 剪贴板类。我读到的问题可能是运行命令的线程未设置为 STA,所以我这样做了。无奈之下,我也放了一个计时器,但没有什么能完全解决问题。
我也阅读了使用挂钩来监视剪贴板的选项,但我想避免这种情况,除非这是唯一的方法。
这是我的 VB.NET 代码:
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
我还搜索了其他类似的问题,但没有最终解决我的问题:
Send Ctrl+C to previous active window
How do I get the selected text from the focused window using native Win32 API?
【问题讨论】:
-
为什么不使用自动化?
-
什么意思?请你说得具体一点。
-
我的意思是 UI 自动化。您的方法会破坏剪贴板。
-
我知道我的方法目前会破坏剪贴板内容,但如果我成功了,我可以先保存剪贴板内容,然后在完成后加载旧内容。
-
哪种语言?你忘记了 Java。
标签: c# c++ vb.net clipboard sendkeys