Calls to lstrlenA or lstrlenW are returning unexpected values

别来无恙 提交于 2021-01-29 15:21:45

问题


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

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!