CString confusion
BlitzMax Forums/BlitzMax Programming/CString confusion
| ||
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. |
| ||
String.FromCString |
| ||
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. |
| ||
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' |
| ||
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( ). |
| ||
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* |
| ||
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. |
| ||
If it returns a CString then its simplyVersion:String = String.FromCString ( GetOSInfo ( "Version" ) ) Returning CString means, that the function is declared in extern like this GetOSInfo$z (...) |
| ||
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") |
| ||
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. |
| ||
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. |
| ||
:) |
| ||
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. |
| ||
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;) |
| ||
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? |
| ||
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? |
| ||
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. |
| ||
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. |
| ||
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 resultEverything is working properly again now. |
| ||
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... |
| ||
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. |
| ||
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 |
| ||
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. |
| ||
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 |
| ||
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. |
| ||
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. |
| ||
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. |
| ||
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! |
| ||
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 ) |
| ||
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 |
| ||
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... |
| ||
@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. |
| ||
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.... |
| ||
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. |
| ||
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 |
| ||
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;) |
| ||
Thanks for 'pointing' all this out clearly for me;) I'd just like to point out(sorry!) that was a *really* bad joke |
| ||
This thread becomes more and more valuable to show "GC haters" why it is that powerfull and why other approaches are really outdated :-) |
| ||
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. |