I use VBA and Windows API to navigate through an read only text box control of a program. I can retrieve each value in the textbox by copying it using Ctrl + C and read the clipboard. Here is my code:
Private Declare PtrSafe Function OpenClipboard Lib "user32" (ByVal hWnd As LongPtr) As LongPtr
Private Declare PtrSafe Function GetClipboardData Lib "user32" (ByVal uFormat As LongPtr) As LongPtr
Private Declare PtrSafe Function CloseClipboard Lib "user32" () As LongPtr
Private Declare PtrSafe Function GlobalLock Lib "kernel32" (ByVal hMem As LongPtr) As LongPtr
Private Declare PtrSafe Function GlobalUnlock Lib "kernel32" (ByVal hMem As LongPtr) As LongPtr
Private Declare PtrSafe Function GlobalSize Lib "kernel32" (ByVal hMem As LongPtr) As LongPtr
Private Declare PtrSafe Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (Destination As Any, Source As Any, ByVal Length As LongPtr)
Private Declare PtrSafe Function lstrcpy Lib "kernel32" Alias "lstrcpyW" (ByVal lpString1 As Any, ByVal lpString2 As Any) As LongPtr
Private Const CF_UNICODETEXT As LongPtr = 13&
Private Const GMEM_MOVEABLE As LongPtr = &H2&
Private Const GMEM_ZEROINIT As LongPtr = &H40&
Public Const GHND = &H42
Public Const CF_TEXT = 1
Public Const MAXSIZE = 4096
Sub main()
Call Ctrl_C
Call ClipBoard_GetData
End Sub
Sub Ctrl_C()
Dim inputArray(1 To 2) As tagINPUT_keybd
' Set up the first input structure for "Alt" key press
inputArray(1).INPUTTYPE = 1 ' INPUT_KEYBOARD
inputArray(1).ki.wVk = VK_CONTROL
inputArray(1).ki.dwFlags = 0 ' Key press
' Set up the second input structure for "M" key press
inputArray(2).INPUTTYPE = 1 ' INPUT_KEYBOARD
inputArray(2).ki.wVk = VK_C
inputArray(2).ki.dwFlags = 0 ' Key press
' Send the keydown events
Call SendKeybdInput(2, inputArray(1), LenB(inputArray(1)))
' Set up the first input structure for "Atl" key release
inputArray(1).ki.dwFlags = KEYEVENTF_KEYUP ' Key release
' Set up the second input structure for "O" key release
inputArray(2).ki.dwFlags = KEYEVENTF_KEYUP ' Key release
' Send the keyup events
Call SendKeybdInput(2, inputArray(1), LenB(inputArray(1)))
Sleep (100)
End Sub
Function ClipBoard_GetData()
Dim hClipMemory As LongPtr
Dim lpClipMemory As LongPtr
Dim MyString As String
Dim RetVal As LongPtr
If OpenClipboard(0&) = 0 Then
Debug.Print "Cannot open Clipboard. Another app. may have it open"
End If
' Obtain the handle to the global memory
' block that is referencing the text.
hClipMemory = GetClipboardData(CF_TEXT)
If IsNull(hClipMemory) Then
MsgBox "Could not allocate memory"
GoTo OutOfHere
End If
' Lock Clipboard memory so we can reference
' the actual data string.
lpClipMemory = GlobalLock(hClipMemory)
If Not IsNull(lpClipMemory) Then
MyString = Space$(MAXSIZE)
RetVal = lstrcpy(MyString, lpClipMemory)
RetVal = GlobalUnlock(hClipMemory)
' Peel off the null terminating character.
MyString = Mid(MyString, 1, InStr(1, MyString, Chr$(0), 0) - 1)
Else
MsgBox "Could not lock memory to copy string from."
End If
OutOfHere:
RetVal = CloseClipboard()
ClipBoard_GetData = MyString
End Function
This code works but sometimes it could not get the value of the clipboard which I copied from the program. Sometimes the message:
"Cannot open Clipboard. Another app. may have it open"
appear and I understand that the value of OpenClipboard(0&)
is equal 0. So I wonder if there is anyway to let the GetClipboardData
always get the copied data, or should I loop the copy function again to let the copied strings appears?