-
Dec 18th, 2017, 12:43 PM
#1
Using lstrlen in VB6
Microsoft says this about lstrlen:
---------------------------------------
Using this function incorrectly can compromise the security of your application. lstrlen assumes that lpString is a null-terminated string, or NULL. If it is not, this could lead to a buffer overrun or a denial of service attack against your application.
Consider using one of the following alternatives: StringCbLength or StringCchLength.
--------------------------------------
but I cannot find a declaration for these functions. Has anyone ever used them?
J.A. Coutts
-
Dec 18th, 2017, 12:52 PM
#2
Re: Using lstrlen in VB6
Read this. It's not implemented in the Windows API if that's what you were expecting.
-
Dec 18th, 2017, 01:45 PM
#3
Re: Using lstrlen in VB6
What's wrong with Len() or LenB()? They use the length in the BSTR header to get the length, so they're not concerned with nulls in the string. Also, just an FYI, they don't count the null terminator of a BSTR, nor the header bytes. It's purely the BSTR's data characters (Len) or bytes (LenB).
Happy Holidays,
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.
-
Dec 18th, 2017, 02:16 PM
#4
Re: Using lstrlen in VB6
Originally Posted by Elroy
What's wrong with Len() or LenB()? They use the length in the BSTR header to get the length, so they're not concerned with nulls in the string. Also, just an FYI, they don't count the null terminator of a BSTR, nor the header bytes. It's purely the BSTR's data characters (Len) or bytes (LenB).
Happy Holidays,
Elroy
Unfortunately, that doesn't work in this particular case (see http://www.vbforums.com/showthread.p...-User-SID-Path ). The call returns a length of 36 bytes, but requires 46 bytes of memory, so I had to use "lstrlen".
J.A. Coutts
-
Dec 20th, 2017, 09:01 AM
#5
Last edited by dz32; Apr 26th, 2019 at 11:26 AM.
-
Dec 20th, 2017, 09:33 AM
#6
Re: Using lstrlen in VB6
Note: If you want to replicate those functions, it can be done with arrays. Both of those functions you mentioned require that you know the max length of the possible string.
So, knowing that, size a byte array appropriately (maxLength+1 ANSI, or maxLength*2+2 Unicode). Copy the data from the pointer passing all MaxLength/MaxLength*2 bytes. Now you can use lstren/lstrlenW safely on VarPtr(array(0)) since the array is guaranteed to have a null terminator. Now you can copy the desired bytes to a StrPtr (unicode) or ReDim Preserve the array and use StrConv (ANSI). Just thinking out loud
-
Dec 20th, 2017, 09:36 AM
#7
Re: Using lstrlen in VB6
Originally Posted by dz32
Its useful when working between C and VB6. Scenario: C library has an ascii string that I want to pass back to vb6 with a call back...
Hi dz32,
Hopefully it's an ASCIIZ string and not just an ASCII string, or you'll be running into exactly the situation about which Microsoft warns.
And yeah, I was quite aware that lstrlenA and lstrlenW had uses when all you had was a pointer. Here are the two functions I use in those situations to build a VB6 usable BSTR:
Code:
Option Explicit
'
Private Declare Function lstrlenA Lib "kernel32.dll" (ByVal lpString As Long) As Long
Private Declare Function lstrlenW Lib "kernel32.dll" (ByVal lpString As Long) As Long
Private Declare Sub CopyMemory Lib "kernel32.dll" Alias "RtlMoveMemory" (ByRef Dest As Any, ByRef Source As Any, ByVal Bytes As Long)
Private Declare Function WideCharToMultiByte Lib "kernel32" (ByVal CodePage As Long, ByVal dwFlags As Long, lpWideCharStr As Any, ByVal cchWideChar As Long, lpMultiByteStr As Any, ByVal cchMultiByte As Long, ByVal lpDefaultChar As String, ByVal lpUsedDefaultChar As Long) As Long
'
Public Function MakeStringFromAsciizPtr(lpszA As Long) As String
' Input is a pointer to an ANSI null terminated string, possibly returned from an API call.
Dim Buffer() As Byte
Dim nLen As Long
'
If lpszA Then
nLen = lstrlenA(ByVal lpszA)
If nLen Then
ReDim Buffer(0 To (nLen - 1)) As Byte
CopyMemory Buffer(0), ByVal lpszA, nLen
MakeStringFromAsciizPtr = StrConv(Buffer, vbUnicode)
End If
End If
End Function
Public Function MakeStringFromUnicodezPtr(lpszW As Long) As String
' Input is a pointer to a Unicode null terminated string.
' This is effectively a BSTR, but we may have cases where we only have the pointer (such as API returns).
' MsgBox MakeStringFromUnicodezPtr(strptr("asdf")) ' Just makes a copy.
Dim s As String
Const CP_ACP As Long = 0&
'
s = String$(lstrlenW(ByVal lpszW) * 2&, vbNullChar)
WideCharToMultiByte CP_ACP, 0&, ByVal lpszW, -1, ByVal s, Len(s), 0&, 0&
MakeStringFromUnicodezPtr = Left$(s, lstrlenW(StrPtr(s)))
End Function
Happy Holidays,
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.
-
Nov 24th, 2021, 02:42 PM
#8
Fanatic Member
Re: Using lstrlen in VB6
Originally Posted by Elroy
Code:
Public Function MakeStringFromUnicodezPtr(lpszW As LongPtr) As String
' Input is a pointer to a Unicode null terminated string.
' This is effectively a BSTR, but we may have cases where we only have the pointer (such as API returns).
' MsgBox MakeStringFromUnicodezPtr(strptr("asdf")) ' Just makes a copy.
Dim s As String
Const CP_ACP As Long = 0&
s = String$(lstrlenW(ByVal lpszW) * 2&, vbNullChar)
WideCharToMultiByte CP_ACP, 0&, ByVal lpszW, -1, ByVal s, Len(s), 0&, 0&
MakeStringFromUnicodezPtr = Left$(s, lstrlenW(StrPtr(s)))
End Function
Hi Elroy,
In the above MakeStringFromUnicodezPtr function. Why do we have to double the size of the s buffer ?
Wouldn't using the following be large enough to incorporate the lpszW string ?
s = String$(lstrlenW(ByVal lpszW) * 1&, vbNullChar)
-
Nov 24th, 2021, 03:19 PM
#9
Re: Using lstrlen in VB6
Jaafar, from a cursory look, it seems that you're correct. Maybe I was trying to cover the case of lpszW pointing to a string with surrogate pairs. However, I don't believe that lstrlenW correctly handles surrogate pairs, so that wouldn't matter. Also, if that were the case, my final Left$(...) truncation wouldn't be correct either.
Not sure. It does work the way it is though.
Also, you must be trying to plug this into the VBA (LongPtr? <-- Your change). If that's what you're doing, be careful that you'll need to rework all the API declarations as well.
Maybe if you just posted a thread outlining what you're trying to do, we could possibly help more.
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.
-
Nov 24th, 2021, 03:30 PM
#10
Junior Member
Re: Using lstrlen in VB6
Originally Posted by JAAFAR
Wouldn't using the following be large enough to incorporate the lpszW string ?
s = String$(lstrlenW(ByVal lpszW) * 1&, vbNullChar)
I thought this too at first, but it's because of the call to WideCharToMultiByte after that, which actually converts the unicode string passed to the function to ANSI and then back to unicode, killing any characters not representable in your current ANSI codepage.
The proper solution would be:
Code:
Dim L as Long
L = lstrlenW(lpszW)
If L = 0 Then Exit Function
s = String$(L, 0)
CopyMemory ByVal StrPtr(s), ByVal lpszW, L * 2
-
Nov 24th, 2021, 03:31 PM
#11
Re: Using lstrlen in VB6
haha, yeah, I was just looking at it too, and wondering why I didn't use RtlMoveMemory. But, 4+ years ago, who knows.
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.
-
Nov 24th, 2021, 03:38 PM
#12
Fanatic Member
Re: Using lstrlen in VB6
Originally Posted by Elroy
Jaafar, from a cursory look, it seems that you're correct. Maybe I was trying to cover the case of lpszW pointing to a string with surrogate pairs. However, I don't believe that lstrlenW correctly handles surrogate pairs, so that wouldn't matter. Also, if that were the case, my final Left$(...) truncation wouldn't be correct either.
Not sure. It does work the way it is though.
Also, you must be trying to plug this into the VBA (LongPtr? <-- Your change). If that's what you're doing, be careful that you'll need to rework all the API declarations as well.
Maybe if you just posted a thread outlining what you're trying to do, we could possibly help more.
Thanks.
I was just trying to familiarize myself with the WideCharToMultiByte api which I have never used before.
And yes, I am using this in vba and this is what I normally use to get a string from a BSTR pointer.
I am ok with the x32bit vs x64bit required amendments.
Code:
Private Declare PtrSafe Sub CopyMemory Lib "kernel32.dll" Alias "RtlMoveMemory" (ByRef Dest As Any, ByRef Source As Any, ByVal Bytes As LongPtr)
Public Function BSTRPointerToString(pAddr As LongPtr) As String
Dim lLen As Long
Dim lBuffer() As Byte
CopyMemory lLen, ByVal pAddr - LenB(lLen), LenB(lLen)
ReDim lBuffer(0 To lLen - 1)
CopyMemory lBuffer(0), ByVal pAddr, lLen
BSTRPointerToString = lBuffer
End Function
I would use your other handy MakeStringFromAsciizPtr function to get a string from an null terminated ANSI string returned by some apis... I just bumped yesterday into one such api callback :
Code:
Function EnumResNameProc(ByVal HModule As LongPtr, ByVal lpszType As Long, _
ByVal lpszName As LongPtr, ByVal lParam As LongPtr) As LongPtr
MakeStringFromAsciizPtr(lpszName ) is just what is needed to retrieve the resource item ID\Name.
Thanks for your help.
-
Nov 24th, 2021, 03:46 PM
#13
Fanatic Member
Re: Using lstrlen in VB6
Originally Posted by Zahl
I thought this too at first, but it's because of the call to WideCharToMultiByte after that, which actually converts the unicode string passed to the function to ANSI and then back to unicode, killing any characters not representable in your current ANSI codepage.
The proper solution would be:
Code:
Dim L as Long
L = lstrlenW(lpszW)
If L = 0 Then Exit Function
s = String$(L, 0)
CopyMemory ByVal StrPtr(s), ByVal lpszW, L * 2
Cool Zahl !
Thank you.
-
Nov 24th, 2021, 04:02 PM
#14
Re: Using lstrlen in VB6
Good to see that you've got the Length declared as LongPtr in your CopyMemory declaration. You'll see it declared as Long (even for 64-bit VBA) which is wrong but sort of works. It's a SIZE_T type which is either 32-bit or 64-bit depending on the bitness of the program (and which is exactly how LongPtr behaves).
Here, for grins, I wrote a VBA version that uses RtlMoveMemory:
Code:
Option Explicit
'
Private Declare PtrSafe Sub CopyMemory Lib "kernel32.dll" Alias "RtlMoveMemory" (ByRef Dest As LongPtr, ByRef Source As LongPtr, ByVal Bytes As LongPtr)
Private Declare PtrSafe Function lstrlenW Lib "kernel32.dll" (ByVal lpString As LongPtr) As Long
'
Public Function StringFromUnicodeZPtr(pUnicodeZ As LongPtr) As String
StringFromUnicodeZPtr = String$(lstrlenW(pUnicodeZ), vbNullChar)
CopyMemory ByVal StrPtr(StringFromUnicodeZPtr), ByVal pUnicodeZ, LenB(StringFromUnicodeZPtr)
End Function
EDIT: And here's a quick down-and-dirty test of it (done in another module in VBA, 64-bit BTW):
Code:
Option Explicit
'
Private Declare PtrSafe Function GetCommandLineW Lib "kernel32" () As LongPtr
'
Public Function GetTheCommandLine() As String
GetTheCommandLine = StringFromUnicodeZPtr(GetCommandLineW)
End Function
Last edited by Elroy; Nov 24th, 2021 at 04:11 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.
Posting Permissions
- You may not post new threads
- You may not post replies
- You may not post attachments
- You may not edit your posts
-
Forum Rules
|
Click Here to Expand Forum to Full Width
|