how to tell if BSTR var has been freed ?

General FreeBASIC programming questions.
srvaldez
Posts: 3373
Joined: Sep 25, 2005 21:54

how to tell if BSTR var has been freed ?

Post by srvaldez »

with all the talk about BSTR strings, is there a way to determine if the memory allocated has been freed?
is it OK to use SysFreeString on a BSTR that has already been freed ? I am hoping that SysFreeString would just ignore if the var was already clear.
[edit]
the answer to the second part of the question is no https://stackoverflow.com/questions/477 ... -correctly
Josep Roca
Posts: 564
Joined: Sep 27, 2016 18:20
Location: Valencia, Spain

Re: how to tell if BSTR var has been freed ?

Post by Josep Roca »

As with any other kind of pointer, set it to null after freeing it. Calling SysFreeString with a null pointer does nothing, but calling it with a wrong pointer can be harmful.
srvaldez
Posts: 3373
Joined: Sep 25, 2005 21:54

Re: how to tell if BSTR var has been freed ?

Post by srvaldez »

thanks Josep Roca, makes perfect sense.
deltarho[1859]
Posts: 4292
Joined: Jan 02, 2017 0:34
Location: UK
Contact:

Re: how to tell if BSTR var has been freed ?

Post by deltarho[1859] »

I have been trying to fathom out exactly what SysAllocString and SysAllocStringByteLen actually do.

According to MSDN they both return strings. I won't argue with Microsoft but that description is a bit vague and, unfortunately, that is the case with many of the APIs.

As José Roca pointed out in another thread a BSTR consists of a length prefix, a data string, and a terminator. For Ansi BSTR the data string will be in Ubytes and the terminator will be a single null. For Unicode BSTR the data string will be in Ushorts and the terminator will be a double null.

After some experimentation it turns out that the return value of the two above functions is a pointer to the BSTR data string. That may have been obvious to some but it was not to me.

In keeping with Microsoft's appending function names with either 'A' or 'W' to denote Ansi or Unicode(Wide) the following uses StrToBSTRA and StrToBSTRW.

This is a typical output:

Code: Select all

70            F
114           r
101           e
101           e
66            B
65            A
83            S
73            I
67            C
0
 
Count 10
 
70            F
114           r
101           e
101           e
66            B
65            A
83            S
73            I
67            C
0
0
 
Count 10
The second part is the same as the first part. However, in the second part I am stepping along the data string two bytes at a time.

With regard SysFreeString MSDN describes it's parameter as "The previously allocated string". I take that to mean the pointer returned by either SysAllocString or SysAllocStringByteLen.

So, we should not use 'StrToBSTRA( "filepath" )', for example, as a FreeBASIC parameter. Yes, it will pass a pointer but we need to make a note of that pointer first otherwise we will not be able to use SysFreeString.

BSTR.bas

Code: Select all

#include once "win/ole2.bi"
 
' ******
 
Function StrToBSTRA( FB_String As String ) As BSTR
  Return  SysAllocStringByteLen( FB_String, Len(FB_String) )
End Function
 
Function StrToBSTRW( FB_WString As WString ) As BSTR
  Return  SysAllocString( FB_WString )
End Function
 
' ******
 
' ANSI
 
Dim As String S = "FreeBASIC"
Dim As BSTR bstrS
Dim As Long i
 
bstrS = StrToBSTRA( S )
 
Dim temp As Byte Ptr
Dim As Ubyte bvalue
Dim As Long count
 
temp = Cast( Byte Ptr, bstrS )
 
Do
  bvalue = Peek( Ubyte, temp )
  Print bvalue, Chr( bvalue )
  temp += 1
  count += 1
Loop Until bvalue = 0
 
Print
Print "Count";count
Print
 
SysFreeString( bstrS )
 
count = 0
 
' UNICODE
 
Dim As Wstring*20 wS = "FreeBASIC"
Dim As BSTR bstrwS
 
bstrwS = StrToBSTRW( wS )
 
temp = Cast( Byte Ptr, bstrwS )
 
Do
  bvalue = Peek( Ubyte, temp )
  Print bvalue, Chr( bvalue )
  temp += 2  ' <=============== Stepping two bytes (Unicode)
  count += 1
Loop Until bvalue = 0
temp -= 1    ' <=============== Step back one byte
Print Peek( Ubyte, temp )
 
Print
Print "Count";count
 
SysFreeString( bstrwS )
 
Print : Print "Done"
 
Sleep
dodicat
Posts: 7976
Joined: Jan 10, 2006 20:30
Location: Scotland

Re: how to tell if BSTR var has been freed ?

Post by dodicat »

Bstr is actually a pointer.
You can use any ptr for any instance of bstring (as in my previous code)
You can substitute any ptr for bsrt in your above code.
deltarho[1859]
Posts: 4292
Joined: Jan 02, 2017 0:34
Location: UK
Contact:

Re: how to tell if BSTR var has been freed ?

Post by deltarho[1859] »

dodicat wrote:Bstr is actually a pointer.
I have just shown that to be the case in my last post. The giveaway is in "is a pointer to the BSTR data string".

As for your next two statements I have absolutely no idea what you are talking about.
Josep Roca
Posts: 564
Joined: Sep 27, 2016 18:20
Location: Valencia, Spain

Re: how to tell if BSTR var has been freed ?

Post by Josep Roca »

> For Ansi BSTR the data string will be in Ubytes and the terminator will be a single null.

Wrong. The terminator is always a double null. Both are the same thing, and the only thing that changes is how you treat them when you store or read data from it. Since Windows always uses unicode for strings (most of the "A" functions are simply wrappers that convert the passed strings to unicode and call the "W" function), allowing single bytes was mainly intended to store binary data, for example to use a BSTR as a buffer to store an image. Notice that SysAllocStringByteLen stores bytes and SysAllocString words, but there is only a SysFreeString (no one for ansi and another for wide) because the memory is allocated in the same way.

And to extract characters from an unicode string with PEEK, cast it to USHORT PTR (or WORD PTR), to read 2 bytes at a time, not as BYTE PTR and skipping the leading byte. Unicode characters are two bytes wide, and if you skip the first one it will only work with these unicode characters whose first byte is NULL, i.e. English and a few more.
Last edited by Josep Roca on Feb 20, 2018 20:09, edited 4 times in total.
Josep Roca
Posts: 564
Joined: Sep 27, 2016 18:20
Location: Valencia, Spain

Re: how to tell if BSTR var has been freed ?

Post by Josep Roca »

> As for your next two statements I have absolutely no idea what you are talking about.

That you can use AS ANY PTR instead of AS BSTR, but this isn't going to offer you any advantage. I cast it as WSTRING PTR because BSTR is defined as WCHAR PTR in the latest FB includes (previously it was defined as WSTRING PTR) and if you try to print a BSTR deferencing the pointer it will only print the first character.

As this change broke my code, I use AFX_BSTR since then:

Code: Select all

' // The definition for BSTR in the FreeBASIC headers was inconveniently changed to WCHAR
#ifndef AFX_BSTR
   #define AFX_BSTR WSTRING PTR
#ENDIF
Just in case it is changed again...
deltarho[1859]
Posts: 4292
Joined: Jan 02, 2017 0:34
Location: UK
Contact:

Re: how to tell if BSTR var has been freed ?

Post by deltarho[1859] »

Josep Roca wrote:Wrong. The terminator is always a double null.
So, I added 'Print Peek( Ubyte, temp )' after my first loop and found that to be the case. With the second loop I already stepped back a byte to see the first null.

I used Byte Ptr, as opposed to Ushort Ptr, because I knew that I was dealing with ANSI characters. Of course, if I was writing code which could be used in all countries then I would not do that because I would not know what I was dealing with.
deltarho[1859]
Posts: 4292
Joined: Jan 02, 2017 0:34
Location: UK
Contact:

Re: how to tell if BSTR var has been freed ?

Post by deltarho[1859] »

I can see what dodicat was on about now. I wish folk would use either mixed case or upper case. The penny dropped when José wrote 'ANY PTR' as opposed to 'any ptr'.

I see posts here using wall to wall lower case. Perhaps it is just me but I find them almost unreadable. I get emails from folk using lower case. That is not how the English language is intended to be written. Before I post my code I use the formatting feature of FBIde. I don't use FBIde for anything else but neither poseidonFB nor WinFBE are very well behaved in this department.
dodicat
Posts: 7976
Joined: Jan 10, 2006 20:30
Location: Scotland

Re: how to tell if BSTR var has been freed ?

Post by dodicat »

Sorry If I confused you DeltaRho.
FreeBasic is generally not case sensitive.
Power basic?? ( I forget)


Pulling functions directly from dll's has the advantage of bypassing .bi files.
Any pointer is a handy generality, it won't spoil anything.
How you dereference any returned pointer is optional.
Trivial example.

Code: Select all



declare function allocstring1 lib "oleaut32.dll" alias "SysAllocString"( as any ptr) as any ptr
declare function allocstring2 lib "oleaut32.dll" alias "SysAllocStringByteLen"( as any ptr,as long) as any ptr
declare sub free lib "oleaut32.dll" alias "SysFreeString"(as any ptr) 

dim as wstring * 6 h="Hello"
dim as any ptr p=allocstring1(@h)
dim as wstring * 20 w= *cptr(wstring ptr,p)
print w


free(p)
p=allocstring2(@"FreeBASIC",len("FreeBASIC"))
dim as string ans= *cptr(zstring ptr,p)
print ans
free(p)
p=0
sleep
 
deltarho[1859]
Posts: 4292
Joined: Jan 02, 2017 0:34
Location: UK
Contact:

Re: how to tell if BSTR var has been freed ?

Post by deltarho[1859] »

dodicat wrote:FreeBasic is generally not case sensitive.
Power basic?? ( I forget)
Neither is PowerBASIC.

The point I was getting at is that, IMHO, keywords should be either mixed case or upper case; although I find upper case a bit 'in my face'. Lower case, to me, is indicative of the 'I don't 'phone anybody anymore, I text them' brigade. <smile>
Pulling functions directly from dll's has the advantage of bypassing .bi files.
Why is that an advantage?
Any pointer is a handy gerality, it won't spoil anything.
It spoils everything as far as I am concerned. I prefer to know what I am pointing at.

When I use the Windows APIs I use their data types and their identifiers. The bi files do exactly the same so all three of us are 'on the same page', so to speak. It makes debugging a lot easier when we are all talking in the same language. Before version 10 of PowerBASIC its declarations bore little resemblance to MSDN and I used to get mauled pretty badly by the crypto' APIs'. Version 10 saw a complete rewrite and the declarations adhered to MSDN like glue. I stopped getting mauled; not entirely but very much less. <smile>

Here is how I would write your last post's code.

Code: Select all

#include once "win/ole2.bi"
 
Function StrToBstrA( FB_String As String ) As Bstr ' ANSI
  Return  SysAllocStringByteLen( FB_String, Len(FB_String) )
End Function
 
Function StrToBstrW( FB_WString As WString ) As Bstr ' UNICODE
  Return  SysAllocString( FB_WString )
End Function
 
Dim As Bstr p
p = StrToBstrW( Wstr( "Hello" ) )
Dim As Wstring * 20 w = *Cptr( Wstring Ptr, p )
Print w
SysFreeString( p )
 
p = StrToBstrA( "FreeBASIC" )
Dim As String ans = *Cptr( Zstring ptr, p )
Print ans
SysFreeString( p )
p = 0
Sleep
Your code compiles to 36,352 bytes. My code compiles to 35,328 bytes; using -gen gcc -Wc -O3

It is a minor point but you are using a suffix of '1' for Unicode and a suffix of '2' for Ansi. If I was a few screens away from the declarations I may not remember how they worked. I would need to be in a pretty bad state not to remember what StrToBstrA and StrToBstrW meant. <smile>

Blimey, I have had a right go. Still, that is what happens with differing opinions.
dodicat
Posts: 7976
Joined: Jan 10, 2006 20:30
Location: Scotland

Re: how to tell if BSTR var has been freed ?

Post by dodicat »

Thank you DeltaRho.
I thought that you were working with FreeBASIC and PowerBASIC WinApi functions and you wanted a path through the middle.
(To steer your Pb .dll towards FreeBASIC use)
The Winapi is there for all windows coders so in itself is very general, although most of their examples are in C I notice.
I do notice that fb strings are not very well received out there (In the big wide world).
I have experimented trying to pass them to C,C++, Panoramic.
I think that what is written in fb strings stays in fb strings.
Sorry about the lower case thing.
As you say fbide does a neat job with keywords, I usually format and auto indent before posting (but I forget sometimes)
I think with all the snippets regarding BSTR, srvaldez, yourself, e.t.c. BSTR has been exposed a bit more, and I, myself, have learned a bit about them, because truthfully, before last week I had never heard of 'em.
deltarho[1859]
Posts: 4292
Joined: Jan 02, 2017 0:34
Location: UK
Contact:

Re: how to tell if BSTR var has been freed ?

Post by deltarho[1859] »

dodicat wrote:I thought that you were working with FreeBASIC and PowerBASIC WinApi functions and you wanted a path through the middle.
(To steer your Pb .dll towards FreeBASIC use)
Actually what I want is the easiest way to write in FB.

One way is to leave the PB code as is and use the BSTR functions, developed above, applied to FB strings. The other way is to re-compile PB, using 'szInFile As Stringz * 260'. for example, and use either 'ByRef As ZString' or 'As ZString Ptr' in FB; provided, of course, the strings do not excedd 260 bytes. In my particular case they do not as I am passing filepaths and passwords. In fact, I am not even using passwords, I am using passkeys limited to 256 bits (32 bytes).

Personally, I reckon that the latter method is easier because we avoid messing about with converting to BSTR; that works but it is a bit of a 'carry on' converting to BSTR and then freeing up memory.

As mentioned earlier if we do not have access to the dll's compiler then we cannot re-compile so we have to go the BSTR route, like it or not. With C, even when we have a C compiler, we have to go the BSTR route because C has no idea what ByRef is. <laugh>
Sorry about the lower case thing.
I sometimes post and see that some keywords are lower case. However, I rip it out immediately as it very nearly brings me out in a rash. I could takes tablets for it but it is only a foible.

I wrongly maligned poseidenFB earlier. Perhaps Kuan has made changes since I last used it. Whatever we are looking at if we go to Options>Tools>Convert Keyword Case>Mixedcase and apply to the whole source then we get mixed case. More to the point we get 'What you see is what you get' (WYSIWYG) because if we now Copy and Paste the mixed case persists. WinFBE, on the other hand, does not do that. What we are looking at does not persist on a Copy and Paste. I shall now use poseidenFB for that task because FBIde crashes on closing after a Format. It did not used to do that but I think one of the last Windows 10 updates screwed up FBIde. So, why not use poseidonFB for the editing and be done with it? I do not like how builds are handled. WinFBE is straight forward; like FBEdit. WinFBE also has WinFBX templates built in and since I am getting more involved in WinFBX it is great to be able to 'pull in' a template quickly.

Added: BTW, you may have noticed that my BSTR conversion functions do not need strings passed with a '@' prefix. When 'As Any Ptr' is used we mask what we are pointing at. In my case I am passing via 'As LPCSTR' as per MSDN and the bi file and LPCSTR is defined in FB as 'const zstring ptr'.
dodicat
Posts: 7976
Joined: Jan 10, 2006 20:30
Location: Scotland

Re: how to tell if BSTR var has been freed ?

Post by dodicat »

OK DeltaRho.
Good luck with your fb translations.
A pity you couldn't bring across PowerBASIC macro capabilities.
I remember they are quite powerful (Recursive e.t.c.)
Here is my last snippet here.
Use wstring and zstring as parameters instead of Any Pointer.
A small test to see the viability of utf16 with SysAllocString

Code: Select all

Declare Function allocstringU Lib "oleaut32.dll" Alias "SysAllocString"( As wstring) As Any Ptr
Declare Function allocstringA Lib "oleaut32.dll" Alias "SysAllocStringByteLen"( As zstring,As Long) As Any Ptr
Declare Sub _free Lib "oleaut32.dll" Alias "SysFreeString"(As Any Ptr) 

#include "file.bi"
Sub savefileUTF(filename As String,p As wstring)
    Dim As Long n=Freefile
    Open filename For Output Encoding "utf16" As #n
    Print #n,p
    Close #n
End Sub

'==================================

Dim As Wstring * 1100 h

For n As Long=2000 To 2500
    h+=Wchr(Val("&h"+Str(n)))+" "
Next
h+="END"
Dim As Any Ptr p=allocstringU(h)

Dim As wstring * 1100 w= *Cptr(wstring Ptr,p)

savefileUTF("Tester.txt",w)
Shell "notepad tester.txt"
Kill  "tester.txt"
Print Iif (Fileexists("tester.txt"),"dlete the file manually","Temp file is deleted")
_free(p)
p=allocstringA("That's all",Len("That's all"))
Dim As String ans= *Cptr(zstring Ptr,p)
Print ans
_free(p)
p=0


Sleep
 
Post Reply