Compartilhar via


Formatos de caminho de arquivo em sistemas Windows

Membros de muitos dos tipos no System.IO namespace incluem um path parâmetro que permite especificar um caminho absoluto ou relativo para um recurso do sistema de arquivos. Esse caminho é então passado para as APIs do sistema de arquivos do Windows. Este tópico discute os formatos para caminhos de arquivo que você pode usar em sistemas Windows.

Caminhos de DOS tradicionais

Um caminho no DOS padrão pode consistir em três componentes:

Se todos os três componentes estiverem presentes, o caminho será absoluto. Se nenhum volume ou letra da unidade for especificado e o nome do diretório começar com o caractere separador de diretório, o caminho será relativo na raiz da unidade atual. Caso contrário, o caminho será relativo ao diretório atual. A tabela a seguir mostra alguns possíveis caminhos de arquivo e diretório.

Caminho Descrição
C:\Documents\Newsletters\Summer2018.pdf Um caminho de arquivo absoluto da raiz da unidade C:.
\Program Files\Custom Utilities\StringFinder.exe Um caminho relativo da raiz da unidade atual.
2018\January.xlsx Um caminho relativo para um arquivo em um subdiretório do diretório atual.
..\Publications\TravelBrochure.pdf Um caminho relativo para um arquivo em um diretório a partir do diretório atual.
C:\Projects\apilibrary\apilibrary.sln Um caminho absoluto para um arquivo na raiz da unidade C:.
C:Projects\apilibrary\apilibrary.sln Um caminho relativo do diretório atual da unidade C:.

Importante

Observe a diferença entre os dois últimos caminhos. Ambos especificam o especificador de volume opcional (C: em ambos os casos), mas o primeiro começa com a raiz do volume especificado, enquanto o segundo não. Como resultado, o primeiro é um caminho absoluto do diretório raiz da unidade C:, enquanto o segundo é um caminho relativo do diretório atual da unidade C:. O uso do segundo formulário quando o primeiro é destinado é uma fonte comum de bugs que envolvem caminhos de arquivo do Windows.

Você pode determinar se um caminho de arquivo é totalmente qualificado (ou seja, se o caminho é independente do diretório atual e não é alterado quando o diretório atual é alterado) chamando o Path.IsPathFullyQualified método. Observe que esse caminho pode incluir segmentos relativos de diretório (. e ..) e ainda ser totalmente qualificado se o caminho resolvido sempre apontar para o mesmo local.

O exemplo a seguir ilustra a diferença entre caminhos absolutos e relativos. Ele pressupõe que o diretório D:\FY2018\ existe e que nenhum diretório atual foi definido para D:\ no prompt de comando antes da execução do exemplo.

using System;
using System.Diagnostics;
using System.IO;
using System.Reflection;

public class Example2
{
    public static void Main(string[] args)
    {
        Console.WriteLine($"Current directory is '{Environment.CurrentDirectory}'");
        Console.WriteLine("Setting current directory to 'C:\\'");

        Directory.SetCurrentDirectory(@"C:\");
        string path = Path.GetFullPath(@"D:\FY2018");
        Console.WriteLine($"'D:\\FY2018' resolves to {path}");
        path = Path.GetFullPath(@"D:FY2018");
        Console.WriteLine($"'D:FY2018' resolves to {path}");

        Console.WriteLine("Setting current directory to 'D:\\Docs'");
        Directory.SetCurrentDirectory(@"D:\Docs");

        path = Path.GetFullPath(@"D:\FY2018");
        Console.WriteLine($"'D:\\FY2018' resolves to {path}");
        path = Path.GetFullPath(@"D:FY2018");

        // This will be "D:\Docs\FY2018" as it happens to match the drive of the current directory
        Console.WriteLine($"'D:FY2018' resolves to {path}");

        Console.WriteLine("Setting current directory to 'C:\\'");
        Directory.SetCurrentDirectory(@"C:\");

        path = Path.GetFullPath(@"D:\FY2018");
        Console.WriteLine($"'D:\\FY2018' resolves to {path}");

        // This will be either "D:\FY2018" or "D:\FY2018\FY2018" in the subprocess. In the sub process,
        // the command prompt set the current directory before launch of our application, which
        // sets a hidden environment variable that is considered.
        path = Path.GetFullPath(@"D:FY2018");
        Console.WriteLine($"'D:FY2018' resolves to {path}");

        if (args.Length < 1)
        {
            Console.WriteLine(@"Launching again, after setting current directory to D:\FY2018");
            Uri currentExe = new(Assembly.GetExecutingAssembly().Location, UriKind.Absolute);
            string commandLine = $"/C cd D:\\FY2018 & \"{currentExe.LocalPath}\" stop";
            ProcessStartInfo psi = new("cmd", commandLine); ;
            Process.Start(psi).WaitForExit();

            Console.WriteLine("Sub process returned:");
            path = Path.GetFullPath(@"D:\FY2018");
            Console.WriteLine($"'D:\\FY2018' resolves to {path}");
            path = Path.GetFullPath(@"D:FY2018");
            Console.WriteLine($"'D:FY2018' resolves to {path}");
        }
        Console.WriteLine("Press any key to continue... ");
        Console.ReadKey();
    }
}
// The example displays the following output:
//      Current directory is 'C:\Programs\file-paths'
//      Setting current directory to 'C:\'
//      'D:\FY2018' resolves to D:\FY2018
//      'D:FY2018' resolves to d:\FY2018
//      Setting current directory to 'D:\Docs'
//      'D:\FY2018' resolves to D:\FY2018
//      'D:FY2018' resolves to D:\Docs\FY2018
//      Setting current directory to 'C:\'
//      'D:\FY2018' resolves to D:\FY2018
//      'D:FY2018' resolves to d:\FY2018
//      Launching again, after setting current directory to D:\FY2018
//      Sub process returned:
//      'D:\FY2018' resolves to D:\FY2018
//      'D:FY2018' resolves to d:\FY2018
// The subprocess displays the following output:
//      Current directory is 'C:\'
//      Setting current directory to 'C:\'
//      'D:\FY2018' resolves to D:\FY2018
//      'D:FY2018' resolves to D:\FY2018\FY2018
//      Setting current directory to 'D:\Docs'
//      'D:\FY2018' resolves to D:\FY2018
//      'D:FY2018' resolves to D:\Docs\FY2018
//      Setting current directory to 'C:\'
//      'D:\FY2018' resolves to D:\FY2018
//      'D:FY2018' resolves to D:\FY2018\FY2018
Imports System.IO
Imports System.Reflection

Public Module Example2

    Public Sub Main(args() As String)
        Console.WriteLine($"Current directory is '{Environment.CurrentDirectory}'")
        Console.WriteLine("Setting current directory to 'C:\'")
        Directory.SetCurrentDirectory("C:\")

        Dim filePath As String = Path.GetFullPath("D:\FY2018")
        Console.WriteLine($"'D:\\FY2018' resolves to {filePath}")
        filePath = Path.GetFullPath("D:FY2018")
        Console.WriteLine($"'D:FY2018' resolves to {filePath}")

        Console.WriteLine("Setting current directory to 'D:\\Docs'")
        Directory.SetCurrentDirectory("D:\Docs")

        filePath = Path.GetFullPath("D:\FY2018")
        Console.WriteLine($"'D:\\FY2018' resolves to {filePath}")
        filePath = Path.GetFullPath("D:FY2018")

        ' This will be "D:\Docs\FY2018" as it happens to match the drive of the current directory
        Console.WriteLine($"'D:FY2018' resolves to {filePath}")

        Console.WriteLine("Setting current directory to 'C:\\'")
        Directory.SetCurrentDirectory("C:\")

        filePath = Path.GetFullPath("D:\FY2018")
        Console.WriteLine($"'D:\\FY2018' resolves to {filePath}")

        ' This will be either "D:\FY2018" or "D:\FY2018\FY2018" in the subprocess. In the sub process,
        ' the command prompt set the current directory before launch of our application, which
        ' sets a hidden environment variable that is considered.
        filePath = Path.GetFullPath("D:FY2018")
        Console.WriteLine($"'D:FY2018' resolves to {filePath}")

        If args.Length < 1 Then
            Console.WriteLine("Launching again, after setting current directory to D:\FY2018")
            Dim currentExe As New Uri(Assembly.GetExecutingAssembly().GetName().CodeBase, UriKind.Absolute)
            Dim commandLine As String = $"/C cd D:\FY2018 & ""{currentExe.LocalPath}"" stop"
            Dim psi As New ProcessStartInfo("cmd", commandLine)
            Process.Start(psi).WaitForExit()

            Console.WriteLine("Sub process returned:")
            filePath = Path.GetFullPath("D:\FY2018")
            Console.WriteLine($"'D:\\FY2018' resolves to {filePath}")
            filePath = Path.GetFullPath("D:FY2018")
            Console.WriteLine($"'D:FY2018' resolves to {filePath}")
        End If
        Console.WriteLine("Press any key to continue... ")
        Console.ReadKey()
    End Sub
End Module
' The example displays the following output:
'      Current directory is 'C:\Programs\file-paths'
'      Setting current directory to 'C:\'
'      'D:\FY2018' resolves to D:\FY2018
'      'D:FY2018' resolves to d:\FY2018
'      Setting current directory to 'D:\Docs'
'      'D:\FY2018' resolves to D:\FY2018
'      'D:FY2018' resolves to D:\Docs\FY2018
'      Setting current directory to 'C:\'
'      'D:\FY2018' resolves to D:\FY2018
'      'D:FY2018' resolves to d:\FY2018
'      Launching again, after setting current directory to D:\FY2018
'      Sub process returned:
'      'D:\FY2018' resolves to D:\FY2018
'      'D:FY2018' resolves to d:\FY2018
' The subprocess displays the following output:
'      Current directory is 'C:\'
'      Setting current directory to 'C:\'
'      'D:\FY2018' resolves to D:\FY2018
'      'D:FY2018' resolves to D:\FY2018\FY2018
'      Setting current directory to 'D:\Docs'
'      'D:\FY2018' resolves to D:\FY2018
'      'D:FY2018' resolves to D:\Docs\FY2018
'      Setting current directory to 'C:\'
'      'D:\FY2018' resolves to D:\FY2018
'      'D:FY2018' resolves to D:\FY2018\FY2018

Caminhos UNC

Os caminhos unc (convenção de nomenclatura universal), que são usados para acessar recursos de rede, têm o seguinte formato:

  • Um nome de servidor ou host, que é precedido por \\. O nome do servidor pode ser um nome de computador NetBIOS ou um endereço IP/FQDN (há suporte para IPv4 e v6).
  • Um nome de compartilhamento, que é separado do nome do host por \. Juntos, o servidor e o nome do compartilhamento compõem o volume.
  • Um nome de diretório. O caractere separador de diretório separa subdiretórios dentro da hierarquia de diretório aninhada.
  • Um nome de arquivo opcional. O caractere separador de diretório separa o caminho do arquivo e o nome do arquivo.

Veja alguns exemplos de caminhos UNC:

Caminho Descrição
\\system07\C$\ O diretório raiz da unidade C: em system07.
\\Server2\Share\Test\Foo.txt O arquivo Foo.txt no diretório Test do volume \\Server2\Share.

Caminhos UNC devem sempre ser totalmente qualificados. Eles podem incluir segmentos de diretório relativo (. e ..), mas eles devem fazer parte de um caminho totalmente qualificado. É possível usar caminhos relativos somente mapeando um caminho UNC para uma letra da unidade.

Caminhos de dispositivo DOS

O sistema operacional Windows tem um modelo de objeto unificado que aponta para todos os recursos, incluindo arquivos. Esses caminhos de objeto são acessíveis na janela do console e são expostos à camada Win32 por meio de uma pasta especial de links simbólicos para os quais os caminhos DOS e UNC herdados são mapeados. Essa pasta especial é acessada por meio da sintaxe do caminho do dispositivo DOS, que é uma das seguintes:

\\.\C:\Test\Foo.txt \\?\C:\Test\Foo.txt

Além de identificar uma unidade pela letra, você pode identificar um volume usando a GUID do volume. Isso assume a forma:

\\.\Volume{b75e2c83-0000-0000-0000-602f00000000}\Test\Foo.txt \\?\Volume{b75e2c83-0000-0000-0000-602f00000000}\Test\Foo.txt

Observação

A sintaxe do caminho do dispositivo DOS tem suporte em implementações do .NET em execução no Windows, começando com o .NET Core 1.1 e o .NET Framework 4.6.2.

O caminho do dispositivo DOS consiste nos seguintes componentes:

  • O especificador do caminho do dispositivo (\\.\ ou \\?\), que identifica o caminho como um caminho do dispositivo DOS.

    Observação

    O \\?\ é compatível com todas as versões do .NET Core e do .NET 5+ e no .NET Framework a partir da versão 4.6.2.

  • Um link simbólico para o objeto de dispositivo "real" (C: no caso de um nome de unidade ou Volume{b75e2c83-0000-0000-0000-602f00000000} no caso de um GUID de volume).

    O primeiro segmento do caminho de dispositivo DOS depois do especificador de caminho do dispositivo identifica o volume ou a unidade. (Por exemplo, \\?\C:\ e \\.\BootPartition\.)

    Há um link específico para uncs que é chamado, não surpreendentemente, UNC. Por exemplo:

    \\.\UNC\Server\Share\Test\Foo.txt \\?\UNC\Server\Share\Test\Foo.txt

    Para UNCs de dispositivo, a parte do servidor/compartilhamento forma o volume. Por exemplo, em \\?\server1\utilities\\filecomparer\, a parte de servidor/compartilhamento é server1\utilities. Isso é significativo ao chamar um método como Path.GetFullPath(String, String) com segmentos de diretório relativos; nunca é possível navegar além do volume.

Os caminhos do dispositivo DOS são totalmente qualificados por definição e não podem começar com um segmento de diretório relativo (. ou ..). Os diretórios atuais nunca são inseridos no uso deles.

Exemplo: maneiras de se referir ao mesmo arquivo

O exemplo a seguir ilustra algumas das maneiras pelas quais você pode consultar um arquivo ao usar as APIs no System.IO namespace. O exemplo instancia um objeto FileInfo e usa suas propriedades Name e Length para exibir o nome do arquivo e o tamanho do arquivo.

using System;
using System.IO;

class Program
{
    static void Main()
    {
        string[] filenames = {
            @"c:\temp\test-file.txt",
            @"\\127.0.0.1\c$\temp\test-file.txt",
            @"\\LOCALHOST\c$\temp\test-file.txt",
            @"\\.\c:\temp\test-file.txt",
            @"\\?\c:\temp\test-file.txt",
            @"\\.\UNC\LOCALHOST\c$\temp\test-file.txt" };

        foreach (string filename in filenames)
        {
            FileInfo fi = new(filename);
            Console.WriteLine($"file {fi.Name}: {fi.Length:N0} bytes");
        }
    }
}
// The example displays output like the following:
//      file test-file.txt: 22 bytes
//      file test-file.txt: 22 bytes
//      file test-file.txt: 22 bytes
//      file test-file.txt: 22 bytes
//      file test-file.txt: 22 bytes
//      file test-file.txt: 22 bytes
Imports System.IO

Module Program
    Sub Main()
        Dim filenames() As String = {
                "c:\temp\test-file.txt",
                "\\127.0.0.1\c$\temp\test-file.txt",
                "\\LOCALHOST\c$\temp\test-file.txt",
                "\\.\c:\temp\test-file.txt",
                "\\?\c:\temp\test-file.txt",
                "\\.\UNC\LOCALHOST\c$\temp\test-file.txt"}

        For Each filename In filenames
            Dim fi As New FileInfo(filename)
            Console.WriteLine($"file {fi.Name}: {fi.Length:N0} bytes")
        Next
    End Sub
End Module

Normalização de caminho

Quase todos os caminhos passados para APIs do Windows são normalizados. Durante a normalização, o Windows executa as seguintes etapas:

  • Identifica o caminho.
  • Aplica o diretório atual a caminhos (relativos) parcialmente qualificados.
  • Padroniza separadores de componentes e diretórios.
  • Avalia os componentes relativos do diretório (. para o diretório atual e .. para o diretório pai).
  • Corta alguns caracteres.

Essa normalização ocorre implicitamente, mas você pode fazer isso explicitamente chamando o Path.GetFullPath método, que encapsula uma chamada para a função GetFullPathName(). Você também pode chamar a função Windows GetFullPathName() diretamente usando P/Invoke.

Identificar o caminho

A primeira etapa na normalização do caminho é identificar o tipo de caminho. Os caminhos se enquadram em uma das poucas categorias:

  • São caminhos de dispositivo, ou seja, começam com dois separadores e um ponto de interrogação ou ponto final (\\? ou \\.).
  • São caminhos UNC, ou seja, começam com dois separadores sem um ponto de interrogação ou ponto final.
  • São caminhos DOS totalmente qualificados, ou seja, começam com uma letra da unidade, um separador de volume e um separador de componente (C:\).
  • Designam um dispositivo herdado (CON, LPT1).
  • Estão relacionados com a raiz da unidade atual, ou seja, começam com um separador de componente único (\).
  • Estão relacionados com o diretório atual de uma unidade especificada, ou seja, começam com uma letra da unidade, um separador de volume e sem um separador de componente (C:).
  • Eles são relativos ao diretório atual; ou seja, eles começam com qualquer outra coisa (temp\testfile.txt).

O tipo do caminho determina se um diretório atual é aplicado ou não de alguma forma. Ele também determina qual é a "raiz" do caminho.

Manipular dispositivos herdados

Se o caminho for um dispositivo DOS herdado como CON, COM1 ou LPT1, será convertido em um caminho de dispositivo pelo \\.\ precedente e retornado.

Antes do Windows 11, um caminho que começa com um nome de dispositivo herdado é sempre interpretado como um dispositivo herdado pelo Path.GetFullPath(String) método. Por exemplo, o caminho do dispositivo DOS para CON.TXT é \\.\CON, e o caminho do dispositivo DOS para COM1.TXT\file1.txt é \\.\COM1. Como isso não se aplica mais ao Windows 11, especifique o caminho completo para o dispositivo DOS herdado, como \\.\CON.

Aplicar o diretório atual

Se um caminho não for totalmente qualificado, o Windows aplicará o diretório atual a ele. O diretório atual não foi aplicado a UNCs e caminhos de dispositivo. Nem a unidade total com o separador C:\.

Se o caminho começar com um único separador de componente, a unidade do diretório atual será aplicada. Por exemplo, se o caminho do arquivo for \utilities e o diretório atual for C:\temp\, a normalização produzirá C:\utilities.

Se o caminho começar com uma letra da unidade, um separador de volume e sem um separador de componente, o último diretório atual definido do shell de comando para a unidade especificada será aplicado. Se o último diretório atual não tiver sido definido, a unidade isolada será aplicada. Por exemplo, se o caminho do arquivo for D:sources, o diretório atual for C:\Documents\, e o último diretório atual na unidade D: foi D:\sources\, o resultado será D:\sources\sources. Esses caminhos "relativos à unidade" são uma fonte comum de erros lógicos de script e programas. Assumir que um caminho iniciado com uma letra e dois-pontos não é relativo é claramente incorreto.

Se o caminho começar com algo diferente de um separador, a unidade e o diretório atuais serão aplicados. Por exemplo, se o caminho for filecompare e o diretório atual for C:\utilities\, o resultado será C:\utilities\filecompare\.

Importante

Caminhos relativos são perigosos em aplicativos multiencadeados (ou seja, a maioria dos aplicativos) porque o diretório atual é uma configuração por processo. Qualquer thread pode alterar o diretório atual a qualquer momento. A partir do .NET Core 2.1, é possível chamar o método Path.GetFullPath(String, String) para obter um caminho absoluto de um caminho relativo, bem como o caminho base (o diretório atual) que você precisa para resolvê-lo.

Canonizar separadores

Todas as barras (/) são convertidas no separador padrão do Windows, a barra invertida (\). Se estiverem presentes, uma série de barras que segue as duas primeiras barras serão ocultadas e exibidas como uma barra só.

Observação

A partir do .NET 8 em sistemas operacionais baseados em Unix, o runtime não converte mais caracteres de barra invertida (\) em separadores de diretório (barras /). Para obter mais informações, consulte Mapeamento de barra invertida em caminhos de arquivo Unix.

Avaliar componentes relativos

À medida que o caminho é processado, todos os componentes ou segmentos compostos por um único ou um período duplo (. ou ..) são avaliados:

  • Por um único período, o segmento atual é removido, pois se refere ao diretório atual.

  • No caso do ponto duplo, o segmento atual e o segmento pai serão removidos, pois o ponto duplo se refere ao diretório pai.

    Os diretórios pais só serão removidos se não forem maiores que a raiz do caminho. A raiz do caminho depende do tipo de caminho. É a unidade (C:\) dos caminhos DOS, o servidor/compartilhamento de UNCs (\\Server\Share) e o prefixo do caminho de dispositivo dos caminhos de dispositivo (\\?\ ou \\.\).

Cortar caracteres

Juntamente com as sequências de separadores e segmentos relativos removidos anteriormente, alguns caracteres adicionais são removidos durante a normalização.

  • Se um segmento terminar em um único período, esse período será removido. (Um segmento de um período único ou duplo é normalizado na etapa anterior. Um segmento de três ou mais períodos não é normalizado e, na verdade, é um nome de arquivo/diretório válido.)

  • Se o caminho não terminar em um separador, todos os pontos e espaços à direita (U+0020) serão removidos. Se o último segmento for simplesmente um período único ou duplo, ele se enquadra na regra de componentes relativos acima.

    Essa regra significa que você pode criar um nome de diretório com um espaço final adicionando um separador ao final após o espaço.

    Importante

    Você nunca deve criar um diretório ou nome de arquivo com um espaço à direita. Espaços à direita podem dificultar ou impossibilitar o acesso a um diretório, e os aplicativos geralmente falham ao tentar lidar com diretórios ou arquivos cujos nomes incluem espaços à direita.

Ignorar a normalização

Normalmente, qualquer caminho passado para uma API do Windows é (efetivamente) passado para a função GetFullPathName e normalizado. Há uma exceção importante: um caminho de dispositivo que começa com um ponto de interrogação em vez de um ponto final. A menos que o caminho comece exatamente com \\?\ (observe o uso da barra invertida canônica), ele é normalizado.

Por que você deseja ignorar a normalização? Há três motivos principais:

  1. Para obter acesso a caminhos normalmente indisponíveis, mas que são legais. Um arquivo ou diretório chamado hidden., por exemplo, é impossível de acessar de qualquer outra maneira.

  2. Para melhorar o desempenho ignorando a normalização, se você já tiver normalizado.

  3. Somente no .NET Framework, ignorar a verificação MAX_PATH do tamanho do caminho para permitir caminhos com mais de 259 caracteres. A maioria das APIs permite isso, com algumas exceções.

Observação

O .NET Core e o .NET 5+ manipulam caminhos longos implicitamente e não executam uma MAX_PATH verificação. A MAX_PATH verificação se aplica somente ao .NET Framework.

Ignorar a normalização e as verificações de tamanho do caminho é a única diferença entre as duas sintaxes de caminho de dispositivo. Caso contrário, elas serão idênticas. Tenha cuidado ao ignorar a normalização, pois você pode facilmente criar caminhos que são difíceis para aplicativos "normais" lidarem com.

Os caminhos que começam com \\?\ ainda serão normalizados se você passá-los explicitamente para a função GetFullPathName.

É possível passar caminhos com mais de MAX_PATH caracteres para GetFullPathName sem \\?\. Ele dá suporte a caminhos de comprimento arbitrário até o tamanho máximo da cadeia de caracteres que o Windows pode manipular.

Case e o sistema de arquivos do Windows

Uma peculiaridade do sistema de arquivos do Windows que usuários e desenvolvedores que não usam Windows acham confusa é que os nomes de caminho e diretório são insensíveis a maiúsculas e minúsculas. Isto é, os nomes do caminho e do diretório refletem as cadeias de caracteres utilizadas no momento da criação. Por exemplo, a chamada de método

Directory.Create("TeStDiReCtOrY");
Directory.Create("TeStDiReCtOrY")

cria um diretório chamado TeStDiReCtOrY. Se você renomear um diretório ou arquivo para alterar seu caso, o diretório ou o nome do arquivo refletirá o caso da cadeia de caracteres usada ao renomeá-lo. Por exemplo, o código a seguir renomeia um arquivo chamado test.txt para Test.txt:

using System.IO;

class Example3
{
    static void Main()
    {
        var fi = new FileInfo(@".\test.txt");
        fi.MoveTo(@".\Test.txt");
    }
}
Imports System.IO

Module Example3
    Public Sub Main()
        Dim fi As New FileInfo(".\test.txt")
        fi.MoveTo(".\Test.txt")
    End Sub
End Module

No entanto, as comparações entre o nome do diretório e do arquivo não diferenciam maiúsculas e minúsculas. Se você pesquisar um arquivo chamado "test.txt", as APIs do sistema de arquivos .NET ignorarão o caso na comparação. "Test.txt", "TEST.TXT", "test.TXT" e qualquer outra combinação de letras maiúsculas e minúsculas corresponderá a "test.txt".