Appdata and foreign characters?

BlitzMax Forums/BlitzMax Programming/Appdata and foreign characters?

Robby(Posted 2016) [#1]
A quick question about a function to locate the users appdata folder.
I've had this run perfectly on many user's PC's, until a Polish friend of
mine apparently got caught in an infinite loop where the function is being called. The only difference is his name under \users is Michal, but the "l" is a stylized one with a line through it, much like a German Z is sometimes written.
Is this an issue with Windows? Here's the code, which elsewhere works fine:

' gets appdata folder in files_path, ex.
c:\users\robby\appdata\roaming

Const CSIDL_APPDATA = $1a
Const CSIDL_COMMON_APPDATA = $23
Global appdata_path_ptr:Byte Ptr
Global files_path:String


Extern "win32"
Function SHGetFolderPathA(hWnd:Int, folder:Int, token:Int, FLAGS:Int, str:Byte Ptr)
End Extern

GetAppDataFolder()

' we will use this with just \madman to test shortly - putting here so no \\ double stuff
files_path_test = files_path

' append my madman_data directory to it. \\ needed to make, but will be just \ in actual folder
files_path = files_path + "\\madman_data\\"

'append the "test if exists" one with the real dir, \madman_data
files_path_test = files_path_test + "\madman_data"


' If the folder does not exist, make it - we test with \madman_data but must create with \\madman_data\\
If FileType(files_path_test) = 0 Then ' to test, must have the \madman_data one
CreateDir(files_path) ' to create, must have the \\madman_data\\ one
End If

' wether created or already there, set files_path to final form to use globally on all file I\O, \ appended for ease of use
files_path = files_path_test + "\"

(main program here)

Function GetAppDataFolder()
Local res, dir, cnt

appdata_path_ptr = New Byte[260]
res = SHGetFolderPathA(Null, CSIDL_APPDATA, Null, 1,appdata_path_ptr)

cnt = 0
While (appdata_path_ptr[cnt] <> 0)
cnt :+ 1
Wend
files_path = String.FromBytes(appdata_path_ptr, cnt)
EndFunction


xlsior(Posted 2016) [#2]
It could be that it can't handle unicode...

IIRC bah.volumes can retrieve those folder names if they contain unusual characters.


Brucey(Posted 2016) [#3]
The "A" in SHGetFolderPathA is your problem.


Robby(Posted 2016) [#4]
I'm curious how the A would affect it? I checked on the msdn site and it seems that SHGetFolderPath has been depreciated, replaced by the newer SHGetKnownFolderPath. Not sure if that would make a difference, but does anyone know how this function would be implemented to get the correct AppData folder?
The API can be a bit dizzying! Thanks, -Rob


Brucey(Posted 2016) [#5]
"A" is presumably for ASCII, and "W" for Wide?

Where Wide supports Unicode characters.

As xlsior points out, BaH.Volumes handles unicode paths on all platforms.


Robby(Posted 2016) [#6]
The guy's actual path is:

C:\Users\Micha&#322;\Appdata\Roaming

But my variable assignment is truncating the stylized L to:

C:\Users\Micha\Appdata\Roaming

I see that I can copy & paste this all over windows, even in the boards here, so windows, etc. seems to know the character.

Then I logged in a new account on my notebook, using Micha&#322; and sure enough I was able to get the same error.

The path string returns with the "L" chopped off.

Since this is not the actual path, madman is trying to append madman_data to a non-existant folder, which it thinks
is Micha. This would explain why it keeps running but doesn't actually crash out.

So it would seem that Blitzmax isn't seeing the character, or the function isn't.

The function I'm now using is this one. Its finding the path in the section I marked "Found in W"

The key here is that the truncation is occurring at:
result = String.FromWString( Short Ptr( LockBank( tempBank ) ) )

result returns with the "Micha" and the Polish small L gone.

Also, do you know the meaning of the ?Win32 and the other hanging ? near the end? I found this elsewhere on the Blitz boards.

Also, are there any examples anywhere about how to get and use a Brucey
module to get the appdata folder? -Rob


Function SHGetFolderPath:String( nFolder:Int, dwFlags:Int )

?Win32
Local tempBank:TBank
Local success:Byte = False

If _bbusew Then
tempBank = CreateBank( MAX_PATH * 2 ) 'WCHAR
success = SHGetFolderPathW( 0, nFolder, 0, dwFlags, LockBank( tempBank ) ) = S_OK
backfrom = "Found in W"
Else
tempBank = CreateBank( MAX_PATH )
success = SHGetFolderPathA( 0, nFolder, 0, dwFlags, LockBank( tempBank ) ) = S_OK
backfrom = "Found in A"
EndIf
UnlockBank( tempBank )

If success Then
Local result:String

If _bbusew Then
result = String.FromWString( Short Ptr( LockBank( tempBank ) ) )
Else
result = String.FromCString( LockBank( tempBank ) )
EndIf

UnlockBank( tempBank )
Return result
EndIf
?
Return Null 'Check for a Null result to identify errors.

End Function


xlsior(Posted 2016) [#7]
As mentioned above, the path contains a unicode character that's getting stripped. There are two workarounds posted above to keep that from happening.


Brucey(Posted 2016) [#8]
Here's an example of BaH.Volumes GetUserAppDir() :
SuperStrict

Import BaH.Volumes

Notify GetUserAppDir()

Notify displays the string in a message box. You can "Print" the text, but MaxIDE's output window doesn't display the text correctly, even though it is correct within the String.

Here is the output :



<uninteresting side note>
GCC, funnily enough, refused to build anything under that username, as the Temp path it wanted, had the user name as "Michal" (normal ASCII lowercase "L"), which of course did not exist. Creating the temp dir with suitable access allowed GCC to compile...
So even big tools have issues with unicode usernames... pah.
</end>


Robby(Posted 2016) [#9]
Thanks for all the info, guys. I must admit I'm a little confused with a few things.

For example, clicking some of the links seems to go to several places, which list a lot of modules. Which one do I download?

Implementing or importing it seems different - I checked a few other board posts about appdata. For example one has:
SuperStrict
Framework BaH.Volumes
Import BRL.Standardio

Another has:
SuperStrict
Import BaH.Volumes before the GetUserAppDir() can be used.

Brucey mentioned that "A" is presumably for ASCII, and "W" for Wide?
Where Wide supports Unicode characters.

I put the backfrom = "Found in W" and backfrom = "Found in A" to check which form was getting the path, and it does indeed find it in W, but Michal's name is still getting the Polish L truncated.

You can see on the post where I pasted the name, the L turned into the odd &@233;

So while these functions usually find the path, yet various foreign characters see to get muffed. There may be other users with foreign letters in their names, Germans with a crossed Z and the like.

My friend uses these same functions in C, but it appears a C String assigns better than a Blitz string. Maybe Blitz strings can only use ASCII chars?

Anyone ever solve this?


Robby(Posted 2016) [#10]
Oh! I must be getting absent-minded! I missed the part about displaying the text!
All I had to do was change my SHGetFolderPathA function to the SHGetFolderPathW and it worked!

It is indeed finding the path! Even saved and read back a few variables to test.

You guys really saved me here! My game, Madman, was recently greenlit on Steam. More than 5 years of work. But my bank was going through a merger so I didn't put it up on Steam right away. If I released it with that A function???

Saved me a lot of grief, guys! Can't thank you all, especially our Brucey, enough!

Also - Brucey - I looked through your many modules and commend you on your excellence. As a programmer since the TRS-80 days, I'm sure I can make use of your modules in the future. -Rob, aka "Dr. Dungeon"


Robby(Posted 2016) [#11]
That's here if anyone wants to see:
http://steamcommunity.com/sharedfiles/filedetails/?id=457828980