Deprecated: Array and string offset access syntax with curly braces is deprecated in /homepages/21/d38531796/htdocs/jose/smfforum/Sources/Subs.php on line 3825
Print Page - Dynamic strings in TYPE structures

Theo's Forum

IT-Berater: Theo Gottwald (IT-Consultant) => General Tips and Discussion => Topic started by: Paul Squires on August 06, 2007, 05:44:37 AM

Title: Dynamic strings in TYPE structures
Post by: Paul Squires on August 06, 2007, 05:44:37 AM

Code: [Select]

'  Demonstrate one way of creating dynamic strings in TYPE structures.

#Compile Exe

#Include ""

   nCustID    As Long
   zCustName  As Asciiz Ptr   ' dynamic string
   zAddress   As Asciiz Ptr   ' dynamic string
   nUnpaid    As Single
   nActive    As Byte
End Type

'//  Manually allocate a block of memory at the specified location
Function MemAlloc(ByVal nSize As Long) As Dword
   If nSize > 0 Then
      Function = HeapAlloc( GetProcessHeap(), %HEAP_ZERO_MEMORY, nSize)
   End If
End Function

'//  Free memory located at a specified location
Function MemFree(ByRef pMem As Dword) As Long
   If pMem Then
      Function = HeapFree( GetProcessHeap(), 0, ByVal pMem)
      pMem = 0
   End If
End Function

'//  Sub that will assign a dynamic string to a memory location
'//  and free any memory that may have already been allocated.
'//  pMem must be ByRef.
Sub MemString( ByRef pMem As Dword, _
               ByVal sData As String )

    If pMem Then HeapFree( GetProcessHeap(), 0, ByVal pMem)  ' free any existing memory
    sData = sData & $Nul
    pMem = HeapAlloc( GetProcessHeap(), %HEAP_ZERO_MEMORY, Len( sData ) + 1)
    If pMem Then Poke$ pMem, sData

End Sub

Function PBMain() As Long

' (1)  Using manual memory allocation to manage dynamic strings in TYPE's.

   ' Assign some default values to a TYPE structure
   Local cust As CUSTOMER_TYPE
   cust.nCustID = 1000
   cust.nUnpaid = 2300.00
   cust.nActive = %TRUE
   MemString cust.zCustName, "Paul Squires"
   MemString cust.zAddress,  "SomeTown, Anywhere, 12345"
   ' Display the record
   MsgBox "nCustID   = " & Format$(cust.nCustID)                & $CrLf & _
          "nUnpaid   = " & Format$(cust.nUnpaid, "######.00")   & $CrLf & _
          "nActive   = " & IIf$(cust.nActive, "TRUE", "FALSE" ) & $CrLf & _
          "zCustName = " & cust.@zCustName & $CrLf & _
          "zAddress  = " & cust.@zAddress
   ' Modify the name and address.
   ' The functions take care of freeing the original string memory
   ' and then creating a new string.
   MemString cust.zCustName, "Paul Squires (PlanetSquires)"
   MemString cust.zAddress,  "MyTown, CloseBy, 55555"
   ' Display the modified record
   MsgBox "nCustID   = " & Format$(cust.nCustID)                & $CrLf & _
          "nUnpaid   = " & Format$(cust.nUnpaid, "######.00")   & $CrLf & _
          "nActive   = " & IIf$(cust.nActive, "TRUE", "FALSE" ) & $CrLf & _
          "zCustName = " & cust.@zCustName & $CrLf & _
          "zAddress  = " & cust.@zAddress

   ' Free the allocated memory for the dynamic strings.
   MemFree cust.zCustName
   MemFree cust.zAddress                                         

' (2)  Let's get fancy... Create the TYPE structure itself dynamically
'      and add some dynamic strings to it.
   Local pCust As CUSTOMER_TYPE Ptr
   pCust = MemAlloc( SizeOf(CUSTOMER_TYPE) )
   @pCust.nCustID = 5000
   @pCust.nUnpaid = 9000.00
   @pCust.nActive = %TRUE
   MemString @pCust.zCustName, "Jose Roca"
   MemString @pCust.zAddress,  "Across the Pond, OverThere, 919191"
   ' Display the record
   MsgBox "nCustID   = " & Format$(@pCust.nCustID)                & $CrLf & _
          "nUnpaid   = " & Format$(@pCust.nUnpaid, "######.00")   & $CrLf & _
          "nActive   = " & IIf$(@pCust.nActive, "TRUE", "FALSE" ) & $CrLf & _
          "zCustName = " & @pCust.@zCustName & $CrLf & _
          "zAddress  = " & @pCust.@zAddress

   ' Free the allocated memory for the dynamic strings. Not really
   ' necessary at this point because the application is about to
   ' terminate and Windows will reclaim the memory automatically, but
   ' it is good programming practice nonetheless.
   MemFree @pCust.zCustName
   MemFree @pCust.zAddress                                         
   ' Also free the dynamically created TYPE (pCust) itself
   MemFree pCust
End Function

Title: Re: Dynamic strings in TYPE structures
Post by: Donald Darden on August 06, 2007, 08:21:48 AM
I think we should clarify here that any PTR reference does not define string or any
other type of space in memory.  It can only be made to point to existing structures,.  Strings are only allocated when actually defined somewhere, at which
point you can establish a pointer reference to them.  So a TYPE structure can
either contain strings which are allocated when you DIM a variable of that type,
or if they have string pointers as you advise, those strings must be allocated elsewhere, since only the 32-bit pointer reference will be allocated in the new
variable of that type.  And the pointer references will be set to NUL until such
time as you create a reference to the specific string in question.  Firther, you
specified here that these are pointers to ASCIIZ strings, which have a maximum
length specified, but you specifically mentioned dynamic strings, which one would think of as the dynamic, variable length variety.  That's a different anamal.  If you set the PTR references to the string locations for this type of string, you are at risk of the STRPTR() value becoming invalid, because the Heap
where these strings are stored is constantly being released and reallocated as
other strings have their contents changed or discarded.  For these strings, you
might better use VARPTR(), which will remain constant, and which will give you
access to both the location of the string contents and the length (number of
characters currently allocated to the string).
Title: Re: Dynamic strings in TYPE structures
Post by: Eros Olmi on August 06, 2007, 12:13:30 PM
PTR to ASCIIZ are bad beats. They can produce unwanted behave if user is not conscious of what he/she is doing. There are also some limitations if that pointer is passed to something expecting big dynamic string because ASCIIZ strings are limited to about 16Mb. Also user must be aware he/she cannot embed NULL chars as real chars. If user is all aware, than no problem.

A different approach can be the one I suggested in another post that is to use just a LONG and using PB power to do the hard work:

Code: [Select]
'---Define some LONGs in place of strings
type MyUDT
  aString1 as long
  aString2 as long
end type

'---Define a UDT variable
dim MyVar as MyUDT

'---Define a dummy string that in reality is a place holder pointer pointing to the LONG inside the UDT
dim DummyString as string at varptr(MyVar.aString1)

'---Do what you need with DummyString like any other standard dynamic string
DummyString = "abcdef"
DummyString = repeat$(10, DummyString)

'---The bad part, free allocated string data manually
remove MyString

In this case DIM ... AT will make the hard work. Programmer have to just deal with deallocation.

Code: [Select]
REDIM DummyString(1& to 1&) AS STRING AT VARPTR(MyVar.aString1)
can be used when accessing different string dynamically because you can use REDIM ... AT many times in code always with the same DummyString name but at different mem locations.

ADDED: I've not made any speed test of using DIM or REDIM ... AT. Maybe worth to check how much CPU is taken when making a lot of REDIM ... AT

Title: Re: Dynamic strings in TYPE structures
Post by: Petr Schreiber on August 06, 2007, 01:26:10 PM
Hi Eros,

this looks like a good approach !
DIM .. AT is very powerful.

Title: Re: Dynamic strings in TYPE structures
Post by: Eros Olmi on August 06, 2007, 02:18:23 PM
The most interesting part of this approach is that after DIM or REDIM ... AT, the string is a 100% dynamic string
It can be used in any place a dynamic string is used in exact the same way as a standard dynamic string, passed BYREF or BYVAL.
Working with pointers on such created strings will follow the same rules as a standard dynamic string.

In any case, every approach is interesting. It depends on what you need to do. Than keep the best fits your needs.
Personally I do not like to deal with ASCIIZ strings because of the limitations they have especially on NULL chars. But this is just my personal approach.

Title: Re: Dynamic strings in TYPE structures
Post by: Paul Squires on August 06, 2007, 02:49:25 PM
I guess my use of "dynamic string" is a little misleading. I am not referring to an OLE string but rather the fact that you can create strings in a TYPE that need not be of one pre-defined length. I guess that embedded Nulls would be an issue but for my uses I have never had to deal with them so I have always stuck with my code above.

I could have used the OLE engine directly:

Code: [Select]
#Compile Exe

#Include ""                   

Function PBMain() As Long                   

   Local st    As String
   Local pStr  As Asciiz Ptr
   st = "Paul Squires"
   ' Example of using the OLE dynamic string functions. Be aware
   ' of the ByVal's... they are needed.
   pStr = SysAllocStringByteLen( ByCopy st, ByVal Len(st) )
   MsgBox "pStr="   & Format$(pStr) & $CrLf & _
          "len="    & Format$(SysStringByteLen( ByVal pStr )) & $CrLf & _
          "String=" & @pStr & $CrLf & _
          "len="    & Format$(Len(@pStr))
   ' Free the string
   SysFreeString ByVal pStr
   ' Error <> 0 indicates that string was not freed.
   MsgBox "String freed. GetLastError=" & Format$(GetLastError)
End Function

Title: Re: Dynamic strings in TYPE structures
Post by: Eros Olmi on August 06, 2007, 03:03:16 PM
No problem here. For me it was clear what "dynamic" stand for in this post.
Every technique is really interesting because it shows the many different ways things can be done.

Maybe a little confusing for those start programming of for those gui that have never deal with pointers.

Thanks a lot