Results 1 to 7 of 7

Thread: FindFirstFileEx and Unicode

  1. #1

    Thread Starter
    Hyperactive Member
    Join Date
    Jan 2007
    Posts
    448

    FindFirstFileEx and Unicode

    Howdie.

    I am researching how to use the Unicode version of FindFirstFileEx() in vb.Net and am having a heck of a time getting this to work. I had downloaded an example and it used the standard version, FindFirstFile(). I then modified the code to work with FindFirstFileEx(), instead, and got that to work, too, but only for ANSI characters (Alias "FindFirstFileExA"). But when I attempted to use the Unicode version (Alias "FindFirstFileExW"), it fails miserably. The best I have been able to do is get a non-zero return value from the function (which means it found the folder), but all the returned WFD structure contains are null strings for the folder name and alternate folder name.

    I have googled the problem (MSDN is of no help in this regard) and did find one reference on how to alter the function parameters to provide pointers instead of a string and the WFD structure, themselves.

    At any rate, here is my code for just the top half that looks for folders. The bottom half deals with searching for files, but it is just a repeat of this example, essentially. If I can get this top half to work with your help, I can certainly get the bottom half to work.

    I have also attached a zip containing the project and the test folder I used:

    Thanks for any help!


    Code:
    Option Strict Off
    Option Explicit On
    'Option Infer Off
    
    Imports VB = Microsoft.VisualBasic
    Imports System.Runtime.InteropServices
    
    
    Friend Class Form1
    	Inherits System.Windows.Forms.Form
    
      'Private Declare Function FindFirstFileExW Lib "kernel32.dll" Alias "FindFirstFileExW" (ByVal lpFileName As String, ByVal fInfoLevelId As FINDEX_INFO_LEVELS, ByVal lpFindFileData As WIN32_FIND_DATA, ByVal fSearchOp As FINDEX_SEARCH_OPS, ByRef lpSearchFilter As Int32, ByVal dwAdditionalFlags As Integer) As Long
      Private Declare Function FindFirstFileExW Lib "kernel32.dll" Alias "FindFirstFileExW" (ByVal lpFileName_IntPtr As IntPtr, ByVal fInfoLevelId As FINDEX_INFO_LEVELS, ByRef lpFindFileData_IntPtr As IntPtr, ByVal fSearchOp As FINDEX_SEARCH_OPS, ByRef lpSearchFilter As Int32, ByVal dwAdditionalFlags As Integer) As Long
    
      Private Declare Function FindNextFileW Lib "kernel32" Alias "FindNextFileW" (ByVal hFindFile As Long, ByRef lpFindFileData As WIN32_FIND_DATA) As Integer
    
      Private Declare Function GetFileAttributesW Lib "kernel32" Alias "GetFileAttributesW" (ByVal lpFileName As String) As Integer
    
      Private Declare Function FindClose Lib "kernel32" (ByVal hFindFile As Long) As Integer
    
      Private Declare Function GetLastError Lib "kernel32" Alias "GetLastError" () As Integer
    
      Const MAX_PATH As UInt16 = 260
      Const MAXDWORD As UInt64 = 4294967295   '&HFFFFFFFF is not accepted, why?
      Const INVALID_HANDLE_VALUE As Int16 = -1
      Const FILE_ATTRIBUTE_ARCHIVE As UInt16 = &H20
      Const FILE_ATTRIBUTE_DIRECTORY As UInt16 = &H10
      Const FILE_ATTRIBUTE_HIDDEN As UInt16 = &H2
      Const FILE_ATTRIBUTE_NORMAL As UInt16 = &H80
      Const FILE_ATTRIBUTE_READONLY As UInt16 = &H1
      Const FILE_ATTRIBUTE_SYSTEM As UInt16 = &H4
      Const FILE_ATTRIBUTE_TEMPORARY As UInt16 = &H100
    
    '------------------------------------------------------------
    '------------------------------------------------------------
    '------------------------------------------------------------
    'These are specifically for the FindFirstFileEx() function:
    
    Public Enum FINDEX_INFO_LEVELS
      FindExInfoStandard = 0
      FindExInfoBasic = 1
      FindExInfoMaxInfoLevel = 2
    End Enum
    
     Enum FINDEX_SEARCH_OPS
      FindExSearchNameMatch = 0
      FindExSearchLimitToDirectories = 1
      FindExSearchLimitToDevices = 2
    End Enum
    '------------------------------------------------------------
    '------------------------------------------------------------
    '------------------------------------------------------------
    
      Private Structure FILETIME
        Dim dwLowDateTime As UInt32
        Dim dwHighDateTime As UInt32
      End Structure
    
      Private Structure WIN32_FIND_DATA
        Dim dwFileAttributes As UInt32
        Dim ftCreationTime As FILETIME
        Dim ftLastAccessTime As FILETIME
        Dim ftLastWriteTime As FILETIME
        Dim nFileSizeHigh As UInt32
        Dim nFileSizeLow As UInt32
        Dim dwReserved0 As UInt32
        Dim dwReserved1 As UInt32
    
        <VBFixedString(MAX_PATH), MarshalAs(UnmanagedType.ByValTStr, SizeConst:=MAX_PATH)> Public cFileName As String
    
        <VBFixedString(14), MarshalAs(UnmanagedType.ByValTStr, SizeConst:=14)> Public cAlternate As String
    
      End Structure
    
      Function StripNulls(ByRef OriginalStr As String) As String
        If (InStr(OriginalStr, Chr(0)) > 0) Then
          OriginalStr = VB.Left(OriginalStr, InStr(OriginalStr, Chr(0)) - 1)
        End If
        StripNulls = OriginalStr
      End Function
    
      Function FindFilesAPI(ByRef path As String, ByRef SearchStr As String, ByRef FileCount As Integer, ByRef DirCount As Integer) As Long
        Dim FileName As String ' Walking filename variable...
    
        Dim DirName As String ' SubDirectory Name
        Dim dirNames() As String ' Buffer for directory name entries
        Dim nDir As Integer = 0 ' Number of directories in this path
    
        Dim i As Long ' For-loop counter...
    
        Dim hSearchEx As Long 'Int32 ' Search Handle for FindFirstFileEx().
        Dim WFD As WIN32_FIND_DATA = Nothing 'Memory block
        Static Cont As Boolean 'Long
    
        FindFilesAPI = 0
    
        If VB.Right(path, 1) = "\" Then path = VB.Left(path, Len(path) - 1)
    
        'Search for subdirectories:
        nDir = 0
        ReDim dirNames(nDir)
        Cont = True
    
        'For the FindFirstFileEx() function:
        'Dim lpFileName As String = ".*"
        Dim fInfoLevelId As FINDEX_INFO_LEVELS = 0
        Dim lpFindFileData As WIN32_FIND_DATA = Nothing
        Dim fSearchOp As FINDEX_SEARCH_OPS = 0
        Dim lpSearchFilter As Int32 = Nothing
        Dim dwAdditionalFlags As Integer = 0    '1 = FIND_FIRST_EX_CASE_SENSITIVE, 2 = FIND_FIRST_EX_LARGE_FETCH (May not be supported in Win7)
    
        '--------------------------------------------------------------------------------------------------------------------------------------
        'Make pointers for the lpFileName string, and lpFindFileData structure, then execute API function:
    
        '-------------------------------------------------------------------------------------
        'For the string:
    
        Dim lpFileName_str As String = "\\?\UNC\" & path & "\*"
    
        'Initialize unmanged memory to hold the lpFileName string: 
        'Dim lpFileName_IntPtr As IntPtr = Marshal.AllocHGlobal(Marshal.SizeOf(lpFileName_str))
        Dim lpFileName_IntPtr As IntPtr = Marshal.StringToHGlobalAuto(lpFileName_str)
        '-------------------------------------------------------------------------------------
        'For the structure:
    
        Dim lpFindFileData_Original As WIN32_FIND_DATA = Nothing
    
        'Initialize unmanged memory to hold the struct: 
        Dim lpFindFileData_IntPtr As IntPtr = Marshal.AllocHGlobal(Marshal.SizeOf(lpFindFileData_Original))
    
        '-------------------------------------------------------------------------------------
        'Do folder/file search:
    
        Try
          'Copy the struct to unmanaged memory:
          Marshal.StructureToPtr(lpFindFileData_Original, lpFindFileData_IntPtr, False)
          hSearchEx = FindFirstFileExW(lpFileName_IntPtr, fInfoLevelId, lpFindFileData_IntPtr, fSearchOp, lpSearchFilter, dwAdditionalFlags)
          WFD = CType(Marshal.PtrToStructure(lpFindFileData_IntPtr, GetType(WIN32_FIND_DATA)), WIN32_FIND_DATA)
    
        Finally
          'Free the unmanaged memory for the string:
          Marshal.FreeHGlobal(lpFileName_IntPtr)
    
          'Free the unmanaged memory for the structure:
          Marshal.FreeHGlobal(lpFindFileData_IntPtr)
        End Try
    
        '-------------------------------------------------------------------------------------
        'Process folders:
    
        If hSearchEx <> INVALID_HANDLE_VALUE Then
          Do While Cont
            System.Windows.Forms.Application.DoEvents()
    
            DirName = StripNulls(WFD.cFileName)
    
            'Ignore the current and encompassing directories:
            If (DirName <> ".") And (DirName <> "..") And (DirName <> "") Then
              'Check for directory with bitwise comparison:
    
              Dim FileAttributes As Integer = GetFileAttributesW("\\?\UNC\" & path & "\" & DirName)
    
              If FileAttributes And FILE_ATTRIBUTE_DIRECTORY Then
                dirNames(nDir) = DirName
                DirCount = DirCount + 1
                nDir = nDir + 1
                ReDim Preserve dirNames(nDir)
                List1.Items.Add(path & "\" & DirName)
              End If
            End If
    
            Cont = FindNextFileW(hSearchEx, WFD) 'Get next subdirectory.
    
          Loop
    
          Cont = FindClose(hSearchEx)
    
          If Cont = False Then
            MsgBox("Error:  " + Str(GetLastError))
          End If
    
        End If
    
      End Function
    
      Private Sub Go_cmd_Click(ByVal eventSender As System.Object, ByVal eventArgs As System.EventArgs) Handles Go_cmd.Click
        Dim SearchPath, FindStr As String
        Dim FileSize As Long
        Dim NumFiles, NumDirs As Long
    
        System.Windows.Forms.Cursor.Current = System.Windows.Forms.Cursors.WaitCursor
    
        List1.Items.Clear()
        SearchPath = Text1.Text
        FindStr = Text2.Text
    
        FileSize = FindFilesAPI(SearchPath, FindStr, NumFiles, NumDirs)
        Text3.Text = NumFiles & " Files found in " & NumDirs + 1 & " Directories"
        Text4.Text = "Size of files found under " & SearchPath & " = " & VB6.Format(FileSize, "#,###,###,##0") & " Bytes"
    
        System.Windows.Forms.Cursor.Current = System.Windows.Forms.Cursors.Default
    
      End Sub
    
      Private Sub Quit_cmd_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Quit_cmd.Click
        End
      End Sub
    
    
    
    End ClassThe Project.zipThe Project.zip

  2. #2

    Re: FindFirstFileEx and Unicode

    Without checking your code thoroughly, I hope you looked here to see if your declarations are correct.

  3. #3
    PowerPoster dunfiddlin's Avatar
    Join Date
    Jun 2012
    Posts
    8,245

    Re: FindFirstFileEx and Unicode

    Have you tried this on a local file system? I'm not entirely sure that this is designed to work over a network though frankly I don't understand why you need it at all. The Vb.Net file methods are more than adequate and I can't imagine any scenario in which you need anything beyond them. If you want to research the API it makes far more sense to do it in a language built to accommodate the functions 'as is'.

    Const MAXDWORD As UInt64 = 4294967295 '&HFFFFFFFF is not accepted, why?
    Because it's ambiguous in the context of an unsigned integer. Incidentally it's also only a 32 bit value. Are you sure it's meant to be UInt64?
    As the 6-dimensional mathematics professor said to the brain surgeon, "It ain't Rocket Science!"

    Reviews: "dunfiddlin likes his DataTables" - jmcilhinney

    Please be aware that whilst I will read private messages (one day!) I am unlikely to reply to anything that does not contain offers of cash, fame or marriage!

  4. #4
    PowerPoster dunfiddlin's Avatar
    Join Date
    Jun 2012
    Posts
    8,245

    Re: FindFirstFileEx and Unicode

    Quote Originally Posted by formlesstree4 View Post
    Without checking your code thoroughly, I hope you looked here to see if your declarations are correct.
    Unfortunately one cannot always be sure that they are correct there. A lot of the VB declarations there use the now largely unnecessary Import Dll method, for example, and seem to be totally unaware of the Auto option. I've had many an occasion to vary from the prescribed types too. It's certainly a good starting point but slavish imitation will let you down from time to time.
    As the 6-dimensional mathematics professor said to the brain surgeon, "It ain't Rocket Science!"

    Reviews: "dunfiddlin likes his DataTables" - jmcilhinney

    Please be aware that whilst I will read private messages (one day!) I am unlikely to reply to anything that does not contain offers of cash, fame or marriage!

  5. #5

    Thread Starter
    Hyperactive Member
    Join Date
    Jan 2007
    Posts
    448

    Re: FindFirstFileEx and Unicode

    Without checking your code thoroughly, I hope you looked here to see if your declarations are correct.
    and
    Unfortunately one cannot always be sure that they are correct there.
    Exactly. In fact, according to one source, and when using the Unicode "W" version of the function, the string and WFD parameters need to be pointed to. Also, due to the differences between 64bit and 32bit systems, and the inexplicable decision to redefine the term "Integer" for those platforms, it is nearly impossible to know what many sources (and even MSDN) are claiming is even accurate anymore. In other words, many sources are out of date, or in the least so not qualify what system they are referring to. The only other option is to Google extensively and do trial-and-error to get to the right solution...Which is a bit scary because you might find yourself doing something that works in one instance, but bites you in another.

    Have you tried this on a local file system?
    This is all local. The prefix "\\?\" is required to get past the MAX_PATH barrier...It does not refer to a server.

    I'm not entirely sure that this is designed to work over a network though frankly I don't understand why you need it at all.
    Not entirely true. Native vb.Net methods are limited to the MAX_PATH = 260 character barrier.

  6. #6

    Thread Starter
    Hyperactive Member
    Join Date
    Jan 2007
    Posts
    448

    Re: FindFirstFileEx and Unicode

    It just dawned on me how to sort out part of the typing confusion. Win32.api is just that...A 32bit set of libraries. Even when run on a 64bit system, it's still win32. Therefore, no parameter can ever be passed successfully to win32 that is greater than 32 bits. So, if any source claims that a declaration should be "Long", it is obviously a 32bit Long which converts to a 64bit system's 32bit Integer. I have heard that the usual convention is that floats are pretty much a standard 32bits. So unless there is a particular time when maybe a Short would be required, you can generally assume that floats will be a 32bit Long (for 32bit systems) and a 32bit Integer (for 64bit systems). Not sure how water tight that idea is. Sounds logical anyway.

    That all having been said, it seems that there is still confusion on 64bit systems for when to use, for instance, Integer or Int32. My impression is that they are not identical. MSDN seems to state that "Integer" is a data type, and that "Int32" is a structure containing a 32bit integer. I see a lot of references to Int32 in win32 functions online, and am curious how they knew how to use Int32 instead of just Integer?

  7. #7

    Thread Starter
    Hyperactive Member
    Join Date
    Jan 2007
    Posts
    448

    Re: FindFirstFileEx and Unicode

    OK, I figured out the problems in my code. I will post the resultant demo program when I get some test code copied over and running. The problems were mostly major data-typing problems.

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