Results 1 to 19 of 19

Thread: [RESOLVED] SysAllocStringByteLen

  1. #1

    Thread Starter
    PowerPoster Elroy's Avatar
    Join Date
    Jun 2014
    Location
    Near Nashville TN
    Posts
    9,936

    Resolved [RESOLVED] SysAllocStringByteLen

    Alright, I'm learning a couple of new things here, and I want to make sure I've got it correct. In this thread, I want to make sure I'm using SysAllocStringByteLen correctly.

    I want to talk about the situation where we wind up with a pointer to valid variable length string data (unicode) in memory. This can happen in a variety of ways, but typically involves some combination of StrPtr, VarPtr, and/or ObjPtr. But I won't go into those details.

    Suffice it to say that we've got some pointer, and we'd like to make a copy of that string data for our own purposes. Said differently, we'd like a copy of that data in a typical VB6 string variable that we can manipulate and operate on. Now, when dealing with other data types (numeric), it's fairly straightforward to just use RtlMoveMemory, or one of the nice GetMem? functions in the msvbvm60 library.

    However, with variable length strings, we can get into trouble doing this. We could certainly take a RtlMoveMemory approach. However, if we use VarPtr, we're going to wind up with a string (our own string) that's pointing to a BSTR buffer that our string didn't make. Therefore, when our string goes out of scope, it's going to de-allocate that memory, and then VB6-crash. There are two other approaches that could work: 1) "borrow" a string, swap out the pointers, copy to another string, swap back in original pointers; or 2) Look at the BSTR header, create a string that size with Space(??), and then copy the string's data.

    However, both of these approaches require jumping through a few hoops. Apparently, there's a call that makes all of this trivial. And the reason for this post is to make sure I understand this correctly. Here's my newly devised function, and I want to make sure it's as simple as it can be, and that I have nothing to fear from using it:

    Code:
    
    Option Explicit
    '
    Private Declare Function lstrlenA Lib "kernel32.dll" (ByVal lpString As String) As Long
    Private Declare Function lstrlenW Lib "kernel32.dll" (ByVal lpString As String) As Long
    Private Declare Function SysAllocStringByteLen Lib "oleaut32" (ByVal Ptr As Long, ByVal Length As Long) As String
    Private Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (pDest As Any, pSource As Any, ByVal ByteLen As Long)
    '
    
    
    Public Function StrFromUniPtr(ByVal lpStr As Long) As String
        Dim i As Long
        i = lstrlenA(lpStr)
        StrFromUniPtr = SysAllocStringByteLen(lpStr, i)
        CopyMemory ByVal StrPtr(StrFromUniPtr), ByVal lpStr, i * 2
    End Function
    
    Private Sub Form_Load()
        Dim s1 As String
        Dim s2 As String
        Dim p1 As Long
    
        s1 = "dsdfasdf"
    
        p1 = StrPtr(s1)
        ' OK, from here down, I want to forget I know anything about s1, other than this pointer. 
        ' and I want to create another string with the data. 
    
    
        s2 = StrFromUniPtr(p1)
    
        MsgBox s2
    
    
    End Sub
    
    Now, my StrFromUniPtr appears to work as expected. However, I'm somewhat confused as to why. Here are my questions:

    1. Why must I use lstrlenA? Since the pointer is pointing at a Unicode string in memory, it seems that I should use lstrlenW.
    2. Why is the call to CopyMemory necessary? If I don't do that, the returned string is the correct length, but it only contains the first character of the original string.
    3. Am I adequately protecting memory? And, when my s2 goes out of scope, will memory be correctly de-allocated (to be cleaned up by garbage collector)?


    Thanks in advance for your replies,
    Elroy
    Any software I post in these forums written by me is provided "AS IS" without warranty of any kind, expressed or implied, and permission is hereby granted, free of charge and without restriction, to any person obtaining a copy. To all, peace and happiness.

  2. #2
    PowerPoster
    Join Date
    Jun 2015
    Posts
    2,224

    Re: SysAllocStringByteLen

    Ahhh... Lot's of potential breaking here.

    First thing's first - when you're Declaring an API as returning a String, you're asking the runtime to Do ANSI to Unicode conversion.
    The easiest thing to do is what Bonnie does, and use SysReallocString to avoid that whole mess.

  3. #3
    PowerPoster
    Join Date
    Jun 2015
    Posts
    2,224

    Re: SysAllocStringByteLen

    Code:
    Declare Function SysReAllocString Lib "OleAut32" (ByVal pbstr As Long, ByVal psz As Long) As Long
    
    Public Function StrFromUniPtr(ByVal lpStr As Long) As String
        SysReallocString VarPtr(StrFromUniPtr), lpStr
    End Function

  4. #4

    Thread Starter
    PowerPoster Elroy's Avatar
    Join Date
    Jun 2014
    Location
    Near Nashville TN
    Posts
    9,936

    Re: SysAllocStringByteLen

    DEX, do you have a link? I Googled SysReallocString "Bonnie West" and didn't come up with anything that was spot on. It only came up with this link which isn't exactly on-topic.

    Thanks,
    Elroy

    EDIT1: Ahhh, I didn't refresh soon enough.
    Any software I post in these forums written by me is provided "AS IS" without warranty of any kind, expressed or implied, and permission is hereby granted, free of charge and without restriction, to any person obtaining a copy. To all, peace and happiness.

  5. #5

    Thread Starter
    PowerPoster Elroy's Avatar
    Join Date
    Jun 2014
    Location
    Near Nashville TN
    Posts
    9,936

    Re: SysAllocStringByteLen

    Excellent, it certainly seems to work.

    So, using the function in post #3, it's safe to assume that the original string created for StrFromUniPtr is de-allocated, and then a new string is allocated with a copy from memory for what ever lpStr is pointing at?

    For instance, if we did this...

    Code:
    
    Public Function StrFromUniPtr(ByVal lpStr As Long) As String
    
        StrFromUniPtr = "asdfasdfasdf"
    
        SysReAllocString VarPtr(StrFromUniPtr), lpStr
    End Function
    
    ... we'd want to make sure our "asdfasdfasdf" string got thrown away.

    Elroy
    Any software I post in these forums written by me is provided "AS IS" without warranty of any kind, expressed or implied, and permission is hereby granted, free of charge and without restriction, to any person obtaining a copy. To all, peace and happiness.

  6. #6
    PowerPoster
    Join Date
    Jun 2015
    Posts
    2,224

    Re: SysAllocStringByteLen

    SysReallocAllocString frees the BSTR before reallocating it

    if you really wanted to use SysAllocStringByteLen, you could declare it as returning Long, and Jam the pointer into your String Variable.

    Code:
    Private Declare Function GetMem4 Lib "msvbvm60" (Src As Any, Dst As Any) As Long
    Private Declare Function SysAllocStringByteLen Lib "oleaut32" (ByVal psz As Long, ByVal cbLen As Long) As Long
    Private Declare Function lstrlenW Lib "kernel32" (ByVal lpString As Long) As Long
    
    Public Function StrFromUniPtr(ByVal lpStr As Long) As String
        GetMem4 SysAllocStringByteLen(lpStr, lstrlenW(lpStr) * 2), ByVal VarPtr(StrFromUniPtr)
    End Function
    edit: but doing it this way doesn't free anything that was previously in your string.
    Last edited by DEXWERX; Sep 1st, 2016 at 04:09 PM.

  7. #7
    PowerPoster
    Join Date
    Feb 2015
    Posts
    2,687

    Re: SysAllocStringByteLen

    You can declare SysAllocString in TLB then it'll return String or so:
    Code:
    Option Explicit
    
    Private Declare Function SysAllocString Lib "oleaut32.dll" (ByRef pOlechar As Any) As Long
    Private Declare Function GetMem4 Lib "msvbvm60" (src As Any, Dst As Any) As Long
    
    Private Sub Form_Load()
        Dim z As String
        
        z = MakeStringCopy(StrPtr("The trick"))
        z = MakeStringCopy(StrPtr("VB6"))
        
    End Sub
    
    Private Function MakeStringCopy( _
                     ByVal ptr As Long) As String
        GetMem4 SysAllocString(ByVal ptr), ByVal VarPtr(MakeStringCopy)
    End Function

  8. #8

    Thread Starter
    PowerPoster Elroy's Avatar
    Join Date
    Jun 2014
    Location
    Near Nashville TN
    Posts
    9,936

    Re: SysAllocStringByteLen

    I'm not partial to any particular API call.

    I just want to do things in the safest, and most elegant (simplest) way possible.

    It seems like the SysReAllocString is a good way to go. I'll file it away in my AllPurpose.bas library, and call this one done.


    My next task is to sort out how to move an AsciiZ string from memory into a VB6 string in the most elegant way, when we've got nothing but a pointer to our AsciiZ string. I can currently do it, but it's sloppy, taking each byte and doing a Chr$(TheByte) with it.

    Thanks To All,
    Elroy
    Any software I post in these forums written by me is provided "AS IS" without warranty of any kind, expressed or implied, and permission is hereby granted, free of charge and without restriction, to any person obtaining a copy. To all, peace and happiness.

  9. #9
    PowerPoster
    Join Date
    Jun 2015
    Posts
    2,224

    Re: SysAllocStringByteLen

    Ah... do you need to convert to unicode, and if so do you need to specify a locale, or does the default work.

    If you want the default locale - you can actually use SysAllocStringByteLen() As String to have the Runtime do the conversion.

  10. #10

    Thread Starter
    PowerPoster Elroy's Avatar
    Join Date
    Jun 2014
    Location
    Near Nashville TN
    Posts
    9,936

    Re: SysAllocStringByteLen

    Oh yeah, I'm so USA/American, I forget about that.

    For the time being, I'm thinking just handling it the same way that it'd be handled by default, such as writing to a text file, or the clipboard, or a non-W API call. Or, the same way that Chr$(0 to 255) would handle it.

    Thanks,
    Elroy

    EDIT1: Interestingly, I do have an installation of my software in a hospital in Manila, Philippines. But they make due with everything in English. With some old accounting software I used to sell, we had installations all over, but made them all use English. The one fun installation was in Kuwait, with money that went to 3 decimal places.
    Last edited by Elroy; Sep 1st, 2016 at 04:41 PM.
    Any software I post in these forums written by me is provided "AS IS" without warranty of any kind, expressed or implied, and permission is hereby granted, free of charge and without restriction, to any person obtaining a copy. To all, peace and happiness.

  11. #11
    PowerPoster
    Join Date
    Jun 2015
    Posts
    2,224

    Re: SysAllocStringByteLen

    Code:
    Private Declare Function SysAllocStringByteLen Lib "oleaut32" (ByVal psz As Long, ByVal cbLen As Long) As String
    Private Declare Function lstrlenA Lib "kernel32" (ByVal lpString As Long) As Long
    Public Function StrFromAnsiPtr(ByVal lpStr As Long) As String
        StrFromAnsiPtr = SysAllocStringByteLen(lpStr, lstrlenA(lpStr))
    End Function
    or this
    Code:
    Private Declare Function GetMem4 Lib "msvbvm60" (Src As Any, Dst As Any) As Long
    Private Declare Function SysAllocStringByteLen Lib "oleaut32" (ByVal psz As Long, ByVal cbLen As Long) As Long
    Private Declare Function lstrlenA Lib "kernel32" (ByVal lpString As Long) As Long
    Public Function StrFromAnsiPtr(ByVal lpStr As Long, Optional LocaleID As Long) As String
        GetMem4 SysAllocStringByteLen(lpStr, lstrlenA(lpStr)), ByVal VarPtr(StrFromAnsiPtr)
        StrFromAnsiPtr = StrConv(StrFromAnsiPtr, vbUnicode, LocaleID)
    End Function
    Last edited by DEXWERX; Sep 1st, 2016 at 04:49 PM.

  12. #12

    Thread Starter
    PowerPoster Elroy's Avatar
    Join Date
    Jun 2014
    Location
    Near Nashville TN
    Posts
    9,936

    Re: SysAllocStringByteLen

    Great stuff, DEX!

    Thank you,
    Elroy
    Any software I post in these forums written by me is provided "AS IS" without warranty of any kind, expressed or implied, and permission is hereby granted, free of charge and without restriction, to any person obtaining a copy. To all, peace and happiness.

  13. #13
    Fanatic Member
    Join Date
    Aug 2016
    Posts
    603

    Re: SysAllocStringByteLen

    Quote Originally Posted by DEXWERX View Post
    Code:
    Private Declare Function SysAllocStringByteLen Lib "oleaut32" (ByVal psz As Long, ByVal cbLen As Long) As String
    Private Declare Function lstrlenA Lib "kernel32" (ByVal lpString As Long) As Long
    Public Function StrFromAnsiPtr(ByVal lpStr As Long) As String
        StrFromAnsiPtr = SysAllocStringByteLen(lpStr, lstrlenA(lpStr))
    End Function
    or this
    Code:
    Private Declare Function GetMem4 Lib "msvbvm60" (Src As Any, Dst As Any) As Long
    Private Declare Function SysAllocStringByteLen Lib "oleaut32" (ByVal psz As Long, ByVal cbLen As Long) As Long
    Private Declare Function lstrlenA Lib "kernel32" (ByVal lpString As Long) As Long
    Public Function StrFromAnsiPtr(ByVal lpStr As Long, Optional LocaleID As Long) As String
        GetMem4 SysAllocStringByteLen(lpStr, lstrlenA(lpStr)), ByVal VarPtr(StrFromAnsiPtr)
        StrFromAnsiPtr = StrConv(StrFromAnsiPtr, vbUnicode, LocaleID)
    End Function
    ANSI? Why not Unicode? For Unicode, Replace lstrlenA with lstrlenW and remove StrConv?

    Edited: You wrote at #6 for Unicode:
    Code:
    Private Declare Function GetMem4 Lib "msvbvm60" (Src As Any, Dst As Any) As Long
    Private Declare Function SysAllocStringByteLen Lib "oleaut32" (ByVal psz As Long, ByVal cbLen As Long) As Long
    Private Declare Function lstrlenW Lib "kernel32" (ByVal lpString As Long) As Long
    
    Private Function StrFromUniPtr(ByVal lpStr As Long) As String
        GetMem4 SysAllocStringByteLen(lpStr, lstrlenW(lpStr) * 2), ByVal VarPtr(StrFromUniPtr)
    End Function
    
    Private Sub Form_Load()
        
        Dim str As String
        str = "日本、アジア"
        Debug.Print StrFromUniPtr(StrPtr(str))
        
    End Sub
    Last edited by DaveDavis; Sep 1st, 2016 at 07:55 PM.

  14. #14
    PowerPoster
    Join Date
    Jun 2015
    Posts
    2,224

    Re: [RESOLVED] SysAllocStringByteLen

    @daveDavis you missed the thread progression. He wanted both, now he has standard ways to get both. see post #3 for Unicode.

  15. #15

    Thread Starter
    PowerPoster Elroy's Avatar
    Join Date
    Jun 2014
    Location
    Near Nashville TN
    Posts
    9,936

    Re: [RESOLVED] SysAllocStringByteLen

    Here's what I wound up putting in my library:

    Code:
    
    Option Explicit
    '
    Private Declare Function GetMem4 Lib "msvbvm60" (Src As Any, Dst As Any) As Long
    Private Declare Function SysAllocStringByteLenLng Lib "oleaut32" Alias "SysAllocStringByteLen" (ByVal psz As Long, ByVal cbLen As Long) As Long
    Private Declare Function SysAllocStringByteLenStr Lib "oleaut32" Alias "SysAllocStringByteLen" (ByVal Ptr As Long, ByVal Length As Long) As String
    Private Declare Function lstrlenA Lib "kernel32.dll" (ByVal lpString As String) As Long
    Private Declare Function lstrlenW Lib "kernel32.dll" (ByVal lpString As String) As Long
    Private Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (pDest As Any, pSource As Any, ByVal ByteLen As Long)
    Private Declare Function SysReAllocString Lib "oleaut32" (ByVal pbstr As Long, ByVal psz As Long) As Long
    '
    
    Public Function StrFromUniPtr(lpStr As Long) As String
        ' Retrieves a Unicode string from memory and places it in a VB6 string (Unicode) variable. 
        SysReAllocString VarPtr(StrFromUniPtr), lpStr
    End Function
    
    Public Function StrFromAnsiPtr(ByVal lpStr As Long, Optional LocaleID As Long) As String
        ' Retrieves an ANSI string from memory and places it in a VB6 string (Unicode) variable. 
        '
        ' This gets the string as ANSI. 
        GetMem4 SysAllocStringByteLenLng(lpStr, lstrlenA(lpStr)), ByVal VarPtr(StrFromAnsiPtr)
        ' This converts it to Unicode. 
        StrFromAnsiPtr = StrConv(StrFromAnsiPtr, vbUnicode, LocaleID)
    End Function
    
    Private Sub Form_Load()
        Dim s1 As String
        Dim s2 As String
        Dim p1 As Long
    
        s1 = StrConv("dsdfasdf", vbFromUnicode) ' Create an ANSI string. 
        p1 = StrPtr(s1)
        s2 = StrFromAnsiPtr(p1)
        MsgBox s2
    
    
        s1 = "dsdfasdf"
        p1 = StrPtr(s1)
        s2 = StrFromUniPtr(p1)
        MsgBox s2
    
    
        Unload Me
    End Sub
    
    
    Elroy
    Any software I post in these forums written by me is provided "AS IS" without warranty of any kind, expressed or implied, and permission is hereby granted, free of charge and without restriction, to any person obtaining a copy. To all, peace and happiness.

  16. #16
    Fanatic Member
    Join Date
    Aug 2016
    Posts
    603

    Re: [RESOLVED] SysAllocStringByteLen

    Quote Originally Posted by Elroy View Post
    Here's what I wound up putting in my library:

    Code:
    
    Option Explicit
    '
    Private Declare Function GetMem4 Lib "msvbvm60" (Src As Any, Dst As Any) As Long
    Private Declare Function SysAllocStringByteLenLng Lib "oleaut32" Alias "SysAllocStringByteLen" (ByVal psz As Long, ByVal cbLen As Long) As Long
    Private Declare Function SysAllocStringByteLenStr Lib "oleaut32" Alias "SysAllocStringByteLen" (ByVal Ptr As Long, ByVal Length As Long) As String
    Private Declare Function lstrlenA Lib "kernel32.dll" (ByVal lpString As String) As Long
    Private Declare Function lstrlenW Lib "kernel32.dll" (ByVal lpString As String) As Long
    Private Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (pDest As Any, pSource As Any, ByVal ByteLen As Long)
    Private Declare Function SysReAllocString Lib "oleaut32" (ByVal pbstr As Long, ByVal psz As Long) As Long
    '
    
    Public Function StrFromUniPtr(lpStr As Long) As String
        ' Retrieves a Unicode string from memory and places it in a VB6 string (Unicode) variable. 
        SysReAllocString VarPtr(StrFromUniPtr), lpStr
    End Function
    
    Public Function StrFromAnsiPtr(ByVal lpStr As Long, Optional LocaleID As Long) As String
        ' Retrieves an ANSI string from memory and places it in a VB6 string (Unicode) variable. 
        '
        ' This gets the string as ANSI. 
        GetMem4 SysAllocStringByteLenLng(lpStr, lstrlenA(lpStr)), ByVal VarPtr(StrFromAnsiPtr)
        ' This converts it to Unicode. 
        StrFromAnsiPtr = StrConv(StrFromAnsiPtr, vbUnicode, LocaleID)
    End Function
    
    Private Sub Form_Load()
        Dim s1 As String
        Dim s2 As String
        Dim p1 As Long
    
        s1 = StrConv("dsdfasdf", vbFromUnicode) ' Create an ANSI string. 
        p1 = StrPtr(s1)
        s2 = StrFromAnsiPtr(p1)
        MsgBox s2
    
    
        s1 = "dsdfasdf"
        p1 = StrPtr(s1)
        s2 = StrFromUniPtr(p1)
        MsgBox s2
    
    
        Unload Me
    End Sub
    
    
    Elroy
    Good summary. Thank you.
    I marked this thread for reference.

  17. #17
    Fanatic Member
    Join Date
    Aug 2016
    Posts
    603

    Re: [RESOLVED] SysAllocStringByteLen

    Does call SysFreeString after SysAllocString ?

    Remarks

    You can free strings created with SysAllocString using SysFreeString.

  18. #18

    Thread Starter
    PowerPoster Elroy's Avatar
    Join Date
    Jun 2014
    Location
    Near Nashville TN
    Posts
    9,936

    Re: [RESOLVED] SysAllocStringByteLen

    Hmmm, that's a good question, DaveDavis.

    I assumed that the string would be de-allocated by VB6 automatically when it fell-out-of-scope. But I'm not absolutely sure about that.

    Maybe The Trick (or someone else) will provide a definitive answer.

    Elroy
    Any software I post in these forums written by me is provided "AS IS" without warranty of any kind, expressed or implied, and permission is hereby granted, free of charge and without restriction, to any person obtaining a copy. To all, peace and happiness.

  19. #19
    Default Member Bonnie West's Avatar
    Join Date
    Jun 2012
    Location
    InIDE
    Posts
    4,060

    Re: SysAllocStringByteLen

    Quote Originally Posted by Elroy View Post
    DEX, do you have a link? I Googled SysReallocString "Bonnie West" and didn't come up with anything that was spot on.
    Here's my CodeBank thread: [VB6] Dereferencing Pointers sans CopyMemory



    Quote Originally Posted by Bonnie West View Post
    Code:
    'Returns a copy of a null-terminated ANSI string (LPSTR/LPCSTR) from the given pointer
    Public Function GetStrFromPtrA(ByVal Ptr As Long) As String
        GetStrFromPtrA = SysAllocStringByteLen(Ptr, lstrlenA(Ptr))
    End Function
    
    'Returns a copy of a null-terminated Unicode string (LPWSTR/LPCWSTR) from the given pointer
    Public Function GetStrFromPtrW(ByVal Ptr As Long) As String
        #If WantFasterWay Then
        PutMem4 VarPtr(GetStrFromPtrW), SysAllocString(Ptr)
        #Else
        SysReAllocString VarPtr(GetStrFromPtrW), Ptr
        #End If
    End Function
    On Local Error Resume Next: If Not Empty Is Nothing Then Do While Null: ReDim i(True To False) As Currency: Loop: Else Debug.Assert CCur(CLng(CInt(CBool(False Imp True Xor False Eqv True)))): Stop: On Local Error GoTo 0
    Declare Sub CrashVB Lib "msvbvm60" (Optional DontPassMe As Any)

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  



Click Here to Expand Forum to Full Width