CString confusion

BlitzMax Forums/BlitzMax Programming/CString confusion

rdodson41(Posted 2006) [#1]
How do you convert a CString back to a normal String? I want to use getenv() but it returns a CString and I cant seem to make that back into a normal BMX String.


Azathoth(Posted 2006) [#2]
String.FromCString


rdodson41(Posted 2006) [#3]
Yeah I tried that but it said something like "CString and Byte Ptr are not compatable" which is absurd since a cstring is exactly a byte ptr. Maybe I'm doing something wrong so i'll go back and check.


kenshin(Posted 2006) [#4]
I'm getting this too. This line used to work fine for me in earlier versions:
Global Name:String = String.FromCString ( Byte Ptr ("test")  )

Now it gives an error:

Unable to convert from 'String' to 'Byte Ptr'


N(Posted 2006) [#5]
As far as I know, if something returns a {CW}String ($z or $w, respectively) you should be able to just do Local myString$ = somethingThatReturnsACString( ... stuff ... ).

If it returns a Byte Ptr then you use String.FromCString( somethingThatReturnsABytePtr( ... stuff ... ).

Unable to convert from 'String' to 'Byte Ptr'


This is because strings are objects and you're not trying to convert a variable to a pointer, but rather a value to a pointer.

The compiler no longer allows you to create a pointer to an object via VarPtr (this was always supposed to be the case, but somehow it got overlooked!). Therefore, 'Object Ptr' (or ' Ptr' etc) is no longer accepted. This is because there is not any safe way to write to such a pointer without massively confusing garbage collection/detection. It is still legal to convert an object reference to a 'Byte Ptr', but you should be aware the pointer will only be valid while the object remains 'in scope'.


Use String.To{CW}String( ).


kenshin(Posted 2006) [#6]
I'm still stumped.

I've got a function in a DLL called GetOSInfo() that returns a CString and want to read that string. This used to work:

Version:String = String.FromCString ( Byte Ptr ( GetOSInfo ( "Version" ) ) )


I've tried various other combinations but always get some conversion error.

I remember having trouble getting it to work before.

*Confused*


rdodson41(Posted 2006) [#7]
SuperStrict

Local home:String = String.FromCString(getenv_("HOME"))

Print home

I get the error 'Unable to Convert from CString to Byte Ptr' for this piece of code. But that is absolutely crazy because a C string is nothing but a pointer to a byte. Theres something really wrong here...

[EDIT] Casting the return value of getenv_ to a Byte Ptr doesn't work either.


Dreamora(Posted 2006) [#8]
If it returns a CString then its simply

Version:String = String.FromCString ( GetOSInfo ( "Version" )  )


Returning CString means, that the function is declared in extern like this


  GetOSInfo$z (...)



skidracer(Posted 2006) [#9]
could you post your GetOSInfo extern, if it's returning a cstring simply define it as

Extern Function GetOSInfo$z(param$z) "win32"

and then you can just use

Version$=GetOSInfo("Version")

and similarly

Local home$ = getenv_("HOME")


rdodson41(Posted 2006) [#10]
Ah yeah just doing Local s:String = getenv_("HOME") actually works. But then it was originally give me some other problem when I tried to do this.


kenshin(Posted 2006) [#11]
My extern was defined like:
Field GetOSInfo:String Ptr ( typ:Byte Ptr ) "win32"

Changed it to this:
Field GetOSInfo$z ( typ$z ) "win32"

and it's working properly now.

Thankyou for the help.


kenshin(Posted 2006) [#12]
:)


kenshin(Posted 2006) [#13]
v1.18 has broken this again. Here's a test I'm trying to get working:
Extern "win32"
	Function GetOSInfo$z ( typ$z ) "win32"
EndExtern
		Local DLL:Int = LoadLibraryA ( "Data/DLL/OSInfo.dll" )
		Local GetOSInfoProc:Byte Ptr = GetProcAddress ( DLL, "GetOSInfo" )
		Local Version:String = GetOSInfo ( "Version" )

This new version is bugging out on a lot of my DLL handling stuff.


kenshin(Posted 2006) [#14]
I'm not sure why this doesn't work either:
Extern "win32"
	Function GetOSInfoProc$z ( typ$z ) "win32"
EndExtern
Global DLL:Int = LoadLibraryA ( "Data/DLL/OSInfo.dll" )
GetOSInfoProc = GetProcAddress ( DLL, "GetOSInfo" )

I think I need more caffeine;)


kenshin(Posted 2006) [#15]
I give up. Seems you can only use the cstring in the extern declaration with v1.18. But this doesn't work:
Global DLL:Int = LoadLibraryA ( "Data/DLL/OSInfo.dll" )
Extern "win32"
Function GetOSInfoProc$z ( typ$z ) "win32" = GetProcAddress ( DLL, "GetOSInfo" )
EndExtern

and neither does this:
Global DLL:Int = LoadLibraryA ( "Data/DLL/OSInfo.dll" )
GetOSInfoProc$z ( typ$z ) "win32" = GetProcAddress ( DLL, "GetOSInfo" )

Anyone kind enough to give me some help?


Dreamora(Posted 2006) [#16]
The first can't work. Your function is not part of the regular windows "core dlls" thus win32 will not work.

I can't say if it works, but did you try to use :Byte Ptr instead of $z with your examples?


kenshin(Posted 2006) [#17]
I tried this:
Global DLL:Int = LoadLibraryA ( "Data/DLL/OSInfo.dll" )
GetOSInfoProc:Byte Ptr ( typ:Byte Ptr ) = GetProcAddress ( DLL, "GetOSInfo" )

but it gives an error:

Expecting expression but encountered ')'

on the second line.


kenshin(Posted 2006) [#18]
Friggin' hooray. Got it working:
Global DLL:Int = LoadLibraryA ( "Data/DLL/OSInfo.dll" )
Global GetOSInfoProc:Byte Ptr ( typ:Byte Ptr ) = GetProcAddress ( DLL, "GetOSInfo" )
result:String = String.FromCString ( GetOSInfoProc("Name".ToCString ( ) ) )
Print result

This passes the "Name" string to the dll, and gets a different string back properly under v1.18.

Thanks for the Byte Ptr tip. I was using this before.


kenshin(Posted 2006) [#19]
One final thing I've noticed. When I moved one of my dll calls into a method it gave unhandled exceptions until I added the "win32" directive like this:
Local GetOSInfoProc:Byte Ptr ( typ:Byte Ptr ) "win32" = GetProcAddress ( DLL, "GetOSInfo" )
local result:String = String.FromCString ( GetOSInfoProc("Name".ToCString ( ) ) )
Print result
Everything is working properly again now.


Chris C(Posted 2006) [#20]
the "win32" bit is just the calling converntion theres
"C" as well if you use the following in c++

extern "c" int blah(){
...
}

I'd test all your calls in a loop a few hundred thousand times with a good process explorer just to be sure theres no memory leaks...


kenshin(Posted 2006) [#21]
Yep. It leaks:

For Local i:Int = 1 To 1000000
	Local DLL:Int = LoadLibraryA ( "Data/DLL/OSInfo.dll" )
	Local GetOSInfoProc:Byte Ptr ( typ:Byte Ptr ) "win32" = GetProcAddress ( DLL, "GetOSInfo" )
	Local Name:String = String.FromCString ( GetOSInfoProc ( "Name".ToCString ( ) ) )
	FreeLibrary ( DLL )
	GCCollect()
Next

The leak is occuring on the line before the FreeLibrary command. If I comment this out then there's no leak.


kenshin(Posted 2006) [#22]
Even a simplified version of the line leaks:
For Local i:Int = 1 To 1000000
	Local DLL:Int = LoadLibraryA ( "Data/DLL/OSInfo.dll" )
	Local GetOSInfoProc:Byte Ptr ( typ:Byte Ptr ) "win32" = GetProcAddress ( DLL, "GetOSInfo" )
	Local Name:Byte Ptr = GetOSInfoProc ( ("Name".ToCString ( ) ) )
	FreeLibrary ( DLL )
	GCCollect()
Next



Dreamora(Posted 2006) [#23]
you need to free the cstring manually!
They are NOT GC controlled (which is a reason they can't be used outside of extern anymore I assume)

Call MemFree Name to get rid of it again.


Chris C(Posted 2006) [#24]
Local DLL:Int = LoadLibraryA ( "Data/DLL/OSInfo.dll" )

ya only need to do this once, fortunatly once mapped to your
memory space it wont load another instance of the dll!!!

if you look at DLL you should notice that it retains the same value

for for info regarding Cstrings look at the last post I made on
http://www.blitzbasic.com/Community/posts.php?topic=57505


kenshin(Posted 2006) [#25]
Like this:
	Local DLL:Int = LoadLibraryA ( "Data/DLL/OSInfo.dll" )
	For Local i:Int = 1 To 1000000
		Local GetOSInfoProc:Byte Ptr ( typ:Byte Ptr ) "win32" = GetProcAddress ( DLL, "GetOSInfo" )
		Local Name:Byte Ptr = GetOSInfoProc ( "Name".ToCString ( ) ) 
		MemFree Name
		GCCollect()
	Next
	FreeLibrary ( DLL )

Still leaks though, regardless of whether the MemFree comes before or after the GCCollect()

I had the DLL open/close in the loop just to test whether that was leaking. I'll do a few more tests now.


Dreamora(Posted 2006) [#26]
Yeah in that case you need to memfree GetOSInfoProc as well.

You need to manually free all Ptr Variables you create.
They are not managed.


kenshin(Posted 2006) [#27]
Okay, got rid of the leak. This works fine:
	Local DLL:Int = LoadLibraryA ( "Data/DLL/OSInfo.dll" )
	Local GetOSInfoProc:Byte Ptr ( typ:Byte Ptr ) "win32" = GetProcAddress ( DLL, "GetOSInfo" )
	For Local i:Int = 1 To 10000000
		Local tp_ptr1:Byte Ptr = "Name".ToCString ( )
		Local tp_ptr2:Byte Ptr = GetOSInfoProc ( tp_ptr1 )
		Local Name:String = String.FromCString (tp_ptr2 )
		MemFree tp_ptr1
		MemFree tp_ptr2
	Next
	MemFree GetOSInfoProc
	FreeLibrary ( DLL )

Looks like I have to break it into a few steps so I can free the culprit.

Thankyou once again. I'm not really very good at this cstring stuff. Never had to use it before.


Chris C(Posted 2006) [#28]
Local tp_ptr1:Byte Ptr = "Name".ToCString ( )

if you think your going to call GetOSInfoProc many times with the same string "Name" make it at the start of your app and release it after the last time you need to use GetOSInfoProc

just realised I need to look into LoadLibraryA and GetProcAddress to see what they do with their strings....

oh ain't C strings extra fun now!


Chris C(Posted 2006) [#29]
Function LoadLibraryA( dll$z )
Function GetProcAddress:Byte Ptr( libhandle,func$z )

guess to be correct you should allocate and deallocate a Cstring for these too...

would be nice to have somthing like $dz (disposable z!) which gets disposed of automatically when the function returns

then you could pass Cstr's that need to remain valid with $z and just do GetProcAddress(dll,"blah") which would be defined as
Function GetProcAddress:Byte Ptr( libhandle,func$dz )


Dreamora(Posted 2006) [#30]
I fear it has a reason it is called CString and not string ;)
with some planing and design, using CStrings and other byte ptr depending types from external libraries is no problem, you have memfree to prevent memory leaks


Chris C(Posted 2006) [#31]

I fear it has a reason it is called CString and not string ;)


not once did I use the word string...

fine its not a problem, but allocating and deallocating before and after a function call is a pain

a disposable Cstring thats automatically deallocated after the function has returned would make life easier is all...


kenshin(Posted 2006) [#32]
@Chris C : Thank's for the pointer tips. The DLL's I'm working on at the moment are only called once at the start of my program so execution speed isn't an issue. They just retrieve specific details about the user's system like the OS type, version, build etc. I only had the pointers in the loop in order to test for the leak. I'll be sure to use your tip about storing the pointers if I need to use them on a regular basis, like every frame.

My confusion came from a few things:

- I'd never even heard of a cstring before.
- I have been using pointers but was not aware you needed to free them manually.

So, I have a few more memory leaks to fix. It's not that bad though, because I haven't been using them heavily throughout the program. I'll eliminate the remaining memory leaks this weekend and from now on I'll be checking for leaks regularly with ProcessExplorer.

Thanks for clearing this up for me.


Chris C(Posted 2006) [#33]
max strings are 2 bytes per character (I assume for unicode support in the future)

a "C string" is basically single byte per character terminated by a "null" character (=0)

pointers themselves dont need freeing they are just a variable holding a memory location
What needs freeing is the memory the pointer points to that has been allocated for the string (ie X number of characters)

hope this makes it plainer for you....


kenshin(Posted 2006) [#34]
Understood. I've done some more reading on pointers and think I've got it straight.

A pointer is a variable which contains the address of some data stored at a memory location. It's this data which needs to be manually freed. The pointer itself will be handled by GC. There's 2 problems I can create with pointers.

One is that I can 'forget' to free the memory that the pointer is pointing at. This will cause a memory leak like I was doing above. The pointer was pointing at a memory area containing a cstring. It's this memory area which I need to manually free.

The other problem I can cause is that I can free the memory the pointer is pointing at, but neglect the fact that the pointer now points at unallocated memory. I'm assuming that if I then attempt to access the memory via the pointer it would cause an exception/error as it's now unallocated memory which I'm trying to access. Probably best to set the pointer to NULL once I've freed the memory it points at. Either that or I point the pointer at a different, allocated memory area.


Chris C(Posted 2006) [#35]

There's 2 problems I can create with pointers.



LOL only 2 problems... lucky you!

but seriously yeah you've about got it


Either that or I point the pointer at a different, allocated memory area.


is probably not too good an idea, NULLing the pointer is less likly to lead to confusion


kenshin(Posted 2006) [#36]

LOL only 2 problems... lucky you!


Heh. I was reading here today:
http://en.wikipedia.org/wiki/Pointer
Looks like there's a number of other pitfalls too.


NULLing the pointer is less likly to lead to confusion


Okay, I'll do that then.

I think I know enough about using them now so as not to cause any problems for myself.

Thanks for 'pointing' all this out clearly for me;)


Chris C(Posted 2006) [#37]

Thanks for 'pointing' all this out clearly for me;)



I'd just like to point out(sorry!) that was a *really* bad joke


Dreamora(Posted 2006) [#38]
This thread becomes more and more valuable to show "GC haters" why it is that powerfull and why other approaches are really outdated :-)


kenshin(Posted 2006) [#39]

I'd just like to point out(sorry!) that was a *really* bad joke


Yep. Corny as hell. Which is exactly why you were grinning as you read it, maybe. But, I get the point(?) you're making.


This thread becomes more and more valuable to show "GC haters" why it is that powerfull and why other approaches are really outdated :-)


I can see that it would be really painful if there was no GC on any variables. This discussion of pointers, cstrings, and passing data to and from dll's has been extremely helpful. Stuff like this really needs to be explained in a lot more depth in the manual.