View Full Version : Never use Len when you use unicode string.
Hi all,
Last day, I was trying to create a static control (Label) for my gui toolkit. Eventually, I need to implement the AutoSize property for my tLabel type.
After a few minutes of google search, I ended up in GetTextExtentPoint32W function. Here is the header.
Declare Function GetTextExtentPoint32 Lib "Gdi32.dll" Alias "GetTextExtentPoint32W"(Byval hdc As Dword,
Byval lpString As string,
ByVal c As Int32,
Byval psizl As Long) As Boolean
The third parameter c is the length of the string in question. At first, I've use Len(Me.Text) for that. I got wrong results. My primary suspect was the type of second parameter. So I tried to change it to Asciiz, Wstring etc. But no luck. After wasting some hours, I decided to read about string functions in Win32. At that time, I noticed the lstrlenW function. Suddenly, I realized my mistake. Then I warped up lstrlen in a function like this.
Function StrLen(sValue As String) As Int32
Return lstrlen(UTF8ToWideChar$(sValue))
End Function
And here is the header.
Declare Function lstrlen Lib "Kernel32.dll" Alias "lstrlenW"(ByVal lpString as string) As int32
Then I got the correct result.
ReneMiner
01-05-2021, 13:58
Never use Len when you use unicode string. (https://www.thinbasic.com/community/showthread.php?13117-Never-use-Len-when-you-use-unicode-string)
thats right. For Unicode, especially WideString use LenW (https://www.thinbasic.com/public/products/thinBasic/help/html/stringhandling.htm).
Hint: If an Win32-Function ends with a Capital Char and involves Strings as Parameter or returned value it a signals
...A: to be ANSI
...W: for unicode wide
...Ex: there was previously another Function that got Extended in any way now.
and for thinBasic: if you see something trailing some keywords:
...$ : get ready to receive a string
...W : widechar involved
...Z : terminating zero ,for Utf-7/8 Ascii append $NUL,
for Utf-16 append mkWrd$(0) or mkByt$(0, 0) or String$(2, $Nul)
UCS-2/Utf-32 append mkDwd$(0), i.e. Repeat$(4, $Nul) or Chr$(0, 0, 0, 0)
...F is a faster solution of a function but requires mostly to work with a value stored to a dimensioned variable
accepts virtually applied variables upon the memory as well.
In some cases if subelement of subelement within an array of udt...
its anyway fast style to name the thing locally when its used more than once.
saves you repeatedly to type in "the same corners where to go around" like
local lSetting as Long at varptr(
myApp_settings.Editorsettings(
%Userdefined + currentUserID).codepage )
' now that long thing above is just: lSetting
before you type this a second or third time you will certainly consider to place a local name there and not calculate a second time %Userdefined+currrentUserID and additional every dot to signal it goes down another branch.of.something = that means for the processor to find the matching subelements name, lookup the distance from the root and add it - for each and every dot you typed
Consider to move the name that is covering the bytes in memory and puts the mask of a variable onto it to another position relative to its current position is to prefer over calculating from the root (here: varptr(myApp_Settings)) again.
To find distance and direction of movement subtract the actual position from the target position. Thats easy.
setAt( lSetting, GetAt(lSetting)+(anotherUserID - currentUserID) * SizeOf(Editorsettings) )
' or if the next setting just were following the current:
setAt(lSetting, GetAt(lSetting)+ SizeOf(lSetting))
What you pre-calculate using "Brains 2.0 incl. ExpansionPack" while you are coding will make your code run faster
@Rene Miner,
I am sorry, I didn't get you. I am confused about "SetAt". What is it used for ? To set a pointer at new memory location ?
ReneMiner
02-05-2021, 13:36
@Rene Miner,
I am sorry, I didn't get you. I am confused about "SetAt". What is it used for ? To set a pointer at new memory location ?
SetAt will place a virtual variable to another position
GetAt will retrieve the current position
IF you
Dim x Like myObject.typename$ At myObject.DataPtr
' now x will let you access myObject
' and if you have another object of the same type:
if myOtherObject.typename$ = myObject.typename$ then
setAt(x, myOtherObject.DataPtr)
' now x is myOtherObject
endif
'if you have an array/string/heap of dataptrs that point to objects
'lets say
String sPtrs= mkDWD$(ptr1, ptr2, ptr3)
' easy to append more:
sPtrs &= mkdwd$(ptr4, ptr5)
store them to a heap
Dword pMyHeap=Heap_AllocByStr(sPtrs)
' append more?
Heap_Set(pMyHeap, Heap_Get(pMyHeap) & mkdwd$(ptr7, ptr8,...))
'you could set a surfing-dword at the first or last one
Local surfer as Dword at pMyHeap
' (or at Heap_End(pMyHeap -3) to go backwards)
repeat
' do something with the object the surfer points now
' will be one of ptr1, ptr2, ptr3 etc.
' then push the surfer forward
SetAt(Surfer, GetAt(Surfer) + SizeOf(Surfer) )
' - SizeOf... to go backward
until getAt(Surfer)>= Heap_End(pMyHeap)
' < pMyHeap if backwards
this allows to iterate through an array. if the array is pointers (Dword) then the pointers can point different types,
its like an array where any member can be of another type
And
Sub DoSomethingWith(byval pData as dword, byval sTypename as string)
Local X LIKE sTypename at pData
x.doSomething()
End Sub
lets you call same named functions on different types