问题
Summary: In Excel VBA, I've got a function that gets CF_TEXT from the clipboard, and another function for CF_UNICODETEXT. I use lstrlenA / lstrlenW to get the length of the string, but I'm getting unexpected results from those calls. I'd like help to understand why.
Details:
Within my functions, I use CopyMemory after opening the clipboard to get a byte array that I can then turn into a string. I'm in the process of adapting the project for 64-bit Office, and wanted to make sure everything involving CopyMemory is working correctly. That got me testing these clipboard functions, which is how I encountered this issue.
Before calling CopyMemory, I'm calling lstrlenA(pMem) / lstrlenW(pMem) to determine how much to copy. But these calls are returning unexpected lengths.
Here's one of the functions (ignoring locales):
Function GetANSITextFromClipboard() As String
Dim hClip As LongPtr, pMem As LongPtr
Dim cbText As Integer
Dim abRetString() As Byte
Dim RetString As String
If IsClipboardFormatAvailable(CF_TEXT) Then
If OpenClipboard(0) Then
hClip = GetClipboardData(CF_TEXT)
Dim clipSize As LongPtr 'added to check on mem size vs what's reported by lstrlenW
clipSize = GlobalSize(hClip)
pMem = GlobalLock(hClip)
cbText = lstrlenA(pMem)
If cbText > 0 Then
ReDim abRetString(0 To cbText)
CopyMemory abRetString(0), ByVal pMem, cbText '64-bit: should be good if cbText is right
'strip terminating null
If abRetString(UBound(abRetString)) = 0 Then
ReDim Preserve abRetString(0 To UBound(abRetString) - 1)
End If
RetString = StrConv(abRetString, vbUnicode)
End If
GlobalUnlock (hClip)
CloseClipboard
GetANSITextFromClipboard = RetString
End If
End If
End Function
These are relevant declares for that function:
Declare PtrSafe Function OpenClipboard Lib "user32" (ByVal hwnd As LongPtr) As Long 'returns BOOL
Declare PtrSafe Function GetClipboardData Lib "user32" (ByVal wFormat As Long) As LongPtr 'returns HANDLE
Declare PtrSafe Function CloseClipboard Lib "user32" () As Long 'returns BOOL
Declare PtrSafe Function GlobalSize Lib "kernel32" (ByVal hMem As LongPtr) As LongPtr 'returns SIZE_T
Declare PtrSafe Function GlobalLock Lib "kernel32" (ByVal hMem As LongPtr) As LongPtr 'returns LPVOID
Declare PtrSafe Function GlobalUnlock Lib "kernel32" (ByVal hMem As LongPtr) As Long 'returns BOOL
Declare PtrSafe Function lstrlenA Lib "kernel32" (ByVal lpString As String) As Long 'returns int
Declare PtrSafe Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" ( _
Destination As Any, _
Source As Any, _
ByVal length As LongPtr)
To test, I copy something to the clipboard then run this:
Private Sub TestReadClipboard()
Dim acbfFormats() As CBFormat
Dim i As Integer
Dim strPrompt As String
Dim strBuffer As String
If GetClipboardFormats(acbfFormats()) Then
strPrompt = UBound(acbfFormats()) - LBound(acbfFormats()) + 1 & " formats:" & vbCrLf & vbCrLf
For i = LBound(acbfFormats()) To UBound(acbfFormats())
strPrompt = strPrompt & acbfFormats(i).uFormat & " = " & acbfFormats(i).name _
& " (" & acbfFormats(i).Size & " bytes)" & vbCrLf
Next
Else
strPrompt = "Formats not available."
End If
MsgBox strPrompt
strPrompt = "Plain text: " & GetANSITextFromClipboard & vbCrLf & vbCrLf
strPrompt = strPrompt & "Unicode text: " & GetUnicodeTextFromClipboard & vbCrLf & vbCrLf
strPrompt = strPrompt & "Locale: " & Right("00000000" & Hex(GetClipboardLocale), 8)
MsgBox strPrompt
End Sub
CBFormat is a custom type, and GetClipboardFormats is a custom function that collects details about the formats in a CBFormat array. In particular, CBFormat.size for each format is set by calling GlobalSize().
At the first MsgBox strPrompt call, the formats I see are as expected. And the size of data for each format is as expected: at least as long as the text I copied. Then when I call into GetANSITextFromClipboard, the size seen earlier is the same as clipSize.
But cbText is consistently wrong. I've tried copying to the clipboard multiple times from different sources, and lstrlenA is always returning 13. In the Unicode function, lstrlenW is always returning 7.
(I don't use clipSize as the length to copy because then I end up with null and following garbage in the string. I could walk the byte array looking for null and then redim to that, but I shouldn't need to.)
Question: Why don't my calls to lstrlenA/lstrlenW work?
回答1:
Your declaration of lstrlenA() is wrong. Don't use String, that is not an 8bit string in VBA, it is a 16bit COM BSTR string. Since you are passing a LongPtr from GlobalLock() to lstrlenA(), use LongPtr as the parameter type instead of String. Otherwise, you will just have to scan the byte array manually.
来源:https://stackoverflow.com/questions/62107542/calls-to-lstrlena-or-lstrlenw-are-returning-unexpected-values