.netvb.netfile-iopathfileinfo

IO.FileInfo strange exception "Illegal characters in path" when there is any illegal char


I'm getting an "illegal characters in path" exception but the string don't seems to have any illegal character.

How I can resolve this problem?:

Private Sub Button_Send_Click(sender As Object, e As EventArgs) Handles Button_Send.Click

    For Each item As ListViewItem In ListView1.Items

        Clipboard.SetText(item.SubItems(0).Text)
        ' First I copy the text to the clipboard to ensure in Explorer.exe if all is ok and also ifthe file exists... and yes, it exists.
        ' The example text is this:
        ' C:\Electro\Nueva carpeta\Aggresivnes - Dance Or Die.mp3
        ' (without any quotes)


        ' ...But this throws an "not exists":
        If IO.File.Exists(item.SubItems(0).Text) Then MsgBox("exists") Else MsgBox("not exists")


        ' And here throws an exception of "*Illegal characters in path*" :
        Dim File As New IO.FileInfo(item.SubItems(0).Text)

    Next

End Su

UPDATE 2

I did a mistake in my own code, don't noticed that is returning true when no problems and returns false when problem exists, so i've decided to delete my old update and edit this one:

I did a function to verify (again) if the filename conversion or something in the string variable has any invalid character, following the @Jim Mischel answer indications:

#Region " Validate Windows FileName "

    ' [ Validate Windows FileName Function ]
    '
    ' By Elektro H@cker
    '
    ' Examples :
    ' MsgBox(Validate_Windows_FileName("C:\Test.txt"))  ' Result: True
    ' MsgBox(Validate_Windows_FileName("C:\Te|st.txt")) ' Result: False

    Private Function Validate_Windows_FileName(ByRef FileName As String)
        Dim Directory As String = Nothing
        Dim File As String = Nothing

        Try
            Directory = FileName.Substring(0, FileName.LastIndexOf("\")) & "\"
            File = FileName.Split("\").Last
        Catch
            If Directory Is Nothing Then File = FileName
        End Try

        If Directory Is Nothing AndAlso File Is Nothing Then Return False

        If Not Directory Is Nothing Then
            For Each InvalidCharacter As Char In IO.Path.GetInvalidPathChars
                If Directory.Contains(InvalidCharacter) Then
                    ' MsgBox(InvalidCharacter)
                    Return False
                End If
            Next
        End If

        If Not File Is Nothing Then
            For Each InvalidCharacter As Char In IO.Path.GetInvalidFileNameChars
                If File.Contains(InvalidCharacter) Then
                    ' MsgBox(InvalidCharacter)
                    Return False
                End If
            Next
        End If

        Return True ' FileName is valid
    End Function

#End Region

well... now I use the function to ensure again that the fullpath/filename and the result is "false" which means the string contains a invalid char, and that char is something like an "space".

 MsgBox(Validate_Windows_FileName(item.SubItems(0).Text))

I dont know how to resolve this.

If you want to see the full class, here is:

Public Class Main

    Dim WinAmpTitle As String = String.Empty
    Dim WinAmpFile As String = String.Empty

    Dim Sendto_Path As String

    Dim WithEvents WinAmp_Timer As New Timer With {.Interval = 25, .Enabled = True}

    Private Sub Main_Load(sender As Object, e As EventArgs) Handles MyBase.Load
        ' Nothing to do here at the momento...
    End Sub

    Private Sub WinAmp_Timer_Tick(sender As Object, e As EventArgs) Handles WinAmp_Timer.Tick

        WinAmpTitle = WinAmpInfo.Title
        WinAmpFile = WinAmpInfo.FileName

        If Not TextBox_Title.Text = WinAmpTitle Then TextBox_Title.Text = WinAmpTitle

        If Not TextBox_Filename.Text = WinAmpFile Then TextBox_Filename.Text = WinAmpFile

    End Sub

    Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button_Copy.Click
        Dim item = ListView1.Items.Add(WinAmpFile)
        item.SubItems.Add(ComboBox_Sendto.Text)

    End Sub

    Private Sub Button_Send_Click(sender As Object, e As EventArgs) Handles Button_Send.Click

        For Each item As ListViewItem In ListView1.Items


            Clipboard.SetText(item.SubItems(0).Text)
            ' First I copy the text to the clipboard to ensure in Explorer.exe if the file exists... and yes, it exists.
            ' The example text is this:
            ' C:\Electro\Nueva carpeta\Aggresivnes - Dance Or Die.mp3
            ' (without any quotes)


            ' ...But this throws an "not exists":
            If IO.File.Exists(item.SubItems(0).Text) Then MsgBox("exists") Else MsgBox("not exists")

            MsgBox(Validate_Windows_FileName(item.SubItems(0).Text)) ' Result: False (any invalid character)


            ' Here, an exception: "Illegal characters in path" :
            Dim File As New IO.FileInfo(item.SubItems(0).Text)

            ' If item.SubItems(1).Text.ToLower = "electro" Then Sendto_Path = "C:\Electro"
            ' If item.SubItems(1).Text.ToLower = "techno" Then Sendto_Path = "C:\Techno"
            ' If item.SubItems(1).Text.ToLower = "trance" Then Sendto_Path = "C:\Trance"

            'IO.File.Copy(File.FullName, IO.Path.Combine(Sendto_Path, File.Name))

        Next

    End Sub


#Region " Validate Windows FileName "

    ' [ Validate Windows FileName Function ]
    '
    ' By Elektro H@cker
    '
    ' Examples :
    ' MsgBox(Validate_Windows_FileName("C:\Test.txt"))  ' Result: True
    ' MsgBox(Validate_Windows_FileName("C:\Te|st.txt")) ' Result: False

    Private Function Validate_Windows_FileName(ByRef FileName As String)
        Dim Directory As String = Nothing
        Dim File As String = Nothing

        Try
            Directory = FileName.Substring(0, FileName.LastIndexOf("\")) & "\"
            File = FileName.Split("\").Last
        Catch
            If Directory Is Nothing Then File = FileName
        End Try

        If Directory Is Nothing AndAlso File Is Nothing Then Return False

        If Not Directory Is Nothing Then
            For Each InvalidCharacter As Char In IO.Path.GetInvalidPathChars
                If Directory.Contains(InvalidCharacter) Then
                    ' MsgBox(InvalidCharacter)
                    Return False
                End If
            Next
        End If

        If Not File Is Nothing Then
            For Each InvalidCharacter As Char In IO.Path.GetInvalidFileNameChars
                If File.Contains(InvalidCharacter) Then
                    ' MsgBox(InvalidCharacter)
                    Return False
                End If
            Next
        End If

        Return True ' FileName is valid
    End Function

#End Region

End Class

...Part two (less important I think):

#Region " WinAmp Info "

' [ WinAmp Info Functions ]
'
' // By Elektro H@cker
'
' Examples:
' MsgBox(WinAmpInfo.Title)    ' Result: Artist - Title
' MsgBox(WinAmpInfo.FileName) ' Result: C:\Title.ext

Public Class WinAmpInfo

    Private Const WinampClassName As String = "Winamp v1.x"

    Private Declare Auto Function FindWindow Lib "user32" (ByVal lpClassName As String, ByVal lpWindowName As String) As IntPtr
    Private Declare Auto Function GetWindowText Lib "user32" (ByVal hwnd As IntPtr, ByVal lpString As String, ByVal cch As Integer) As Integer
    Private Declare Function GetWindowThreadProcessId Lib "user32" (ByVal hWnd As Long, ByRef lpdwProcessId As Long) As Long
    Private Declare Function OpenProcess Lib "kernel32" (ByVal dwDesiredAccess As Long, ByVal bInheritHandle As Long, ByVal dwProcessId As Long) As Long
    Private Declare Function SendMessage Lib "user32" Alias "SendMessageA" (ByVal hWnd As Long, ByVal Msg As Long, ByVal wParam As Long, ByVal lParam As Long) As Long
    Private Declare Function ReadProcessMemory Lib "kernel32" (ByVal hProcess As Long, ByVal lpBaseAddress As Long, ByRef lpBuffer As Byte, ByVal nSize As Long, ByRef lpNumberOfBytesRead As Long) As Long
    Private Declare Function CloseHandle Lib "kernel32" (ByVal hObject As Long) As Long

    Public Shared Function Title() As String

        Dim hwnd As IntPtr = FindWindow(WinampClassName, vbNullString)

        Dim lpText As String = String.Empty
        Dim strTitle As String = String.Empty

        Dim intLength As Integer = 0
        Dim intName As Integer = 0
        Dim intLeft As Integer = 0
        Dim intRight As Integer = 0
        Dim intDot As Integer = 0

        If hwnd.Equals(IntPtr.Zero) Then Return "WinAmp is not running"

        lpText = New String(Chr(0), 100)
        intLength = GetWindowText(hwnd, lpText, lpText.Length)

        If (intLength <= 0) _
        OrElse (intLength > lpText.Length) _
        Then Return "Unknown"

        strTitle = lpText.Substring(0, intLength)
        intName = strTitle.IndexOf(" - Winamp")
        intLeft = strTitle.IndexOf("[")
        intRight = strTitle.IndexOf("]")

        If (intName >= 0) _
        AndAlso (intLeft >= 0) _
        AndAlso (intName < intLeft) _
        AndAlso (intRight >= 0) _
        AndAlso (intLeft + 1 < intRight) _
        Then Return strTitle.Substring(intLeft + 1, intRight - intLeft - 1)

        If (strTitle.EndsWith(" - Winamp")) _
        AndAlso (strTitle.Length > " - Winamp".Length) _
        Then strTitle = strTitle.Substring(0, strTitle.Length - " - Winamp".Length)

        intDot = strTitle.IndexOf(".")

        If (intDot > 0) _
        AndAlso (IsNumeric(strTitle.Substring(0, intDot))) _
        Then strTitle = strTitle.Remove(0, intDot + 1)

        Return strTitle.Trim

    End Function

    Public Shared Function FileName() As String

        Dim lp As Long, lpWinamp As Long, iIndex As Long, PID As Long, bRet As Long, dwRead As Long
        Dim Buffer(260) As Byte

        Dim hWndWinamp As IntPtr = FindWindow(WinampClassName, vbNullString)
        If hWndWinamp = 0 Then Return Nothing

        iIndex = SendMessage(hWndWinamp, &H400, 0, 125)

        lp = SendMessage(hWndWinamp, &H400, iIndex, 211)
        If lp = 0 Then Return Nothing

        Call GetWindowThreadProcessId(hWndWinamp, PID)

        lpWinamp = OpenProcess(&H10, 0, PID)
        If lpWinamp = 0 Then Return Nothing

        bRet = ReadProcessMemory(lpWinamp, lp, Buffer(0), 260, dwRead)

        Call CloseHandle(lpWinamp)

        Return System.Text.UnicodeEncoding.Default.GetString(Buffer)

    End Function

End Class

#End Region

UPDATE 3:

My new try... all is explained, I get a non-valid characters and I don't know why...

Private Sub Button_Send_Click(sender As Object, e As EventArgs) Handles Button_Send.Click

    For Each item As ListViewItem In ListView1.Items

        Dim filenameee As String = item.SubItems(0).Text.Trim ' The trim is...fuck, is just 'cause I don't know what more try to get a valid path...)

        Clipboard.SetText(filenameee) ' result: "C:\Test.mp3" (Without any double quote of course)

        ' ...but this launchs an "not exists" msgbox with the filename "C:\Test.mp3":
        If IO.File.Exists(filenameee) Then MsgBox("exists") Else MsgBox("not exists")

        MsgBox(filenameee) ' this showns "C:\Test.mp3" /without double quotes)
        MsgBox("filename is vlaid?:" & Validate_Windows_FileName(filenameee)) ' Result: False (path is invalid) (REALLY!!!!??? WTF)


        ' Here, an exception: "Illegal characters in path" :
        Dim File As New IO.FileInfo(item.SubItems(0).Text) ' (REALLY!!!!???)

    Next

End Sub

UPDATE 4:

PHOTOS:

The application itself:

enter image description here

(C:\Test.mp3 is the filenameee variable text)

Io.file.exists comprobation:

enter image description here

Showing the filenameee variable content:

enter image description here

cheking if filename is valid:

enter image description here

Showing the supposed illegal char in the filenameee:

enter image description here

(a space?)

The exception :

enter image description here

UPDATE 5

I think the real problem is here, in the "filename()" function of my winamp class which I've posted in the UPDATE Nº 2:

Return System.Text.UnicodeEncoding.Default.GetString(Buffer)

Because it appears to return a valid string as you can see in the images, but in the "Autos" appears to be a large string:

enter image description here

So please if someone can help me to fix this...

Thanks.

UPDATE 6:

This is working, I want to know if exist a method or something to improve this to avoid the use of a For loop:

Dim i As Int32 = 0
For Each by In System.Text.Encoding.Default.GetString(Buffer)
    If by = Nothing Then Exit For 'MsgBox(i)
    i += 1
Next

Return System.Text.Encoding.Default.GetString(Buffer, 0, i)

Solution

  • Check your file name against the invalid path chars (GetInvalidPathChars) and invalid filename characters (GetInvalidFilenameChars). That should tell you what characters are bad.

    Note that File.Exists does not "throw" anything. It returns false to say that the file doesn't exist. As the documentation states:

    The Exists method should not be used for path validation, this method merely checks if the file specified in path exists. Passing an invalid path to Exists returns false. To check whether the path contains any invalid characters, you can call the GetInvalidPathChars method to retrieve the characters that are invalid for the file system. You can also create a regular expression to test the whether the path is valid for your environment.

    In other words, File.Exists doesn't care if you pass it garbage. It'll tell you that a file with that trashy name doesn't exist. It won't tell you that you gave it a bad path name.

    Update after comment

    I don't know exactly what you're doing there with ReadProcessMemory, etc., but likely your problem is with this code:

        bRet = ReadProcessMemory(lpWinamp, lp, Buffer(0), 260, dwRead)
    
        Call CloseHandle(lpWinamp)
    
        Return System.Text.UnicodeEncoding.Default.GetString(Buffer)
    

    Here you're reading data into a 260-byte buffer and then creating a string, but you're not telling GetString how many bytes of that buffer to use, so it's going to use (or try to use) all of them. So you'll end up with all manner of junk (possibly) at the end of your string.

    Also, it's not clear to me whether you're trying to use Unicode encoding or the Default encoding. You have System.Text.UnicodeEncoding.Default. That's going to give you the encoding for the Default code page, which is the system's default ANSI code page. If you want the Default encoding, then just use System.Text.Encoding.Default.

    Assuming that you're expecting null-terminated ANSI strings, then you need to have your code find the length of the string before you call GetString. Do that by searching the buffer for the first 0 byte. That'll give you the length, and then you can call GetString(buffer, 0, length).

    To find the length, do this:

    Dim Len as Integer
    For Len = 0 to 260
        if Buffer(Len) = 0 Then Exit For
    Next
    Return System.Text.Encoding.Default.GetString(Buffer, 0, Len)