Results 1 to 39 of 39

Thread: [RESOLVED] FindFirstFile(W) Unicode problem with the windows non-unicode settings

  1. #1

    Thread Starter
    Fanatic Member Mith's Avatar
    Join Date
    Jul 2017
    Location
    Thailand
    Posts
    540

    Resolved [RESOLVED] FindFirstFile(W) Unicode problem with the windows non-unicode settings

    Dev-PC: Windows 7, VB6SP6

    With my app i use the API FindFirstFileWide to enum files with unicode characters in the name from a folder.

    I get the correct unicode file names if the regional settings for non-unicode programs are set to "English" or another non-unicode-language:

    Name:  Screenshot - 06.08.2021 , 11_23_07.png
Views: 1906
Size:  10.4 KB

    If i change the regional settings for non-unicode programs to "Chinese" the file names from the API FindFirstFileWide are all wrong:

    Name:  Screenshot - 06.08.2021 , 11_14_57.png
Views: 1781
Size:  52.6 KB

    Does anyone know how to fix this problem and get the real file names?

    Attached you will find a test project with the chinese file names.
    The project will need VBCCR17.OCX to display the unicode characters in the textbox.
    Attached Files Attached Files
    Last edited by Mith; Aug 6th, 2021 at 12:25 AM. Reason: added info at the title

  2. #2
    Angel of Code Niya's Avatar
    Join Date
    Nov 2011
    Posts
    9,017

    Re: FindFirstFile(W) Unicode problem with the windows non-unicode settings

    I couldn't test your program but I did look over the code and saw this:-
    Code:
    sFileName$ = TrimNull(StrConv(WFDW.cFileName$, vbFromUnicode))
    This looks a bit suspicious because it seems to be assuming that a VB6 String is an ANSI String which it is not. A VB6 String is actually a COM type called a BSTR which is Unicode String. There should be no need for that conversion. Assuming that FindFirstFileW is filling that member with a Unicode String, what your code would actually be doing is converting that String to an ANSI String that conforms to the current codepage of the system and then it casts it to a BSTR. This process could screw up the String irreversibly.

    This is just my guess though. I can't run your program because of some component missing or something and I'm not gonna risk screwing up my system settings by fiddling around with the locale settings.

    Try changing the above code to this:-
    Code:
    sFileName$ = TrimNull(WFDW.cFileName$)
    See if that works.
    Last edited by Niya; Aug 6th, 2021 at 07:20 AM.
    Treeview with NodeAdded/NodesRemoved events | BlinkLabel control | Calculate Permutations | Object Enums | ComboBox with centered items | .Net Internals article(not mine) | Wizard Control | Understanding Multi-Threading | Simple file compression | Demon Arena

    Copy/move files using Windows Shell | I'm not wanted

    C++ programmers will dismiss you as a cretinous simpleton for your inability to keep track of pointers chained 6 levels deep and Java programmers will pillory you for buying into the evils of Microsoft. Meanwhile C# programmers will get paid just a little bit more than you for writing exactly the same code and VB6 programmers will continue to whitter on about "footprints". - FunkyDexter

    There's just no reason to use garbage like InputBox. - jmcilhinney

    The threads I start are Niya and Olaf free zones. No arguing about the benefits of VB6 over .NET here please. Happiness must reign. - yereverluvinuncleber

  3. #3
    Angel of Code Niya's Avatar
    Join Date
    Nov 2011
    Posts
    9,017

    Re: FindFirstFile(W) Unicode problem with the windows non-unicode settings

    Oh I forgot to mention. There is one other possible point failure, the API call itself. I believe that VB6 marshals all Strings as ANSI Strings whenever it calls into the Win32 API. It's also possible that it's performing an ANSI conversion when it returns that WIN32_FIND_DATA structure. It won't matter if you're using the wide version of the API call. The way you get around that is by passing an array instead:-
    Code:
    Private Type WIN32_FIND_DATA_WIDE
            dwFileAttributes As Long
            ftCreationTime As FILETIME
            ftLastAccessTime As FILETIME
            ftLastWriteTime As FILETIME
            nFileSizeHigh As Long
            nFileSizeLow As Long
            dwReserved0 As Long
            dwReserved1 As Long
            cFileName(MAX_PATH_WIDE) As Byte
            cAlternate As String * 28
    End Type
    Then in your code you do this:-
    Replace:-
    Code:
    sFileName$ = TrimNull(StrConv(WFDW.cFileName$, vbFromUnicode))
    with this:-
    Code:
          sFileName$ = WFDW.cFileName
          sFileName$ = TrimNull(sFileName$)
    Treeview with NodeAdded/NodesRemoved events | BlinkLabel control | Calculate Permutations | Object Enums | ComboBox with centered items | .Net Internals article(not mine) | Wizard Control | Understanding Multi-Threading | Simple file compression | Demon Arena

    Copy/move files using Windows Shell | I'm not wanted

    C++ programmers will dismiss you as a cretinous simpleton for your inability to keep track of pointers chained 6 levels deep and Java programmers will pillory you for buying into the evils of Microsoft. Meanwhile C# programmers will get paid just a little bit more than you for writing exactly the same code and VB6 programmers will continue to whitter on about "footprints". - FunkyDexter

    There's just no reason to use garbage like InputBox. - jmcilhinney

    The threads I start are Niya and Olaf free zones. No arguing about the benefits of VB6 over .NET here please. Happiness must reign. - yereverluvinuncleber

  4. #4

    Thread Starter
    Fanatic Member Mith's Avatar
    Join Date
    Jul 2017
    Location
    Thailand
    Posts
    540

    Re: FindFirstFile(W) Unicode problem with the windows non-unicode settings

    Quote Originally Posted by Niya View Post
    This looks a bit suspicious because it seems to be assuming that a VB6 String is an ANSI String which it is not. A VB6 String is actually a COM type called a BSTR which is Unicode String. There should be no need for that conversion.
    I guess you never worked with this kind of Wide-APIs.
    This conversion is needed because each character at the mem block of the filename is stored with double bytes and need to be converted to a VB unicode string:
    Name:  Screenshot - 06.08.2021 , 19_47_23.png
Views: 1706
Size:  1.5 KB

  5. #5

    Thread Starter
    Fanatic Member Mith's Avatar
    Join Date
    Jul 2017
    Location
    Thailand
    Posts
    540

    Re: FindFirstFile(W) Unicode problem with the windows non-unicode settings

    Code:
            cFileName(MAX_PATH_WIDE) As Byte
    I tried your code change suggestion above and now i get the correct chinese file names on a computer with a chinese non-unicode program setting!

    Thanks a lot for the hint!

  6. #6
    Angel of Code Niya's Avatar
    Join Date
    Nov 2011
    Posts
    9,017

    Re: FindFirstFile(W) Unicode problem with the windows non-unicode settings

    Quote Originally Posted by Mith View Post
    I guess you never worked with this kind of Wide-APIs.
    I've can write string functions that work properly with Unicode in pure assembly. I know what I'm talking about.

    Also, here is a more correct implementation of what you are doing:-
    Code:
    Option Explicit
    
    Private Const MAX_PATH_WIDE = 520
    Private Const INVALID_HANDLE_VALUE = -1
    
    Private Type FILETIME
       dwLowDateTime As Long
       dwHighDateTime As Long
    End Type
    
    Private Type WIN32_FIND_DATA_WIDE
            dwFileAttributes As Long
            ftCreationTime As FILETIME
            ftLastAccessTime As FILETIME
            ftLastWriteTime As FILETIME
            nFileSizeHigh As Long
            nFileSizeLow As Long
            dwReserved0 As Long
            dwReserved1 As Long
            cFileName(MAX_PATH_WIDE) As Byte
            cAlternate(28) As Byte
            dwFileType As Long
            dwCreatorType As Long
            wFinderFlags As Integer
    End Type
    
    Private Declare Function FindFirstFileWide Lib "kernel32" Alias "FindFirstFileW" (ByVal lpFileName As Long, lpFindFileData As WIN32_FIND_DATA_WIDE) As Long
    Private Declare Function FindNextFileWide Lib "kernel32" Alias "FindNextFileW" (ByVal hFindFile As Long, lpFindFileData As WIN32_FIND_DATA_WIDE) As Long
    Private Declare Function FindClose Lib "kernel32" (ByVal hFindFile As Long) As Long
    
    
    Private Sub Form_Load()
        GetFileNames
    End Sub
    
    
    Private Sub GetFileNames()
    
    Dim hFile As Long
    Dim WFDW As WIN32_FIND_DATA_WIDE
    Dim sFileName As String
    
    hFile = FindFirstFileWide(StrPtr("D:\UnicodeFileNames\*.*"), WFDW)
    
    If hFile <> INVALID_HANDLE_VALUE Then
    
       Do
    
          sFileName = TrimNull(WFDW.cFileName)
    
          If (WFDW.dwFileAttributes And vbDirectory) <> vbDirectory Then
             
             Text1.Text = Text1.Text & sFileName & vbCrLf
    
          End If
    
       Loop While FindNextFileWide(hFile, WFDW)
    
       Call FindClose(hFile)
    
    End If
    
    End Sub
    
    Private Function TrimNull(ByVal s As String) As String
        Dim fn As Long
        fn = InStr(1, s, ChrW(0), vbTextCompare)
        
        TrimNull = Mid$(s, 1, fn - 1)
    End Function
    I've tested this and it works with Unicode file names correctly:-



    I've also corrected a serious problem with your original code. The WIN32_FIND_DATA_WIDE data structure in your original code was defined as:-
    Code:
    Private Type WIN32_FIND_DATA_WIDE
            dwFileAttributes As Long
            ftCreationTime As FILETIME
            ftLastAccessTime As FILETIME
            ftLastWriteTime As FILETIME
            nFileSizeHigh As Long
            nFileSizeLow As Long
            dwReserved0 As Long
            dwReserved1 As Long
            cFileName As String * MAX_PATH_WIDE
            cAlternate As String * 28
    End Type
    It's missing the last 3 fields which could be disastrous. Those missing fields come up to 10 bytes worth of data which means every time you call the API with that structure, 10 bytes of data is being written to a memory addresses it's not supposed to be writing to. This could result in an access violation error which would cause Windows to terminate your application. It may not happen 95% of the time you run that program but it could happen. You really want to fix this before you put it out into the wild.

    Here is the full source of my version.

    Additional credit to Elroy for his Unicode TextBox control.
    Attached Files Attached Files
    Treeview with NodeAdded/NodesRemoved events | BlinkLabel control | Calculate Permutations | Object Enums | ComboBox with centered items | .Net Internals article(not mine) | Wizard Control | Understanding Multi-Threading | Simple file compression | Demon Arena

    Copy/move files using Windows Shell | I'm not wanted

    C++ programmers will dismiss you as a cretinous simpleton for your inability to keep track of pointers chained 6 levels deep and Java programmers will pillory you for buying into the evils of Microsoft. Meanwhile C# programmers will get paid just a little bit more than you for writing exactly the same code and VB6 programmers will continue to whitter on about "footprints". - FunkyDexter

    There's just no reason to use garbage like InputBox. - jmcilhinney

    The threads I start are Niya and Olaf free zones. No arguing about the benefits of VB6 over .NET here please. Happiness must reign. - yereverluvinuncleber

  7. #7

    Thread Starter
    Fanatic Member Mith's Avatar
    Join Date
    Jul 2017
    Location
    Thailand
    Posts
    540

    Re: FindFirstFile(W) Unicode problem with the windows non-unicode settings

    the last part of your WIN32_FIND_DATA_WIDE type must be new added by MS:

    Code:
    dwFileType As Long
    dwCreatorType As Long
    wFinderFlags As Integer
    i never saw this 3 properties before and i cant find any information about them at the internet.
    The internet is full with the old declaration of the WIN32_FIND_DATA type.
    I guess MS changed the internal handling of the API call to support the old and the new style of this type.
    I also checked the official docs from MS for this type: the 3 new properties are listed but without any description.
    Im sure older windows versions will not support them!
    Take care if you use the new properties and run your app with older windows versions!

  8. #8
    Angel of Code Niya's Avatar
    Join Date
    Nov 2011
    Posts
    9,017

    Re: FindFirstFile(W) Unicode problem with the windows non-unicode settings

    https://docs.microsoft.com/en-us/win...n32_find_dataw

    FindFirstFileW expects this version of the structure to be passed. It says this in the documentation for the API:-

    Code:
    HANDLE FindFirstFileW(
      LPCWSTR            lpFileName,
      LPWIN32_FIND_DATAW lpFindFileData
    );
    You have to play close attention to the type:-
    LPWIN32_FIND_DATAW
    Treeview with NodeAdded/NodesRemoved events | BlinkLabel control | Calculate Permutations | Object Enums | ComboBox with centered items | .Net Internals article(not mine) | Wizard Control | Understanding Multi-Threading | Simple file compression | Demon Arena

    Copy/move files using Windows Shell | I'm not wanted

    C++ programmers will dismiss you as a cretinous simpleton for your inability to keep track of pointers chained 6 levels deep and Java programmers will pillory you for buying into the evils of Microsoft. Meanwhile C# programmers will get paid just a little bit more than you for writing exactly the same code and VB6 programmers will continue to whitter on about "footprints". - FunkyDexter

    There's just no reason to use garbage like InputBox. - jmcilhinney

    The threads I start are Niya and Olaf free zones. No arguing about the benefits of VB6 over .NET here please. Happiness must reign. - yereverluvinuncleber

  9. #9
    Angel of Code Niya's Avatar
    Join Date
    Nov 2011
    Posts
    9,017

    Re: FindFirstFile(W) Unicode problem with the windows non-unicode settings

    Quote Originally Posted by Mith View Post
    the 3 new properties are listed but without any description.
    If it came down to it and I really needed to know what those fields are for, I'd ask on a site like StackOverflow. They usually have people with deep knowledge that can answer these kinds of questions but most of the time, you only really need to know that they are there so you can account for them in your code.
    Treeview with NodeAdded/NodesRemoved events | BlinkLabel control | Calculate Permutations | Object Enums | ComboBox with centered items | .Net Internals article(not mine) | Wizard Control | Understanding Multi-Threading | Simple file compression | Demon Arena

    Copy/move files using Windows Shell | I'm not wanted

    C++ programmers will dismiss you as a cretinous simpleton for your inability to keep track of pointers chained 6 levels deep and Java programmers will pillory you for buying into the evils of Microsoft. Meanwhile C# programmers will get paid just a little bit more than you for writing exactly the same code and VB6 programmers will continue to whitter on about "footprints". - FunkyDexter

    There's just no reason to use garbage like InputBox. - jmcilhinney

    The threads I start are Niya and Olaf free zones. No arguing about the benefits of VB6 over .NET here please. Happiness must reign. - yereverluvinuncleber

  10. #10

    Thread Starter
    Fanatic Member Mith's Avatar
    Join Date
    Jul 2017
    Location
    Thailand
    Posts
    540

    Re: FindFirstFile(W) Unicode problem with the windows non-unicode settings

    I checked my installed MSDN Library for the WIN32_FIND_DATA type and i found this:

    Code:
    WIN32_FIND_DATA
    
    The WIN32_FIND_DATA structure describes a file found by the FindFirstFile, FindFirstFileEx, or FindNextFile function. 
    typedef struct _WIN32_FIND_DATA { // wfd 
        DWORD dwFileAttributes; 
        FILETIME ftCreationTime; 
        FILETIME ftLastAccessTime; 
        FILETIME ftLastWriteTime; 
        DWORD    nFileSizeHigh; 
        DWORD    nFileSizeLow; 
        DWORD    dwReserved0; 
        DWORD    dwReserved1; 
        TCHAR    cFileName[ MAX_PATH ]; 
        TCHAR    cAlternateFileName[ 14 ]; 
    } WIN32_FIND_DATA;
    MS changed the type without to tell the devs that they can use both types, the old and the new style...
    I will stay with the old style...

  11. #11
    Angel of Code Niya's Avatar
    Join Date
    Nov 2011
    Posts
    9,017

    Re: FindFirstFile(W) Unicode problem with the windows non-unicode settings

    Quote Originally Posted by Mith View Post
    I checked my installed MSDN Library for the WIN32_FIND_DATA type and i found this:

    Code:
    WIN32_FIND_DATA
    
    The WIN32_FIND_DATA structure describes a file found by the FindFirstFile, FindFirstFileEx, or FindNextFile function. 
    typedef struct _WIN32_FIND_DATA { // wfd 
        DWORD dwFileAttributes; 
        FILETIME ftCreationTime; 
        FILETIME ftLastAccessTime; 
        FILETIME ftLastWriteTime; 
        DWORD    nFileSizeHigh; 
        DWORD    nFileSizeLow; 
        DWORD    dwReserved0; 
        DWORD    dwReserved1; 
        TCHAR    cFileName[ MAX_PATH ]; 
        TCHAR    cAlternateFileName[ 14 ]; 
    } WIN32_FIND_DATA;
    OMG....TCHAR! This thing actually uses TCHAR.

    Quote Originally Posted by Mith View Post
    I will stay with the old style...
    Don't do this. I promise you, those missing 10 bytes will byte you in the ass eventually. The fact that this structure uses TCHARs in it's definition means this wasn't made to target any OS past Windows 98. If your program is not going to be on Windows 95/98, ME or any of those dinosaurs, use the current structure definition. It would be valid all the way back to XP and perhaps even Windows NT where Windows first became a Unicode OS. Windows 95/98 etc were ANSI which is why you see that horribly outdated TCHAR typedef.
    Treeview with NodeAdded/NodesRemoved events | BlinkLabel control | Calculate Permutations | Object Enums | ComboBox with centered items | .Net Internals article(not mine) | Wizard Control | Understanding Multi-Threading | Simple file compression | Demon Arena

    Copy/move files using Windows Shell | I'm not wanted

    C++ programmers will dismiss you as a cretinous simpleton for your inability to keep track of pointers chained 6 levels deep and Java programmers will pillory you for buying into the evils of Microsoft. Meanwhile C# programmers will get paid just a little bit more than you for writing exactly the same code and VB6 programmers will continue to whitter on about "footprints". - FunkyDexter

    There's just no reason to use garbage like InputBox. - jmcilhinney

    The threads I start are Niya and Olaf free zones. No arguing about the benefits of VB6 over .NET here please. Happiness must reign. - yereverluvinuncleber

  12. #12

    Thread Starter
    Fanatic Member Mith's Avatar
    Join Date
    Jul 2017
    Location
    Thailand
    Posts
    540

    Re: FindFirstFile(W) Unicode problem with the windows non-unicode settings

    I did a test with the "new" WIN32_FIND_DATA type and scanned all files recursively inside the folder "C:\Windows" on WinXPSP3 and Win10.
    The result: the values for the properties dwFileType, dwCreatorType and wFinderFlags are always 0...

    btw, i searched the web for WIN32_FIND_DATA and checked several websites: noone is using the new declaration with the 3 new properties at the end!

    I don't know why MS added this 3 new properties without any statement at the docs but i will stick with the old style of the WIN32_FIND_DATA declaration.

  13. #13
    Angel of Code Niya's Avatar
    Join Date
    Nov 2011
    Posts
    9,017

    Re: FindFirstFile(W) Unicode problem with the windows non-unicode settings

    Aite. Suit yourself.
    Treeview with NodeAdded/NodesRemoved events | BlinkLabel control | Calculate Permutations | Object Enums | ComboBox with centered items | .Net Internals article(not mine) | Wizard Control | Understanding Multi-Threading | Simple file compression | Demon Arena

    Copy/move files using Windows Shell | I'm not wanted

    C++ programmers will dismiss you as a cretinous simpleton for your inability to keep track of pointers chained 6 levels deep and Java programmers will pillory you for buying into the evils of Microsoft. Meanwhile C# programmers will get paid just a little bit more than you for writing exactly the same code and VB6 programmers will continue to whitter on about "footprints". - FunkyDexter

    There's just no reason to use garbage like InputBox. - jmcilhinney

    The threads I start are Niya and Olaf free zones. No arguing about the benefits of VB6 over .NET here please. Happiness must reign. - yereverluvinuncleber

  14. #14

    Thread Starter
    Fanatic Member Mith's Avatar
    Join Date
    Jul 2017
    Location
    Thailand
    Posts
    540

    Lightbulb Re: FindFirstFile(W) Unicode problem with the windows non-unicode settings

    i got an answer about these 3 properties:

    You should use these properties only if you develop and compile the app for a MAC computer!

    See stackoverflow - WIN32_FIND_DATA

    See stackoverflow - MAC

    As you can see these 3 properties should not be used if you develop and compile for windows machines!

  15. #15
    Angel of Code Niya's Avatar
    Join Date
    Nov 2011
    Posts
    9,017

    Re: FindFirstFile(W) Unicode problem with the windows non-unicode settings

    Ah ok. Didn't see that one coming. Good detective work.
    Treeview with NodeAdded/NodesRemoved events | BlinkLabel control | Calculate Permutations | Object Enums | ComboBox with centered items | .Net Internals article(not mine) | Wizard Control | Understanding Multi-Threading | Simple file compression | Demon Arena

    Copy/move files using Windows Shell | I'm not wanted

    C++ programmers will dismiss you as a cretinous simpleton for your inability to keep track of pointers chained 6 levels deep and Java programmers will pillory you for buying into the evils of Microsoft. Meanwhile C# programmers will get paid just a little bit more than you for writing exactly the same code and VB6 programmers will continue to whitter on about "footprints". - FunkyDexter

    There's just no reason to use garbage like InputBox. - jmcilhinney

    The threads I start are Niya and Olaf free zones. No arguing about the benefits of VB6 over .NET here please. Happiness must reign. - yereverluvinuncleber

  16. #16
    PowerPoster wqweto's Avatar
    Join Date
    May 2011
    Location
    Sofia, Bulgaria
    Posts
    6,167

    Re: FindFirstFile(W) Unicode problem with the windows non-unicode settings

    cFileName As String * MAX_PATH_WIDE vs cFileName(MAX_PATH_WIDE) As Byte + StrConv(cFileName, vbFromUnicode) completely obfuscates the root cause of the problem.

    There is nothing wrong with cFileName As String * MAX_PATH_WIDE per se but the machine it fails on has most probably a weirdly configured locales for the user (numbers and dates format etc.) vs for the system one (the locale an admin sets for non-Unicode applications).

    StrConv(cFileName, vbFromUnicode) has a 3-rd parameter for locale ID which if not specified defaults to LOCALE_USER_DEFAULT = &H400 while automagic ANSI<->Unicode conversion uses LOCALE_SYSTEM_DEFAULT = &H800

    Mismatching user vs system locale is IMO a very plausible reason for the original failure.

    I might be completely wrong with the above hypothesis though. I'm personally using cFileName As String * MAX_PATH_WIDE declaration with FindFirstFileW API and I'm trying to figure out if I should be worried about it being incompatible or could this failure be attributed unsupported locale config.

    cheers,
    </wqw>

  17. #17

    Thread Starter
    Fanatic Member Mith's Avatar
    Join Date
    Jul 2017
    Location
    Thailand
    Posts
    540

    Re: FindFirstFile(W) Unicode problem with the windows non-unicode settings

    hi wqweto,

    cFileName As String * MAX_PATH_WIDE can be a problem if the user set the windows non-unicode program settings to a unicode language like chinese etc.
    Before i changed my code everywhere to cFileName(MAX_PATH_WIDE) As Byte and i tried to fix this problem by using a non-unicode localeID with the StrConv function but it doesnt helped to fix the problem.
    Now i use everywhere cFileName(MAX_PATH_WIDE) As Byte with FindFirstFileW and my chinese customer have no problem anymore with my app!

  18. #18
    PowerPoster wqweto's Avatar
    Join Date
    May 2011
    Location
    Sofia, Bulgaria
    Posts
    6,167

    Re: FindFirstFile(W) Unicode problem with the windows non-unicode settings

    Quote Originally Posted by Mith View Post
    cFileName As String * MAX_PATH_WIDE can be a problem if the user set the windows non-unicode program settings to a unicode language like chinese etc.
    First time I hear about "unicode language". Probably you mean multi-byte but still this cannot be the reason for the original failure because both StrConv and API declares string conversion use the same WideCharToMultiByte family of API functions. The difference in output must come from both using different arguments to these API functions hence my hypothesis above.

    cheers,
    </wqw>

  19. #19
    Angel of Code Niya's Avatar
    Join Date
    Nov 2011
    Posts
    9,017

    Re: FindFirstFile(W) Unicode problem with the windows non-unicode settings

    I'm not gonna pretend I know more about VB6's internals than you wqweto but the reason I even suggested using Byte arrays in the first place was because I seem to remember VB6 always marshalling Strings from a COM BSTR to a null terminated ANSI String whenever you make API calls. Now ANSI is sensitive to code pages, meaning a single ANSI String can mean different thing depending on the code page. If you use the wrong code page when converting to an ANSI String, problems can arise. Unicode doesn't suffer from this limitation. A Unicode code point is universal, it always means the same thing. For example, U+0409 will always be Љ, whether you're on a Greek computer, Chinese computer or an English computer. All Windows versions descended from Windows NT like XP use Unicode internally. I used Byte arrays to prevent VB6 from interfering with the Unicode Strings between the API calls.

    Now you also mentioned WideCharToMultiByte family of APIs. Even this is a problem. The MultiByte part of that is referring to MBCS which encompasses String formats similar to UTF-8 but unlike UTF-8, which is a Unicode String format, MBCS formats are sensitive to code pages. WideCharToMultiByte converts a String from Windows' native UTF-16 to an MBCS format.

    In any case, when writing apps for the modern world, you want to avoid all these older String formats and stick with Unicode wherever possible. This is the principle I was guided by when I was attempting to help him solve this problem. If you have a Unicode String and you're passing it to a Unicode API, you NEVER EVER want to involve older String formats like ANSI or MBCS, hence the use of Byte arrays.
    Treeview with NodeAdded/NodesRemoved events | BlinkLabel control | Calculate Permutations | Object Enums | ComboBox with centered items | .Net Internals article(not mine) | Wizard Control | Understanding Multi-Threading | Simple file compression | Demon Arena

    Copy/move files using Windows Shell | I'm not wanted

    C++ programmers will dismiss you as a cretinous simpleton for your inability to keep track of pointers chained 6 levels deep and Java programmers will pillory you for buying into the evils of Microsoft. Meanwhile C# programmers will get paid just a little bit more than you for writing exactly the same code and VB6 programmers will continue to whitter on about "footprints". - FunkyDexter

    There's just no reason to use garbage like InputBox. - jmcilhinney

    The threads I start are Niya and Olaf free zones. No arguing about the benefits of VB6 over .NET here please. Happiness must reign. - yereverluvinuncleber

  20. #20
    PowerPoster wqweto's Avatar
    Join Date
    May 2011
    Location
    Sofia, Bulgaria
    Posts
    6,167

    Re: FindFirstFile(W) Unicode problem with the windows non-unicode settings

    @Niya: As I was writing a reply to you it just dawned me what the real problem with OP actually is with the API declare.

    In fact cFileName As String * MAX_PATH_WIDE is completely fine, no need to use a byte-array and StrConv.

    It is the FindFirstFileWide declare that has to use ByVal lpFindFileData As Long and subsequently VarPtr(WFDW) at the callsite to *prevent* any ANSI<->Unicode conversion from kicking in with the fixed-length cFileName string.

    That's what I actual do with no problems in any locale ("unicode" or not) so for me the case is closed with a satisfactory explanation :-))

    cheers,
    </wqw>

  21. #21
    Angel of Code Niya's Avatar
    Join Date
    Nov 2011
    Posts
    9,017

    Re: FindFirstFile(W) Unicode problem with the windows non-unicode settings

    Quote Originally Posted by wqweto View Post
    @Niya: As I was writing a reply to you it just dawned me what the real problem with OP actually is with the API declare.

    In fact cFileName As String * MAX_PATH_WIDE is completely fine, no need to use a byte-array and StrConv.

    It is the FindFirstFileWide declare that has to use ByVal lpFindFileData As Long and subsequently VarPtr(WFDW) at the callsite to *prevent* any ANSI<->Unicode conversion from kicking in with the fixed-length cFileName string.

    That's what I actual do with no problems in any locale ("unicode" or not) so for me the case is closed with a satisfactory explanation :-))

    cheers,
    </wqw>
    This is actually a great suggestion. That solution didn't occur to me.

    One thing bugs me about this though. A BSTR and a WCHAR are two different things. They are both UTF-16 encoded Strings, yes but a BSTR is prepended with a 32 bit value that represents the length of the String. A WCHAR has no header, it's just null terminated. What scares me here is that if you pass a BSTR like that, the API can overwrite that 32 length value because it expects a WCHAR. This can effectively corrupt the String. Now, you say it works for you but I'm curious as to why. You have an explanation?

    EDIT:

    Would it be safe to assume that VB6 doesn't prepend a length header when you declare a fixed length String field in a UDT? That's the only explanation I can think of as a reason that works.
    Last edited by Niya; Aug 7th, 2021 at 10:01 AM.
    Treeview with NodeAdded/NodesRemoved events | BlinkLabel control | Calculate Permutations | Object Enums | ComboBox with centered items | .Net Internals article(not mine) | Wizard Control | Understanding Multi-Threading | Simple file compression | Demon Arena

    Copy/move files using Windows Shell | I'm not wanted

    C++ programmers will dismiss you as a cretinous simpleton for your inability to keep track of pointers chained 6 levels deep and Java programmers will pillory you for buying into the evils of Microsoft. Meanwhile C# programmers will get paid just a little bit more than you for writing exactly the same code and VB6 programmers will continue to whitter on about "footprints". - FunkyDexter

    There's just no reason to use garbage like InputBox. - jmcilhinney

    The threads I start are Niya and Olaf free zones. No arguing about the benefits of VB6 over .NET here please. Happiness must reign. - yereverluvinuncleber

  22. #22
    PowerPoster dilettante's Avatar
    Join Date
    Feb 2006
    Posts
    24,487

    Re: FindFirstFile(W) Unicode problem with the windows non-unicode settings

    BTW: Always check the Windows SDK headers for something like struct definitions. It often contains details you don't find elsewhere:

    Code:
    typedef struct _WIN32_FIND_DATAW {
        DWORD dwFileAttributes;
        FILETIME ftCreationTime;
        FILETIME ftLastAccessTime;
        FILETIME ftLastWriteTime;
        DWORD nFileSizeHigh;
        DWORD nFileSizeLow;
        DWORD dwReserved0;
        DWORD dwReserved1;
        _Field_z_ WCHAR  cFileName[ MAX_PATH ];
        _Field_z_ WCHAR  cAlternateFileName[ 14 ];
    #ifdef _MAC
        DWORD dwFileType;
        DWORD dwCreatorType;
        WORD  wFinderFlags;
    #endif
    } WIN32_FIND_DATAW, *PWIN32_FIND_DATAW, *LPWIN32_FIND_DATAW;
    Fixed-length String types are not BSTRs.
    Last edited by dilettante; Aug 7th, 2021 at 10:07 AM.

  23. #23
    PowerPoster dilettante's Avatar
    Join Date
    Feb 2006
    Posts
    24,487

    Re: FindFirstFile(W) Unicode problem with the windows non-unicode settings

    Code:
    Option Explicit
    
    Private Type T
        A As Long
        B As String * 16
        C As Long
    End Type
    
    Private Sub Form_Load()
        Dim T As T
        Debug.Print Len(T), LenB(T) '<--- Always use LenB() for measuring UDT byte count.
    End Sub

  24. #24
    Angel of Code Niya's Avatar
    Join Date
    Nov 2011
    Posts
    9,017

    Re: FindFirstFile(W) Unicode problem with the windows non-unicode settings

    Quote Originally Posted by dilettante View Post
    Fixed-length String types are not BSTRs.
    Ah, I thought as much. I just didn't want to make that assumption blindly.

    @Mith

    If you use wqweto's suggestion remember to halve the String length values. MAX_PATH should be 260 and cAlternateFileName should be of length 14. Since his method allows you to use Strings instead of Byte arrays, VB6 will take care of allocating the correct amount of memory.
    Treeview with NodeAdded/NodesRemoved events | BlinkLabel control | Calculate Permutations | Object Enums | ComboBox with centered items | .Net Internals article(not mine) | Wizard Control | Understanding Multi-Threading | Simple file compression | Demon Arena

    Copy/move files using Windows Shell | I'm not wanted

    C++ programmers will dismiss you as a cretinous simpleton for your inability to keep track of pointers chained 6 levels deep and Java programmers will pillory you for buying into the evils of Microsoft. Meanwhile C# programmers will get paid just a little bit more than you for writing exactly the same code and VB6 programmers will continue to whitter on about "footprints". - FunkyDexter

    There's just no reason to use garbage like InputBox. - jmcilhinney

    The threads I start are Niya and Olaf free zones. No arguing about the benefits of VB6 over .NET here please. Happiness must reign. - yereverluvinuncleber

  25. #25
    Angel of Code Niya's Avatar
    Join Date
    Nov 2011
    Posts
    9,017

    Re: FindFirstFile(W) Unicode problem with the windows non-unicode settings

    Quote Originally Posted by dilettante View Post
    Code:
    Option Explicit
    
    Private Type T
        A As Long
        B As String * 16
        C As Long
    End Type
    
    Private Sub Form_Load()
        Dim T As T
        Debug.Print Len(T), LenB(T) '<--- Always use LenB() for measuring UDT byte count.
    End Sub
    Ah yes. This answers the question perfectly.
    Treeview with NodeAdded/NodesRemoved events | BlinkLabel control | Calculate Permutations | Object Enums | ComboBox with centered items | .Net Internals article(not mine) | Wizard Control | Understanding Multi-Threading | Simple file compression | Demon Arena

    Copy/move files using Windows Shell | I'm not wanted

    C++ programmers will dismiss you as a cretinous simpleton for your inability to keep track of pointers chained 6 levels deep and Java programmers will pillory you for buying into the evils of Microsoft. Meanwhile C# programmers will get paid just a little bit more than you for writing exactly the same code and VB6 programmers will continue to whitter on about "footprints". - FunkyDexter

    There's just no reason to use garbage like InputBox. - jmcilhinney

    The threads I start are Niya and Olaf free zones. No arguing about the benefits of VB6 over .NET here please. Happiness must reign. - yereverluvinuncleber

  26. #26
    PowerPoster wqweto's Avatar
    Join Date
    May 2011
    Location
    Sofia, Bulgaria
    Posts
    6,167

    Re: FindFirstFile(W) Unicode problem with the windows non-unicode settings

    Btw, FindFirstFile does not accept struct size neither as parameter nor as WIN32_FIND_DATA (first) member. LenB(WFD) should be correct when VarPtr(WFD) is used at callside, otherwise Len(WFD) calculates size *after* Unicode<->ANSI conversion on fixed-length strings.

    cFileName As String * MAX_PATH is exactly TCHAR cFileName[MAX_PATH] i.e. a fixed-size array so being zero-terminated is by convention (not required by the data-type) to be usable w/ C/C++ string functions.

    Fixed-size strings in VB6 are not length-prefixed the way fixed-size arrays are not prefixed in C/C++ so these are 100% compatible with fixed-size arrays of chars or wchar_t's (depending on VarPtr being used at callsize or not).

    cheers,
    </wqw>

  27. #27
    Angel of Code Niya's Avatar
    Join Date
    Nov 2011
    Posts
    9,017

    Re: FindFirstFile(W) Unicode problem with the windows non-unicode settings

    Here is an alternate version of the code I posted in post #6 using wqweto's method of preventing interference with the Strings:-
    Code:
    Option Explicit
    
    Private Const MAX_PATH = 260
    Private Const INVALID_HANDLE_VALUE = -1
    
    Private Type FILETIME
       dwLowDateTime As Long
       dwHighDateTime As Long
    End Type
    
    Private Type WIN32_FIND_DATA_WIDE
            dwFileAttributes As Long
            ftCreationTime As FILETIME
            ftLastAccessTime As FILETIME
            ftLastWriteTime As FILETIME
            nFileSizeHigh As Long
            nFileSizeLow As Long
            dwReserved0 As Long
            dwReserved1 As Long
            cFileName  As String * MAX_PATH
            cAlternate As String * 14
            dwFileType As Long
            dwCreatorType As Long
            wFinderFlags As Integer
    End Type
    
    Private Declare Function FindFirstFileWide Lib "kernel32" Alias "FindFirstFileW" (ByVal lpFileName As Long, ByVal lpFindFileData As Long) As Long
    Private Declare Function FindNextFileWide Lib "kernel32" Alias "FindNextFileW" (ByVal hFindFile As Long, ByVal lpFindFileData As Long) As Long
    Private Declare Function FindClose Lib "kernel32" (ByVal hFindFile As Long) As Long
    
    
    Private Sub Form_Load()
        GetFileNames
    End Sub
    
    
    Private Sub GetFileNames()
    
    Dim hFile As Long
    Dim WFDW As WIN32_FIND_DATA_WIDE
    Dim sFileName As String
    
    hFile = FindFirstFileWide(StrPtr(App.Path & "\UnicodeFileNames\*.*"), VarPtr(WFDW))
    
    If hFile <> INVALID_HANDLE_VALUE Then
    
       Do
    
          sFileName = TrimNull(WFDW.cFileName)
    
          If (WFDW.dwFileAttributes And vbDirectory) <> vbDirectory Then
             
             Text1.TextUnicode = Text1.TextUnicode & sFileName & vbCrLf
    
          End If
    
       Loop While FindNextFileWide(hFile, VarPtr(WFDW))
    
       Call FindClose(hFile)
    
    End If
    
    End Sub
    
    Private Function TrimNull(ByVal s As String) As String
        Dim fn As Long
        fn = InStr(1, s, ChrW(0), vbTextCompare)
        
        TrimNull = Mid$(s, 1, fn - 1)
    End Function
    Proof it still works and handles the Unicode correctly:-
    Treeview with NodeAdded/NodesRemoved events | BlinkLabel control | Calculate Permutations | Object Enums | ComboBox with centered items | .Net Internals article(not mine) | Wizard Control | Understanding Multi-Threading | Simple file compression | Demon Arena

    Copy/move files using Windows Shell | I'm not wanted

    C++ programmers will dismiss you as a cretinous simpleton for your inability to keep track of pointers chained 6 levels deep and Java programmers will pillory you for buying into the evils of Microsoft. Meanwhile C# programmers will get paid just a little bit more than you for writing exactly the same code and VB6 programmers will continue to whitter on about "footprints". - FunkyDexter

    There's just no reason to use garbage like InputBox. - jmcilhinney

    The threads I start are Niya and Olaf free zones. No arguing about the benefits of VB6 over .NET here please. Happiness must reign. - yereverluvinuncleber

  28. #28
    Angel of Code Niya's Avatar
    Join Date
    Nov 2011
    Posts
    9,017

    Re: FindFirstFile(W) Unicode problem with the windows non-unicode settings

    Quote Originally Posted by wqweto View Post
    Fixed-size strings in VB6 are not length-prefixed the way fixed-size arrays are not prefixed in C/C++ so these are 100% compatible with fixed-size arrays of chars or wchar_t's (depending on VarPtr being used at callsize or not).
    This makes fixed lengths Strings in VB6 functionally identical to WCHAR/wchar_t. This actually makes perfect sense. They probably designed it this way deliberately to work with the WCHAR type that is prevalent in the Win32 API.
    Treeview with NodeAdded/NodesRemoved events | BlinkLabel control | Calculate Permutations | Object Enums | ComboBox with centered items | .Net Internals article(not mine) | Wizard Control | Understanding Multi-Threading | Simple file compression | Demon Arena

    Copy/move files using Windows Shell | I'm not wanted

    C++ programmers will dismiss you as a cretinous simpleton for your inability to keep track of pointers chained 6 levels deep and Java programmers will pillory you for buying into the evils of Microsoft. Meanwhile C# programmers will get paid just a little bit more than you for writing exactly the same code and VB6 programmers will continue to whitter on about "footprints". - FunkyDexter

    There's just no reason to use garbage like InputBox. - jmcilhinney

    The threads I start are Niya and Olaf free zones. No arguing about the benefits of VB6 over .NET here please. Happiness must reign. - yereverluvinuncleber

  29. #29
    Angel of Code Niya's Avatar
    Join Date
    Nov 2011
    Posts
    9,017

    Re: FindFirstFile(W) Unicode problem with the windows non-unicode settings

    I just randomly stumbled onto this when it showed up in my YouTube feed. Was from one of the channels I watch on YouTube. A segment of it speaks directly about some of the historical stuff I mentioned earlier concerning Windows and it's relationship to Unicode.

    The relevant portion is at 2:33. For some reason the Forum is not working correctly with the time code. It just plays from the start.



    It's not really relevant to the OP's problem but I felt it is relevant to the Unicode discussion itself. It's really nice to hear an actual Microsoft Windows engineer talking about Windows and what actually goes on.
    Treeview with NodeAdded/NodesRemoved events | BlinkLabel control | Calculate Permutations | Object Enums | ComboBox with centered items | .Net Internals article(not mine) | Wizard Control | Understanding Multi-Threading | Simple file compression | Demon Arena

    Copy/move files using Windows Shell | I'm not wanted

    C++ programmers will dismiss you as a cretinous simpleton for your inability to keep track of pointers chained 6 levels deep and Java programmers will pillory you for buying into the evils of Microsoft. Meanwhile C# programmers will get paid just a little bit more than you for writing exactly the same code and VB6 programmers will continue to whitter on about "footprints". - FunkyDexter

    There's just no reason to use garbage like InputBox. - jmcilhinney

    The threads I start are Niya and Olaf free zones. No arguing about the benefits of VB6 over .NET here please. Happiness must reign. - yereverluvinuncleber

  30. #30

    Thread Starter
    Fanatic Member Mith's Avatar
    Join Date
    Jul 2017
    Location
    Thailand
    Posts
    540

    Re: FindFirstFile(W) Unicode problem with the windows non-unicode settings

    Quote Originally Posted by wqweto View Post
    In fact cFileName As String * MAX_PATH_WIDE is completely fine, no need to use a byte-array and StrConv.

    It is the FindFirstFileWide declare that has to use ByVal lpFindFileData As Long and subsequently VarPtr(WFDW) at the callsite to *prevent* any ANSI<->Unicode conversion from kicking in with the fixed-length cFileName string.
    Great tip for further optimization!

    I changed my code back to "cFileName As String * MAX_PATH_WIDE" and now i use "ByVal lpFindFileData As Long" in combination with "VarPtr(WFDW)".

    Works great!

  31. #31
    Angel of Code Niya's Avatar
    Join Date
    Nov 2011
    Posts
    9,017

    Re: FindFirstFile(W) Unicode problem with the windows non-unicode settings

    Quote Originally Posted by Mith View Post
    Great tip for further optimization!

    I changed my code back to "cFileName As String * MAX_PATH_WIDE" and now i use "ByVal lpFindFileData As Long" in combination with "VarPtr(WFDW)".

    Works great!
    Did you remember to half the MAX_PATH constant?
    Treeview with NodeAdded/NodesRemoved events | BlinkLabel control | Calculate Permutations | Object Enums | ComboBox with centered items | .Net Internals article(not mine) | Wizard Control | Understanding Multi-Threading | Simple file compression | Demon Arena

    Copy/move files using Windows Shell | I'm not wanted

    C++ programmers will dismiss you as a cretinous simpleton for your inability to keep track of pointers chained 6 levels deep and Java programmers will pillory you for buying into the evils of Microsoft. Meanwhile C# programmers will get paid just a little bit more than you for writing exactly the same code and VB6 programmers will continue to whitter on about "footprints". - FunkyDexter

    There's just no reason to use garbage like InputBox. - jmcilhinney

    The threads I start are Niya and Olaf free zones. No arguing about the benefits of VB6 over .NET here please. Happiness must reign. - yereverluvinuncleber

  32. #32

    Thread Starter
    Fanatic Member Mith's Avatar
    Join Date
    Jul 2017
    Location
    Thailand
    Posts
    540

    Re: FindFirstFile(W) Unicode problem with the windows non-unicode settings

    Quote Originally Posted by Niya View Post
    Did you remember to half the MAX_PATH constant?
    No, i did not

    Thanks for the reminder!

  33. #33

    Thread Starter
    Fanatic Member Mith's Avatar
    Join Date
    Jul 2017
    Location
    Thailand
    Posts
    540

    Question Re: FindFirstFile(W) Unicode problem with the windows non-unicode settings

    i checked my code for other APIs were i use the StrConv-function to get a unicode string:

    Code:
    Private Declare Function GetComputerNameWIDE Lib "kernel32" Alias "GetComputerNameW" (ByVal lpBuffer As String, nSize As Long) As Long
    Public Declare Function GetUserNameWIDE Lib "advapi32.dll" Alias "GetUserNameW" (ByVal lpBuffer As String, ByRef nSize As Long) As Long
    Public Declare Function GetEnvironmentStringsWide Lib "kernel32" Alias "GetEnvironmentStringsW" () As Long
    Code:
    Public Function GetCurrentUserNameWIDE() As String
    	GetCurrentUserNameWIDE = String$(512, vbNullChar)
    	GetUserNameWIDE GetCurrentUserNameWIDE, 512
    	GetCurrentUserNameWIDE = TrimNull(StrConv(GetCurrentUserNameWIDE, vbFromUnicode))
    End Function
    Code:
    Public Function GetLocalComputerNameWIDE() As String
    	Dim sName As String
    	Dim lBufferSize As Long
    	Const MAX_COMPUTERNAME As Long = 16         
    	lBufferSize = MAX_COMPUTERNAME * 2
    	sName = String$(lBufferSize, vbNullChar)
    	If GetComputerNameWIDE(sName, lBufferSize) <> 0 Then
    		GetLocalComputerNameWIDE = TrimNull(StrConv(sName, vbFromUnicode))
    	End If
    End Function
    Code:
    Public Function GetAllEnvironmentVariables() As String
    
    Dim lngRet As Long
    Dim strDest As String
    Dim lLen As Long
    Dim sReturn As String
     
    lngRet = GetEnvironmentStringsWide
    Do
       lLen = lstrlen(lngRet)
       If lLen = 0 Then Exit Do
       strDest = Space$(lLen * 2)
       CopyMemory ByVal strDest, ByVal lngRet, lLen * 2
       strDest = StrConv(strDest, vbFromUnicode)
       sReturn = sReturn & strDest & vbCrLf
       lngRet = lngRet + (lLen * 2) + 2
    Loop
     
    Call FreeEnvironmentStrings(lngRet)
     
    GetAllEnvironmentVariables = sReturn
        
    End Function
    I guess on a windows pc with a chinese GUI language these APIs can return wrong unicode strings too?

    Im not sure if its possible to use VarPtr with these APIs to avoid StrConv.

    Any suggestions from you guys?

  34. #34
    Angel of Code Niya's Avatar
    Join Date
    Nov 2011
    Posts
    9,017

    Re: FindFirstFile(W) Unicode problem with the windows non-unicode settings

    Yea, these are definitely problematic. Passing the String by value to these APIs would trigger ANSI conversions. This is how I'd do the GetComputerName API:-
    Code:
    Private Declare Function GetComputerNameWIDE Lib "kernel32" Alias "GetComputerNameW" (ByVal lpBuffer As Long, nSize As Long) As Long
    
    Private Function GetComputerName() As String
       
        Dim nm As String
        Dim numChars As Long
        
        numChars = 50
        
        nm = Space(numChars)
        
        If GetComputerNameWIDE(StrPtr(nm), numChars) Then
            GetComputerName = Left(nm, numChars)
        End If
    End Function
    
    Private Sub Form_Load()
        
        Dim cname As String
        cname = GetComputerName
        
    End Sub
    Instead of passing the String by value, I pass a pointer which bypasses the ANSI conversions. This is the same idea proposed by wqweto. Although StrPtr is working on a BSTR it actually passes the address of a String after the BSTR byte length header so it works as a LPWSTR which is what the API GetComputerNameW expects. Do you think you can work out the rest on your own? Or you want me to help with those too?
    Last edited by Niya; Aug 7th, 2021 at 11:29 PM.
    Treeview with NodeAdded/NodesRemoved events | BlinkLabel control | Calculate Permutations | Object Enums | ComboBox with centered items | .Net Internals article(not mine) | Wizard Control | Understanding Multi-Threading | Simple file compression | Demon Arena

    Copy/move files using Windows Shell | I'm not wanted

    C++ programmers will dismiss you as a cretinous simpleton for your inability to keep track of pointers chained 6 levels deep and Java programmers will pillory you for buying into the evils of Microsoft. Meanwhile C# programmers will get paid just a little bit more than you for writing exactly the same code and VB6 programmers will continue to whitter on about "footprints". - FunkyDexter

    There's just no reason to use garbage like InputBox. - jmcilhinney

    The threads I start are Niya and Olaf free zones. No arguing about the benefits of VB6 over .NET here please. Happiness must reign. - yereverluvinuncleber

  35. #35

    Thread Starter
    Fanatic Member Mith's Avatar
    Join Date
    Jul 2017
    Location
    Thailand
    Posts
    540

    Re: FindFirstFile(W) Unicode problem with the windows non-unicode settings

    Quote Originally Posted by Niya View Post
    Do you think you can work out the rest on your own? Or you want me to help with those too?
    I only have problems with the API "GetEnvironmentStringsWide" because there are no parameters.
    The string variable will be filled with "CopyMemory"...

  36. #36
    Angel of Code Niya's Avatar
    Join Date
    Nov 2011
    Posts
    9,017

    Re: FindFirstFile(W) Unicode problem with the windows non-unicode settings

    Quote Originally Posted by Mith View Post
    I only have problems with the API "GetEnvironmentStringsWide" because there are no parameters.
    The string variable will be filled with "CopyMemory"...
    This one was a little tricky because of the weird format of the environment block. This is what I came up with for this:-
    Code:
    Private Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (ByVal lpDest As Long, ByVal lpSrc As Long, ByVal count As Long)
    Private Declare Function GetEnvironmentStringsWide Lib "kernel32" Alias "GetEnvironmentStringsW" () As Long
    Private Declare Function FreeEnvironmentStringsWide Lib "kernel32" Alias "FreeEnvironmentStringsW" (ByVal ptr As Long) As Long
    
    Private Function ReadByte(ByVal pointer As Long) As Byte
        Dim b As Byte
        
        CopyMemory VarPtr(b), pointer, 1
        
        ReadByte = b
       
    End Function
    
    Public Function GetAllEnvironmentVariables() As String
        
        Dim ptr As Long
        
        ptr = GetEnvironmentStringsWide()
        
        If ptr <> 0 Then
            Dim readPos As Long
            Dim charCount As Long
            Dim strn As String
            Dim b As Long
            
            readPos = ptr
            
            'Look for the double null terminator
            Do Until ReadByte(readPos) = 0 And ReadByte(readPos + 1) = 0 And ReadByte(readPos + 2) = 0 And ReadByte(readPos + 3) = 0
                charCount = charCount + 1 'Counts the number of characters in the entire string including embedded nulls
                readPos = readPos + 2 'Next character
            Loop
                           
            strn = Space(charCount) 'Allocate a String to hold the entire thing
            CopyMemory StrPtr(strn), ptr, charCount * 2 'Copy the memory to the VB String
            
            GetAllEnvironmentVariables = Replace(strn, ChrW(0), vbCrLf) 'Convert embedded nulls to newline characters and return it
            
            If Not CBool(FreeEnvironmentStringsWide(ptr)) Then
                Err.Raise 9000, "", "FreeEnvironmentString failed"
            End If
            
        End If
    End Function
    
    Private Sub Form_Load()
        Debug.Print GetAllEnvironmentVariables
    End Sub
    This one is safe from ANSI conversions since I perform the copy on pointers instead of passing Strings around.

    Note that I didn't put a lot of thought into efficiency while writing that. That code essentially sweeps the entire block a total of 3 times, once to measure the length of the block, a second time to copy the block and a 3rd time to replace all the embedded nulls with newline characters. This entire process could be reduced to a single sweep but I wanted the code to be easy to understand so I didn't bother to optimize it.
    Treeview with NodeAdded/NodesRemoved events | BlinkLabel control | Calculate Permutations | Object Enums | ComboBox with centered items | .Net Internals article(not mine) | Wizard Control | Understanding Multi-Threading | Simple file compression | Demon Arena

    Copy/move files using Windows Shell | I'm not wanted

    C++ programmers will dismiss you as a cretinous simpleton for your inability to keep track of pointers chained 6 levels deep and Java programmers will pillory you for buying into the evils of Microsoft. Meanwhile C# programmers will get paid just a little bit more than you for writing exactly the same code and VB6 programmers will continue to whitter on about "footprints". - FunkyDexter

    There's just no reason to use garbage like InputBox. - jmcilhinney

    The threads I start are Niya and Olaf free zones. No arguing about the benefits of VB6 over .NET here please. Happiness must reign. - yereverluvinuncleber

  37. #37
    Angel of Code Niya's Avatar
    Join Date
    Nov 2011
    Posts
    9,017

    Re: FindFirstFile(W) Unicode problem with the windows non-unicode settings

    Here is a more optimized version that does the sweep only once:-
    Code:
    Private Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (ByVal lpDest As Long, ByVal lpSrc As Long, ByVal count As Long)
    Private Declare Function GetEnvironmentStringsWide Lib "kernel32" Alias "GetEnvironmentStringsW" () As Long
    Private Declare Function FreeEnvironmentStringsWide Lib "kernel32" Alias "FreeEnvironmentStringsW" (ByVal ptr As Long) As Long
    
    Private Function ReadUTF16Char(ByVal pointer As Long) As String
        ReadUTF16Char = Space(1)
        
        CopyMemory StrPtr(ReadUTF16Char), pointer, 2
    End Function
    
    Public Function GetAllEnvironmentVariables2() As String
        
        Dim ptr As Long
        
        ptr = GetEnvironmentStringsWide()
        
        If ptr <> 0 Then
            Dim readPos As Long
            Dim char As String
            
            readPos = ptr
            
            Do
                char = ReadUTF16Char(readPos)
                
                If char <> ChrW(0) Then
                    GetAllEnvironmentVariables2 = GetAllEnvironmentVariables2 & char
                Else
                    If ReadUTF16Char(readPos + 2) = ChrW(0) Then
                        Exit Do
                    Else
                        GetAllEnvironmentVariables2 = GetAllEnvironmentVariables2 & vbCrLf
                    End If
                
                End If
                
                readPos = readPos + 2
            Loop
            
            If Not CBool(FreeEnvironmentStringsWide(ptr)) Then
                Err.Raise 9000, "", "FreeEnvironmentString failed"
            End If
            
        End If
    End Function
    
    Private Sub Form_Load()
        Debug.Print GetAllEnvironmentVariables2
    End Sub
    There is more room for optimization here but I don't expect this function would be called in a tight loop or anything like that so personally for me it's optimized enough.
    Treeview with NodeAdded/NodesRemoved events | BlinkLabel control | Calculate Permutations | Object Enums | ComboBox with centered items | .Net Internals article(not mine) | Wizard Control | Understanding Multi-Threading | Simple file compression | Demon Arena

    Copy/move files using Windows Shell | I'm not wanted

    C++ programmers will dismiss you as a cretinous simpleton for your inability to keep track of pointers chained 6 levels deep and Java programmers will pillory you for buying into the evils of Microsoft. Meanwhile C# programmers will get paid just a little bit more than you for writing exactly the same code and VB6 programmers will continue to whitter on about "footprints". - FunkyDexter

    There's just no reason to use garbage like InputBox. - jmcilhinney

    The threads I start are Niya and Olaf free zones. No arguing about the benefits of VB6 over .NET here please. Happiness must reign. - yereverluvinuncleber

  38. #38

    Thread Starter
    Fanatic Member Mith's Avatar
    Join Date
    Jul 2017
    Location
    Thailand
    Posts
    540

    Re: FindFirstFile(W) Unicode problem with the windows non-unicode settings

    Quote Originally Posted by Niya View Post
    Here is a more optimized version that does the sweep only once:
    Great, thank you!

    I tested your function on a chinese windows with chinese non-unicode settings:

    All chinese characters from the environment variables are correctly displayed!

  39. #39
    Angel of Code Niya's Avatar
    Join Date
    Nov 2011
    Posts
    9,017

    Re: FindFirstFile(W) Unicode problem with the windows non-unicode settings

    No problem
    Treeview with NodeAdded/NodesRemoved events | BlinkLabel control | Calculate Permutations | Object Enums | ComboBox with centered items | .Net Internals article(not mine) | Wizard Control | Understanding Multi-Threading | Simple file compression | Demon Arena

    Copy/move files using Windows Shell | I'm not wanted

    C++ programmers will dismiss you as a cretinous simpleton for your inability to keep track of pointers chained 6 levels deep and Java programmers will pillory you for buying into the evils of Microsoft. Meanwhile C# programmers will get paid just a little bit more than you for writing exactly the same code and VB6 programmers will continue to whitter on about "footprints". - FunkyDexter

    There's just no reason to use garbage like InputBox. - jmcilhinney

    The threads I start are Niya and Olaf free zones. No arguing about the benefits of VB6 over .NET here please. Happiness must reign. - yereverluvinuncleber

Tags for this Thread

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  



Click Here to Expand Forum to Full Width