MAX_PATH et les pièges des chemins et noms de fichiers Windows — la limite de 260 caractères, les noms réservés, les points de fin et la casse
· Go Komura · MAX_PATH, Chemin de fichier, Chemin long, Nom de fichier, NTFS, Win32, C#, .NET, Développement Windows, Investigation de bugs, Conseil technique
« Sur le poste d’un seul utilisateur, ça donne “fichier introuvable” ». « La copie a réussi, mais ce fichier ne s’ouvre pas. » Il n’est pas rare, en enquêtant sur un bug d’une application métier manipulant des entrées-sorties de fichiers, de découvrir que la cause profonde tenait finalement à la longueur d’un chemin ou au nom de fichier lui-même. Les utilisateurs entassent noms de dossiers de projet et dates dans les noms de dossiers, creusent des hiérarchies profondes, et dépassent allègrement toutes les hypothèses que l’on avait pu poser.
Ce qui rend ce domaine délicat, c’est que les limites sont réparties sur plusieurs couches — la limite propre de l’API Win32, celle du système de fichiers, celle du shell (l’Explorateur) et celle du runtime .NET — ce qui rend difficile de savoir jusqu’où on peut réellement aller et ce qui continuera à poser problème. Cet article détaille ce que recouvre réellement MAX_PATH=260, les conditions pour manipuler légalement des chemins plus longs, les pièges des noms de fichiers comme les noms réservés (CON, etc.) et les points de fin, ainsi que la gestion de la casse — avec, pour chaque point, les contre-mesures pratiques en C#.
1. La conclusion d’abord
- MAX_PATH=260 est une limite de l’API Win32 qui inclut la lettre de lecteur, les deux-points, la barre oblique inverse, jusqu’à 256 caractères de texte de chemin et le NUL terminal. La couche du système de fichiers (NTFS, etc.) peut gérer des chemins bien plus longs : en ajoutant le préfixe
\\?\à un appel d’API Unicode, on peut spécifier environ 32 767 caractères au total.12 - Lever la limite de 260 caractères sous Windows 10 version 1607 ou ultérieure exige à la fois « LongPathsEnabled=1 dans le registre » et « longPathAware dans le manifeste de l’application ». N’en définir qu’un seul n’a aucun effet.3
- Le runtime .NET (Core)/.NET 5 et versions ultérieures n’effectue pas de vérification MAX_PATH et gère implicitement les chemins longs. Cibler .NET Framework 4.6.2 ou ultérieur supprime la vérification de 260 caractères propre au runtime.45
- Cela dit, des applications qui ne prennent pas en charge les chemins longs subsistent bel et bien. La documentation officielle elle-même indique clairement que le shell (l’Explorateur) peut échouer à interpréter correctement un chemin que l’API Win32 a pourtant réussi à créer.1
- Les noms de fichiers ne peuvent pas contenir
< > : " / \ | ? *ni les caractères de contrôle (0-31), et CON, PRN, AUX, NUL, COM1-9 et LPT1-9 sont traités comme des noms réservés même avec une extension (par exemple CON.txt).6 - Les espaces et points de fin dans un nom sont supprimés silencieusement lors de la normalisation du chemin. C’est la source d’incidents tels que taper « rapport-v2. » et se retrouver avec « rapport-v2 », ou ne pas pouvoir accéder depuis Windows à un fichier créé par un autre OS avec une espace de fin.67
- Par défaut, les noms de fichiers Windows « conservent la casse mais n’en tiennent pas compte ». NTFS prend aussi en charge la distinction de casse par répertoire (
fsutil.exe file setCaseSensitiveInfo), mais l’activer a pour effet secondaire que les applications Windows ne suivent pas nécessairement.89 - Côté implémentation, méfiez-vous du comportement de
Path.Combinequi rejette les arguments précédents dès qu’un argument ultérieur est un chemin enraciné (Path.Joinest une alternative sur .NET Core), et assainissez les noms de fichiers fournis par l’utilisateur avecPath.GetInvalidFileNameCharscomplété par vos propres vérifications des noms réservés et des caractères de fin.1011
2. Ce que recouvre réellement MAX_PATH=260
La longueur maximale d’un chemin dans l’API Win32 est, à quelques exceptions près, définie comme MAX_PATH=260 caractères. Ces 260 caractères ont une composition précise. Un chemin local est constitué de « la lettre de lecteur, les deux-points, la barre oblique inverse, les composants de nom séparés par des barres obliques inverses, et le caractère NUL terminal » — pour un lecteur D:, par exemple, le maximum est « D:\ + 256 caractères de texte de chemin + le NUL terminal ».1
Autrement dit, si l’on considère 260 comme « la longueur disponible pour le nom de fichier », on se trompe de quatre caractères une fois pris en compte la notation du lecteur et le NUL terminal. Il existe une restriction encore plus fine : comme les API de création de répertoire doivent laisser de la place pour ajouter ensuite un nom de fichier au format 8.3, un chemin de répertoire ne peut pas dépasser MAX_PATH moins 12 caractères.1
Ce qui est important, c’est qu’il s’agit là d’une limite de la couche API Win32, et non d’un plafond imposé par le système de fichiers. NTFS prend en charge les noms de fichiers longs et les chemins de longueur étendue, et les versions Unicode de nombreuses fonctions Win32 acceptent des chemins de longueur étendue d’environ 32 767 caractères au total. La limite supérieure d’un composant individuel du chemin (un seul nom de dossier ou de fichier) correspond à la valeur renvoyée par GetVolumeInformation, et elle est en général de 255 caractères.12
Cet écart — « l’API dit 260, le système de fichiers dit environ 32 767 » — est précisément la source des dysfonctionnements observés sur le terrain. Il est tout à fait légitime qu’un chemin qu’un outil a pu créer devienne impossible à ouvrir avec un autre outil (ou votre propre application). Extraire un dépôt profond via git clone dans un dossier au nom long, puis constater que la compilation échoue ensuite, est un exemple classique cité par la documentation officielle elle-même.1
À noter également : dans les anciennes versions de .NET Framework, un chemin complet de 260 caractères ou plus déclenchait System.IO.PathTooLongException. Si vous rencontrez cette exception, soupçonnez d’abord la longueur du chemin.12
3. Comment franchir le mur des 260 caractères, et à quelles conditions
Il existe globalement deux façons de gérer les chemins longs : le préfixe \\?\ et l’activation des chemins longs au niveau de l’OS.
3.1. Le préfixe \\?\
En ajoutant \\?\ au début d’une chaîne de chemin, l’API Win32 cesse d’analyser la chaîne et la transmet directement au système de fichiers. Cela permet de dépasser la limite MAX_PATH (un chemin UNC prend la forme \\?\UNC\serveur\partage). Il existe toutefois des conditions et des effets de bord.16
- Il faut utiliser une API Unicode (les fonctions suffixées « W », ou tout appel effectué en UTF-16, comme le fait .NET).
- Comme la normalisation est ignorée, on ne peut pas utiliser
/comme séparateur, ni la notation relative avec.ou... Le préfixe\\?\ne peut pas être attaché à un chemin relatif, donc les chemins relatifs restent toujours limités à MAX_PATH.1 - Toutes les API ne le prennent pas en charge : il faut vérifier la documentation de référence de chaque API pour savoir si elle le supporte.6
3.2. Activer les chemins longs sous Windows 10 1607 et ultérieur — la condition, c’est « les deux »
Sous Windows 10 version 1607 et ultérieure, il est possible de lever la limite MAX_PATH pour de nombreuses fonctions de fichiers et de répertoires Win32 courantes (CreateFileW, FindFirstFileW, GetFileAttributesW, etc.). Cela reste toutefois un choix explicite de l’application, et exige les deux conditions suivantes simultanément.3
- La valeur de registre
LongPathsEnabled(REG_DWORD) sousHKLM\SYSTEM\CurrentControlSet\Control\FileSystemdoit valoir 1. Elle peut aussi être définie via la stratégie de groupe « Configuration ordinateur > Modèles d’administration > Système > Système de fichiers > Activer les chemins d’accès Win32 longs ». - Le manifeste de l’application doit contenir l’élément
longPathAware.
<application xmlns="urn:schemas-microsoft-com:asm.v3">
<windowsSettings xmlns:ws2="http://schemas.microsoft.com/SMI/2016/WindowsSettings">
<ws2:longPathAware>true</ws2:longPathAware>
</windowsSettings>
</application>
# Côté registre (nécessite des privilèges administrateur)
New-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Control\FileSystem" `
-Name "LongPathsEnabled" -Value 1 -PropertyType DWORD -Force
Le classique « j’ai configuré le registre mais ça ne marche toujours pas » vient presque toujours d’une entrée de manifeste manquante. La documentation officielle elle-même insiste sur ce point : « ce paramètre de registre n’affecte que les applications qui ont été modifiées pour tirer parti de cette nouvelle fonctionnalité ». Par ailleurs, la valeur de registre est mise en cache par processus lors du premier appel à une fonction de fichier et n’est pas rechargée pendant toute la durée de vie du processus ; un redémarrage peut donc être nécessaire pour garantir que le changement de paramètre soit bien pris en compte par toutes les applications.3
3.3. Et du côté de .NET ?
- .NET (Core) / .NET 5 et ultérieur : le runtime n’effectue aucune vérification MAX_PATH et gère implicitement les chemins longs. Aucun code particulier n’est nécessaire côté application.4
- .NET Framework : cibler la version 4.6.2 ou ultérieure supprime la vérification de 260 caractères du runtime, et
PathTooLongExceptionn’est désormais levée que lorsque le chemin dépasse 32 767 caractères ou lorsque l’OS lui-même renvoie une erreur. Même une application existante ciblant une version antérieure peut opter pour ce comportement via les commutateurs AppContextSwitch.System.IO.BlockLongPaths=false(et, pour désactiver la gestion héritée des chemins,Switch.System.IO.UseLegacyPathHandling=false).512 - Pour qu’une application .NET Framework puisse réellement faire passer des chemins longs en pratique, il faut combiner les paramètres du runtime ci-dessus avec l’activation côté OS et le manifeste. La documentation sur la prise en charge des chemins longs de NuGet.exe détaille précisément cette configuration comme exemple pratique (Windows 10 1607 ou ultérieur, manifeste
longPathAware, et désactivation deUseLegacyPathHandling).13
3.4. Malgré tout cela, la réalité des « applications non compatibles » persiste
Même après avoir tout mis en place, cela ne signifie pas que toutes les applications existantes gèrent soudain les chemins longs. La documentation officielle indique clairement que « le shell et le système de fichiers ont des exigences différentes — l’interface du shell peut ne pas interpréter correctement un chemin qui a pu être créé via l’API Win32 »1, et en pratique, des outils qui ne revendiquent pas la prise en charge des chemins longs subsistent (par exemple, la documentation de NuGet elle-même précise que la restauration (restore) dans Visual Studio ou via msbuild -t:restore ne prend pas en charge les chemins longs13). Même si votre propre application peut créer un fichier à un chemin long, la question de savoir si l’utilisateur pourra ensuite l’ouvrir dans l’Explorateur ou un autre outil est une question distincte. Le tableau de décision du chapitre 6 récapitule les choix de conception qui tiennent compte de cette asymétrie.
4. Caractères interdits, noms de périphériques réservés, points et espaces de fin
Aux côtés de la longueur des chemins, l’autre champ de mines est constitué des règles portant sur le nom de fichier lui-même. Voici un tour d’horizon des points des règles officielles de nommage les plus souvent source de problèmes dans les applications métier.6
| Catégorie | Contenu | Remarques |
|---|---|---|
| Caractères réservés | < > : " / \ \| ? * |
Inclut le séparateur de chemin \ (et /) et les deux-points : du lecteur |
| Caractères de contrôle | Valeur entière 0 (NUL) et 1 à 31 | Interdits, sauf à l’intérieur d’un flux de données alternatif |
| Noms de périphériques réservés | CON, PRN, AUX, NUL, COM1-COM9, LPT1-LPT9 (ainsi que les formes en chiffres en exposant COM¹-³, LPT¹-³) | Interdits même avec une extension (NUL.txt et NUL.tar.gz équivalent à NUL) |
| Caractères de fin | Un nom se terminant par une espace ou un point | Le système de fichiers peut le tolérer, mais pas le shell ni l’interface utilisateur |
4.1. Noms de périphériques réservés — même CON.txt est interdit
CON et NUL sont des noms de périphériques hérités de l’époque MS-DOS, et ils restent aujourd’hui encore réservés dans l’espace de noms NT. C’est pourquoi un fichier nommé « CON » ne peut pas être créé par les moyens ordinaires, et même en ajoutant une extension, comme dans CON.txt, le nom continue d’être interprété comme le nom réservé.6 Sur le terrain, cela se traduit par des incidents tels que tenter d’enregistrer un journal de port série sous un nom comme « COM1.log », ou un dossier nommé « aux » créé sous Linux et impossible à extraire sous Windows.
En complément, selon les règles de normalisation des chemins, un chemin commençant par un nom réservé — « CON » ou « COM1.TXT », par exemple — était auparavant converti et interprété comme un chemin de périphérique (\\.\CON). Windows 11 a changé cette interprétation, et il faut désormais une forme pleinement qualifiée comme \\.\CON pour réellement viser le périphérique hérité.7 Cela dit, comme les anciennes versions de l’OS et les applications existantes conservent toutes deux l’interprétation traditionnelle, la conclusion selon laquelle il faut éviter les noms réservés pour les données métier reste inchangée.
4.2. Les espaces et points de fin disparaissent « silencieusement »
Les règles officielles de nommage précisent qu’un nom de fichier ou de répertoire ne doit pas se terminer par une espace ou un point.6 Plus précisément, la normalisation des chemins Windows comporte une règle explicite : « si le chemin ne se termine pas par un caractère séparateur, supprimer tous les points et espaces (U+0020) de fin ».7
Ce qui rend ce comportement réellement problématique en pratique, c’est qu’il se transforme en un nom différent silencieusement, sans aucune erreur. Si un utilisateur saisit « rapport-v2. » comme nom, ce qui est effectivement créé est un fichier nommé « rapport-v2 » — le point de fin a disparu. À l’inverse, un fichier comme « rapport » (avec une espace de fin) créé depuis Linux via SMB devient inaccessible via la notation de chemin normale de Windows, car la normalisation modifie le nom au passage. Le moyen d’atteindre un nom « légal mais inaccessible via la normalisation » est le préfixe \\?\, qui ignore la normalisation. La documentation officielle mentionne explicitement ce cas d’usage, en indiquant qu’un fichier comme hidden. est inaccessible par tout autre moyen.7
Notez qu’un point en début de nom est parfaitement légal (un nom comme .gitignore peut être créé sans aucun problème).6
5. La casse est « conservée mais non distinguée »
Le comportement par défaut du système de fichiers Windows est de conserver la casse sans en tenir compte pour la comparaison (case-preserving, case-insensitive). Créez un fichier nommé Readme.txt et sa casse est conservée à l’affichage, mais les recherches et comparaisons ignorent la casse, si bien que README.TXT atteint ce même fichier. Les lettres de lecteur sont, de la même façon, insensibles à la casse.86
Les règles officielles de nommage indiquent aux développeurs d’applications de ne pas présumer la distinction de casse (« OSCAR, Oscar et oscar doivent être considérés comme le même nom »), tout en précisant en même temps que NTFS lui-même prend en charge la distinction de casse à la POSIX (bien qu’elle soit désactivée par défaut).6
Ce point se manifeste concrètement autour de l’interopérabilité avec Linux. Depuis la build 17107 de Windows 10, il est possible d’activer la distinction de casse par répertoire.9
# Dans une invite PowerShell administrateur
fsutil.exe file setCaseSensitiveInfo C:\work\linux-src enable
fsutil.exe file queryCaseSensitiveInfo C:\work\linux-src
C’est un outil valable lorsqu’on travaille avec une arborescence de sources issue de Linux (où, par exemple, Makefile et makefile coexistent), mais la documentation officielle elle-même met en garde contre un effet secondaire : une application Windows qui présume l’insensibilité à la casse du système de fichiers peut échouer à accéder aux fichiers d’un répertoire où la distinction de casse a été activée. Par ailleurs, il n’est pas possible de basculer ce drapeau tant que le répertoire cible n’est pas vide, et tout sous-répertoire nouvellement créé hérite du paramètre de son parent.9 Historiquement, il existe aussi un phénomène documenté officiellement où, pour deux fichiers dont les noms ne diffèrent que par la casse, l’Explorateur affichait les deux, mais sélectionner l’un ou l’autre ouvrait toujours le même fichier unique.9
Pour la conception d’applications métier, la règle pratique à retenir est la suivante : traiter par défaut sous Windows deux noms qui ne diffèrent que par la casse comme identiques, mais vérifier les collisions de casse dès qu’un nom de fichier est destiné à traverser vers Linux. L’interopérabilité avec Linux comporte des pièges liés à l’encodage des caractères avant même d’en arriver aux noms de fichiers ; il vaut donc la peine de consulter également « Introduction à l’encodage des caractères sous Windows - Les problèmes d’affichage rencontrés lors de l’interopérabilité avec Linux ».
6. En pratique dans les applications métier — combinaison de chemins, assainissement et tableau de décision
6.1. Connaître le comportement de Path.Combine avant de l’utiliser
Concaténer des chemins avec + est évidemment à proscrire, mais même Path.Combine a un comportement dont il faut être conscient. Si un argument à partir du deuxième est un chemin enraciné, tous les arguments qui le précèdent sont entièrement rejetés.10
var baseDir = @"C:\App\Data";
// Si l'entrée utilisateur est enracinée, baseDir est silencieusement rejeté
Path.Combine(baseDir, @"C:\Windows\secret.txt"); // -> "C:\Windows\secret.txt"
Path.Combine(baseDir, @"\evil.txt"); // -> "\evil.txt" (racine du lecteur courant)
Transmettre directement en second argument une chaîne provenant d’une entrée utilisateur ou d’un fichier de configuration ouvre une vulnérabilité permettant d’écrire en dehors du dossier de destination prévu. La documentation officielle elle-même avertit que ce comportement peut conduire à un accès non intentionnel à des fichiers sensibles, et propose Path.Join / Path.TryJoin (indisponibles sur .NET Framework) comme alternatives.1014 Quel que soit le choix retenu, la pratique standard consiste finalement à vérifier que le résultat, une fois normalisé avec Path.GetFullPath, se trouve bien sous le répertoire de base.
// Normaliser également le répertoire de base, puis convertir en chemin relatif pour vérifier.
// Plus robuste qu'une comparaison de préfixe de chaîne face aux subtilités
// comme un séparateur final ou une base qui est la racine d'un lecteur
var baseFull = Path.GetFullPath(baseDir);
var fullPath = Path.GetFullPath(Path.Combine(baseFull, userInput));
var relative = Path.GetRelativePath(baseFull, fullPath);
if (relative == ".." ||
relative.StartsWith(".." + Path.DirectorySeparatorChar) ||
Path.IsPathRooted(relative)) // un chemin absolu est renvoyé en cas d'échappement vers un autre lecteur ou un chemin UNC
{
throw new InvalidOperationException("L'emplacement d'enregistrement pointe en dehors du dossier attendu.");
}
Path.GetRelativePath compare les chemins selon la convention par défaut de l’OS — c’est-à-dire, sous Windows, une comparaison insensible à la casse, ce qui correspond au comportement « Windows ignore la casse par défaut » décrit au chapitre précédent. Le revers de la médaille, c’est que dans un emplacement où la distinction de casse par répertoire a été activée (voir le chapitre précédent), Data et data peuvent être des répertoires distincts, ce qui laisse la place à une vérification insensible à la casse pour confondre à tort « un dossier différent qui ne diffère que par la casse » avec un dossier réellement situé sous la base. S’il existe la moindre chance de rencontrer une telle configuration, la politique la plus sûre consiste à refuser d’accepter comme répertoire de base un emplacement où la distinction de casse est activée.
Un dernier point à garder à l’esprit : cette vérification ne porte jamais que sur une chaîne de caractères normalisée en tant que chemin. Si une jonction ou un lien symbolique existe sous le répertoire de base, la chaîne peut sembler pointer à l’intérieur de la base alors que la cible réelle se trouve à l’extérieur. Et comme un lien peut se glisser non seulement au niveau du fichier final mais aussi au niveau d’un dossier intermédiaire (sous la forme base\lien\fichier.txt), vérifier uniquement le point final avec File.ResolveLinkTarget ne suffit pas à le détecter. La première ligne de défense consiste simplement à éviter les configurations où un utilisateur non fiable peut créer des liens ou des jonctions sous le répertoire de base. Si une application stricte de cette contrainte est réellement nécessaire au-delà de cela, il faut soit ouvrir le fichier, obtenir le chemin résolu à partir du descripteur (l’API Win32 GetFinalPathNameByHandle) et vérifier qu’il se trouve bien sous la base, soit examiner un par un chaque composant de dossier du chemin pour détecter s’il s’agit d’un lien.
6.2. Assainir les noms de fichiers fournis par l’utilisateur
Pour une fonctionnalité qui construit un nom de fichier à partir d’une saisie utilisateur — quelque chose comme « nom du client + date.csv » —, centralisez l’assainissement en un seul endroit. Path.GetInvalidFileNameChars constitue le point de départ, mais la documentation officielle indique explicitement que ce tableau n’est pas garanti d’être l’ensemble complet des caractères invalides.11 Les noms de périphériques réservés ainsi que les points et espaces de fin ne sont pas détectés par cette API ; il faut donc ajouter vos propres vérifications.
private static readonly HashSet<string> ReservedNames =
new(StringComparer.OrdinalIgnoreCase)
{
"CON", "PRN", "AUX", "NUL",
"COM1","COM2","COM3","COM4","COM5","COM6","COM7","COM8","COM9",
"LPT1","LPT2","LPT3","LPT4","LPT5","LPT6","LPT7","LPT8","LPT9",
"COM¹","COM²","COM³", // Les formes en chiffres en exposant COM¹-COM³ sont aussi des noms réservés
"LPT¹","LPT²","LPT³", // De même pour LPT¹-LPT³
};
public static string SanitizeFileName(string input)
{
var invalid = Path.GetInvalidFileNameChars();
var name = new string(input.Select(c => invalid.Contains(c) ? '_' : c).ToArray());
name = name.TrimEnd(' ', '.'); // Supprime les espaces/points de fin, puisqu'ils seraient de toute façon rejetés silencieusement
// Reste dans la limite de longueur d'un seul composant de nom de fichier (généralement 255 caractères).
// Tronque de manière conservatrice, en laissant de la place pour la hiérarchie de dossiers et un éventuel suffixe ajouté ensuite par l'application
const int MaxNameLength = 120;
if (name.Length > MaxNameLength)
{
var ext = Path.GetExtension(name);
if (ext.Length > 20)
{
ext = ""; // Ne conserve pas comme extension une « extension » anormalement longue (évite une exception due à une plage négative)
}
name = name[..(MaxNameLength - ext.Length)].TrimEnd(' ', '.') + ext;
}
// Vérifie toujours le nom vide/réservé sur le résultat « final »,
// pour détecter les cas où la troncature ou TrimEnd transforme le nom en chaîne vide ou en nom réservé (comme NUL)
var stem = name.Split('.')[0]; // Protection contre NUL.txt : vérifie la liste des noms réservés sur la partie avant l'extension
if (name.Length == 0 || ReservedNames.Contains(stem))
{
name = "_" + name; // Ajouter un caractère reste largement en dessous de la limite de 255
}
return name;
}
Ce problème se rencontre souvent autour des noms de fichiers d’export CSV ; pour les aspects pratiques du CSV lui-même, voir aussi « Le CSV n’est pas « juste du texte » — Manipulation pratique du CSV dans les applications métier C# (encodage des caractères, compatibilité Excel, contre-mesures contre les injections) ».
6.3. Les pièges des chemins relatifs et du répertoire courant
Les chemins relatifs comportent deux pièges. D’abord, le répertoire courant est un paramètre par processus, et il peut donc être modifié à tout moment depuis n’importe quel thread. La documentation officielle va jusqu’à dire que « les chemins relatifs sont dangereux dans les applications multithread », et sur .NET Core 2.1 et versions ultérieures, on peut utiliser Path.GetFullPath(string, string), qui permet de spécifier explicitement le chemin de base.7 Ensuite, une forme comme C:tmp.txt — sans barre oblique inverse immédiatement après la lettre de lecteur — désigne « un chemin relatif au répertoire courant du lecteur C: », et non un chemin absolu. Ce « chemin relatif au lecteur » est explicitement désigné par la documentation officielle comme une source classique de bugs dans les programmes et scripts.7
Prenez l’habitude de convertir en chemin absolu, via Path.GetFullPath, tout chemin reçu d’un fichier de configuration ou d’une saisie utilisateur, dès sa réception, avant de le consigner dans un journal ou de le valider.
6.4. Tableau de décision — faut-il prendre en charge les chemins longs, ou les rejeter d’emblée ?
| Situation | Recommandation | Raison |
|---|---|---|
| Application métier générale où l’utilisateur choisit librement l’emplacement d’enregistrement | Valider à l’entrée et rejeter (vérifier la longueur du chemin complet et le nom de fichier avant l’enregistrement, et afficher une erreur explicite) | Même si votre propre application le prend en charge, le risque subsiste que l’Explorateur ou un outil en aval ne puisse pas l’ouvrir1 |
| Sauvegarde, synchronisation, extraction d’archives — c’est-à-dire « lire » une hiérarchie profonde créée par quelqu’un d’autre | Prendre en charge les chemins longs (famille .NET Core + manifeste si nécessaire ; sur Framework, configuration 4.6.2+) | Vous ne contrôlez pas l’entrée, et l’activité s’arrête net si vous ne pouvez pas la lire54 |
| Votre propre application est celle qui « crée » une hiérarchie profonde | En règle générale, repenser la conception pour éviter cela (aplatir la hiérarchie, adopter des noms hachés, etc.) | Il y a de fortes chances que celui qui utilisera ensuite le chemin créé — une personne ou une autre application — ne le prenne pas en charge1 |
| Échange de fichiers avec Linux/WSL | Vérifier les noms réservés, les collisions de casse et les caractères de fin avant le transfert | Sinon, on se retrouve avec des fichiers inaccessibles côté Windows69 |
| Génération d’un nom de fichier à partir d’une saisie utilisateur | Centraliser l’assainissement dans une fonction commune, combinant GetInvalidFileNameChars avec des vérifications des noms réservés et des caractères de fin |
Le tableau fourni par l’API seul est incomplet11 |
7. Dépannage — « Le fichier est visible dans l’Explorateur, mais impossible de l’ouvrir »
Voici une démarche pas à pas pour la plainte classique : « le fichier apparaît bien dans l’Explorateur, mais l’ouvrir depuis l’application renvoie “fichier introuvable” ».
| Point à vérifier | Méthode | Si le cas correspond |
|---|---|---|
| Le chemin complet approche-t-il 260 caractères ? | Dans PowerShell : (Get-ChildItem -Recurse).FullName \| Where-Object { $_.Length -ge 250 } |
Raccourcir un nom de dossier parent, ou envisager la prise en charge des chemins longs (chapitre 3) |
| Le nom de fichier est-il un nom réservé (aux, con, com1, etc.) ? | Inspecter visuellement le nom ; cela inclut les noms avec une extension6 | Renommer (si la source est Linux ou autre, convertir lors du transfert) |
| Se termine-t-il par une espace ou un point ? | Vérifier avec cmd /c dir /x ou un affichage entre guillemets |
Supprimer ou renommer via un chemin préfixé par \\?\7 |
| Existe-t-il deux fichiers de même nom ne différant que par la casse ? | Fréquent dans les dossiers issus de WSL/Git9 | Renommer l’un des deux, ou repenser l’usage de ce répertoire |
| Utilisez-vous un chemin relatif ou un chemin relatif au lecteur ? | Consigner en journal le chemin absolu réellement tenté | Convertir en chemin absolu avec Path.GetFullPath avant utilisation7 |
La première chose qu’il vaut la peine de recommander pour une investigation est de consigner dans le journal d’erreurs de l’application le chemin exact que l’application a tenté d’ouvrir, sous forme absolue et entre guillemets. Un message d’exception qui se contente de dire « fichier introuvable » ne permet pas de distinguer après coup si le chemin a été tronqué, renommé lors de la normalisation, ou s’il pointait tout simplement vers un autre répertoire depuis le début. En l’entourant de guillemets lors de la consignation, un problème difficile à repérer, comme une espace de fin, devient évident au premier coup d’œil.
Une dernière remarque : l’échec de chargement d’une DLL fait également partie de la famille classique des erreurs de type « fichier introuvable », mais il s’agit plus souvent d’un problème d’ordre de recherche que de longueur de chemin — nous abordons ce sujet dans « Le fonctionnement de la résolution des noms de DLL sous Windows - Ordre de recherche et SxS ».
8. Résumé
- MAX_PATH=260 est une limite de l’API Win32 couvrant «
D:\+ jusqu’à 256 caractères + le NUL terminal », tandis que NTFS lui-même peut gérer des chemins de longueur étendue d’environ 32 767 caractères. Les répertoires sont soumis à une restriction supplémentaire de MAX_PATH moins 12. - Franchir la limite de 260 caractères exige soit le préfixe
\\?\(API Unicode uniquement, pas de chemins relatifs), soit l’activation des chemins longs sous Windows 10 1607 ou ultérieur (à la foisLongPathsEnableddans le registre etlongPathAwaredans le manifeste). - .NET (Core)/5+ gère implicitement les chemins longs, et cibler .NET Framework 4.6.2 ou ultérieur supprime la vérification du runtime. Des applications non compatibles, y compris l’Explorateur, subsistent malgré tout ; il faut donc traiter séparément les questions « peut-on le créer » et « l’utilisateur peut-il le manipuler ».
- Les noms de fichiers ne peuvent pas contenir les caractères réservés (
< > : " / \ | ? *) ni les caractères de contrôle ; les noms de périphériques réservés comme CON, NUL et COM1 sont interdits même avec une extension ; et les espaces/points de fin sont supprimés silencieusement lors de la normalisation. - La casse est « conservée mais non distinguée » par défaut. La distinction de casse par répertoire via
fsutil file setCaseSensitiveInfoest utile pour l’interopérabilité avec WSL, mais au prix d’un risque de dysfonctionnement des applications Windows. - Côté implémentation, les bonnes pratiques consistent à valider le répertoire de base en tenant compte du comportement de
Path.Combineface aux arguments enracinés, à centraliser l’assainissement autour deGetInvalidFileNameCharscomplété par des vérifications des noms réservés et des caractères de fin, et à éliminer les chemins relatifs en les convertissant en chemins absolus avecPath.GetFullPath.
Articles connexes
- Le CSV n’est pas « juste du texte » — Manipulation pratique du CSV dans les applications métier C# (encodage des caractères, compatibilité Excel, contre-mesures contre les injections)
- Introduction à l’encodage des caractères sous Windows - Les problèmes d’affichage rencontrés lors de l’interopérabilité avec Linux
- Encodage des caractères et fins de ligne sous Windows - Les bases des problèmes d’affichage et de CRLF/LF
- Le fonctionnement de la résolution des noms de DLL sous Windows - Ordre de recherche et SxS
Domaines de conseil associés
Komura Software LLC prend en charge l’investigation des causes profondes de bugs liés aux entrées-sorties de fichiers, tels que « seul un environnement ou un fichier spécifique ne s’ouvre pas », la révision de la prise en charge des chemins longs et de la conception de la validation des noms de fichiers dans des applications métier existantes, ainsi que le conseil en conception pour l’interopérabilité des fichiers dans des environnements mixtes Windows/Linux.
- Développement d’applications Windows
- Investigation de bugs et analyse des causes profondes
- Conseil technique et revue de conception
- Contact
Références
-
Microsoft Learn, Maximum Path Length Limitation. Sur la définition de MAX_PATH=260 et sa composition « lettre de lecteur + deux-points + barre oblique inverse + 256 caractères + NUL terminal », le chemin de longueur étendue d’environ 32 767 caractères disponible via les API Unicode et le préfixe
\\?\, la limite de longueur d’un composant (généralement 255 caractères), les chemins relatifs toujours limités à MAX_PATH, la création de répertoire plafonnée à MAX_PATH moins 12, et le fait que le shell et le système de fichiers ont des exigences différentes au point que l’interface du shell peut ne pas interpréter un chemin créable via Win32. ↩ ↩2 ↩3 ↩4 ↩5 ↩6 ↩7 ↩8 ↩9 ↩10 ↩11 -
Microsoft Learn, NTFS overview. Sur le fait que NTFS prend en charge les noms de fichiers longs et les chemins de longueur étendue d’environ 32 767 caractères, et sur la compatibilité ascendante via les alias 8.3. ↩ ↩2
-
Microsoft Learn, Maximum Path Length Limitation — Enable long paths in Windows 10, version 1607, and later. Sur la nécessité de définir à la fois la valeur de registre LongPathsEnabled=1 et l’élément longPathAware du manifeste d’application sous Windows 10 1607 et ultérieur, le paramétrage via stratégie de groupe, la mise en cache par processus de la valeur de registre, et la liste des fonctions Win32 concernées par la levée de la limite. ↩ ↩2 ↩3
-
Microsoft Learn, File path formats on Windows systems — Skip normalization. Sur le fait que .NET Core et .NET 5+ gèrent implicitement les chemins longs sans vérification MAX_PATH (la vérification MAX_PATH ne s’applique qu’à .NET Framework), et sur le fait que
\\?\est le mécanisme qui permet d’ignorer la normalisation. ↩ ↩2 ↩3 -
Microsoft Learn, Retargeting changes for migration to .NET Framework 4.6.x. Sur le fait que cibler .NET Framework 4.6.2 prend en charge les chemins longs (jusqu’à 32 000 caractères) et supprime la limite de 260 caractères, et sur le fait que les applications existantes ciblant une version antérieure peuvent y opter via Switch.System.IO.BlockLongPaths=false. ↩ ↩2 ↩3
-
Microsoft Learn, Naming Files, Paths, and Namespaces. Sur les caractères réservés (
< > : " / \ | ? *) et les caractères de contrôle (0-31), les noms de périphériques réservés (CON/PRN/AUX/NUL/COM1-9/LPT1-9 et leurs formes en chiffres en exposant), le fait qu’un nom comme NUL.txt avec extension équivaut au nom réservé, l’interdiction de terminer un nom par une espace ou un point, la légalité d’un point en début de nom, le fait de ne pas présumer la distinction de casse ainsi que la sémantique POSIX de NTFS, et le comportement et l’exigence d’API Unicode du préfixe\\?\. ↩ ↩2 ↩3 ↩4 ↩5 ↩6 ↩7 ↩8 ↩9 ↩10 ↩11 ↩12 -
Microsoft Learn, File path formats on Windows systems — Path normalization. Sur le fait que la normalisation des chemins supprime les points et espaces de fin, qu’un nom comme
hidden.n’est accessible que via\\?\, l’interprétation des noms de périphériques hérités comme CON et son changement sous Windows 11, le fait que les chemins relatifs au lecteur (C:tmp.txt) sont une source courante de bugs, le fait que le répertoire courant est propre au processus et que les chemins relatifs sont dangereux en environnement multithread, et Path.GetFullPath(String, String). ↩ ↩2 ↩3 ↩4 ↩5 ↩6 ↩7 ↩8 -
Microsoft Learn, File path formats on Windows systems — Case and the Windows file system. Sur le fait que les noms de répertoires et de fichiers conservent la casse utilisée à la création, tandis que les comparaisons de noms restent insensibles à la casse. ↩ ↩2
-
Microsoft Learn, Adjust case sensitivity. Sur la distinction de casse par répertoire (fsutil.exe file setCaseSensitiveInfo) disponible depuis la build 17107 de Windows 10, l’exigence de privilèges administrateur et d’un répertoire vide pour la modifier, l’héritage du paramètre par les nouveaux sous-répertoires, l’avertissement selon lequel les applications Windows présumant l’insensibilité à la casse peuvent mal se comporter, et le cas historique où deux fichiers ne différant que par la casse apparaissaient tous deux dans l’Explorateur mais où un seul pouvait réellement être ouvert. ↩ ↩2 ↩3 ↩4 ↩5 ↩6
-
Microsoft Learn, Path.Combine Method. Sur le fait qu’un chemin enraciné dans n’importe quel argument après le premier entraîne le rejet de tous les éléments de chemin précédents et le retour d’une chaîne commençant à l’élément enraciné, que cela peut conduire à un accès non intentionnel à des fichiers sensibles, et que Join/TryJoin (indisponibles sur .NET Framework) sont proposés comme alternative. ↩ ↩2 ↩3
-
Microsoft Learn, Path.GetInvalidFileNameChars Method. Sur le fait que cette méthode renvoie un tableau des caractères non autorisés dans un nom de fichier, et que le tableau renvoyé n’est pas garanti d’être l’ensemble complet des caractères invalides, celui-ci pouvant varier selon le système de fichiers. ↩ ↩2 ↩3
-
Microsoft Learn, PathTooLongException Class. Sur le fait qu’il s’agit de l’exception levée lorsqu’un chemin dépasse la longueur maximale définie par le système, et sur le fait que .NET Framework 4.6.2 et ultérieur ne la lève que lorsque le chemin dépasse 32 767 caractères ou que l’OS lui-même renvoie une erreur. ↩ ↩2
-
Microsoft Learn, Long Path Support (NuGet CLI). Sur la configuration réelle requise pour que des outils basés sur .NET Framework utilisent les chemins longs (Windows 10 1607+ ou 1511 + .NET Framework 4.6.2, la stratégie des chemins longs Win32, le manifeste longPathAware combiné à la désactivation de UseLegacyPathHandling), et sur le fait que la restauration (restore) dans Visual Studio et msbuild ne prend pas en charge les chemins longs. ↩ ↩2
-
Microsoft Learn, Path.Join Method. Sur le fait que Join concatène un chemin ultérieur enraciné plutôt que de le rejeter, avec des exemples concrets de la différence de comportement avec Combine. ↩
Articles associés
Articles récents partageant les mêmes étiquettes, pour approfondir des sujets proches.
Veille, mise en veille prolongée, Modern Standby et applications longue durée — concevoir pour éviter « ça s'était arrêté pendant la nuit »
Pourquoi une application Windows censée tourner en continu se retrouve « arrêtée quand on la consulte le matin » : différences entre la v...
Les pièges des lecteurs réseau et des chemins UNC — Travailler avec un serveur de fichiers (dossier partagé) depuis une application métier
Cet article recense les problèmes classiques rencontrés lorsqu'une application métier écrit dans un dossier partagé ou le surveille : pou...
Appeler les API Win32 en toute sécurité depuis C# — Guide pratique de P/Invoke (DllImport / LibraryImport / CsWin32)
Un tour d'horizon pratique des points essentiels pour appeler des API Win32 depuis C# via P/Invoke : différences entre DllImport et Libra...
Quand votre application Windows maison est signalée comme un virus — gérer les faux positifs de Microsoft Defender et composer avec l'impact sur les performances
Nous détaillons la marche à suivre officielle lorsque Microsoft Defender signale à tort une application Windows développée en interne com...
Les applications métier fonctionnent-elles sous Windows on Arm ? — La réalité de l'émulation x64 (Prism) et des DLL/COM natifs
Une réponse, destinée aux développeurs et aux services informatiques, à la question « notre application métier fonctionnera-t-elle sous W...
Sujets associés
Ces pages replacent le sujet dans un contexte plus large de services et de décisions.
Thèmes techniques Windows
Portail des sujets sur le développement Windows, l'analyse des incidents et la valorisation des actifs existants.
Services liés à ce sujet
Cet article est directement lié aux services suivants.
Développement d'applications Windows
Applications métier, intégration d'équipements et outils de communication, des besoins au développement.
Questions fréquentes
Questions souvent posées lors d’une consultation sur le sujet de cet article.
- Combien de caractères un chemin Windows peut-il comporter ?
- Par défaut, la limite de l'API Win32 est MAX_PATH=260 caractères, une longueur qui inclut la lettre de lecteur, les deux-points, la barre oblique inverse, jusqu'à 256 caractères de texte de chemin, et le NUL terminal. Le système de fichiers lui-même (NTFS et autres) peut gérer des chemins bien plus longs : en préfixant un chemin passé à une API Unicode par \\?\, on peut spécifier environ 32 767 caractères au total. Cela dit, un seul dossier ou nom de fichier (un composant du chemin) est généralement plafonné à 255 caractères, et les chemins relatifs sont toujours limités à MAX_PATH.
- Comment lever la limite de 260 caractères de MAX_PATH ?
- Sous Windows 10 version 1607 et ultérieure, il faut définir à la fois la valeur de registre LongPathsEnabled=1 (ou la stratégie de groupe « Activer les chemins d'accès Win32 longs ») et l'élément longPathAware du manifeste de l'application pour supprimer la limite de 260 caractères sur de nombreuses fonctions de fichiers Win32. Définir un seul des deux n'a aucun effet. Le runtime .NET (Core)/.NET 5 et versions ultérieures n'effectue aucune vérification MAX_PATH et gère implicitement les chemins longs, et cibler .NET Framework 4.6.2 ou ultérieur supprime la vérification de 260 caractères propre au runtime. Cependant, des applications qui ne prennent pas en charge les chemins longs — y compris l'Explorateur Windows lui-même — subsistent bel et bien, il faut donc réfléchir à qui devra manipuler le chemin long que l'on crée.
- Pourquoi ne puis-je pas créer un fichier nommé CON ou NUL ?
- CON, PRN, AUX, NUL, COM1 à COM9 et LPT1 à LPT9 sont des noms de périphériques réservés hérités de l'époque MS-DOS, et Windows interprète ces noms comme des périphériques plutôt que comme des fichiers. Ajouter une extension, comme dans NUL.txt, ne change rien : le nom est toujours traité comme NUL. Windows 11 a modifié une partie de l'interprétation de ces chemins, mais comme les anciennes versions de l'OS et un grand nombre d'applications existantes conservent l'interprétation traditionnelle, il reste plus sûr d'éviter ces noms pour des fichiers de données métier.
- Windows fait-il la distinction entre majuscules et minuscules dans les noms de fichiers ?
- Par défaut, Windows conserve la casse mais n'en tient pas compte pour la comparaison (case-preserving, case-insensitive). Créez un fichier nommé Readme.txt et sa casse sera préservée à l'affichage, mais essayer d'ouvrir README.TXT atteindra tout de même le même fichier. NTFS prend également en charge la distinction de casse à la POSIX, et depuis la build 17107 de Windows 10, il est possible d'activer la distinction de casse par répertoire avec fsutil.exe file setCaseSensitiveInfo — mais cela a pour effet secondaire de pouvoir casser des applications Windows qui présument l'insensibilité à la casse ; il faut donc réserver cela aux situations où c'est réellement nécessaire, comme l'interopérabilité avec WSL.
Profil de l’auteur
Page de présentation de l’auteur de l’article.
Go Komura
Représentant de KomuraSoft LLC
Spécialisé dans le développement de logiciels Windows, le conseil technique et l’analyse de pannes, notamment pour les systèmes existants et les incidents difficiles à reproduire.
Liens publics