├── .gitattributes ├── .gitignore ├── .vs └── config │ └── applicationhost.config ├── LICENSE ├── LongPath.sln ├── LongPath.vs2013.sln ├── Pri.LongPath.net20 ├── ExtensionMethods.cs ├── Pri.LongPath.net20.csproj └── Properties │ └── AssemblyInfo.cs ├── Pri.LongPath.net40 ├── Pri.LongPath.net40.csproj └── Properties │ └── AssemblyInfo.cs ├── Pri.LongPath ├── Common.cs ├── Directory.cs ├── DirectoryInfo.cs ├── File.cs ├── FileInfo.cs ├── FileSystemInfo.cs ├── JunctionPoint.cs ├── NativeMethods.cs ├── Path.cs ├── Pri.LongPath.csproj ├── Priviledge.cs ├── Properties │ └── AssemblyInfo.cs ├── SafeFindHandle.cs └── SafeTokenHandle.cs ├── README.md ├── Tests ├── DirectoryInfoTests.cs ├── DirectoryTests.cs ├── FileInfoTests.cs ├── FileSystemInfoTests.cs ├── FileTests.cs ├── JunctionPointTests.cs ├── NativeMethods.cs ├── PathTests.cs ├── Properties │ └── AssemblyInfo.cs ├── Tests.csproj ├── UncDirectoryInfoTests.cs ├── UncDirectoryTests.cs ├── UncFileInfoTests.cs ├── UncFileSystemInfoTests.cs ├── UncFileTests.cs ├── UncHelper.cs ├── UncPathTests.cs ├── UnitTest1.cs ├── Util.cs ├── packages.config └── tests.runsettings ├── appveyor.yml ├── cleartestresults.bat ├── nuget └── LongPath.nuspec ├── packages ├── NUnit.3.12.0 │ ├── .signature.p7s │ ├── CHANGES.md │ ├── LICENSE.txt │ ├── NOTICES.txt │ ├── NUnit.3.12.0.nupkg │ ├── build │ │ └── NUnit.props │ └── lib │ │ ├── net35 │ │ ├── nunit.framework.dll │ │ └── nunit.framework.xml │ │ ├── net40 │ │ ├── nunit.framework.dll │ │ └── nunit.framework.xml │ │ ├── net45 │ │ ├── nunit.framework.dll │ │ └── nunit.framework.xml │ │ ├── netstandard1.4 │ │ ├── nunit.framework.dll │ │ └── nunit.framework.xml │ │ └── netstandard2.0 │ │ ├── nunit.framework.dll │ │ └── nunit.framework.xml └── NUnit3TestAdapter.3.15.1 │ ├── .signature.p7s │ ├── LICENSE.txt │ ├── NUnit3TestAdapter.3.15.1.nupkg │ └── build │ ├── net35 │ ├── NUnit3.TestAdapter.dll │ ├── NUnit3.TestAdapter.pdb │ ├── NUnit3TestAdapter.props │ ├── nunit.engine.api.dll │ └── nunit.engine.dll │ ├── netcoreapp1.0 │ ├── NUnit3.TestAdapter.dll │ ├── NUnit3.TestAdapter.pdb │ ├── NUnit3TestAdapter.props │ ├── nunit.engine.api.dll │ └── nunit.engine.dll │ └── netcoreapp2.0 │ ├── NUnit3.TestAdapter.dll │ ├── NUnit3.TestAdapter.pdb │ ├── NUnit3TestAdapter.props │ ├── nunit.engine.api.dll │ └── nunit.engine.dll ├── ref └── mscorlib.xml ├── runtests.bat ├── test.playlist └── util └── NuGet.exe /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | 4 | # Custom for Visual Studio 5 | *.cs diff=csharp 6 | *.sln merge=union 7 | *.csproj merge=union 8 | *.vbproj merge=union 9 | *.fsproj merge=union 10 | *.dbproj merge=union 11 | 12 | # Standard to msysgit 13 | *.doc diff=astextplain 14 | *.DOC diff=astextplain 15 | *.docx diff=astextplain 16 | *.DOCX diff=astextplain 17 | *.dot diff=astextplain 18 | *.DOT diff=astextplain 19 | *.pdf diff=astextplain 20 | *.PDF diff=astextplain 21 | *.rtf diff=astextplain 22 | *.RTF diff=astextplain 23 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # User-specific files 2 | *.suo 3 | *.user 4 | *.sln.docstates 5 | 6 | # Build results 7 | [Dd]ebug*/ 8 | [Rr]elease*/ 9 | [Bb]uild/ 10 | *_i.c 11 | *_p.c 12 | *.ilk 13 | *.log 14 | *.lib 15 | *.meta 16 | *.[Oo]bj 17 | *.pch 18 | *.pdb 19 | *.pgc 20 | *.pgd 21 | *.rsp 22 | *.sbr 23 | *.tlb 24 | *.tli 25 | *.tlh 26 | *.tmp 27 | *.tmp_Release_x86.vcproj 28 | *.tmp_Debug_x86.vcproj 29 | *.vspscc 30 | *.vssscc 31 | .builds 32 | Ankh.NoLoad 33 | .vs 34 | 35 | # Visual Studio profiler 36 | *.psess 37 | *.vsp 38 | 39 | # ReSharper is a .NET coding add-in 40 | _ReSharper* 41 | *.resharper 42 | [Tt]est[Rr]esult* 43 | 44 | # Click-Once directory 45 | publish 46 | 47 | # Office Temp Files 48 | ~$* 49 | 50 | # Others 51 | [Oo]bj 52 | [Tt]humbs.db 53 | sql 54 | TestResults 55 | *.Cache 56 | ClientBin 57 | stylecop.* 58 | *.dbmdl 59 | 60 | *.DS_Store 61 | 62 | # Backup & report files from converting an old project file to a newer 63 | # Visual Studio version. Backup files are not needed, because we have git ;-) 64 | _UpgradeReport_Files/ 65 | Backup*/ 66 | UpgradeLog*.XML 67 | UpgradeLog*.htm 68 | /nuget/lib 69 | /nuget/*.nupkg 70 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU LESSER GENERAL PUBLIC LICENSE 2 | Version 3, 29 June 2007 3 | 4 | Copyright (C) 2007 Free Software Foundation, Inc. 5 | Everyone is permitted to copy and distribute verbatim copies 6 | of this license document, but changing it is not allowed. 7 | 8 | 9 | This version of the GNU Lesser General Public License incorporates 10 | the terms and conditions of version 3 of the GNU General Public 11 | License, supplemented by the additional permissions listed below. 12 | 13 | 0. Additional Definitions. 14 | 15 | As used herein, "this License" refers to version 3 of the GNU Lesser 16 | General Public License, and the "GNU GPL" refers to version 3 of the GNU 17 | General Public License. 18 | 19 | "The Library" refers to a covered work governed by this License, 20 | other than an Application or a Combined Work as defined below. 21 | 22 | An "Application" is any work that makes use of an interface provided 23 | by the Library, but which is not otherwise based on the Library. 24 | Defining a subclass of a class defined by the Library is deemed a mode 25 | of using an interface provided by the Library. 26 | 27 | A "Combined Work" is a work produced by combining or linking an 28 | Application with the Library. The particular version of the Library 29 | with which the Combined Work was made is also called the "Linked 30 | Version". 31 | 32 | The "Minimal Corresponding Source" for a Combined Work means the 33 | Corresponding Source for the Combined Work, excluding any source code 34 | for portions of the Combined Work that, considered in isolation, are 35 | based on the Application, and not on the Linked Version. 36 | 37 | The "Corresponding Application Code" for a Combined Work means the 38 | object code and/or source code for the Application, including any data 39 | and utility programs needed for reproducing the Combined Work from the 40 | Application, but excluding the System Libraries of the Combined Work. 41 | 42 | 1. Exception to Section 3 of the GNU GPL. 43 | 44 | You may convey a covered work under sections 3 and 4 of this License 45 | without being bound by section 3 of the GNU GPL. 46 | 47 | 2. Conveying Modified Versions. 48 | 49 | If you modify a copy of the Library, and, in your modifications, a 50 | facility refers to a function or data to be supplied by an Application 51 | that uses the facility (other than as an argument passed when the 52 | facility is invoked), then you may convey a copy of the modified 53 | version: 54 | 55 | a) under this License, provided that you make a good faith effort to 56 | ensure that, in the event an Application does not supply the 57 | function or data, the facility still operates, and performs 58 | whatever part of its purpose remains meaningful, or 59 | 60 | b) under the GNU GPL, with none of the additional permissions of 61 | this License applicable to that copy. 62 | 63 | 3. Object Code Incorporating Material from Library Header Files. 64 | 65 | The object code form of an Application may incorporate material from 66 | a header file that is part of the Library. You may convey such object 67 | code under terms of your choice, provided that, if the incorporated 68 | material is not limited to numerical parameters, data structure 69 | layouts and accessors, or small macros, inline functions and templates 70 | (ten or fewer lines in length), you do both of the following: 71 | 72 | a) Give prominent notice with each copy of the object code that the 73 | Library is used in it and that the Library and its use are 74 | covered by this License. 75 | 76 | b) Accompany the object code with a copy of the GNU GPL and this license 77 | document. 78 | 79 | 4. Combined Works. 80 | 81 | You may convey a Combined Work under terms of your choice that, 82 | taken together, effectively do not restrict modification of the 83 | portions of the Library contained in the Combined Work and reverse 84 | engineering for debugging such modifications, if you also do each of 85 | the following: 86 | 87 | a) Give prominent notice with each copy of the Combined Work that 88 | the Library is used in it and that the Library and its use are 89 | covered by this License. 90 | 91 | b) Accompany the Combined Work with a copy of the GNU GPL and this license 92 | document. 93 | 94 | c) For a Combined Work that displays copyright notices during 95 | execution, include the copyright notice for the Library among 96 | these notices, as well as a reference directing the user to the 97 | copies of the GNU GPL and this license document. 98 | 99 | d) Do one of the following: 100 | 101 | 0) Convey the Minimal Corresponding Source under the terms of this 102 | License, and the Corresponding Application Code in a form 103 | suitable for, and under terms that permit, the user to 104 | recombine or relink the Application with a modified version of 105 | the Linked Version to produce a modified Combined Work, in the 106 | manner specified by section 6 of the GNU GPL for conveying 107 | Corresponding Source. 108 | 109 | 1) Use a suitable shared library mechanism for linking with the 110 | Library. A suitable mechanism is one that (a) uses at run time 111 | a copy of the Library already present on the user's computer 112 | system, and (b) will operate properly with a modified version 113 | of the Library that is interface-compatible with the Linked 114 | Version. 115 | 116 | e) Provide Installation Information, but only if you would otherwise 117 | be required to provide such information under section 6 of the 118 | GNU GPL, and only to the extent that such information is 119 | necessary to install and execute a modified version of the 120 | Combined Work produced by recombining or relinking the 121 | Application with a modified version of the Linked Version. (If 122 | you use option 4d0, the Installation Information must accompany 123 | the Minimal Corresponding Source and Corresponding Application 124 | Code. If you use option 4d1, you must provide the Installation 125 | Information in the manner specified by section 6 of the GNU GPL 126 | for conveying Corresponding Source.) 127 | 128 | 5. Combined Libraries. 129 | 130 | You may place library facilities that are a work based on the 131 | Library side by side in a single library together with other library 132 | facilities that are not Applications and are not covered by this 133 | License, and convey such a combined library under terms of your 134 | choice, if you do both of the following: 135 | 136 | a) Accompany the combined library with a copy of the same work based 137 | on the Library, uncombined with any other library facilities, 138 | conveyed under the terms of this License. 139 | 140 | b) Give prominent notice with the combined library that part of it 141 | is a work based on the Library, and explaining where to find the 142 | accompanying uncombined form of the same work. 143 | 144 | 6. Revised Versions of the GNU Lesser General Public License. 145 | 146 | The Free Software Foundation may publish revised and/or new versions 147 | of the GNU Lesser General Public License from time to time. Such new 148 | versions will be similar in spirit to the present version, but may 149 | differ in detail to address new problems or concerns. 150 | 151 | Each version is given a distinguishing version number. If the 152 | Library as you received it specifies that a certain numbered version 153 | of the GNU Lesser General Public License "or any later version" 154 | applies to it, you have the option of following the terms and 155 | conditions either of that published version or of any later version 156 | published by the Free Software Foundation. If the Library as you 157 | received it does not specify a version number of the GNU Lesser 158 | General Public License, you may choose any version of the GNU Lesser 159 | General Public License ever published by the Free Software Foundation. 160 | 161 | If the Library as you received it specifies that a proxy can decide 162 | whether future versions of the GNU Lesser General Public License shall 163 | apply, that proxy's public statement of acceptance of any version is 164 | permanent authorization for you to choose that version for the 165 | Library. -------------------------------------------------------------------------------- /LongPath.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 17 4 | VisualStudioVersion = 17.1.31911.260 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Pri.LongPath", "Pri.LongPath\Pri.LongPath.csproj", "{DF1F23CA-9DC6-4604-8400-6C4B656E0F5D}" 7 | EndProject 8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tests", "Tests\Tests.csproj", "{4BE35CEA-F520-4EC7-9621-53FE061C8C35}" 9 | EndProject 10 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{DBB6E65B-AAA4-445E-B442-44D3B068CA56}" 11 | ProjectSection(SolutionItems) = preProject 12 | .gitattributes = .gitattributes 13 | .gitignore = .gitignore 14 | appveyor.yml = appveyor.yml 15 | cleartestresults.bat = cleartestresults.bat 16 | LICENSE = LICENSE 17 | nuget\LongPath.nuspec = nuget\LongPath.nuspec 18 | README.md = README.md 19 | runtests.bat = runtests.bat 20 | test.playlist = test.playlist 21 | EndProjectSection 22 | EndProject 23 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Pri.LongPath.net40", "Pri.LongPath.net40\Pri.LongPath.net40.csproj", "{0D02A959-5B75-4492-BA65-2005EB5A12CC}" 24 | EndProject 25 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Pri.LongPath.net20", "Pri.LongPath.net20\Pri.LongPath.net20.csproj", "{4FB40806-2B5C-4475-A653-71B37AAAEEF5}" 26 | EndProject 27 | Global 28 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 29 | Debug|Any CPU = Debug|Any CPU 30 | Release|Any CPU = Release|Any CPU 31 | EndGlobalSection 32 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 33 | {DF1F23CA-9DC6-4604-8400-6C4B656E0F5D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 34 | {DF1F23CA-9DC6-4604-8400-6C4B656E0F5D}.Debug|Any CPU.Build.0 = Debug|Any CPU 35 | {DF1F23CA-9DC6-4604-8400-6C4B656E0F5D}.Release|Any CPU.ActiveCfg = Release|Any CPU 36 | {DF1F23CA-9DC6-4604-8400-6C4B656E0F5D}.Release|Any CPU.Build.0 = Release|Any CPU 37 | {4BE35CEA-F520-4EC7-9621-53FE061C8C35}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 38 | {4BE35CEA-F520-4EC7-9621-53FE061C8C35}.Debug|Any CPU.Build.0 = Debug|Any CPU 39 | {4BE35CEA-F520-4EC7-9621-53FE061C8C35}.Release|Any CPU.ActiveCfg = Release|Any CPU 40 | {4BE35CEA-F520-4EC7-9621-53FE061C8C35}.Release|Any CPU.Build.0 = Release|Any CPU 41 | {0D02A959-5B75-4492-BA65-2005EB5A12CC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 42 | {0D02A959-5B75-4492-BA65-2005EB5A12CC}.Debug|Any CPU.Build.0 = Debug|Any CPU 43 | {0D02A959-5B75-4492-BA65-2005EB5A12CC}.Release|Any CPU.ActiveCfg = Release|Any CPU 44 | {0D02A959-5B75-4492-BA65-2005EB5A12CC}.Release|Any CPU.Build.0 = Release|Any CPU 45 | {4FB40806-2B5C-4475-A653-71B37AAAEEF5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 46 | {4FB40806-2B5C-4475-A653-71B37AAAEEF5}.Debug|Any CPU.Build.0 = Debug|Any CPU 47 | {4FB40806-2B5C-4475-A653-71B37AAAEEF5}.Release|Any CPU.ActiveCfg = Release|Any CPU 48 | {4FB40806-2B5C-4475-A653-71B37AAAEEF5}.Release|Any CPU.Build.0 = Release|Any CPU 49 | EndGlobalSection 50 | GlobalSection(SolutionProperties) = preSolution 51 | HideSolutionNode = FALSE 52 | EndGlobalSection 53 | GlobalSection(ExtensibilityGlobals) = postSolution 54 | SolutionGuid = {A7C9CDFA-2B45-4554-B879-2658048907AE} 55 | EndGlobalSection 56 | EndGlobal 57 | -------------------------------------------------------------------------------- /LongPath.vs2013.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 2013 4 | VisualStudioVersion = 12.0.40629.0 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Pri.LongPath", "Pri.LongPath\Pri.LongPath.csproj", "{DF1F23CA-9DC6-4604-8400-6C4B656E0F5D}" 7 | EndProject 8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tests", "Tests\Tests.csproj", "{4BE35CEA-F520-4EC7-9621-53FE061C8C35}" 9 | EndProject 10 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{DBB6E65B-AAA4-445E-B442-44D3B068CA56}" 11 | ProjectSection(SolutionItems) = preProject 12 | LICENSE = LICENSE 13 | nuget\LongPath.nuspec = nuget\LongPath.nuspec 14 | README.md = README.md 15 | EndProjectSection 16 | EndProject 17 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Pri.LongPath.net40", "Pri.LongPath.net40\Pri.LongPath.net40.csproj", "{0D02A959-5B75-4492-BA65-2005EB5A12CC}" 18 | EndProject 19 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Pri.LongPath.net20", "Pri.LongPath.net20\Pri.LongPath.net20.csproj", "{4FB40806-2B5C-4475-A653-71B37AAAEEF5}" 20 | EndProject 21 | Global 22 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 23 | Debug|Any CPU = Debug|Any CPU 24 | Release|Any CPU = Release|Any CPU 25 | EndGlobalSection 26 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 27 | {DF1F23CA-9DC6-4604-8400-6C4B656E0F5D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 28 | {DF1F23CA-9DC6-4604-8400-6C4B656E0F5D}.Debug|Any CPU.Build.0 = Debug|Any CPU 29 | {DF1F23CA-9DC6-4604-8400-6C4B656E0F5D}.Release|Any CPU.ActiveCfg = Release|Any CPU 30 | {DF1F23CA-9DC6-4604-8400-6C4B656E0F5D}.Release|Any CPU.Build.0 = Release|Any CPU 31 | {4BE35CEA-F520-4EC7-9621-53FE061C8C35}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 32 | {4BE35CEA-F520-4EC7-9621-53FE061C8C35}.Debug|Any CPU.Build.0 = Debug|Any CPU 33 | {4BE35CEA-F520-4EC7-9621-53FE061C8C35}.Release|Any CPU.ActiveCfg = Release|Any CPU 34 | {4BE35CEA-F520-4EC7-9621-53FE061C8C35}.Release|Any CPU.Build.0 = Release|Any CPU 35 | {0D02A959-5B75-4492-BA65-2005EB5A12CC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 36 | {0D02A959-5B75-4492-BA65-2005EB5A12CC}.Debug|Any CPU.Build.0 = Debug|Any CPU 37 | {0D02A959-5B75-4492-BA65-2005EB5A12CC}.Release|Any CPU.ActiveCfg = Release|Any CPU 38 | {0D02A959-5B75-4492-BA65-2005EB5A12CC}.Release|Any CPU.Build.0 = Release|Any CPU 39 | {4FB40806-2B5C-4475-A653-71B37AAAEEF5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 40 | {4FB40806-2B5C-4475-A653-71B37AAAEEF5}.Debug|Any CPU.Build.0 = Debug|Any CPU 41 | {4FB40806-2B5C-4475-A653-71B37AAAEEF5}.Release|Any CPU.ActiveCfg = Release|Any CPU 42 | {4FB40806-2B5C-4475-A653-71B37AAAEEF5}.Release|Any CPU.Build.0 = Release|Any CPU 43 | EndGlobalSection 44 | GlobalSection(SolutionProperties) = preSolution 45 | HideSolutionNode = FALSE 46 | EndGlobalSection 47 | EndGlobal 48 | -------------------------------------------------------------------------------- /Pri.LongPath.net20/ExtensionMethods.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | namespace Pri.LongPath 5 | { 6 | public static class ExtensionMethods 7 | { 8 | public static T[] ToArray(this IEnumerable source) 9 | { 10 | if (source == null) throw new ArgumentNullException("source"); 11 | ICollection collection = source as ICollection; 12 | T[] array = null; 13 | if (collection != null) 14 | { 15 | array = new T[collection.Count]; 16 | collection.CopyTo(array, 0); 17 | return array; 18 | } 19 | int length = 0; 20 | foreach (T element in source) 21 | { 22 | if (array == null) 23 | array = new T[4]; 24 | else if (array.Length == length) 25 | { 26 | T[] elementArray = new T[checked(length * 2)]; 27 | Array.Copy(array, 0, elementArray, 0, length); 28 | array = elementArray; 29 | } 30 | array[length] = element; 31 | ++length; 32 | } 33 | return array ?? EmptyArrayCache.Empty; 34 | } 35 | 36 | private static class EmptyArrayCache 37 | { 38 | // Used to avoid creating empty array instances unnecessarily 39 | public static readonly T[] Empty = new T[0]; 40 | } 41 | 42 | public static bool Contains(this IEnumerable source, T value) 43 | { 44 | if (source == null) throw new ArgumentNullException("source"); 45 | ICollection collection = source as ICollection; 46 | if(collection != null) return collection.Contains(value); 47 | var comparer = EqualityComparer.Default; 48 | foreach (T e in source) 49 | { 50 | if (comparer.Equals(e, value)) 51 | return true; 52 | } 53 | return false; 54 | } 55 | 56 | public static IEnumerable Select(this IEnumerable source, Func selector) 57 | { 58 | if (source == null) throw new ArgumentNullException("source"); 59 | if (selector == null) throw new ArgumentNullException("selector"); 60 | 61 | foreach (var e in source) 62 | yield return selector(e); 63 | } 64 | } 65 | 66 | public delegate TResult Func(T arg); 67 | } 68 | -------------------------------------------------------------------------------- /Pri.LongPath.net20/Pri.LongPath.net20.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {4FB40806-2B5C-4475-A653-71B37AAAEEF5} 8 | Library 9 | Properties 10 | Pri.LongPath 11 | Pri.LongPath 12 | v2.0 13 | 512 14 | 15 | 16 | true 17 | full 18 | false 19 | bin\Debug\ 20 | TRACE;DEBUG;NET_2_0 21 | prompt 22 | 4 23 | true 24 | bin\Debug\Pri.LongPath.xml 25 | 26 | 27 | pdbonly 28 | true 29 | bin\Release\ 30 | TRACE;NET_2_0 31 | prompt 32 | 4 33 | true 34 | bin\Release\Pri.LongPath.xml 35 | 1591;1734;1572;1573 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | Common.cs 45 | 46 | 47 | Directory.cs 48 | 49 | 50 | DirectoryInfo.cs 51 | 52 | 53 | File.cs 54 | 55 | 56 | FileInfo.cs 57 | 58 | 59 | FileSystemInfo.cs 60 | 61 | 62 | JunctionPoint.cs 63 | 64 | 65 | NativeMethods.cs 66 | 67 | 68 | Path.cs 69 | 70 | 71 | Priviledge.cs 72 | 73 | 74 | SafeFindHandle.cs 75 | 76 | 77 | SafeTokenHandle.cs 78 | 79 | 80 | 81 | 82 | 83 | 90 | -------------------------------------------------------------------------------- /Pri.LongPath.net20/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("Pri.LongPath for .NET 2.0")] 9 | [assembly: AssemblyDescription("Pri.LongPath for .NET 2.0")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("Peter Ritchie Inc.")] 12 | [assembly: AssemblyProduct("Pri.LongPath")] 13 | [assembly: AssemblyCopyright("Copyright © Peter Ritchie 2014")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("2d594de2-de3e-46d6-b900-2c5b8542a372")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Build and Revision Numbers 33 | // by using the '*' as shown below: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("2.0.1.0")] 36 | [assembly: AssemblyFileVersion("2.0.1.0")] 37 | [assembly: AssemblyInformationalVersion("2.0.1")] -------------------------------------------------------------------------------- /Pri.LongPath.net40/Pri.LongPath.net40.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {0D02A959-5B75-4492-BA65-2005EB5A12CC} 8 | Library 9 | Properties 10 | Pri.LongPath 11 | Pri.LongPath 12 | v4.0 13 | 512 14 | Client 15 | 16 | 17 | true 18 | full 19 | false 20 | bin\Debug\ 21 | TRACE;DEBUG;NET_4_0 22 | prompt 23 | 4 24 | true 25 | bin\Debug\Pri.LongPath.xml 26 | 27 | 28 | pdbonly 29 | true 30 | bin\Release\ 31 | TRACE;NET_4_0 32 | prompt 33 | 4 34 | true 35 | bin\Release\Pri.LongPath.xml 36 | 1591;1734;1572;1573 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | Common.cs 50 | 51 | 52 | Directory.cs 53 | 54 | 55 | DirectoryInfo.cs 56 | 57 | 58 | File.cs 59 | 60 | 61 | FileInfo.cs 62 | 63 | 64 | FileSystemInfo.cs 65 | 66 | 67 | JunctionPoint.cs 68 | 69 | 70 | NativeMethods.cs 71 | 72 | 73 | Path.cs 74 | 75 | 76 | Priviledge.cs 77 | 78 | 79 | SafeFindHandle.cs 80 | 81 | 82 | SafeTokenHandle.cs 83 | 84 | 85 | 86 | 87 | 94 | -------------------------------------------------------------------------------- /Pri.LongPath.net40/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("Pri.LongPath for .NET 4.0")] 9 | [assembly: AssemblyDescription("Pri.LongPath for .NET 4.0")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("Peter Ritchie Inc.")] 12 | [assembly: AssemblyProduct("Pri.LongPath")] 13 | [assembly: AssemblyCopyright("Copyright © Peter Ritchie 2014")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("a5130d86-421c-41d8-95e1-a20e0d0a3c74")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Build and Revision Numbers 33 | // by using the '*' as shown below: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("2.0.1.0")] 36 | [assembly: AssemblyFileVersion("2.0.1.0")] 37 | [assembly: AssemblyInformationalVersion("2.0.1")] -------------------------------------------------------------------------------- /Pri.LongPath/FileInfo.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Text; 3 | using FileAccess = System.IO.FileAccess; 4 | using FileMode = System.IO.FileMode; 5 | using FileStream = System.IO.FileStream; 6 | using StreamWriter = System.IO.StreamWriter; 7 | using FileShare = System.IO.FileShare; 8 | using FileOptions = System.IO.FileOptions; 9 | using StreamReader = System.IO.StreamReader; 10 | using FileAttributes = System.IO.FileAttributes; 11 | using System.Security.AccessControl; 12 | 13 | namespace Pri.LongPath 14 | { 15 | /// 16 | public class FileInfo : FileSystemInfo 17 | { 18 | private readonly string name; 19 | 20 | /// 21 | public DirectoryInfo Directory 22 | { 23 | get 24 | { 25 | var dirName = DirectoryName; 26 | return dirName == null ? null : new DirectoryInfo(dirName); 27 | } 28 | } 29 | 30 | /// 31 | public System.IO.FileInfo SysFileInfo 32 | { 33 | get 34 | { 35 | return new System.IO.FileInfo(FullPath); 36 | } 37 | } 38 | 39 | /// 40 | public override System.IO.FileSystemInfo SystemInfo { get { return SysFileInfo; } } 41 | 42 | /// 43 | public string DirectoryName 44 | { 45 | get 46 | { 47 | return Path.GetDirectoryName(FullPath); 48 | } 49 | } 50 | 51 | /// 52 | public override bool Exists 53 | { 54 | get 55 | { 56 | if (Common.IsRunningOnMono() && Common.IsPlatformUnix()) return SysFileInfo.Exists; 57 | 58 | if (state == State.Uninitialized) 59 | { 60 | Refresh(); 61 | } 62 | return state == State.Initialized && 63 | (data.fileAttributes & System.IO.FileAttributes.Directory) != System.IO.FileAttributes.Directory; 64 | } 65 | } 66 | 67 | /// 68 | public long Length 69 | { 70 | get { return GetFileLength(); } 71 | } 72 | 73 | /// 74 | public override string Name 75 | { 76 | get { return name; } 77 | } 78 | 79 | /// 80 | public FileInfo(string fileName) 81 | { 82 | OriginalPath = fileName; 83 | FullPath = Path.GetFullPath(fileName); 84 | name = Path.GetFileName(fileName); 85 | DisplayPath = GetDisplayPath(fileName); 86 | } 87 | 88 | private string GetDisplayPath(string originalPath) 89 | { 90 | return originalPath; 91 | } 92 | 93 | /// 94 | private long GetFileLength() 95 | { 96 | if (Common.IsRunningOnMono() && Common.IsPlatformUnix()) return SysFileInfo.Length; 97 | 98 | if (state == State.Uninitialized) 99 | { 100 | Refresh(); 101 | } 102 | if(state == State.Error) 103 | Common.ThrowIOError(errorCode, FullPath); 104 | return ((long)data.fileSizeHigh) << 32 | (data.fileSizeLow & 0xFFFFFFFFL); 105 | } 106 | 107 | /// 108 | public StreamWriter AppendText() 109 | { 110 | return File.CreateStreamWriter(FullPath, true); 111 | } 112 | 113 | /// 114 | public FileInfo CopyTo(string destFileName) 115 | { 116 | return CopyTo(destFileName, false); 117 | } 118 | 119 | /// 120 | public FileInfo CopyTo(string destFileName, bool overwrite) 121 | { 122 | File.Copy(FullPath, destFileName, overwrite); 123 | return new FileInfo(destFileName); 124 | } 125 | 126 | /// 127 | public FileStream Create() 128 | { 129 | return File.Create(FullPath); 130 | } 131 | 132 | /// 133 | public StreamWriter CreateText() 134 | { 135 | return File.CreateStreamWriter(FullPath, false); 136 | } 137 | 138 | /// 139 | public override void Delete() 140 | { 141 | File.Delete(FullPath); 142 | } 143 | 144 | /// 145 | public void MoveTo(string destFileName) 146 | { 147 | File.Move(FullPath, destFileName); 148 | } 149 | 150 | /// 151 | public FileStream Open(FileMode mode) 152 | { 153 | return Open(mode, FileAccess.ReadWrite, FileShare.None); 154 | } 155 | 156 | /// 157 | public FileStream Open(FileMode mode, FileAccess access) 158 | { 159 | return Open(mode, access, FileShare.None); 160 | } 161 | 162 | /// 163 | public FileStream Open(FileMode mode, FileAccess access, FileShare share) 164 | { 165 | if (Common.IsRunningOnMono() && Common.IsPlatformUnix()) return SysFileInfo.Open(mode, access, share); 166 | 167 | return File.Open(FullPath, mode, access, share, 4096, FileOptions.SequentialScan); 168 | } 169 | 170 | public FileStream OpenRead() 171 | { 172 | if (Common.IsRunningOnMono() && Common.IsPlatformUnix()) return SysFileInfo.OpenRead(); 173 | return File.Open(FullPath, FileMode.Open, FileAccess.Read, FileShare.Read, 4096, FileOptions.None); 174 | } 175 | 176 | /// 177 | public StreamReader OpenText() 178 | { 179 | return File.CreateStreamReader(FullPath, Encoding.UTF8, true, 1024); 180 | } 181 | 182 | /// 183 | public FileStream OpenWrite() 184 | { 185 | return File.Open(FullPath, FileMode.OpenOrCreate, FileAccess.Write, FileShare.None); 186 | } 187 | 188 | /// 189 | public override string ToString() 190 | { 191 | return DisplayPath; 192 | } 193 | 194 | /// 195 | public void Encrypt() 196 | { 197 | File.Encrypt(FullPath); 198 | } 199 | 200 | /// 201 | public void Decrypt() 202 | { 203 | File.Decrypt(FullPath); 204 | } 205 | 206 | /// 207 | public bool IsReadOnly 208 | { 209 | get 210 | { 211 | if (Common.IsRunningOnMono() && Common.IsPlatformUnix()) return SysFileInfo.IsReadOnly; 212 | 213 | return (Attributes & FileAttributes.ReadOnly) != 0; 214 | } 215 | set 216 | { 217 | if (Common.IsRunningOnMono() && Common.IsPlatformUnix()) 218 | { 219 | SysFileInfo.IsReadOnly = value; 220 | return; 221 | } 222 | 223 | if (value) 224 | { 225 | Attributes |= FileAttributes.ReadOnly; 226 | return; 227 | } 228 | Attributes &= ~FileAttributes.ReadOnly; 229 | } 230 | } 231 | 232 | /// 233 | public FileInfo Replace(string destinationFilename, string backupFilename) 234 | { 235 | return Replace(destinationFilename, backupFilename, false); 236 | } 237 | 238 | /// 239 | public FileInfo Replace(string destinationFilename, string backupFilename, bool ignoreMetadataErrors) 240 | { 241 | File.Replace(FullPath, destinationFilename, backupFilename, ignoreMetadataErrors); 242 | return new FileInfo(destinationFilename); 243 | } 244 | 245 | /// 246 | public FileSecurity GetAccessControl() 247 | { 248 | return File.GetAccessControl(FullPath); 249 | } 250 | 251 | /// 252 | public FileSecurity GetAccessControl(AccessControlSections includeSections) 253 | { 254 | return File.GetAccessControl(FullPath, includeSections); 255 | } 256 | 257 | /// 258 | public void SetAccessControl(FileSecurity security) 259 | { 260 | File.SetAccessControl(FullPath, security); 261 | } 262 | } 263 | } -------------------------------------------------------------------------------- /Pri.LongPath/FileSystemInfo.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Runtime.InteropServices; 3 | using System.Runtime.Serialization; 4 | 5 | namespace Pri.LongPath 6 | { 7 | using System.Security.Permissions; 8 | using FileAttributes = System.IO.FileAttributes; 9 | using FILETIME = System.Runtime.InteropServices.ComTypes.FILETIME; 10 | using DirectoryNotFoundException = System.IO.DirectoryNotFoundException; 11 | 12 | /// 13 | public abstract class FileSystemInfo 14 | { 15 | protected string OriginalPath; 16 | protected string FullPath; 17 | protected FileInfo.State state; 18 | protected readonly FileAttributeData data = new FileAttributeData(); 19 | protected int errorCode; 20 | 21 | /// 22 | public abstract System.IO.FileSystemInfo SystemInfo { get; } 23 | 24 | // Summary: 25 | // Gets or sets the attributes for the current file or directory. 26 | // 27 | // Returns: 28 | // System.IO.FileAttributes of the current System.IO.FileSystemInfo. 29 | // 30 | // Exceptions: 31 | // System.IO.FileNotFoundException: 32 | // The specified file does not exist. 33 | // 34 | // System.IO.DirectoryNotFoundException: 35 | // The specified path is invalid; for example, it is on an unmapped drive. 36 | // 37 | // System.Security.SecurityException: 38 | // The caller does not have the required permission. 39 | // 40 | // System.ArgumentException: 41 | // The caller attempts to set an invalid file attribute. -or-The user attempts 42 | // to set an attribute value but does not have write permission. 43 | // 44 | // System.IO.IOException: 45 | // System.IO.FileSystemInfo.Refresh() cannot initialize the data. 46 | /// 47 | public FileAttributes Attributes 48 | { 49 | get 50 | { 51 | if (Common.IsRunningOnMono() && Common.IsPlatformUnix()) return SystemInfo.Attributes; 52 | 53 | return Common.GetAttributes(FullPath); 54 | } 55 | set 56 | { 57 | if (Common.IsRunningOnMono() && Common.IsPlatformUnix()) 58 | SystemInfo.Attributes = value; 59 | else 60 | Common.SetAttributes(FullPath, value); 61 | } 62 | } 63 | 64 | // 65 | // Summary: 66 | // Gets or sets the creation time of the current file or directory. 67 | // 68 | // Returns: 69 | // The creation date and time of the current System.IO.FileSystemInfo object. 70 | // 71 | // Exceptions: 72 | // System.IO.IOException: 73 | // System.IO.FileSystemInfo.Refresh() cannot initialize the data. 74 | // 75 | // System.IO.DirectoryNotFoundException: 76 | // The specified path is invalid; for example, it is on an unmapped drive. 77 | // 78 | // System.PlatformNotSupportedException: 79 | // The current operating system is not Windows NT or later. 80 | // 81 | // System.ArgumentOutOfRangeException: 82 | // The caller attempts to set an invalid creation time. 83 | /// 84 | public DateTime CreationTime 85 | { 86 | get 87 | { 88 | if(Common.IsRunningOnMono() && Common.IsPlatformUnix()) return SystemInfo.CreationTime; 89 | return CreationTimeUtc.ToLocalTime(); 90 | } 91 | 92 | set 93 | { 94 | if (Common.IsRunningOnMono() && Common.IsPlatformUnix()) 95 | SystemInfo.CreationTime = value; 96 | else 97 | CreationTimeUtc = value.ToUniversalTime(); 98 | } 99 | } 100 | 101 | // 102 | // Summary: 103 | // Gets or sets the creation time, in coordinated universal time (UTC), of the 104 | // current file or directory. 105 | // 106 | // Returns: 107 | // The creation date and time in UTC format of the current System.IO.FileSystemInfo 108 | // object. 109 | // 110 | // Exceptions: 111 | // System.IO.IOException: 112 | // System.IO.FileSystemInfo.Refresh() cannot initialize the data. 113 | // 114 | // System.IO.DirectoryNotFoundException: 115 | // The specified path is invalid; for example, it is on an unmapped drive. 116 | // 117 | // System.PlatformNotSupportedException: 118 | // The current operating system is not Windows NT or later. 119 | // 120 | // System.ArgumentOutOfRangeException: 121 | // The caller attempts to set an invalid access time. 122 | /// 123 | public DateTime CreationTimeUtc 124 | { 125 | get 126 | { 127 | if (Common.IsRunningOnMono() && Common.IsPlatformUnix()) return SystemInfo.CreationTimeUtc; 128 | 129 | if (state == State.Uninitialized) 130 | { 131 | Refresh(); 132 | } 133 | if (state == State.Error) 134 | Common.ThrowIOError(errorCode, FullPath); 135 | 136 | long fileTime = ((long)data.ftCreationTime.dwHighDateTime << 32) | (data.ftCreationTime.dwLowDateTime & 0xffffffff); 137 | return DateTime.FromFileTimeUtc(fileTime); 138 | } 139 | set 140 | { 141 | if (Common.IsRunningOnMono() && Common.IsPlatformUnix()) 142 | { 143 | SystemInfo.CreationTimeUtc = value; 144 | return; 145 | } 146 | 147 | if (this is DirectoryInfo) 148 | Directory.SetCreationTimeUtc(FullPath, value); 149 | else 150 | File.SetCreationTimeUtc(FullPath, value); 151 | state = State.Uninitialized; 152 | } 153 | } 154 | 155 | /// 156 | public DateTime LastWriteTime 157 | { 158 | get 159 | { 160 | if (Common.IsRunningOnMono() && Common.IsPlatformUnix()) return SystemInfo.LastWriteTime; 161 | 162 | return LastWriteTimeUtc.ToLocalTime(); 163 | } 164 | set 165 | { 166 | if (Common.IsRunningOnMono() && Common.IsPlatformUnix())SystemInfo.LastWriteTime = value; 167 | else LastWriteTimeUtc = value.ToUniversalTime(); 168 | } 169 | } 170 | 171 | private static void ThrowLastWriteTimeUtcIOError(int errorCode, String maybeFullPath) 172 | { 173 | // This doesn't have to be perfect, but is a perf optimization. 174 | bool isInvalidPath = errorCode == NativeMethods.ERROR_INVALID_NAME || errorCode == NativeMethods.ERROR_BAD_PATHNAME; 175 | String str = isInvalidPath ? Path.GetFileName(maybeFullPath) : maybeFullPath; 176 | 177 | switch (errorCode) 178 | { 179 | case NativeMethods.ERROR_FILE_NOT_FOUND: 180 | break; 181 | 182 | case NativeMethods.ERROR_PATH_NOT_FOUND: 183 | break; 184 | 185 | case NativeMethods.ERROR_ACCESS_DENIED: 186 | if (str.Length == 0) 187 | throw new UnauthorizedAccessException("Empty path"); 188 | else 189 | throw new UnauthorizedAccessException(String.Format("Access denied accessing {0}", str)); 190 | 191 | case NativeMethods.ERROR_ALREADY_EXISTS: 192 | if (str.Length == 0) 193 | goto default; 194 | throw new System.IO.IOException(String.Format("File {0}", str), NativeMethods.MakeHRFromErrorCode(errorCode)); 195 | 196 | case NativeMethods.ERROR_FILENAME_EXCED_RANGE: 197 | throw new System.IO.PathTooLongException("Path too long"); 198 | 199 | case NativeMethods.ERROR_INVALID_DRIVE: 200 | throw new System.IO.DriveNotFoundException(String.Format("Drive {0} not found", str)); 201 | 202 | case NativeMethods.ERROR_INVALID_PARAMETER: 203 | throw new System.IO.IOException(NativeMethods.GetMessage(errorCode), NativeMethods.MakeHRFromErrorCode(errorCode)); 204 | 205 | case NativeMethods.ERROR_SHARING_VIOLATION: 206 | if (str.Length == 0) 207 | throw new System.IO.IOException("Sharing violation with empty filename", NativeMethods.MakeHRFromErrorCode(errorCode)); 208 | else 209 | throw new System.IO.IOException(String.Format("Sharing violation: {0}", str), NativeMethods.MakeHRFromErrorCode(errorCode)); 210 | 211 | case NativeMethods.ERROR_FILE_EXISTS: 212 | if (str.Length == 0) 213 | goto default; 214 | throw new System.IO.IOException(String.Format("File exists {0}", str), NativeMethods.MakeHRFromErrorCode(errorCode)); 215 | 216 | case NativeMethods.ERROR_OPERATION_ABORTED: 217 | throw new OperationCanceledException(); 218 | 219 | default: 220 | throw new System.IO.IOException(NativeMethods.GetMessage(errorCode), NativeMethods.MakeHRFromErrorCode(errorCode)); 221 | } 222 | } 223 | public DateTime LastWriteTimeUtc 224 | { 225 | get 226 | { 227 | if (Common.IsRunningOnMono() && Common.IsPlatformUnix()) return SystemInfo.LastWriteTimeUtc; 228 | 229 | 230 | if (state == State.Uninitialized) 231 | { 232 | Refresh(); 233 | } 234 | if (state == State.Error) 235 | ThrowLastWriteTimeUtcIOError(errorCode, FullPath); 236 | 237 | long fileTime = ((long)data.ftLastWriteTime.dwHighDateTime << 32) | (data.ftLastWriteTime.dwLowDateTime & 0xffffffff); 238 | return DateTime.FromFileTimeUtc(fileTime); 239 | } 240 | set 241 | { 242 | if (Common.IsRunningOnMono() && Common.IsPlatformUnix()) 243 | { 244 | SystemInfo.LastWriteTimeUtc = value; 245 | return; 246 | } 247 | 248 | 249 | if (this is DirectoryInfo) 250 | Directory.SetLastWriteTimeUtc(FullPath, value); 251 | else 252 | File.SetLastWriteTimeUtc(FullPath, value); 253 | state = State.Uninitialized; 254 | } 255 | } 256 | 257 | /// 258 | public DateTime LastAccessTime 259 | { 260 | get 261 | { 262 | if (Common.IsRunningOnMono() && Common.IsPlatformUnix()) return SystemInfo.LastAccessTime; 263 | 264 | return LastAccessTimeUtc.ToLocalTime(); 265 | } 266 | set 267 | { 268 | if (Common.IsRunningOnMono() && Common.IsPlatformUnix())SystemInfo.LastAccessTime = value; 269 | else LastAccessTimeUtc = value.ToUniversalTime(); 270 | } 271 | } 272 | 273 | /// 274 | public DateTime LastAccessTimeUtc 275 | { 276 | get 277 | { 278 | if (Common.IsRunningOnMono() && Common.IsPlatformUnix()) return SystemInfo.LastAccessTimeUtc; 279 | 280 | if (state == State.Uninitialized) 281 | { 282 | Refresh(); 283 | } 284 | if (state == State.Error) 285 | Common.ThrowIOError(errorCode, FullPath); 286 | 287 | long fileTime = ((long)data.ftLastAccessTime.dwHighDateTime << 32) | (data.ftLastAccessTime.dwLowDateTime & 0xffffffff); 288 | return DateTime.FromFileTimeUtc(fileTime); 289 | } 290 | set 291 | { 292 | if (Common.IsRunningOnMono() && Common.IsPlatformUnix()) 293 | { 294 | SystemInfo.LastAccessTimeUtc = value; 295 | return; 296 | } 297 | 298 | 299 | if (this is DirectoryInfo) 300 | Directory.SetLastAccessTimeUtc(FullPath, value); 301 | else 302 | File.SetLastAccessTimeUtc(FullPath, value); 303 | state = State.Uninitialized; 304 | } 305 | } 306 | 307 | /// 308 | public virtual string FullName 309 | { 310 | get { return FullPath; } 311 | } 312 | 313 | /// 314 | public string Extension 315 | { 316 | get 317 | { 318 | return Path.GetExtension(FullPath); 319 | } 320 | } 321 | 322 | /// 323 | public abstract string Name { get; } 324 | /// 325 | public abstract bool Exists { get; } 326 | internal string DisplayPath { get; set; } 327 | 328 | protected enum State 329 | { 330 | Uninitialized, Initialized, Error 331 | } 332 | 333 | protected class FileAttributeData 334 | { 335 | public System.IO.FileAttributes fileAttributes; 336 | public FILETIME ftCreationTime; 337 | public FILETIME ftLastAccessTime; 338 | public FILETIME ftLastWriteTime; 339 | public int fileSizeHigh; 340 | public int fileSizeLow; 341 | 342 | internal void From(NativeMethods.WIN32_FIND_DATA findData) 343 | { 344 | fileAttributes = findData.dwFileAttributes; 345 | ftCreationTime = findData.ftCreationTime; 346 | ftLastAccessTime = findData.ftLastAccessTime; 347 | ftLastWriteTime = findData.ftLastWriteTime; 348 | fileSizeHigh = findData.nFileSizeHigh; 349 | fileSizeLow = findData.nFileSizeLow; 350 | } 351 | } 352 | 353 | /// 354 | public virtual void GetObjectData(SerializationInfo info, StreamingContext context) 355 | { 356 | //(new FileIOPermission(FileIOPermissionAccess.PathDiscovery, this.FullPath)).Demand(); 357 | info.AddValue("OriginalPath", this.OriginalPath, typeof(string)); 358 | info.AddValue("FullPath", this.FullPath, typeof(string)); 359 | } 360 | 361 | internal virtual string GetNormalizedPathWithSearchPattern() 362 | { 363 | // https://docs.microsoft.com/en-us/windows/desktop/api/fileapi/nf-fileapi-findfirstfilew 364 | // "If the string ends with a wildcard, period (.), or directory name, the user must have access permissions to the root and all subdirectories on the path" 365 | // This is a problem if the executing principal has no access to the parent folder; 366 | // appending "\*" fixes this while still allowing retrieval of attributes 367 | if (this is DirectoryInfo) 368 | { 369 | return Path.NormalizeLongPath(Path.Combine(FullPath, "*")); 370 | } 371 | 372 | return Path.NormalizeLongPath(FullPath); 373 | } 374 | 375 | 376 | /// 377 | public void Refresh() 378 | { 379 | try 380 | { 381 | NativeMethods.WIN32_FIND_DATA findData; 382 | 383 | // TODO: BeginFind fails on "\\?\c:\" 384 | using (var handle = Directory.BeginFind(GetNormalizedPathWithSearchPattern(), out findData)) 385 | { 386 | if (handle == null) 387 | { 388 | state = State.Error; 389 | errorCode = Marshal.GetLastWin32Error(); 390 | } 391 | else 392 | { 393 | data.From(findData); 394 | state = State.Initialized; 395 | } 396 | } 397 | } 398 | catch (DirectoryNotFoundException) 399 | { 400 | state = State.Error; 401 | errorCode = NativeMethods.ERROR_PATH_NOT_FOUND; 402 | } 403 | catch (Exception) 404 | { 405 | if (state != State.Error) 406 | Common.ThrowIOError(Marshal.GetLastWin32Error(), FullPath); 407 | } 408 | } 409 | 410 | /// 411 | public abstract void Delete(); 412 | } 413 | } -------------------------------------------------------------------------------- /Pri.LongPath/JunctionPoint.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Source: http://www.codeproject.com/Articles/15633/Manipulating-NTFS-Junction-Points-in-NET 3 | // 4 | // mainly used for TestDeleteDirectory_JunctionPoint 5 | // 6 | // * could be the base for general junction point support e.g. Directory.CreateJunctionPoint(...) 7 | // * long path support not implemented 8 | // 9 | 10 | using System; 11 | using System.Text; 12 | using System.IO; 13 | using System.Runtime.InteropServices; 14 | using Microsoft.Win32.SafeHandles; 15 | 16 | using Path = Pri.LongPath.Path; 17 | using Directory = Pri.LongPath.Directory; 18 | using File = Pri.LongPath.File; 19 | using DirectoryInfo = Pri.LongPath.DirectoryInfo; 20 | using FileInfo = Pri.LongPath.FileInfo; 21 | using FileSystemInfo = Pri.LongPath.FileSystemInfo; 22 | 23 | namespace Pri.LongPath { 24 | 25 | /// 26 | /// PRELIMINARY Provides access to NTFS junction points in .Net. 27 | /// 28 | public static class JunctionPoint 29 | { 30 | /// 31 | /// Creates a junction point from the specified directory to the specified target directory. 32 | /// 33 | /// 34 | /// Only works on NTFS. 35 | /// 36 | /// The junction point path 37 | /// The target directory 38 | /// If true overwrites an existing reparse point or empty directory 39 | /// Thrown when the junction point could not be created or when 40 | /// an existing directory was found and if false 41 | public static void Create(string junctionPoint, string targetDir, bool overwrite) 42 | { 43 | targetDir = Path.GetFullPath(targetDir); 44 | 45 | if (!Directory.Exists(targetDir)) 46 | throw new IOException("Target path does not exist or is not a directory."); 47 | 48 | if (Directory.Exists(junctionPoint)) 49 | { 50 | if (!overwrite) 51 | throw new IOException("Directory already exists and overwrite parameter is false."); 52 | } 53 | else 54 | { 55 | Directory.CreateDirectory(junctionPoint); 56 | } 57 | 58 | using (SafeFileHandle handle = OpenReparsePoint(junctionPoint, EFileAccess.GenericWrite)) 59 | { 60 | byte[] targetDirBytes = Encoding.Unicode.GetBytes(NonInterpretedPathPrefix + Path.GetFullPath(targetDir)); 61 | 62 | var reparseDataBuffer = new REPARSE_DATA_BUFFER 63 | { 64 | ReparseTag = IO_REPARSE_TAG_MOUNT_POINT, 65 | ReparseDataLength = (ushort) (targetDirBytes.Length + 12), 66 | SubstituteNameOffset = 0, 67 | SubstituteNameLength = (ushort) targetDirBytes.Length, 68 | PrintNameOffset = (ushort) (targetDirBytes.Length + 2), 69 | PrintNameLength = 0, 70 | PathBuffer = new byte[0x3ff0] 71 | }; 72 | 73 | Array.Copy(targetDirBytes, reparseDataBuffer.PathBuffer, targetDirBytes.Length); 74 | 75 | int inBufferSize = Marshal.SizeOf(reparseDataBuffer); 76 | IntPtr inBuffer = Marshal.AllocHGlobal(inBufferSize); 77 | 78 | try 79 | { 80 | Marshal.StructureToPtr(reparseDataBuffer, inBuffer, false); 81 | 82 | int bytesReturned; 83 | bool result = DeviceIoControl(handle.DangerousGetHandle(), FSCTL_SET_REPARSE_POINT, 84 | inBuffer, targetDirBytes.Length + 20, IntPtr.Zero, 0, out bytesReturned, IntPtr.Zero); 85 | 86 | if (!result) 87 | ThrowLastWin32Error("Unable to create junction point."); 88 | } 89 | finally 90 | { 91 | Marshal.FreeHGlobal(inBuffer); 92 | } 93 | } 94 | } 95 | 96 | /// 97 | /// Deletes a junction point at the specified source directory along with the directory itself. 98 | /// Does nothing if the junction point does not exist. 99 | /// 100 | /// 101 | /// Only works on NTFS. 102 | /// 103 | /// The junction point path 104 | public static void Delete(string junctionPoint) 105 | { 106 | if (!Directory.Exists(junctionPoint)) 107 | { 108 | if (File.Exists(junctionPoint)) 109 | throw new IOException("Path is not a junction point."); 110 | 111 | return; 112 | } 113 | 114 | using (SafeFileHandle handle = OpenReparsePoint(junctionPoint, EFileAccess.GenericWrite)) 115 | { 116 | var reparseDataBuffer = new REPARSE_DATA_BUFFER 117 | { 118 | ReparseTag = IO_REPARSE_TAG_MOUNT_POINT, 119 | ReparseDataLength = 0, 120 | PathBuffer = new byte[0x3ff0] 121 | }; 122 | 123 | 124 | int inBufferSize = Marshal.SizeOf(reparseDataBuffer); 125 | IntPtr inBuffer = Marshal.AllocHGlobal(inBufferSize); 126 | try 127 | { 128 | Marshal.StructureToPtr(reparseDataBuffer, inBuffer, false); 129 | 130 | int bytesReturned; 131 | bool result = DeviceIoControl(handle.DangerousGetHandle(), FSCTL_DELETE_REPARSE_POINT, 132 | inBuffer, 8, IntPtr.Zero, 0, out bytesReturned, IntPtr.Zero); 133 | 134 | if (!result) 135 | ThrowLastWin32Error("Unable to delete junction point."); 136 | } 137 | finally 138 | { 139 | Marshal.FreeHGlobal(inBuffer); 140 | } 141 | 142 | try 143 | { 144 | Directory.Delete(junctionPoint); 145 | } 146 | catch (IOException ex) 147 | { 148 | throw new IOException("Unable to delete junction point.", ex); 149 | } 150 | } 151 | } 152 | 153 | /// 154 | /// Determines whether the specified path exists and refers to a junction point. 155 | /// 156 | /// The junction point path 157 | /// True if the specified path represents a junction point 158 | /// Thrown if the specified path is invalid 159 | /// or some other error occurs 160 | public static bool Exists(string path) 161 | { 162 | if (! Directory.Exists(path)) 163 | return false; 164 | 165 | using (SafeFileHandle handle = OpenReparsePoint(path, EFileAccess.GenericRead)) 166 | { 167 | string target = InternalGetTarget(handle); 168 | return target != null; 169 | } 170 | } 171 | 172 | /// 173 | /// Gets the target of the specified junction point. 174 | /// 175 | /// 176 | /// Only works on NTFS. 177 | /// 178 | /// The junction point path 179 | /// The target of the junction point 180 | /// Thrown when the specified path does not 181 | /// exist, is invalid, is not a junction point, or some other error occurs 182 | public static string GetTarget(string junctionPoint) 183 | { 184 | using (SafeFileHandle handle = OpenReparsePoint(junctionPoint, EFileAccess.GenericRead)) 185 | { 186 | string target = InternalGetTarget(handle); 187 | if (target == null) 188 | throw new IOException("Path is not a junction point."); 189 | 190 | return target; 191 | } 192 | } 193 | 194 | private static string InternalGetTarget(SafeFileHandle handle) 195 | { 196 | int outBufferSize = Marshal.SizeOf(typeof(REPARSE_DATA_BUFFER)); 197 | IntPtr outBuffer = Marshal.AllocHGlobal(outBufferSize); 198 | 199 | try 200 | { 201 | int bytesReturned; 202 | bool result = DeviceIoControl(handle.DangerousGetHandle(), FSCTL_GET_REPARSE_POINT, 203 | IntPtr.Zero, 0, outBuffer, outBufferSize, out bytesReturned, IntPtr.Zero); 204 | 205 | if (!result) 206 | { 207 | int error = Marshal.GetLastWin32Error(); 208 | if (error == ERROR_NOT_A_REPARSE_POINT) 209 | return null; 210 | 211 | ThrowLastWin32Error("Unable to get information about junction point."); 212 | } 213 | 214 | REPARSE_DATA_BUFFER reparseDataBuffer = (REPARSE_DATA_BUFFER) 215 | Marshal.PtrToStructure(outBuffer, typeof(REPARSE_DATA_BUFFER)); 216 | 217 | if (reparseDataBuffer.ReparseTag != IO_REPARSE_TAG_MOUNT_POINT) 218 | return null; 219 | 220 | string targetDir = Encoding.Unicode.GetString(reparseDataBuffer.PathBuffer, 221 | reparseDataBuffer.SubstituteNameOffset, reparseDataBuffer.SubstituteNameLength); 222 | 223 | if (targetDir.StartsWith(NonInterpretedPathPrefix)) 224 | targetDir = targetDir.Substring(NonInterpretedPathPrefix.Length); 225 | 226 | return targetDir; 227 | } 228 | finally 229 | { 230 | Marshal.FreeHGlobal(outBuffer); 231 | } 232 | } 233 | 234 | #region private implementation 235 | 236 | /// 237 | /// The file or directory is not a reparse point. 238 | /// 239 | private const int ERROR_NOT_A_REPARSE_POINT = 4390; 240 | 241 | /// 242 | /// The reparse point attribute cannot be set because it conflicts with an existing attribute. 243 | /// 244 | private const int ERROR_REPARSE_ATTRIBUTE_CONFLICT = 4391; 245 | 246 | /// 247 | /// The data present in the reparse point buffer is invalid. 248 | /// 249 | private const int ERROR_INVALID_REPARSE_DATA = 4392; 250 | 251 | /// 252 | /// The tag present in the reparse point buffer is invalid. 253 | /// 254 | private const int ERROR_REPARSE_TAG_INVALID = 4393; 255 | 256 | /// 257 | /// There is a mismatch between the tag specified in the request and the tag present in the reparse point. 258 | /// 259 | private const int ERROR_REPARSE_TAG_MISMATCH = 4394; 260 | 261 | /// 262 | /// Command to set the reparse point data block. 263 | /// 264 | private const int FSCTL_SET_REPARSE_POINT = 0x000900A4; 265 | 266 | /// 267 | /// Command to get the reparse point data block. 268 | /// 269 | private const int FSCTL_GET_REPARSE_POINT = 0x000900A8; 270 | 271 | /// 272 | /// Command to delete the reparse point data base. 273 | /// 274 | private const int FSCTL_DELETE_REPARSE_POINT = 0x000900AC; 275 | 276 | /// 277 | /// Reparse point tag used to identify mount points and junction points. 278 | /// 279 | private const uint IO_REPARSE_TAG_MOUNT_POINT = 0xA0000003; 280 | 281 | /// 282 | /// This prefix indicates to NTFS that the path is to be treated as a non-interpreted 283 | /// path in the virtual file system. 284 | /// 285 | private const string NonInterpretedPathPrefix = @"\??\"; 286 | 287 | [Flags] 288 | private enum EFileAccess : uint 289 | { 290 | GenericRead = 0x80000000, 291 | GenericWrite = 0x40000000, 292 | GenericExecute = 0x20000000, 293 | GenericAll = 0x10000000, 294 | } 295 | 296 | [Flags] 297 | private enum EFileShare : uint 298 | { 299 | None = 0x00000000, 300 | Read = 0x00000001, 301 | Write = 0x00000002, 302 | Delete = 0x00000004, 303 | } 304 | 305 | private enum ECreationDisposition : uint 306 | { 307 | New = 1, 308 | CreateAlways = 2, 309 | OpenExisting = 3, 310 | OpenAlways = 4, 311 | TruncateExisting = 5, 312 | } 313 | 314 | [Flags] 315 | private enum EFileAttributes : uint 316 | { 317 | Readonly = 0x00000001, 318 | Hidden = 0x00000002, 319 | System = 0x00000004, 320 | Directory = 0x00000010, 321 | Archive = 0x00000020, 322 | Device = 0x00000040, 323 | Normal = 0x00000080, 324 | Temporary = 0x00000100, 325 | SparseFile = 0x00000200, 326 | ReparsePoint = 0x00000400, 327 | Compressed = 0x00000800, 328 | Offline = 0x00001000, 329 | NotContentIndexed = 0x00002000, 330 | Encrypted = 0x00004000, 331 | WriteThrough = 0x80000000, 332 | Overlapped = 0x40000000, 333 | NoBuffering = 0x20000000, 334 | RandomAccess = 0x10000000, 335 | SequentialScan = 0x08000000, 336 | DeleteOnClose = 0x04000000, 337 | BackupSemantics = 0x02000000, 338 | PosixSemantics = 0x01000000, 339 | OpenReparsePoint = 0x00200000, 340 | OpenNoRecall = 0x00100000, 341 | FirstPipeInstance = 0x00080000 342 | } 343 | 344 | [StructLayout(LayoutKind.Sequential)] 345 | private struct REPARSE_DATA_BUFFER 346 | { 347 | /// 348 | /// Reparse point tag. Must be a Microsoft reparse point tag. 349 | /// 350 | public uint ReparseTag; 351 | 352 | /// 353 | /// Size, in bytes, of the data after the Reserved member. This can be calculated by: 354 | /// (4 * sizeof(ushort)) + SubstituteNameLength + PrintNameLength + 355 | /// (namesAreNullTerminated ? 2 * sizeof(char) : 0); 356 | /// 357 | public ushort ReparseDataLength; 358 | 359 | /// 360 | /// Reserved; do not use. 361 | /// 362 | // ReSharper disable once MemberCanBePrivate.Local 363 | // ReSharper disable once FieldCanBeMadeReadOnly.Local 364 | public ushort Reserved; 365 | 366 | /// 367 | /// Offset, in bytes, of the substitute name string in the PathBuffer array. 368 | /// 369 | public ushort SubstituteNameOffset; 370 | 371 | /// 372 | /// Length, in bytes, of the substitute name string. If this string is null-terminated, 373 | /// SubstituteNameLength does not include space for the null character. 374 | /// 375 | public ushort SubstituteNameLength; 376 | 377 | /// 378 | /// Offset, in bytes, of the print name string in the PathBuffer array. 379 | /// 380 | public ushort PrintNameOffset; 381 | 382 | /// 383 | /// Length, in bytes, of the print name string. If this string is null-terminated, 384 | /// PrintNameLength does not include space for the null character. 385 | /// 386 | public ushort PrintNameLength; 387 | 388 | /// 389 | /// A buffer containing the unicode-encoded path string. The path string contains 390 | /// the substitute name string and print name string. 391 | /// 392 | [MarshalAs(UnmanagedType.ByValArray, SizeConst = 0x3FF0)] 393 | public byte[] PathBuffer; 394 | } 395 | 396 | [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)] 397 | private static extern bool DeviceIoControl(IntPtr hDevice, uint dwIoControlCode, 398 | IntPtr InBuffer, int nInBufferSize, 399 | IntPtr OutBuffer, int nOutBufferSize, 400 | out int pBytesReturned, IntPtr lpOverlapped); 401 | 402 | [DllImport("kernel32.dll", SetLastError = true)] 403 | private static extern IntPtr CreateFile( 404 | string lpFileName, 405 | EFileAccess dwDesiredAccess, 406 | EFileShare dwShareMode, 407 | IntPtr lpSecurityAttributes, 408 | ECreationDisposition dwCreationDisposition, 409 | EFileAttributes dwFlagsAndAttributes, 410 | IntPtr hTemplateFile); 411 | 412 | 413 | 414 | private static SafeFileHandle OpenReparsePoint(string reparsePoint, EFileAccess accessMode) 415 | { 416 | SafeFileHandle reparsePointHandle = new SafeFileHandle(CreateFile(reparsePoint, accessMode, 417 | EFileShare.Read | EFileShare.Write | EFileShare.Delete, 418 | IntPtr.Zero, ECreationDisposition.OpenExisting, 419 | EFileAttributes.BackupSemantics | EFileAttributes.OpenReparsePoint, IntPtr.Zero), true); 420 | 421 | if (Marshal.GetLastWin32Error() != 0) 422 | ThrowLastWin32Error("Unable to open reparse point."); 423 | 424 | return reparsePointHandle; 425 | } 426 | 427 | private static void ThrowLastWin32Error(string message) 428 | { 429 | throw new IOException(message, Marshal.GetExceptionForHR(Marshal.GetHRForLastWin32Error())); 430 | } 431 | 432 | #endregion 433 | } 434 | } 435 | -------------------------------------------------------------------------------- /Pri.LongPath/NativeMethods.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Runtime.InteropServices; 3 | using System.Text; 4 | using Microsoft.Win32.SafeHandles; 5 | using FILETIME = System.Runtime.InteropServices.ComTypes.FILETIME; 6 | using DWORD=System.UInt32; 7 | using System.Runtime.ConstrainedExecution; 8 | using System.Security.Principal; 9 | 10 | namespace Pri.LongPath 11 | { 12 | internal static class NativeMethods 13 | { 14 | internal const int ERROR_SUCCESS = 0; 15 | internal const int ERROR_FILE_NOT_FOUND = 0x2; 16 | internal const int ERROR_PATH_NOT_FOUND = 0x3; 17 | internal const int ERROR_ACCESS_DENIED = 0x5; 18 | internal const int ERROR_INVALID_HANDLE = 0x6; 19 | internal const int ERROR_NOT_ENOUGH_MEMORY = 0x8; 20 | internal const int ERROR_INVALID_DRIVE = 0xf; 21 | internal const int ERROR_NO_MORE_FILES = 0x12; 22 | internal const int ERROR_NOT_READY = 0x15; 23 | internal const int ERROR_SHARING_VIOLATION = 0x20; 24 | internal const int ERROR_BAD_NETPATH = 0x35; 25 | internal const int ERROR_NETNAME_DELETED = 0x40; 26 | internal const int ERROR_FILE_EXISTS = 0x50; 27 | internal const int ERROR_INVALID_PARAMETER = 0x57; 28 | internal const int ERROR_INVALID_NAME = 0x7B; 29 | internal const int ERROR_BAD_PATHNAME = 0xA1; 30 | internal const int ERROR_ALREADY_EXISTS = 0xB7; 31 | internal const int ERROR_FILENAME_EXCED_RANGE = 0xCE; // filename too long. 32 | internal const int ERROR_DIRECTORY = 0x10B; 33 | internal const int ERROR_OPERATION_ABORTED = 0x3e3; 34 | internal const int ERROR_NO_TOKEN = 0x3f0; 35 | internal const int ERROR_NOT_ALL_ASSIGNED = 0x514; 36 | internal const int ERROR_INVALID_OWNER = 0x51B; 37 | internal const int ERROR_INVALID_PRIMARY_GROUP = 0x51C; 38 | internal const int ERROR_NO_SUCH_PRIVILEGE = 0x521; 39 | internal const int ERROR_PRIVILEGE_NOT_HELD = 0x522; 40 | internal const int ERROR_LOGON_FAILURE = 0x52E; 41 | internal const int ERROR_CANT_OPEN_ANONYMOUS = 0x543; 42 | internal const int ERROR_NO_SECURITY_ON_OBJECT = 0x546; 43 | 44 | internal const int INVALID_FILE_ATTRIBUTES = -1; 45 | internal const int FILE_ATTRIBUTE_DIRECTORY = 0x00000010; 46 | internal const int FILE_WRITE_ATTRIBUTES = 0x0100; 47 | internal const int FILE_FLAG_BACKUP_SEMANTICS = 0x02000000; 48 | internal const int REPLACEFILE_WRITE_THROUGH = 0x1; 49 | internal const int REPLACEFILE_IGNORE_MERGE_ERRORS = 0x2; 50 | 51 | internal const int MAX_PATH = 260; 52 | // While Windows allows larger paths up to a maximum of 32767 characters, because this is only an approximation and 53 | // can vary across systems and OS versions, we choose a limit well under so that we can give a consistent behavior. 54 | internal const int MAX_LONG_PATH = 32000; 55 | internal const int MAX_ALTERNATE = 14; 56 | 57 | public const int FORMAT_MESSAGE_IGNORE_INSERTS = 0x00000200; 58 | public const int FORMAT_MESSAGE_FROM_SYSTEM = 0x00001000; 59 | public const int FORMAT_MESSAGE_ARGUMENT_ARRAY = 0x00002000; 60 | 61 | [Flags] 62 | internal enum EFileAccess : uint 63 | { 64 | GenericRead = 0x80000000, 65 | GenericWrite = 0x40000000, 66 | GenericExecute = 0x20000000, 67 | GenericAll = 0x10000000, 68 | } 69 | 70 | [Serializable] 71 | internal struct WIN32_FILE_ATTRIBUTE_DATA 72 | { 73 | internal System.IO.FileAttributes fileAttributes; 74 | 75 | internal uint ftCreationTimeLow; 76 | 77 | internal uint ftCreationTimeHigh; 78 | 79 | internal uint ftLastAccessTimeLow; 80 | 81 | internal uint ftLastAccessTimeHigh; 82 | 83 | internal uint ftLastWriteTimeLow; 84 | 85 | internal uint ftLastWriteTimeHigh; 86 | 87 | internal int fileSizeHigh; 88 | 89 | internal int fileSizeLow; 90 | public void PopulateFrom(NativeMethods.WIN32_FIND_DATA findData) 91 | { 92 | fileAttributes = findData.dwFileAttributes; 93 | ftCreationTimeLow = (uint)findData.ftCreationTime.dwLowDateTime; 94 | ftCreationTimeHigh = (uint)findData.ftCreationTime.dwHighDateTime; 95 | ftLastAccessTimeLow = (uint)findData.ftLastAccessTime.dwLowDateTime; 96 | ftLastAccessTimeHigh = (uint)findData.ftLastAccessTime.dwHighDateTime; 97 | ftLastWriteTimeLow = (uint)findData.ftLastWriteTime.dwLowDateTime; 98 | ftLastWriteTimeHigh = (uint)findData.ftLastWriteTime.dwHighDateTime; 99 | fileSizeHigh = findData.nFileSizeHigh; 100 | fileSizeLow = findData.nFileSizeLow; 101 | } 102 | } 103 | 104 | [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] 105 | internal struct WIN32_FIND_DATA 106 | { 107 | internal System.IO.FileAttributes dwFileAttributes; 108 | internal FILETIME ftCreationTime; 109 | internal FILETIME ftLastAccessTime; 110 | internal FILETIME ftLastWriteTime; 111 | internal int nFileSizeHigh; 112 | internal int nFileSizeLow; 113 | internal int dwReserved0; 114 | internal int dwReserved1; 115 | [MarshalAs(UnmanagedType.ByValTStr, SizeConst = MAX_PATH)] 116 | internal string cFileName; 117 | [MarshalAs(UnmanagedType.ByValTStr, SizeConst = MAX_ALTERNATE)] 118 | internal string cAlternate; 119 | } 120 | 121 | internal static int MakeHRFromErrorCode(int errorCode) 122 | { 123 | return unchecked((int)0x80070000 | errorCode); 124 | } 125 | 126 | [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)] 127 | [return: MarshalAs(UnmanagedType.Bool)] 128 | internal static extern bool CopyFile(string src, string dst, [MarshalAs(UnmanagedType.Bool)]bool failIfExists); 129 | 130 | [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Auto, BestFitMapping = false)] 131 | internal static extern bool ReplaceFile(String replacedFileName, String replacementFileName, String backupFileName, int dwReplaceFlags, IntPtr lpExclude, IntPtr lpReserved); 132 | 133 | [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)] 134 | internal static extern SafeFindHandle FindFirstFile(string lpFileName, out WIN32_FIND_DATA lpFindFileData); 135 | 136 | [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)] 137 | [return: MarshalAs(UnmanagedType.Bool)] 138 | internal static extern bool FindNextFile(SafeFindHandle hFindFile, out WIN32_FIND_DATA lpFindFileData); 139 | 140 | [DllImport("kernel32.dll", SetLastError = true)] 141 | [return: MarshalAs(UnmanagedType.Bool)] 142 | internal static extern bool FindClose(IntPtr hFindFile); 143 | 144 | [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)] 145 | internal static extern uint GetFullPathName(string lpFileName, uint nBufferLength, 146 | StringBuilder lpBuffer, IntPtr mustBeNull); 147 | 148 | [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)] 149 | [return: MarshalAs(UnmanagedType.Bool)] 150 | internal static extern bool DeleteFile(string lpFileName); 151 | 152 | [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)] 153 | [return: MarshalAs(UnmanagedType.Bool)] 154 | internal static extern bool RemoveDirectory(string lpPathName); 155 | 156 | [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)] 157 | [return: MarshalAs(UnmanagedType.Bool)] 158 | internal static extern bool CreateDirectory(string lpPathName, 159 | IntPtr lpSecurityAttributes); 160 | 161 | [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)] 162 | [return: MarshalAs(UnmanagedType.Bool)] 163 | internal static extern bool MoveFile(string lpPathNameFrom, string lpPathNameTo); 164 | 165 | [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)] 166 | internal static extern SafeFileHandle CreateFile( 167 | string lpFileName, 168 | EFileAccess dwDesiredAccess, 169 | uint dwShareMode, 170 | IntPtr lpSecurityAttributes, 171 | uint dwCreationDisposition, 172 | uint dwFlagsAndAttributes, 173 | IntPtr hTemplateFile); 174 | 175 | [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)] 176 | internal static extern System.IO.FileAttributes GetFileAttributes(string lpFileName); 177 | 178 | [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)] 179 | [return: MarshalAs(UnmanagedType.Bool)] 180 | internal static extern bool SetFileAttributes(string lpFileName, [MarshalAs(UnmanagedType.U4)]System.IO.FileAttributes dwFileAttributes); 181 | 182 | internal static long SetFilePointer(SafeFileHandle handle, long offset, System.IO.SeekOrigin origin) 183 | { 184 | int num1 = (int)(offset >> 32); 185 | int num2 = SetFilePointerWin32(handle, (int)offset, ref num1, (int)origin); 186 | if (num2 == -1 && Marshal.GetLastWin32Error() != 0) 187 | return -1L; 188 | return (long)(uint)num1 << 32 | (uint)num2; 189 | } 190 | 191 | [DllImport("kernel32.dll", EntryPoint = "SetFilePointer", SetLastError = true)] 192 | internal static extern int SetFilePointerWin32(SafeFileHandle handle, int lo, ref int hi, int origin); 193 | 194 | [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)] 195 | internal static extern int FormatMessage(int dwFlags, IntPtr lpSource, int dwMessageId, int dwLanguageId, StringBuilder lpBuffer, int nSize, IntPtr va_list_arguments); 196 | 197 | [DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Auto, BestFitMapping = false)] 198 | internal static extern bool DecryptFile(String path, int reservedMustBeZero); 199 | 200 | [DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Auto, BestFitMapping = false)] 201 | internal static extern bool EncryptFile(String path); 202 | 203 | public static string GetMessage(int errorCode) 204 | { 205 | var sb = new StringBuilder(512); 206 | int result = FormatMessage(FORMAT_MESSAGE_IGNORE_INSERTS | 207 | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ARGUMENT_ARRAY, 208 | IntPtr.Zero, errorCode, 0, sb, sb.Capacity, IntPtr.Zero); 209 | if (result != 0) 210 | { 211 | // result is the # of characters copied to the StringBuilder. 212 | return sb.ToString(); 213 | } 214 | else 215 | { 216 | return string.Format("Unknown error: {0}", errorCode); 217 | } 218 | } 219 | [StructLayout(LayoutKind.Sequential)] 220 | internal struct FILE_TIME 221 | { 222 | public FILE_TIME(long fileTime) 223 | { 224 | ftTimeLow = (uint)fileTime; 225 | ftTimeHigh = (uint)(fileTime >> 32); 226 | } 227 | 228 | public long ToTicks() 229 | { 230 | return ((long)ftTimeHigh << 32) + ftTimeLow; 231 | } 232 | 233 | internal uint ftTimeLow; 234 | internal uint ftTimeHigh; 235 | } 236 | [DllImport("kernel32.dll", SetLastError = true)] 237 | internal static extern unsafe bool SetFileTime(SafeFileHandle hFile, FILE_TIME* creationTime, 238 | FILE_TIME* lastAccessTime, FILE_TIME* lastWriteTime); 239 | 240 | [DllImport("kernel32.dll", BestFitMapping = false, CharSet = CharSet.Auto, ExactSpelling = false, SetLastError = true)] 241 | internal static extern bool GetFileAttributesEx(string name, int fileInfoLevel, ref WIN32_FILE_ATTRIBUTE_DATA lpFileInformation); 242 | 243 | [DllImport("kernel32.dll", CharSet = CharSet.None, EntryPoint = "SetErrorMode", ExactSpelling = true)] 244 | private static extern int SetErrorMode_VistaAndOlder(int newMode); 245 | private static readonly Version ThreadErrorModeMinOsVersion = new Version(6, 1, 7600); 246 | [DllImport("kernel32.dll", CharSet = CharSet.None, EntryPoint = "SetThreadErrorMode", ExactSpelling = false, SetLastError = true)] 247 | private static extern bool SetErrorMode_Win7AndNewer(int newMode, out int oldMode); 248 | 249 | internal static int SetErrorMode(int newMode) 250 | { 251 | int num; 252 | if (Environment.OSVersion.Version < ThreadErrorModeMinOsVersion) 253 | { 254 | return SetErrorMode_VistaAndOlder(newMode); 255 | } 256 | SetErrorMode_Win7AndNewer(newMode, out num); 257 | return num; 258 | } 259 | 260 | [DllImport("advapi32.dll", 261 | EntryPoint = "GetNamedSecurityInfoW", 262 | CallingConvention = CallingConvention.Winapi, 263 | SetLastError = true, 264 | ExactSpelling = true, 265 | CharSet = CharSet.Unicode)] 266 | internal static extern DWORD GetSecurityInfoByName( 267 | string name, 268 | DWORD objectType, 269 | DWORD securityInformation, 270 | out IntPtr sidOwner, 271 | out IntPtr sidGroup, 272 | out IntPtr dacl, 273 | out IntPtr sacl, 274 | out IntPtr securityDescriptor); 275 | 276 | [DllImport( 277 | "advapi32.dll", 278 | EntryPoint = "SetNamedSecurityInfoW", 279 | CallingConvention = CallingConvention.Winapi, 280 | SetLastError = true, 281 | ExactSpelling = true, 282 | CharSet = CharSet.Unicode)] 283 | internal static extern DWORD SetSecurityInfoByName( 284 | string name, 285 | DWORD objectType, 286 | DWORD securityInformation, 287 | byte[] owner, 288 | byte[] group, 289 | byte[] dacl, 290 | byte[] sacl); 291 | 292 | [DllImport( 293 | "advapi32.dll", 294 | EntryPoint = "SetSecurityInfo", 295 | CallingConvention = CallingConvention.Winapi, 296 | SetLastError = true, 297 | ExactSpelling = true, 298 | CharSet = CharSet.Unicode)] 299 | internal static extern DWORD SetSecurityInfoByHandle( 300 | SafeHandle handle, 301 | DWORD objectType, 302 | DWORD securityInformation, 303 | byte[] owner, 304 | byte[] group, 305 | byte[] dacl, 306 | byte[] sacl); 307 | [DllImport( 308 | "advapi32.dll", 309 | EntryPoint = "GetSecurityDescriptorLength", 310 | CallingConvention = CallingConvention.Winapi, 311 | SetLastError = true, 312 | ExactSpelling = true, 313 | CharSet = CharSet.Unicode)] 314 | internal static extern DWORD GetSecurityDescriptorLength( 315 | IntPtr byteArray); 316 | 317 | [DllImport("kernel32.dll", SetLastError = true)] 318 | internal static extern IntPtr LocalFree(IntPtr handle); 319 | 320 | [DllImport("kernel32.dll", BestFitMapping = false, CharSet = CharSet.Auto, ExactSpelling = false, SetLastError = true)] 321 | internal static extern bool SetCurrentDirectory(string path); 322 | #region for Priviledge class 323 | 324 | internal enum SecurityImpersonationLevel 325 | { 326 | Anonymous = 0, 327 | Identification = 1, 328 | Impersonation = 2, 329 | Delegation = 3, 330 | } 331 | 332 | internal enum TokenType 333 | { 334 | Primary = 1, 335 | Impersonation = 2, 336 | } 337 | 338 | internal const uint SE_PRIVILEGE_DISABLED = 0x00000000; 339 | internal const uint SE_PRIVILEGE_ENABLED = 0x00000002; 340 | 341 | [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] 342 | internal struct LUID 343 | { 344 | internal uint LowPart; 345 | internal uint HighPart; 346 | } 347 | 348 | [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] 349 | internal struct LUID_AND_ATTRIBUTES 350 | { 351 | internal LUID Luid; 352 | internal uint Attributes; 353 | } 354 | 355 | [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] 356 | internal struct TOKEN_PRIVILEGE 357 | { 358 | internal uint PrivilegeCount; 359 | internal LUID_AND_ATTRIBUTES Privilege; 360 | } 361 | 362 | 363 | [DllImport( 364 | "kernel32.dll", 365 | SetLastError = true)] 366 | [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)] 367 | internal static extern bool CloseHandle(IntPtr handle); 368 | 369 | [DllImport( 370 | "advapi32.dll", 371 | CharSet = CharSet.Unicode, 372 | SetLastError = true)] 373 | [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)] 374 | internal static extern bool AdjustTokenPrivileges( 375 | [In] SafeTokenHandle TokenHandle, 376 | [In] bool DisableAllPrivileges, 377 | [In] ref TOKEN_PRIVILEGE NewState, 378 | [In] uint BufferLength, 379 | [In, Out] ref TOKEN_PRIVILEGE PreviousState, 380 | [In, Out] ref uint ReturnLength); 381 | 382 | [DllImport( 383 | "advapi32.dll", 384 | CharSet = CharSet.Auto, 385 | SetLastError = true)] 386 | [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)] 387 | internal static extern 388 | bool RevertToSelf(); 389 | 390 | [DllImport( 391 | "advapi32.dll", 392 | EntryPoint = "LookupPrivilegeValueW", 393 | CharSet = CharSet.Auto, 394 | SetLastError = true)] 395 | [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)] 396 | internal static extern 397 | bool LookupPrivilegeValue( 398 | [In] string lpSystemName, 399 | [In] string lpName, 400 | [In, Out] ref LUID Luid); 401 | 402 | [DllImport( 403 | "kernel32.dll", 404 | CharSet = CharSet.Auto, 405 | SetLastError = true)] 406 | [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)] 407 | internal static extern 408 | IntPtr GetCurrentProcess(); 409 | 410 | [DllImport( 411 | "kernel32.dll", 412 | CharSet = CharSet.Auto, 413 | SetLastError = true)] 414 | [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)] 415 | internal static extern 416 | IntPtr GetCurrentThread(); 417 | 418 | [DllImport( 419 | "advapi32.dll", 420 | CharSet = CharSet.Unicode, 421 | SetLastError = true)] 422 | [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)] 423 | internal static extern 424 | bool OpenProcessToken( 425 | [In] IntPtr ProcessToken, 426 | [In] TokenAccessLevels DesiredAccess, 427 | [In, Out] ref SafeTokenHandle TokenHandle); 428 | 429 | [DllImport 430 | ("advapi32.dll", 431 | CharSet = CharSet.Unicode, 432 | SetLastError = true)] 433 | [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)] 434 | internal static extern 435 | bool OpenThreadToken( 436 | [In] IntPtr ThreadToken, 437 | [In] TokenAccessLevels DesiredAccess, 438 | [In] bool OpenAsSelf, 439 | [In, Out] ref SafeTokenHandle TokenHandle); 440 | 441 | [DllImport 442 | ("advapi32.dll", 443 | CharSet = CharSet.Unicode, 444 | SetLastError = true)] 445 | [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)] 446 | internal static extern 447 | bool DuplicateTokenEx( 448 | [In] SafeTokenHandle ExistingToken, 449 | [In] TokenAccessLevels DesiredAccess, 450 | [In] IntPtr TokenAttributes, 451 | [In] SecurityImpersonationLevel ImpersonationLevel, 452 | [In] TokenType TokenType, 453 | [In, Out] ref SafeTokenHandle NewToken); 454 | 455 | [DllImport 456 | ("advapi32.dll", 457 | CharSet = CharSet.Unicode, 458 | SetLastError = true)] 459 | [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)] 460 | internal static extern 461 | bool SetThreadToken( 462 | [In] IntPtr Thread, 463 | [In] SafeTokenHandle Token); 464 | #endregion 465 | } 466 | } -------------------------------------------------------------------------------- /Pri.LongPath/Path.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Globalization; 3 | using System.Text; 4 | #if !NET_2_0 5 | using System.Linq; 6 | #endif 7 | 8 | namespace Pri.LongPath 9 | { 10 | /// 11 | public static class Path 12 | { 13 | /// 14 | public static readonly char[] InvalidPathChars = System.IO.Path.GetInvalidPathChars(); 15 | private static readonly char[] invalidFileNameChars = System.IO.Path.GetInvalidFileNameChars(); 16 | internal const string LongPathPrefix = @"\\?\"; 17 | internal const string UNCLongPathPrefix = @"\\?\UNC\"; 18 | 19 | /// 20 | public static readonly char DirectorySeparatorChar = System.IO.Path.DirectorySeparatorChar; 21 | /// 22 | public static readonly char AltDirectorySeparatorChar = System.IO.Path.AltDirectorySeparatorChar; 23 | /// 24 | public static readonly char VolumeSeparatorChar = ':'; 25 | 26 | /// 27 | public static readonly char PathSeparator = System.IO.Path.PathSeparator; 28 | 29 | internal static string NormalizeLongPath(string path) 30 | { 31 | if (Common.IsRunningOnMono() && Common.IsPlatformUnix()) 32 | return path; 33 | 34 | return NormalizeLongPath(path, "path"); 35 | } 36 | 37 | // Normalizes path (can be longer than MAX_PATH) and adds \\?\ long path prefix 38 | internal static string NormalizeLongPath(string path, string parameterName) 39 | { 40 | if (path == null) 41 | throw new ArgumentNullException(parameterName); 42 | 43 | if (path.Length == 0) 44 | throw new ArgumentException(String.Format(CultureInfo.CurrentCulture, "'{0}' cannot be an empty string.", parameterName), parameterName); 45 | 46 | if (Common.IsPathUnc(path)) return CheckAddLongPathPrefix(path); 47 | StringBuilder buffer = new StringBuilder(path.Length + 1); // Add 1 for NULL 48 | uint length = NativeMethods.GetFullPathName(path, (uint)buffer.Capacity, buffer, IntPtr.Zero); 49 | if (length > buffer.Capacity) 50 | { 51 | // Resulting path longer than our buffer, so increase it 52 | 53 | buffer.Capacity = (int)length; 54 | length = NativeMethods.GetFullPathName(path, length, buffer, IntPtr.Zero); 55 | } 56 | 57 | if (length == 0) 58 | { 59 | throw Common.GetExceptionFromLastWin32Error(parameterName); 60 | } 61 | 62 | if (length > NativeMethods.MAX_LONG_PATH) 63 | { 64 | throw Common.GetExceptionFromWin32Error(NativeMethods.ERROR_FILENAME_EXCED_RANGE, parameterName); 65 | } 66 | 67 | if (length > 1 && buffer[0] == DirectorySeparatorChar && buffer[1] == DirectorySeparatorChar) 68 | { 69 | if (length < 2) throw new ArgumentException("The UNC path should be of the form \\\\server\\share."); 70 | var parts = buffer.ToString().Split(new [] {DirectorySeparatorChar}, StringSplitOptions.RemoveEmptyEntries); 71 | if (parts.Length < 2) throw new ArgumentException("The UNC path should be of the form \\\\server\\share."); 72 | } 73 | 74 | return AddLongPathPrefix(buffer.ToString()); 75 | } 76 | 77 | internal static bool TryNormalizeLongPath(string path, out string result) 78 | { 79 | try 80 | { 81 | result = NormalizeLongPath(path); 82 | return true; 83 | } 84 | catch (ArgumentException) 85 | { 86 | } 87 | catch (System.IO.PathTooLongException) 88 | { 89 | } 90 | 91 | result = null; 92 | return false; 93 | } 94 | 95 | internal static string CheckAddLongPathPrefix(string path) 96 | { 97 | if (string.IsNullOrEmpty(path) || path.StartsWith(@"\\?\")) 98 | { 99 | return path; 100 | } 101 | 102 | var maxPathLimit = NativeMethods.MAX_PATH; 103 | Uri uri; 104 | if (Uri.TryCreate(path, UriKind.Absolute, out uri) && uri.IsUnc) 105 | { 106 | // What's going on here? Empirical evidence shows that Windows has trouble dealing with UNC paths 107 | // longer than MAX_PATH *minus* the length of the "\\hostname\" prefix. See the following tests: 108 | // - UncDirectoryTests.TestDirectoryCreateNearMaxPathLimit 109 | // - UncDirectoryTests.TestDirectoryEnumerateDirectoriesNearMaxPathLimit 110 | var rootPathLength = 3 + uri.Host.Length; 111 | maxPathLimit -= rootPathLength; 112 | } 113 | 114 | if (path.Length >= maxPathLimit) 115 | { 116 | return AddLongPathPrefix(path); 117 | } 118 | 119 | return path; 120 | } 121 | 122 | internal static string RemoveLongPathPrefix(string normalizedPath) 123 | { 124 | 125 | if (string.IsNullOrEmpty(normalizedPath) || !normalizedPath.StartsWith(LongPathPrefix)) 126 | { 127 | return normalizedPath; 128 | } 129 | 130 | if (normalizedPath.StartsWith(UNCLongPathPrefix, StringComparison.InvariantCultureIgnoreCase)) 131 | { 132 | return string.Format(@"\\{0}", normalizedPath.Substring(UNCLongPathPrefix.Length)); 133 | } 134 | 135 | return normalizedPath.Substring(LongPathPrefix.Length); 136 | } 137 | 138 | private static string AddLongPathPrefix(string path) 139 | { 140 | if (string.IsNullOrEmpty(path) || path.StartsWith(LongPathPrefix)) 141 | { 142 | return path; 143 | } 144 | 145 | // http://msdn.microsoft.com/en-us/library/aa365247.aspx 146 | if (path.StartsWith(@"\\")) 147 | { 148 | // UNC. 149 | return UNCLongPathPrefix + path.Substring(2); 150 | } 151 | 152 | return LongPathPrefix + path; 153 | } 154 | 155 | /// 156 | public static string Combine(string path1, string path2) 157 | { 158 | if (path1 == null || path2 == null) 159 | throw new ArgumentNullException(path1 == null ? "path1" : "path2"); 160 | 161 | CheckInvalidPathChars(path1); 162 | CheckInvalidPathChars(path2); 163 | if (path2.Length == 0) 164 | return path1; 165 | if (path1.Length == 0 || IsPathRooted(path2)) 166 | return path2; 167 | char ch = path1[path1.Length - 1]; 168 | if (ch != DirectorySeparatorChar && ch != AltDirectorySeparatorChar && 169 | ch != VolumeSeparatorChar) 170 | return path1 + DirectorySeparatorChar + path2; 171 | return path1 + path2; 172 | } 173 | 174 | /// 175 | public static bool IsPathRooted(string path) 176 | { 177 | return System.IO.Path.IsPathRooted(path); 178 | } 179 | 180 | /// 181 | public static string Combine(string path1, string path2, string path3) 182 | { 183 | if (path1 == null || path2 == null || path3 == null) 184 | throw new ArgumentNullException(path1 == null ? "path1" : path2 == null ? "path2" : "path3"); 185 | 186 | return Combine(Combine(path1, path2), path3); 187 | } 188 | 189 | /// 190 | public static string Combine(string path1, string path2, string path3, string path4) 191 | { 192 | if (path1 == null || path2 == null || path3 == null || path4 == null) 193 | throw new ArgumentNullException(path1 == null ? "path1" : path2 == null ? "path2" : path3 == null ? "path3" : "path4"); 194 | 195 | return Combine(Combine(Combine(path1, path2), path3), path4); 196 | } 197 | 198 | private static void CheckInvalidPathChars(string path) 199 | { 200 | if (HasIllegalCharacters(path)) 201 | throw new ArgumentException("Invalid characters in path", "path"); 202 | } 203 | 204 | private static bool HasIllegalCharacters(string path) 205 | { 206 | #if NET_2_0 207 | foreach (var e in path) 208 | { 209 | if (InvalidPathChars.Contains(e)) 210 | { 211 | return true; 212 | } 213 | } 214 | return false; 215 | #else 216 | return path.Any(InvalidPathChars.Contains); 217 | #endif 218 | } 219 | 220 | /// 221 | public static string GetFileName(string path) 222 | { 223 | if (path == null) return null; 224 | return System.IO.Path.GetFileName(Path.NormalizeLongPath(path)); 225 | } 226 | 227 | /// 228 | public static string GetFullPath(string path) 229 | { 230 | return Common.IsPathUnc(path) ? path : Path.RemoveLongPathPrefix(Path.NormalizeLongPath(path)); 231 | } 232 | 233 | /// 234 | public static string GetDirectoryName(string path) 235 | { 236 | if (Common.IsRunningOnMono() && Common.IsPlatformUnix()) return System.IO.Path.GetDirectoryName(path); 237 | 238 | if (path == null) throw new ArgumentNullException("path"); 239 | Path.CheckInvalidPathChars(path); 240 | string basePath = null; 241 | if (!IsPathRooted(path)) 242 | basePath = System.IO.Directory.GetCurrentDirectory(); 243 | 244 | path = Path.RemoveLongPathPrefix(Path.NormalizeLongPath(path)); 245 | int rootLength = GetRootLength(path); 246 | 247 | if (path.Length <= rootLength) return null; 248 | int length = path.Length; 249 | do 250 | { 251 | } while (length > rootLength && path[--length] != System.IO.Path.DirectorySeparatorChar && 252 | path[length] != System.IO.Path.AltDirectorySeparatorChar); 253 | if (basePath != null) 254 | { 255 | path = path.Substring(basePath.Length + 1); 256 | length = length - basePath.Length - 1; 257 | if (length < 0) 258 | length = 0; 259 | } 260 | return path.Substring(0, length); 261 | } 262 | 263 | private static int GetUncRootLength(string path) 264 | { 265 | var components = path.Split(new[] { DirectorySeparatorChar }, StringSplitOptions.RemoveEmptyEntries); 266 | var root = string.Format(@"\\{0}\{1}\", components[0], components[1]); 267 | return root.Length; 268 | } 269 | 270 | internal static int GetRootLength(string path) 271 | { 272 | if (Common.IsPathUnc(path)) return GetUncRootLength(path); 273 | path = Path .GetFullPath(path); 274 | Path.CheckInvalidPathChars(path); 275 | int rootLength = 0; 276 | int length = path.Length; 277 | if (length >= 1 && IsDirectorySeparator(path[0])) 278 | { 279 | rootLength = 1; 280 | if (length >= 2 && IsDirectorySeparator(path[1])) 281 | { 282 | rootLength = 2; 283 | int num = 2; 284 | while (rootLength >= length || 285 | ((path[rootLength] == System.IO.Path.DirectorySeparatorChar || 286 | path[rootLength] == System.IO.Path.AltDirectorySeparatorChar) && --num <= 0)) 287 | ++rootLength; 288 | } 289 | } 290 | else if (length >= 2 && path[1] == System.IO.Path.VolumeSeparatorChar) 291 | { 292 | rootLength = 2; 293 | if (length >= 3 && IsDirectorySeparator(path[2])) 294 | ++rootLength; 295 | } 296 | return rootLength; 297 | } 298 | 299 | internal static bool IsDirectorySeparator(char c) 300 | { 301 | return c == DirectorySeparatorChar || c == AltDirectorySeparatorChar; 302 | } 303 | 304 | /// 305 | public static char[] GetInvalidPathChars() 306 | { 307 | return InvalidPathChars; 308 | } 309 | 310 | /// 311 | public static char[] GetInvalidFileNameChars() 312 | { 313 | return invalidFileNameChars; 314 | } 315 | 316 | /// 317 | public static string GetRandomFileName() 318 | { 319 | return System.IO.Path.GetRandomFileName(); 320 | } 321 | 322 | /// 323 | public static string GetPathRoot(string path) 324 | { 325 | if (path == null) return null; 326 | if (Path.IsPathRooted(path)) 327 | { 328 | if(!Common.IsPathUnc(path)) 329 | path = Path.RemoveLongPathPrefix(Path.NormalizeLongPath(path)); 330 | return path.Substring(0, Math.Min(path.Length, GetRootLength(path))); 331 | } 332 | return string.Empty; 333 | } 334 | 335 | /// 336 | public static string GetExtension(string path) 337 | { 338 | return System.IO.Path.GetExtension(path); 339 | } 340 | 341 | /// 342 | public static bool HasExtension(string path) 343 | { 344 | return System.IO.Path.HasExtension(path); 345 | } 346 | 347 | /// 348 | public static string GetTempPath() 349 | { 350 | return System.IO.Path.GetTempPath(); 351 | } 352 | 353 | /// 354 | public static string GetTempFileName() 355 | { 356 | return System.IO.Path.GetTempFileName(); 357 | } 358 | 359 | /// 360 | public static string GetFileNameWithoutExtension(string path) 361 | { 362 | return System.IO.Path.GetFileNameWithoutExtension(path); 363 | } 364 | 365 | /// 366 | public static string ChangeExtension(string filename, string extension) 367 | { 368 | return System.IO.Path.ChangeExtension(filename, extension); 369 | } 370 | 371 | /// 372 | public static string Combine(string[] paths) 373 | { 374 | if(paths == null) throw new ArgumentNullException("paths"); 375 | if (paths.Length == 0) return string.Empty; 376 | if (paths.Length == 1) return paths[0]; 377 | var path = paths[0]; 378 | for (int i = 1; i < paths.Length; ++i) 379 | { 380 | path = Path.Combine(path, paths[i]); 381 | } 382 | return path; 383 | } 384 | } 385 | } -------------------------------------------------------------------------------- /Pri.LongPath/Pri.LongPath.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {DF1F23CA-9DC6-4604-8400-6C4B656E0F5D} 8 | Library 9 | Properties 10 | Pri.LongPath 11 | Pri.LongPath 12 | v4.8 13 | 512 14 | 15 | 16 | 17 | true 18 | full 19 | false 20 | bin\Debug\ 21 | TRACE;DEBUG;NET_4_5 22 | prompt 23 | 4 24 | true 25 | bin\Debug\Pri.LongPath.xml 26 | 27 | 28 | pdbonly 29 | true 30 | bin\Release\ 31 | TRACE;NET_4_5 32 | prompt 33 | 4 34 | true 35 | bin\Release\Pri.LongPath.xml 36 | CS1591;CS1734;CS1572;CS1573 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 70 | -------------------------------------------------------------------------------- /Pri.LongPath/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("Pri.LongPath for .NET 4.5")] 9 | [assembly: AssemblyDescription("Pri.LongPath for .NET 4.5")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("Peter Ritchie Inc.")] 12 | [assembly: AssemblyProduct("Pri.LongPath")] 13 | [assembly: AssemblyCopyright("Copyright © Peter Ritchie 2014")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("927bffc7-1ff8-4689-8736-0539c2839377")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Build and Revision Numbers 33 | // by using the '*' as shown below: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("2.0.1.0")] 36 | [assembly: AssemblyFileVersion("2.0.1.0")] 37 | [assembly: AssemblyInformationalVersion("2.0.1")] -------------------------------------------------------------------------------- /Pri.LongPath/SafeFindHandle.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Win32.SafeHandles; 2 | 3 | namespace Pri.LongPath 4 | { 5 | internal sealed class SafeFindHandle : SafeHandleZeroOrMinusOneIsInvalid 6 | { 7 | internal SafeFindHandle() 8 | : base(true) 9 | { 10 | } 11 | 12 | protected override bool ReleaseHandle() 13 | { 14 | return NativeMethods.FindClose(base.handle); 15 | } 16 | } 17 | } -------------------------------------------------------------------------------- /Pri.LongPath/SafeTokenHandle.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Security; 3 | using System.Runtime.InteropServices; 4 | using Microsoft.Win32.SafeHandles; 5 | using System.Runtime.ConstrainedExecution; 6 | 7 | namespace Pri.LongPath 8 | { 9 | internal class SafeTokenHandle : SafeHandleZeroOrMinusOneIsInvalid 10 | { 11 | private SafeTokenHandle() : base(true) { } 12 | 13 | // 0 is an Invalid Handle 14 | internal SafeTokenHandle(IntPtr handle) 15 | : base(true) 16 | { 17 | SetHandle(handle); 18 | } 19 | 20 | internal static SafeTokenHandle InvalidHandle 21 | { 22 | get { return new SafeTokenHandle(IntPtr.Zero); } 23 | } 24 | 25 | [DllImport("kernel32.dll", SetLastError = true), 26 | SuppressUnmanagedCodeSecurity, 27 | ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)] 28 | private static extern bool CloseHandle(IntPtr handle); 29 | 30 | protected override bool ReleaseHandle() 31 | { 32 | return CloseHandle(handle); 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | LongPath 2 | ======== 3 | 4 | [![Build status](https://ci.appveyor.com/api/projects/status/w9410p6garuyba2b?svg=true)](https://ci.appveyor.com/project/peteraritchie/longpath) [![NuGet Badge](https://buildstats.info/nuget/pri.longpath)](https://www.nuget.org/packages/PRI.LongPath/) 5 | 6 | A drop-in library to support long paths in .NET 7 | 8 | |**Note**|_Instead of this package_, You may want to consider native support for long paths in Windows 10≥:
[Enable Long Paths in Windows 10, Version 1607, and Later](https://docs.microsoft.com/en-us/windows/win32/fileio/maximum-file-path-limitation?tabs=cmd#enable-long-paths-in-windows-10-version-1607-and-later) 9 | |-|:-| 10 | 11 | Supporting files and directories with a long path is fairly easy with Windows. Unfortunately, other aspects of Windows haven't supported long paths in their entirely. The file system (NTFS), for example, supports long paths quite well; but other things like Command Prompt and Explorer don't. This makes it hard to entirely support long paths in *any* application, let alone in .NET. 12 | 13 | This has been a bit tricky in .NET. Several attempts like [longpaths.codeplex.com](http://longpaths.codeplex.com/) (which a more up to date version has made its way into .NET in classes like [LongPath](http://referencesource.microsoft.com/#mscorlib/system/io/longpath.cs) [LongPathFile](http://referencesource.microsoft.com/#mscorlib/system/io/longpath.cs#734b3020e7ff04fe#references) and [LongPathDirectory](http://referencesource.microsoft.com/#mscorlib/system/io/longpath.cs#ed4ae27b0c89bf61#references). But, these libraries do not seem to support the entire original API (`Path`, `File`, `Directory`) and not all file-related APSs (including `FileInfo`, `DirectoryInfo`, `FileSystemInfo`). 14 | 15 | Often times long path support is an after thought. Usually after you've released something and someone logs bug (e.g. "When I use a path like c:\\users\\*300 chars removed*\\Document.docx your software gives me an error". You can likely support long paths with the above-mentioned libraries, but you end up having to scrub your code and re-design it to suit these new APIs (causing full re-tests, potential new errors, potential regressions, etc.). 16 | 17 | LongPath attempts to rectify that. 18 | 19 | LongPath originally started as a fork of LongPaths on Codeplex; but after initial usage it was clear that much more work was involved to better support long paths. So, I drastically expanded the API scope to include `FileInfo`, `DirectoryInfo`, `FileSystemInfo` to get 100% API coverage supporting long paths. (with one caveat: `Directory.SetCurrentDirectory`, Windows does not support long paths for a current directory). 20 | 21 | LongPaths allows your code to support long paths by providing a drop-in replacement for the following `System.IO` types: `FileInfo`, `DirectoryInfo`, `FileSystemInfo`, `FileInfo`, `DirectoryInfo`, `FileSystemInfo`. You simply reference the Pri.LongPath types you need and you don't need to change your code. 22 | 23 | Obviously to replace only 6 types in a namespaces (`System.IO`) and not the rest is problematic because you're going to need to use some of those other types (`FileNotFoundException`, `FileMode`, etc.)--which means referencing `System.IO` and re-introducing the original 6 types back into your scope. I feft that not having to modify your code was the greater of the two evils. Resolving this conflict is easily solved through aliases (see below). 24 | 25 | **Note:** *the units test currently expect that the user running the tests has explicit rights (read/write/create) on the `TestResults` directory and all child directories. It's usually easiest just to grant full control to your user on the root `LongPath` directory (and let it inherent to children).* 26 | 27 | Usage 28 | ===== 29 | The APIs provided have been made identical to the System.IO APIs as best I could (if not, please log an issue; fork and provide a failing unit test; or fork, fix, add passing test). So, you really only need to force reference to the LongPath types. Referencing the entire `Pri.LongPath` namespace will cause conflicts with `System.IO` because you almost always need to reference something *else* in `System.IO`. To force reference to the `Pri.LongPath` types simply create aliases to them with the `using` directive: 30 | ``` 31 | using Path = Pri.LongPath.Path; 32 | using Directory = Pri.LongPath.Directory; 33 | using DirectoryInfo = Pri.LongPath.DirectoryInfo; 34 | using File = Pri.LongPath.File; 35 | ``` 36 | 37 | `FileSystemInfo` is abstract so you shouldn't need to alias it, but, if you need it (for a cast, for example) alias it as well: 38 | ``` 39 | using Path = Pri.LongPath.Path; 40 | using Directory = Pri.LongPath.Directory; 41 | using DirectoryInfo = Pri.LongPath.DirectoryInfo; 42 | using File = Pri.LongPath.File; 43 | using FileSystemInfo = Pri.LongPath.FileSystemInfo; 44 | ``` 45 | Then, of course, reference the assembly. 46 | 47 | NuGet 48 | ===== 49 | [https://www.nuget.org/packages/Pri.LongPath/](https://www.nuget.org/packages/Pri.LongPath/) 50 | 51 | Known Issues 52 | ============ 53 | 54 | There are no known issues per se. The only API that does not work as expected is `Directory.SetCurrentDirectory` as Windows does not support long paths for a current directory. 55 | 56 | Caveats 57 | ======= 58 | 59 | **TBD** 60 | 61 | How long paths can be created 62 | ============================= 63 | 64 | Long paths can be created *accidentally* in Windows making them very hard to process. 65 | 66 | One way long paths can get created unintentially is via shares. You can create a share to a directory (if it's not the root) such that the shared directory becomes the root of a virtual drive which then allows up to 260 characters of path under normal use. Which means if the shared directory path is 20 chars, the actual path lenghts in the source can now be up to 280 chars--making them *invalid* in many parts of Windows in that source directory 67 | 68 | **to be continued** 69 | -------------------------------------------------------------------------------- /Tests/FileSystemInfoTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Diagnostics; 3 | using System.Linq; 4 | using System.Text; 5 | using NUnit.Framework; 6 | using Directory = Pri.LongPath.Directory; 7 | using Path = Pri.LongPath.Path; 8 | using FileInfo = Pri.LongPath.FileInfo; 9 | using DirectoryInfo = Pri.LongPath.DirectoryInfo; 10 | using File = Pri.LongPath.File; 11 | using FileMode = System.IO.FileMode; 12 | using FileAccess = System.IO.FileAccess; 13 | using FileShare = System.IO.FileShare; 14 | using BinaryWriter = System.IO.BinaryWriter; 15 | using PathTooLongException = System.IO.PathTooLongException; 16 | using FileAttributes = System.IO.FileAttributes; 17 | using IOException = System.IO.IOException; 18 | using SearchOption = System.IO.SearchOption; 19 | 20 | namespace Tests 21 | { 22 | [TestFixture] 23 | public class FileSystemInfoTests 24 | { 25 | private static string rootTestDir; 26 | private static string longPathDirectory; 27 | private static string longPathFilename; 28 | private static string longPathRoot; 29 | private const string Filename = "filename.ext"; 30 | 31 | [SetUp] 32 | public void SetUp() 33 | { 34 | rootTestDir = TestContext.CurrentContext.TestDirectory; 35 | longPathDirectory = Util.MakeLongPath(rootTestDir); 36 | longPathRoot = longPathDirectory.Substring(0, TestContext.CurrentContext.TestDirectory.Length + 1 + longPathDirectory.Substring(rootTestDir.Length + 1).IndexOf('\\')); 37 | Directory.CreateDirectory(longPathDirectory); 38 | Debug.Assert(Directory.Exists(longPathDirectory)); 39 | longPathFilename = new StringBuilder(longPathDirectory).Append(@"\").Append(Filename).ToString(); 40 | using (var writer = File.CreateText(longPathFilename)) 41 | { 42 | writer.WriteLine("test"); 43 | } 44 | Debug.Assert(File.Exists(longPathFilename)); 45 | } 46 | 47 | [Test] 48 | public void TestExtension() 49 | { 50 | var fi = new FileInfo(longPathFilename); 51 | Assert.AreEqual(".ext", fi.Extension); 52 | } 53 | 54 | [Test] 55 | public void NormalizedPathWithSearchPatternIncludesWildcardForFolders() 56 | { 57 | var di = new DirectoryInfo(longPathDirectory); 58 | Assert.IsTrue(di.GetNormalizedPathWithSearchPattern().EndsWith(@"\*")); 59 | } 60 | 61 | [Test] 62 | public void NormalizedPathWithSearchPatternExcludesWildcardForFiles() 63 | { 64 | var fi = new FileInfo(longPathFilename); 65 | Assert.IsFalse(fi.GetNormalizedPathWithSearchPattern().EndsWith(@"\*")); 66 | } 67 | 68 | [TearDown] 69 | public void TearDown() 70 | { 71 | try 72 | { 73 | if (File.Exists(longPathFilename)) 74 | File.Delete(longPathFilename); 75 | } 76 | catch (Exception e) 77 | { 78 | Trace.WriteLine("Exception {0} deleting \"longPathFilename\"", e.ToString()); 79 | throw; 80 | } 81 | finally 82 | { 83 | if(Directory.Exists(longPathRoot)) 84 | Directory.Delete(longPathRoot, true); 85 | } 86 | } 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /Tests/JunctionPointTests.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Source: http://www.codeproject.com/Articles/15633/Manipulating-NTFS-Junction-Points-in-NET 3 | 4 | using System.IO; 5 | using Pri.LongPath; 6 | using NUnit.Framework; 7 | 8 | namespace Tests 9 | { 10 | 11 | using Path = Pri.LongPath.Path; 12 | using Directory = Pri.LongPath.Directory; 13 | using DirectoryInfo = Pri.LongPath.DirectoryInfo; 14 | using File = Pri.LongPath.File; 15 | using FileSystemInfo = Pri.LongPath.FileSystemInfo; 16 | 17 | [TestFixture] 18 | public class JunctionPointTest 19 | { 20 | private string tempFolder; 21 | 22 | [SetUp] 23 | public void CreateTempFolder() 24 | { 25 | tempFolder = Path.GetTempFileName(); 26 | File.Delete(tempFolder); 27 | Directory.CreateDirectory(tempFolder); 28 | } 29 | 30 | [TearDown] 31 | public void DeleteTempFolder() 32 | { 33 | if (tempFolder != null) 34 | { 35 | foreach (FileSystemInfo file in new DirectoryInfo(tempFolder).GetFileSystemInfos()) 36 | { 37 | file.Delete(); 38 | } 39 | 40 | Directory.Delete(tempFolder); 41 | tempFolder = null; 42 | } 43 | } 44 | 45 | [Test] 46 | public void Exists_NoSuchFile() 47 | { 48 | Assert.IsFalse(JunctionPoint.Exists(Path.Combine(tempFolder, "$$$NoSuchFolder$$$"))); 49 | } 50 | 51 | [Test] 52 | public void Exists_IsADirectory() 53 | { 54 | File.Create(Path.Combine(tempFolder, "AFile")).Close(); 55 | 56 | Assert.IsFalse(JunctionPoint.Exists(Path.Combine(tempFolder, "AFile"))); 57 | } 58 | 59 | [Test] 60 | public void Create_VerifyExists_GetTarget_Delete() 61 | { 62 | string targetFolder = Path.Combine(tempFolder, "ADirectory"); 63 | string junctionPoint = Path.Combine(tempFolder, "SymLink"); 64 | 65 | Directory.CreateDirectory(targetFolder); 66 | try 67 | { 68 | File.Create(Path.Combine(targetFolder, "AFile")).Close(); 69 | try 70 | { 71 | // Verify behavior before junction point created. 72 | Assert.IsFalse(File.Exists(Path.Combine(junctionPoint, "AFile")), 73 | "File should not be located until junction point created."); 74 | 75 | Assert.IsFalse(JunctionPoint.Exists(junctionPoint), "Junction point not created yet."); 76 | 77 | // Create junction point and confirm its properties. 78 | JunctionPoint.Create(junctionPoint, targetFolder, overwrite: false); 79 | 80 | Assert.IsTrue(JunctionPoint.Exists(junctionPoint), "Junction point exists now."); 81 | 82 | Assert.AreEqual(targetFolder, JunctionPoint.GetTarget(junctionPoint)); 83 | 84 | Assert.IsTrue(File.Exists(Path.Combine(junctionPoint, "AFile")), 85 | "File should be accessible via the junction point."); 86 | 87 | // Delete junction point. 88 | JunctionPoint.Delete(junctionPoint); 89 | 90 | Assert.IsFalse(JunctionPoint.Exists(junctionPoint), "Junction point should not exist now."); 91 | 92 | Assert.IsFalse(File.Exists(Path.Combine(junctionPoint, "AFile")), 93 | "File should not be located after junction point deleted."); 94 | 95 | Assert.IsFalse(Directory.Exists(junctionPoint), "Ensure directory was deleted too."); 96 | } 97 | finally 98 | { 99 | File.Delete(Path.Combine(targetFolder, "AFile")); 100 | } 101 | } 102 | finally 103 | { 104 | Directory.Delete(targetFolder); 105 | } 106 | } 107 | 108 | [Test] 109 | public void Create_ThrowsIfOverwriteNotSpecifiedAndDirectoryExists() 110 | { 111 | string targetFolder = Path.Combine(tempFolder, "ADirectory"); 112 | string junctionPoint = Path.Combine(tempFolder, "SymLink"); 113 | 114 | Directory.CreateDirectory(junctionPoint); 115 | 116 | Assert.Throws(() => JunctionPoint.Create(junctionPoint, targetFolder, false), 117 | "Directory already exists and overwrite parameter is false."); 118 | } 119 | 120 | [Test] 121 | public void Create_OverwritesIfSpecifiedAndDirectoryExists() 122 | { 123 | string targetFolder = Path.Combine(tempFolder, "ADirectory"); 124 | string junctionPoint = Path.Combine(tempFolder, "SymLink"); 125 | 126 | Directory.CreateDirectory(junctionPoint); 127 | Directory.CreateDirectory(targetFolder); 128 | 129 | JunctionPoint.Create(junctionPoint, targetFolder, true); 130 | 131 | Assert.AreEqual(targetFolder, JunctionPoint.GetTarget(junctionPoint)); 132 | } 133 | 134 | [Test] 135 | public void Create_ThrowsIfTargetDirectoryDoesNotExist() 136 | { 137 | string targetFolder = Path.Combine(tempFolder, "ADirectory"); 138 | string junctionPoint = Path.Combine(tempFolder, "SymLink"); 139 | 140 | Assert.Throws(() => JunctionPoint.Create(junctionPoint, targetFolder, false), 141 | "Target path does not exist or is not a directory."); 142 | } 143 | 144 | [Test] 145 | public void GetTarget_NonExistentJunctionPoint() 146 | { 147 | Assert.Throws(() => JunctionPoint.GetTarget(Path.Combine(tempFolder, "SymLink")), 148 | "Unable to open reparse point."); 149 | } 150 | 151 | [Test] 152 | public void GetTarget_CalledOnADirectoryThatIsNotAJunctionPoint() 153 | { 154 | Assert.Throws(() => JunctionPoint.GetTarget(tempFolder), 155 | "Path is not a junction point."); 156 | } 157 | 158 | [Test] 159 | public void GetTarget_CalledOnAFile() 160 | { 161 | File.Create(Path.Combine(tempFolder, "AFile")).Close(); 162 | 163 | Assert.Throws(() => JunctionPoint.GetTarget(Path.Combine(tempFolder, "AFile")), 164 | "Path is not a junction point."); 165 | } 166 | 167 | [Test] 168 | public void Delete_NonExistentJunctionPoint() 169 | { 170 | // Should do nothing. 171 | JunctionPoint.Delete(Path.Combine(tempFolder, "SymLink")); 172 | } 173 | 174 | [Test] 175 | public void Delete_CalledOnADirectoryThatIsNotAJunctionPoint() 176 | { 177 | Assert.Throws(() => JunctionPoint.Delete(tempFolder), 178 | "Unable to delete junction point."); 179 | } 180 | 181 | [Test] 182 | public void Delete_CalledOnAFile() 183 | { 184 | File.Create(Path.Combine(tempFolder, "AFile")).Close(); 185 | 186 | Assert.Throws(() => JunctionPoint.Delete(Path.Combine(tempFolder, "AFile")), 187 | "Path is not a junction point."); 188 | } 189 | } 190 | } 191 | -------------------------------------------------------------------------------- /Tests/NativeMethods.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Runtime.InteropServices; 3 | using System.Text; 4 | using Microsoft.Win32.SafeHandles; 5 | 6 | namespace Tests 7 | { 8 | internal static class NativeMethods 9 | { 10 | [Flags] 11 | public enum FileSystemFeature : uint 12 | { 13 | /// 14 | /// The file system supports case-sensitive file names. 15 | /// 16 | CaseSensitiveSearch = 1, 17 | /// 18 | /// The file system preserves the case of file names when it places a name on disk. 19 | /// 20 | CasePreservedNames = 2, 21 | /// 22 | /// The file system supports Unicode in file names as they appear on disk. 23 | /// 24 | UnicodeOnDisk = 4, 25 | /// 26 | /// The file system preserves and enforces access control lists (ACL). 27 | /// 28 | PersistentACLS = 8, 29 | /// 30 | /// The file system supports file-based compression. 31 | /// 32 | FileCompression = 0x10, 33 | /// 34 | /// The file system supports disk quotas. 35 | /// 36 | VolumeQuotas = 0x20, 37 | /// 38 | /// The file system supports sparse files. 39 | /// 40 | SupportsSparseFiles = 0x40, 41 | /// 42 | /// The file system supports re-parse points. 43 | /// 44 | SupportsReparsePoints = 0x80, 45 | /// 46 | /// The specified volume is a compressed volume, for example, a DoubleSpace volume. 47 | /// 48 | VolumeIsCompressed = 0x8000, 49 | /// 50 | /// The file system supports object identifiers. 51 | /// 52 | SupportsObjectIDs = 0x10000, 53 | /// 54 | /// The file system supports the Encrypted File System (EFS). 55 | /// 56 | SupportsEncryption = 0x20000, 57 | /// 58 | /// The file system supports named streams. 59 | /// 60 | NamedStreams = 0x40000, 61 | /// 62 | /// The specified volume is read-only. 63 | /// 64 | ReadOnlyVolume = 0x80000, 65 | /// 66 | /// The volume supports a single sequential write. 67 | /// 68 | SequentialWriteOnce = 0x100000, 69 | /// 70 | /// The volume supports transactions. 71 | /// 72 | SupportsTransactions = 0x200000, 73 | } 74 | 75 | [DllImport("Kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)] 76 | public static extern bool GetVolumeInformation( 77 | string RootPathName, 78 | StringBuilder VolumeNameBuffer, 79 | int VolumeNameSize, 80 | out uint VolumeSerialNumber, 81 | out uint MaximumComponentLength, 82 | out FileSystemFeature FileSystemFlags, 83 | StringBuilder FileSystemNameBuffer, 84 | int nFileSystemNameSize); 85 | } 86 | } -------------------------------------------------------------------------------- /Tests/PathTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Diagnostics; 3 | using System.Linq; 4 | using System.Text; 5 | using NUnit.Framework; 6 | using Directory = Pri.LongPath.Directory; 7 | using Path = Pri.LongPath.Path; 8 | using FileInfo = Pri.LongPath.FileInfo; 9 | using DirectoryInfo = Pri.LongPath.DirectoryInfo; 10 | using File = Pri.LongPath.File; 11 | using FileMode = System.IO.FileMode; 12 | using FileAccess = System.IO.FileAccess; 13 | using FileShare = System.IO.FileShare; 14 | using BinaryWriter = System.IO.BinaryWriter; 15 | using PathTooLongException = System.IO.PathTooLongException; 16 | using FileAttributes = System.IO.FileAttributes; 17 | using IOException = System.IO.IOException; 18 | using SearchOption = System.IO.SearchOption; 19 | 20 | namespace Tests 21 | { 22 | [TestFixture] 23 | public class PathTests 24 | { 25 | private static string rootTestDir; 26 | private static string longPathDirectory; 27 | private static string longPathFilename; 28 | private static string longPathRoot; 29 | private const string Filename = "filename.ext"; 30 | 31 | [SetUp] 32 | public void SetUp() 33 | { 34 | rootTestDir = TestContext.CurrentContext.TestDirectory; 35 | longPathDirectory = Util.MakeLongPath(rootTestDir); 36 | longPathRoot = longPathDirectory.Substring(0, TestContext.CurrentContext.TestDirectory.Length + 1 + longPathDirectory.Substring(rootTestDir.Length + 1).IndexOf('\\')); 37 | Directory.CreateDirectory(longPathDirectory); 38 | Debug.Assert(Directory.Exists(longPathDirectory)); 39 | longPathFilename = new StringBuilder(longPathDirectory).Append(@"\").Append(Filename).ToString(); 40 | using (var writer = File.CreateText(longPathFilename)) 41 | { 42 | writer.WriteLine("test"); 43 | } 44 | Debug.Assert(File.Exists(longPathFilename)); 45 | } 46 | 47 | [Test] 48 | public void GetFileNameReturnsNullWithNullParameter() 49 | { 50 | Assert.IsNull(Path.GetFileName(null)); 51 | } 52 | 53 | [Test] 54 | public void TestGetDirectoryNameAtRoot() 55 | { 56 | string path = @"c:\"; 57 | Assert.IsNull(Path.GetDirectoryName(path)); 58 | } 59 | 60 | [Test] 61 | public void TestGetDirectoryNameWithNullPath() 62 | { 63 | Assert.Throws(() => Path.GetDirectoryName(null)); 64 | } 65 | 66 | [Test] 67 | public void GetDirectoryNameOnRelativePath() 68 | { 69 | const string input = @"foo\bar\baz"; 70 | const string expected = @"foo\bar"; 71 | string actual = Path.GetDirectoryName(input); 72 | Assert.AreEqual(expected, actual); 73 | } 74 | 75 | [Test] 76 | public void GetDirectoryNameOnRelativePathWithNoParent() 77 | { 78 | const string input = @"foo"; 79 | const string expected = @""; 80 | string actual = Path.GetDirectoryName(input); 81 | Assert.AreEqual(expected, actual); 82 | } 83 | 84 | [Test] 85 | public void TestGetParentAtRoot() 86 | { 87 | string path = "c:\\"; 88 | Pri.LongPath.DirectoryInfo parent = Directory.GetParent(path); 89 | Assert.IsNull(parent); 90 | } 91 | 92 | [Test] 93 | public void TestLongPathDirectoryName() 94 | { 95 | var x = Path.GetDirectoryName(@"C:\Vault Data\w\M\Access Midstream\9305 Hopeton Stabilizer Upgrades\08 COMMUNICATION\8.1 Transmittals\9305-005 Access Midstream Hopeton - Electrical Panel Wiring dwgs\TM-9305-005-Access Midstream-Hopeton Stabilizer Upgrades-Electrical Panel Wiring-IFC Revised.msg"); 96 | } 97 | 98 | [Test] 99 | public void TestLongPathDirectoryNameWithInvalidChars() 100 | { 101 | Assert.Throws(() => Path.GetDirectoryName(longPathDirectory + '<')); 102 | } 103 | 104 | [Test] 105 | public void TestGetInvalidFileNameChars() 106 | { 107 | Assert.IsTrue(Path.GetInvalidFileNameChars().SequenceEqual(System.IO.Path.GetInvalidFileNameChars())); 108 | } 109 | 110 | [Test] 111 | public void TestGetInvalidPathChars() 112 | { 113 | Assert.IsTrue(Path.GetInvalidPathChars().SequenceEqual(System.IO.Path.GetInvalidPathChars())); 114 | } 115 | 116 | [Test] 117 | public void TestAltDirectorySeparatorChar() 118 | { 119 | Assert.AreEqual(System.IO.Path.AltDirectorySeparatorChar, Path.AltDirectorySeparatorChar); 120 | } 121 | 122 | [Test] 123 | public void TestDirectorySeparatorChar() 124 | { 125 | Assert.AreEqual(System.IO.Path.DirectorySeparatorChar, Path.DirectorySeparatorChar); 126 | } 127 | 128 | [Test] 129 | public void TestIsDirectorySeparator() 130 | { 131 | Assert.IsTrue(Path.IsDirectorySeparator(System.IO.Path.DirectorySeparatorChar)); 132 | Assert.IsTrue(Path.IsDirectorySeparator(System.IO.Path.AltDirectorySeparatorChar)); 133 | } 134 | 135 | [Test] 136 | public void TestGetRootLength() 137 | { 138 | Assert.AreEqual(3, Path.GetRootLength(longPathFilename)); 139 | } 140 | 141 | [Test] 142 | public void TestGetRootLengthWithUnc() 143 | { 144 | Assert.AreEqual(23, Path.GetRootLength(@"\\servername\sharename\dir\filename.exe")); 145 | } 146 | 147 | [Test] 148 | public void TestGetExtension() 149 | { 150 | var tempLongPathFilename = Path.Combine(longPathDirectory, Path.GetRandomFileName()); 151 | Assert.AreEqual(tempLongPathFilename.Substring(tempLongPathFilename.Length - 4, 4), 152 | Path.GetExtension(tempLongPathFilename)); 153 | } 154 | 155 | [Test] 156 | public void TestGetPathRoot() 157 | { 158 | var root = Path.GetPathRoot(longPathDirectory); 159 | Assert.IsNotNull(root); 160 | Assert.AreEqual(3, root.Length); 161 | } 162 | 163 | [Test] 164 | public void TestGetPathRootWithRelativePath() 165 | { 166 | var root = Path.GetPathRoot(@"foo\bar\baz"); 167 | Assert.IsNotNull(root); 168 | Assert.AreEqual(0, root.Length); 169 | } 170 | 171 | [Test] 172 | public void TestGetPathRootWithNullPath() 173 | { 174 | var root = Path.GetPathRoot(null); 175 | Assert.IsNull(root); 176 | } 177 | 178 | [Test] 179 | public void TestNormalizeLongPath() 180 | { 181 | string result = Path.NormalizeLongPath(longPathDirectory); 182 | Assert.IsNotNull(result); 183 | } 184 | 185 | [Test] 186 | public void TestNormalizeLongPathWithJustUncPrefix() 187 | { 188 | Assert.Throws(() => Path.NormalizeLongPath(@"\\")); 189 | } 190 | 191 | [Test] 192 | public void TestNormalizeLongPathWith() 193 | { 194 | string result = Path.NormalizeLongPath(longPathDirectory); 195 | Assert.IsNotNull(result); 196 | } 197 | 198 | 199 | [Test] 200 | public void TestTryNormalizeLongPathWithJustUncPrefix() 201 | { 202 | string path; 203 | Assert.IsFalse(Path.TryNormalizeLongPath(@"\\", out path)); 204 | } 205 | 206 | [Test] 207 | public void TestTryNormalizeLongPat() 208 | { 209 | string path; 210 | Assert.IsTrue(Path.TryNormalizeLongPath(longPathDirectory, out path)); 211 | Assert.IsNotNull(path); 212 | } 213 | 214 | [Test] 215 | public void TestNormalizeLongPathWithEmptyPath() 216 | { 217 | string path; 218 | Assert.IsFalse(Path.TryNormalizeLongPath(String.Empty, out path)); 219 | } 220 | 221 | [Test] 222 | public void TestTryNormalizeLongPathWithNullPath() 223 | { 224 | string path; 225 | Assert.IsFalse(Path.TryNormalizeLongPath(null, out path)); 226 | } 227 | 228 | [Test] 229 | public void TestNormalizeLongPathWithHugePath() 230 | { 231 | var path = @"c:\"; 232 | var component = Util.MakeLongComponent(path); 233 | component = component.Substring(3, component.Length - 3); 234 | while (path.Length < 32000) 235 | { 236 | path = Path.Combine(path, component); 237 | } 238 | Assert.Throws(() => Path.NormalizeLongPath(path)); 239 | } 240 | 241 | [Test] 242 | public void TestCombine() 243 | { 244 | const string expected = @"c:\Windows\system32"; 245 | var actual = Path.Combine(@"c:\Windows", "system32"); 246 | Assert.AreEqual(expected, actual); 247 | } 248 | 249 | [Test] 250 | public void TestCombineRelativePaths() 251 | { 252 | const string expected = @"foo\bar\baz\test"; 253 | string actual = Path.Combine(@"foo\bar", @"baz\test"); 254 | Assert.AreEqual(expected, actual); 255 | } 256 | 257 | [Test] 258 | public void TestCombineWithNull() 259 | { 260 | Assert.Throws(() => Path.Combine(null, null)); 261 | } 262 | 263 | [Test] 264 | public void TestCombineWithEmpthPath1() 265 | { 266 | Assert.AreEqual("test", Path.Combine("test", string.Empty)); 267 | } 268 | 269 | [Test] 270 | public void TestCombineWithEmpthPath2() 271 | { 272 | Assert.AreEqual(@"C:\test", Path.Combine(string.Empty, @"C:\test")); 273 | } 274 | 275 | [Test] 276 | public void TestCombineWithEmpthPath1EndingInSeparator() 277 | { 278 | Assert.AreEqual(@"C:\test\test2", Path.Combine(@"C:\test\", "test2")); 279 | } 280 | 281 | [Test] 282 | public void TestHasExtensionWithExtension() 283 | { 284 | Assert.IsTrue(Path.HasExtension(longPathFilename)); 285 | } 286 | 287 | [Test] 288 | public void TestHasExtensionWithoutExtension() 289 | { 290 | Assert.IsFalse(Path.HasExtension(longPathDirectory)); 291 | } 292 | 293 | [Test] 294 | public void TestGetTempPath() 295 | { 296 | string path = Path.GetTempPath(); 297 | Assert.IsNotNull(path); 298 | Assert.IsTrue(path.Length > 0); 299 | } 300 | 301 | [Test] 302 | public void TestGetTempFilename() 303 | { 304 | string filename = Path.GetTempFileName(); 305 | Assert.IsNotNull(filename); 306 | Assert.IsTrue(filename.Length > 0); 307 | } 308 | 309 | [Test] 310 | public void TestGetFileNameWithoutExtension() 311 | { 312 | var filename = Path.Combine(longPathDirectory, "filename.ext"); 313 | 314 | Assert.AreEqual("filename", Path.GetFileNameWithoutExtension(filename)); 315 | } 316 | 317 | [Test] 318 | public void TestChangeExtension() 319 | { 320 | var filename = Path.Combine(longPathDirectory, "filename.ext"); 321 | var expectedFilenameWithNewExtension = Path.Combine(longPathDirectory, "filename.txt"); 322 | 323 | Assert.AreEqual(expectedFilenameWithNewExtension, Path.ChangeExtension(filename, ".txt")); 324 | } 325 | 326 | [Test] 327 | public void TestCombineArray() 328 | { 329 | var strings = new[] { longPathDirectory, "subdir1", "subdir2", "filename.ext" }; 330 | Assert.AreEqual(Path.Combine(Path.Combine(Path.Combine(longPathDirectory, "subdir1"), "subdir2"), "filename.ext"), Path.Combine(strings)); 331 | } 332 | 333 | [Test] 334 | public void TestCombineArrayOnePath() 335 | { 336 | var strings = new[] { longPathDirectory }; 337 | Assert.AreEqual(longPathDirectory, Path.Combine(strings)); 338 | } 339 | 340 | [Test] 341 | public void TestCombineArrayTwoPaths() 342 | { 343 | var strings = new[] { longPathDirectory, "filename.ext" }; 344 | Assert.AreEqual(Path.Combine(longPathDirectory, "filename.ext"), Path.Combine(strings)); 345 | } 346 | 347 | [Test] 348 | public void TestCombineArrayNullPath() 349 | { 350 | Assert.Throws(() => Path.Combine((string[])null)); 351 | } 352 | 353 | [Test] 354 | public void TestCombineThreePaths() 355 | { 356 | Assert.AreEqual(Path.Combine(Path.Combine(longPathDirectory, "subdir1"), "filename.ext"), 357 | Path.Combine(longPathDirectory, "subdir1", "filename.ext")); 358 | } 359 | 360 | [Test] 361 | public void TestCombineFourPaths() 362 | { 363 | Assert.AreEqual(Path.Combine(Path.Combine(Path.Combine(longPathDirectory, "subdir1"), "subdir2"), "filename.ext"), 364 | Path.Combine(longPathDirectory, "subdir1", "subdir2", "filename.ext")); 365 | } 366 | 367 | [Test] 368 | public void TestCombineTwoPathsOneNull() 369 | { 370 | Assert.Throws(() => Path.Combine(longPathDirectory, null)); 371 | } 372 | 373 | [Test] 374 | public void TestCombineThreePathsOneNull() 375 | { 376 | Assert.Throws(() => Path.Combine(longPathDirectory, "subdir1", null)); 377 | } 378 | 379 | [Test] 380 | public void TestCombineThreePathsTwoNulls() 381 | { 382 | Assert.Throws(() => Path.Combine(longPathDirectory, null, null)); 383 | } 384 | 385 | [Test] 386 | public void TestCombineThreePathsThreeNulls() 387 | { 388 | Assert.Throws(() => Path.Combine(null, null, null)); 389 | } 390 | 391 | [Test] 392 | public void TestCombineFourPathsOneNull() 393 | { 394 | Assert.Throws(() => Path.Combine(longPathDirectory, "subdir1", "subdir2", null)); 395 | } 396 | 397 | [Test] 398 | public void TestCombineFourPathsTwoNull() 399 | { 400 | Assert.Throws(() => Path.Combine(longPathDirectory, "subdir1", null, null)); 401 | } 402 | 403 | [Test] 404 | public void TestCombineFourPathsThreeNulls() 405 | { 406 | Assert.Throws(() => Path.Combine(longPathDirectory, null, null, null)); 407 | } 408 | 409 | [Test] 410 | public void TestCombineFourPathsFourNulls() 411 | { 412 | Assert.Throws(() => Path.Combine(null, null, null, null)); 413 | } 414 | 415 | [TearDown] 416 | public void TearDown() 417 | { 418 | try 419 | { 420 | if (File.Exists(longPathFilename)) 421 | File.Delete(longPathFilename); 422 | } 423 | catch (Exception e) 424 | { 425 | Trace.WriteLine("Exception {0} deleting \"longPathFilename\"", e.ToString()); 426 | throw; 427 | } 428 | finally 429 | { 430 | if(Directory.Exists(longPathRoot)) 431 | Directory.Delete(longPathRoot, true); 432 | } 433 | } 434 | } 435 | } 436 | -------------------------------------------------------------------------------- /Tests/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("Tests")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("Tests")] 13 | [assembly: AssemblyCopyright("Copyright © 2014")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("48a5082f-a02d-44c7-b71a-aa1a77dd75d8")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Build and Revision Numbers 33 | // by using the '*' as shown below: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.0.0")] 36 | [assembly: AssemblyFileVersion("1.0.0.0")] 37 | -------------------------------------------------------------------------------- /Tests/Tests.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | Debug 7 | AnyCPU 8 | {4BE35CEA-F520-4EC7-9621-53FE061C8C35} 9 | Library 10 | Properties 11 | Tests 12 | Tests 13 | v4.8 14 | 512 15 | {3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} 16 | 10.0 17 | $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) 18 | $(ProgramFiles)\Common Files\microsoft shared\VSTT\$(VisualStudioVersion)\UITestExtensionPackages 19 | False 20 | UnitTest 21 | 22 | 23 | 24 | 25 | 26 | true 27 | full 28 | false 29 | bin\Debug\ 30 | DEBUG;TRACE 31 | prompt 32 | 4 33 | 34 | 35 | pdbonly 36 | true 37 | bin\Release\ 38 | TRACE 39 | prompt 40 | 4 41 | 42 | 43 | 44 | ..\packages\NUnit.3.12.0\lib\net45\nunit.framework.dll 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | {df1f23ca-9dc6-4604-8400-6c4b656e0f5d} 83 | Pri.LongPath 84 | 85 | 86 | 87 | 88 | 89 | 90 | False 91 | 92 | 93 | False 94 | 95 | 96 | False 97 | 98 | 99 | False 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. 109 | 110 | 111 | 112 | 113 | 120 | -------------------------------------------------------------------------------- /Tests/UncFileSystemInfoTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Diagnostics; 3 | using System.Linq; 4 | using System.Text; 5 | using NUnit.Framework; 6 | using Directory = Pri.LongPath.Directory; 7 | using Path = Pri.LongPath.Path; 8 | using FileInfo = Pri.LongPath.FileInfo; 9 | using DirectoryInfo = Pri.LongPath.DirectoryInfo; 10 | using File = Pri.LongPath.File; 11 | using FileMode = System.IO.FileMode; 12 | using FileAccess = System.IO.FileAccess; 13 | using FileShare = System.IO.FileShare; 14 | using BinaryWriter = System.IO.BinaryWriter; 15 | using PathTooLongException = System.IO.PathTooLongException; 16 | using FileAttributes = System.IO.FileAttributes; 17 | using IOException = System.IO.IOException; 18 | using SearchOption = System.IO.SearchOption; 19 | 20 | namespace Tests 21 | { 22 | [TestFixture] 23 | public class UncFileSystemInfoTests 24 | { 25 | private static string uncDirectory; 26 | private static string uncFilePath; 27 | private static string directory; 28 | private static string filePath; 29 | private const string Filename = "filename.ext"; 30 | 31 | [SetUp] 32 | public void SetUp() 33 | { 34 | directory = Path.Combine(TestContext.CurrentContext.TestDirectory, "subdir"); 35 | System.IO.Directory.CreateDirectory(directory); 36 | try 37 | { 38 | uncDirectory = UncHelper.GetUncFromPath(directory); 39 | filePath = new StringBuilder(directory).Append(@"\").Append(Filename).ToString(); 40 | uncFilePath = UncHelper.GetUncFromPath(filePath); 41 | using (var writer = System.IO.File.CreateText(filePath)) 42 | { 43 | writer.WriteLine("test"); 44 | } 45 | Debug.Assert(File.Exists(uncFilePath)); 46 | } 47 | catch (Exception) 48 | { 49 | if (System.IO.Directory.Exists(directory)) 50 | System.IO.Directory.Delete(directory, true); 51 | throw; 52 | } 53 | } 54 | 55 | [Test] 56 | public void TestExtension() 57 | { 58 | var fi = new FileInfo(filePath); 59 | Assert.AreEqual(".ext", fi.Extension); 60 | } 61 | 62 | [TearDown] 63 | public void TearDown() 64 | { 65 | try 66 | { 67 | if (File.Exists(filePath)) 68 | File.Delete(filePath); 69 | } 70 | catch (Exception e) 71 | { 72 | Trace.WriteLine("Exception {0} deleting \"filePath\"", e.ToString()); 73 | throw; 74 | } 75 | finally 76 | { 77 | if (Directory.Exists(directory)) 78 | Directory.Delete(directory, true); 79 | } 80 | } 81 | } 82 | } -------------------------------------------------------------------------------- /Tests/UncHelper.cs: -------------------------------------------------------------------------------- 1 | using Pri.LongPath; 2 | 3 | namespace Tests 4 | { 5 | public static class UncHelper 6 | { 7 | public static string GetUncFromPath(string path) 8 | { 9 | var fullPath = Path.GetFullPath(path); 10 | return string.Format(@"\\localhost\{0}$\{1}", fullPath[0], fullPath.Substring(3)); 11 | } 12 | } 13 | } -------------------------------------------------------------------------------- /Tests/UncPathTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Diagnostics; 3 | using System.Linq; 4 | using System.Text; 5 | using NUnit.Framework; 6 | using Directory = Pri.LongPath.Directory; 7 | using Path = Pri.LongPath.Path; 8 | using FileInfo = Pri.LongPath.FileInfo; 9 | using DirectoryInfo = Pri.LongPath.DirectoryInfo; 10 | using File = Pri.LongPath.File; 11 | using FileMode = System.IO.FileMode; 12 | using FileAccess = System.IO.FileAccess; 13 | using FileShare = System.IO.FileShare; 14 | using BinaryWriter = System.IO.BinaryWriter; 15 | using PathTooLongException = System.IO.PathTooLongException; 16 | using FileAttributes = System.IO.FileAttributes; 17 | using IOException = System.IO.IOException; 18 | using SearchOption = System.IO.SearchOption; 19 | 20 | namespace Tests 21 | { 22 | [TestFixture] 23 | public class UncPathTests 24 | { 25 | private static string uncDirectory; 26 | private static string uncFilePath; 27 | private static string directory; 28 | private static string filePath; 29 | private const string Filename = "filename.ext"; 30 | 31 | [SetUp] 32 | public void SetUp() 33 | { 34 | directory = Path.Combine(TestContext.CurrentContext.TestDirectory, "subdir"); 35 | System.IO.Directory.CreateDirectory(directory); 36 | try 37 | { 38 | uncDirectory = UncHelper.GetUncFromPath(directory); 39 | filePath = new StringBuilder(directory).Append(@"\").Append(Filename).ToString(); 40 | uncFilePath = UncHelper.GetUncFromPath(filePath); 41 | using (var writer = System.IO.File.CreateText(filePath)) 42 | { 43 | writer.WriteLine("test"); 44 | } 45 | Debug.Assert(File.Exists(uncFilePath)); 46 | } 47 | catch (Exception ex) 48 | { 49 | Console.WriteLine("Exception " + ex.GetType().FullName + "occured\n" + ex.Message); 50 | if (System.IO.Directory.Exists(directory)) 51 | System.IO.Directory.Delete(directory, true); 52 | throw; 53 | } 54 | } 55 | 56 | [Test] 57 | public void TestGetDirectoryNameAtRoot() 58 | { 59 | string path = @"c:\"; 60 | Assert.IsNull(Path.GetDirectoryName(path)); 61 | } 62 | 63 | [Test] 64 | public void TestGetDirectoryNameWithNullPath() 65 | { 66 | Assert.Throws(() => Path.GetDirectoryName(null)); 67 | } 68 | 69 | [Test] 70 | public void GetDirectoryNameOnRelativePath() 71 | { 72 | const string input = @"foo\bar\baz"; 73 | const string expected = @"foo\bar"; 74 | string actual = Path.GetDirectoryName(input); 75 | Assert.AreEqual(expected, actual); 76 | } 77 | 78 | [Test] 79 | public void GetDirectoryNameOnRelativePathWithNoParent() 80 | { 81 | const string input = @"foo"; 82 | const string expected = @""; 83 | string actual = Path.GetDirectoryName(input); 84 | Assert.AreEqual(expected, actual); 85 | } 86 | 87 | [Test] 88 | public void TestGetParentAtRoot() 89 | { 90 | string path = "c:\\"; 91 | Pri.LongPath.DirectoryInfo parent = Directory.GetParent(path); 92 | Assert.IsNull(parent); 93 | } 94 | 95 | [Test] 96 | public void TestLongPathDirectoryName() 97 | { 98 | var x = Path.GetDirectoryName(@"C:\Vault Data\w\M\Access Midstream\9305 Hopeton Stabilizer Upgrades\08 COMMUNICATION\8.1 Transmittals\9305-005 Access Midstream Hopeton - Electrical Panel Wiring dwgs\TM-9305-005-Access Midstream-Hopeton Stabilizer Upgrades-Electrical Panel Wiring-IFC Revised.msg"); 99 | } 100 | 101 | [Test] 102 | public void TestLongPathDirectoryNameWithInvalidChars() 103 | { 104 | Assert.Throws(() => Path.GetDirectoryName(uncDirectory + '<')); 105 | } 106 | 107 | [Test] 108 | public void TestGetInvalidFileNameChars() 109 | { 110 | Assert.IsTrue(Path.GetInvalidFileNameChars().SequenceEqual(System.IO.Path.GetInvalidFileNameChars())); 111 | } 112 | 113 | [Test] 114 | public void TestGetInvalidPathChars() 115 | { 116 | Assert.IsTrue(Path.GetInvalidPathChars().SequenceEqual(System.IO.Path.GetInvalidPathChars())); 117 | } 118 | 119 | [Test] 120 | public void TestAltDirectorySeparatorChar() 121 | { 122 | Assert.AreEqual(System.IO.Path.AltDirectorySeparatorChar, Path.AltDirectorySeparatorChar); 123 | } 124 | 125 | [Test] 126 | public void TestDirectorySeparatorChar() 127 | { 128 | Assert.AreEqual(System.IO.Path.DirectorySeparatorChar, Path.DirectorySeparatorChar); 129 | } 130 | 131 | [Test] 132 | public void TestIsDirectorySeparator() 133 | { 134 | Assert.IsTrue(Path.IsDirectorySeparator(System.IO.Path.DirectorySeparatorChar)); 135 | Assert.IsTrue(Path.IsDirectorySeparator(System.IO.Path.AltDirectorySeparatorChar)); 136 | } 137 | 138 | [Test] 139 | public void TestGetRootLength() 140 | { 141 | Assert.AreEqual(15, Path.GetRootLength(uncFilePath)); 142 | } 143 | 144 | [Test] 145 | public void TestGetRootLengthWithUnc() 146 | { 147 | Assert.AreEqual(23, Path.GetRootLength(@"\\servername\sharename\dir\filename.exe")); 148 | } 149 | 150 | [Test] 151 | public void TestGetExtension() 152 | { 153 | var tempLongPathFilename = Path.Combine(uncDirectory, Path.GetRandomFileName()); 154 | Assert.AreEqual(tempLongPathFilename.Substring(tempLongPathFilename.Length - 4, 4), 155 | Path.GetExtension(tempLongPathFilename)); 156 | } 157 | 158 | [Test] 159 | public void TestGetPathRoot() 160 | { 161 | var root = Path.GetPathRoot(uncDirectory); 162 | Assert.IsNotNull(root); 163 | Assert.AreEqual(15, root.Length); 164 | Assert.IsTrue(@"\\localhost\C$\".Equals(root, StringComparison.InvariantCultureIgnoreCase)); 165 | } 166 | 167 | [Test] 168 | public void TestGetPathRootWithRelativePath() 169 | { 170 | var root = Path.GetPathRoot(@"foo\bar\baz"); 171 | Assert.IsNotNull(root); 172 | Assert.AreEqual(0, root.Length); 173 | } 174 | 175 | [Test] 176 | public void TestGetPathRootWithNullPath() 177 | { 178 | var root = Path.GetPathRoot(null); 179 | Assert.IsNull(root); 180 | } 181 | 182 | [Test] 183 | public void TestNormalizeLongPath() 184 | { 185 | string result = Path.NormalizeLongPath(uncDirectory); 186 | Assert.IsNotNull(result); 187 | } 188 | 189 | [Test] 190 | public void TestNormalizeLongPathWithJustUncPrefix() 191 | { 192 | Assert.Throws(() => Path.NormalizeLongPath(@"\\")); 193 | } 194 | 195 | [Test] 196 | public void TestNormalizeLongPathWith() 197 | { 198 | string result = Path.NormalizeLongPath(uncDirectory); 199 | Assert.IsNotNull(result); 200 | } 201 | 202 | 203 | [Test] 204 | public void TestTryNormalizeLongPathWithJustUncPrefix() 205 | { 206 | string path; 207 | Assert.IsFalse(Path.TryNormalizeLongPath(@"\\", out path)); 208 | } 209 | 210 | [Test] 211 | public void TestTryNormalizeLongPat() 212 | { 213 | string path; 214 | Assert.IsTrue(Path.TryNormalizeLongPath(uncDirectory, out path)); 215 | Assert.IsNotNull(path); 216 | } 217 | 218 | [Test] 219 | public void TestNormalizeLongPathWithEmptyPath() 220 | { 221 | string path; 222 | Assert.IsFalse(Path.TryNormalizeLongPath(String.Empty, out path)); 223 | } 224 | 225 | [Test] 226 | public void TestTryNormalizeLongPathWithNullPath() 227 | { 228 | string path; 229 | Assert.IsFalse(Path.TryNormalizeLongPath(null, out path)); 230 | } 231 | 232 | [Test] 233 | public void TestNormalizeLongPathWithHugePath() 234 | { 235 | var path = @"c:\"; 236 | var component = Util.MakeLongComponent(path); 237 | component = component.Substring(3, component.Length - 3); 238 | while (path.Length < 32000) 239 | { 240 | path = Path.Combine(path, component); 241 | } 242 | Assert.Throws(() => Path.NormalizeLongPath(path)); 243 | } 244 | 245 | [Test] 246 | public void TestCombine() 247 | { 248 | const string expected = @"c:\Windows\system32"; 249 | var actual = Path.Combine(@"c:\Windows", "system32"); 250 | Assert.AreEqual(expected, actual); 251 | } 252 | 253 | [Test] 254 | public void TestCombineRelativePaths() 255 | { 256 | const string expected = @"foo\bar\baz\test"; 257 | string actual = Path.Combine(@"foo\bar", @"baz\test"); 258 | Assert.AreEqual(expected, actual); 259 | } 260 | 261 | [Test] 262 | public void TestCombineWithNull() 263 | { 264 | Assert.Throws(() => Path.Combine(null, null)); 265 | } 266 | 267 | [Test] 268 | public void TestCombineWithEmpthPath1() 269 | { 270 | Assert.AreEqual("test", Path.Combine("test", string.Empty)); 271 | } 272 | 273 | [Test] 274 | public void TestCombineWithEmpthPath2() 275 | { 276 | Assert.AreEqual(@"C:\test", Path.Combine(string.Empty, @"C:\test")); 277 | } 278 | 279 | [Test] 280 | public void TestCombineWithEmpthPath1EndingInSeparator() 281 | { 282 | Assert.AreEqual(@"C:\test\test2", Path.Combine(@"C:\test\", "test2")); 283 | } 284 | 285 | [Test] 286 | public void TestHasExtensionWithExtension() 287 | { 288 | Assert.IsTrue(Path.HasExtension(uncFilePath)); 289 | } 290 | 291 | [Test] 292 | public void TestHasExtensionWithoutExtension() 293 | { 294 | Assert.IsFalse(Path.HasExtension(uncDirectory)); 295 | } 296 | 297 | [Test] 298 | public void TestGetTempPath() 299 | { 300 | string path = Path.GetTempPath(); 301 | Assert.IsNotNull(path); 302 | Assert.IsTrue(path.Length > 0); 303 | } 304 | 305 | [Test] 306 | public void TestGetTempFilename() 307 | { 308 | string filename = Path.GetTempFileName(); 309 | Assert.IsNotNull(filename); 310 | Assert.IsTrue(filename.Length > 0); 311 | } 312 | 313 | [Test] 314 | public void TestGetFileNameWithoutExtension() 315 | { 316 | var filename = Path.Combine(uncDirectory, "filename.ext"); 317 | 318 | Assert.AreEqual("filename", Path.GetFileNameWithoutExtension(filename)); 319 | } 320 | 321 | [Test] 322 | public void TestChangeExtension() 323 | { 324 | var filename = Path.Combine(uncDirectory, "filename.ext"); 325 | var expectedFilenameWithNewExtension = Path.Combine(uncDirectory, "filename.txt"); 326 | 327 | Assert.AreEqual(expectedFilenameWithNewExtension, Path.ChangeExtension(filename, ".txt")); 328 | } 329 | 330 | [Test] 331 | public void TestCombineArray() 332 | { 333 | var strings = new[] { uncDirectory, "subdir1", "subdir2", "filename.ext" }; 334 | Assert.AreEqual(Path.Combine(Path.Combine(Path.Combine(uncDirectory, "subdir1"), "subdir2"), "filename.ext"), Path.Combine(strings)); 335 | } 336 | 337 | [Test] 338 | public void TestCombineArrayOnePath() 339 | { 340 | var strings = new[] { uncDirectory }; 341 | Assert.AreEqual(uncDirectory, Path.Combine(strings)); 342 | } 343 | 344 | [Test] 345 | public void TestCombineArrayTwoPaths() 346 | { 347 | var strings = new[] { uncDirectory, "filename.ext" }; 348 | Assert.AreEqual(Path.Combine(uncDirectory, "filename.ext"), Path.Combine(strings)); 349 | } 350 | 351 | [Test] 352 | public void TestCombineArrayNullPath() 353 | { 354 | Assert.Throws(() => Path.Combine((string[])null)); 355 | } 356 | 357 | [Test] 358 | public void TestCombineThreePaths() 359 | { 360 | Assert.AreEqual(Path.Combine(Path.Combine(uncDirectory, "subdir1"), "filename.ext"), 361 | Path.Combine(uncDirectory, "subdir1", "filename.ext")); 362 | } 363 | 364 | [Test] 365 | public void TestCombineFourPaths() 366 | { 367 | Assert.AreEqual(Path.Combine(Path.Combine(Path.Combine(uncDirectory, "subdir1"), "subdir2"), "filename.ext"), 368 | Path.Combine(uncDirectory, "subdir1", "subdir2", "filename.ext")); 369 | } 370 | 371 | [Test] 372 | public void TestCombineTwoPathsOneNull() 373 | { 374 | Assert.Throws(() => Path.Combine(uncDirectory, null)); 375 | } 376 | 377 | [Test] 378 | public void TestCombineThreePathsOneNull() 379 | { 380 | Assert.Throws(() => Path.Combine(uncDirectory, "subdir1", null)); 381 | } 382 | 383 | [Test] 384 | public void TestCombineThreePathsTwoNulls() 385 | { 386 | Assert.Throws(() => Path.Combine(uncDirectory, null, null)); 387 | } 388 | 389 | [Test] 390 | public void TestCombineThreePathsThreeNulls() 391 | { 392 | Assert.Throws(() => Path.Combine(null, null, null)); 393 | } 394 | 395 | [Test] 396 | public void TestCombineFourPathsOneNull() 397 | { 398 | Assert.Throws(() => Path.Combine(uncDirectory, "subdir1", "subdir2", null)); 399 | } 400 | 401 | [Test] 402 | public void TestCombineFourPathsTwoNull() 403 | { 404 | Assert.Throws(() => Path.Combine(uncDirectory, "subdir1", null, null)); 405 | } 406 | 407 | [Test] 408 | public void TestCombineFourPathsThreeNulls() 409 | { 410 | Assert.Throws(() => Path.Combine(uncDirectory, null, null, null)); 411 | } 412 | 413 | [Test] 414 | public void TestCombineFourPathsFourNulls() 415 | { 416 | Assert.Throws(() => Path.Combine(null, null, null, null)); 417 | } 418 | 419 | [TearDown] 420 | public void TearDown() 421 | { 422 | try 423 | { 424 | if (System.IO.File.Exists(filePath)) 425 | System.IO.File.Delete(filePath); 426 | } 427 | catch (Exception e) 428 | { 429 | Trace.WriteLine("Exception {0} deleting \"filePath\"", e.ToString()); 430 | throw; 431 | } 432 | finally 433 | { 434 | if (System.IO.Directory.Exists(directory)) 435 | System.IO.Directory.Delete(directory, true); 436 | } 437 | } 438 | } 439 | } 440 | -------------------------------------------------------------------------------- /Tests/UnitTest1.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Diagnostics; 3 | using System.Linq; 4 | using System.Text; 5 | using NUnit.Framework; 6 | using Directory = Pri.LongPath.Directory; 7 | using Path = Pri.LongPath.Path; 8 | using FileInfo = Pri.LongPath.FileInfo; 9 | using DirectoryInfo = Pri.LongPath.DirectoryInfo; 10 | using FileSystemInfo = Pri.LongPath.FileSystemInfo; 11 | using File = Pri.LongPath.File; 12 | using FileMode = System.IO.FileMode; 13 | using FileAccess = System.IO.FileAccess; 14 | using FileShare = System.IO.FileShare; 15 | using BinaryWriter = System.IO.BinaryWriter; 16 | using PathTooLongException = System.IO.PathTooLongException; 17 | using FileAttributes = System.IO.FileAttributes; 18 | using IOException = System.IO.IOException; 19 | using SearchOption = System.IO.SearchOption; 20 | using System.Reflection; 21 | using System.Collections.Generic; 22 | 23 | namespace Tests 24 | { 25 | [TestFixture] 26 | public class UnitTest1 27 | { 28 | private static string longPathDirectory; 29 | private static string longPathRoot; 30 | 31 | [SetUp] 32 | public void SetUp() 33 | { 34 | longPathDirectory = Util.MakeLongPath(TestContext.CurrentContext.TestDirectory); 35 | longPathRoot = longPathDirectory.Substring(0, TestContext.CurrentContext.TestDirectory.Length + 1 + longPathDirectory.Substring(TestContext.CurrentContext.TestDirectory.Length + 1).IndexOf('\\')); 36 | Directory.CreateDirectory(longPathDirectory); 37 | Debug.Assert(Directory.Exists(longPathDirectory)); 38 | } 39 | 40 | [Test] 41 | public void DirectoryNamesWithNull() 42 | { 43 | var di1 = Directory.CreateDirectory("Test"); 44 | const string WeirdPath = "Test\\\0\0\0\\"; 45 | var di = Directory.CreateDirectory(WeirdPath); 46 | Directory.Delete("Test", recursive:true); 47 | } 48 | 49 | [Test] 50 | public void TestProblemWithSystemIoExists() 51 | { 52 | Assert.Throws(() => 53 | { 54 | var filename = new StringBuilder(longPathDirectory).Append(@"\").Append("file4.ext").ToString(); 55 | using (var writer = File.CreateText(filename)) 56 | { 57 | writer.WriteLine("test"); 58 | } 59 | Assert.IsTrue(File.Exists(filename)); 60 | 61 | try 62 | { 63 | using (var fileStream = new System.IO.FileStream(filename, FileMode.Append, FileAccess.Write, FileShare.None)) 64 | using (var bw = new BinaryWriter(fileStream)) 65 | { 66 | bw.Write(10u); 67 | } 68 | 69 | } 70 | finally 71 | { 72 | File.Delete(filename); 73 | } 74 | }); 75 | } 76 | 77 | [Test] 78 | public void WhatHappensWithBclPathGetDiretoryNameAndRelatiePath() 79 | { 80 | var text = System.IO.Path.GetDirectoryName(@"foo\bar\baz"); 81 | Assert.AreEqual(@"foo\bar", text); 82 | } 83 | 84 | private string MemberToMethodString(MemberInfo member) 85 | { 86 | var method = member as MethodInfo; 87 | if (method == null) return member.Name; 88 | ParameterInfo[] parameters = method.GetParameters(); 89 | return string.Format("{0} {1}({2})", method.ReturnType.Name, method.Name, !parameters.Any() ? "" : (parameters.Select(e => e.ParameterType.Name).Aggregate((c, n) => c + ", " + n))); 90 | } 91 | 92 | [Test] 93 | public void FileClassIsComplete() 94 | { 95 | MemberInfo[] systemIoFileMembers = typeof(System.IO.File).GetMembers(BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static); 96 | MemberInfo[] fileMembers = typeof(File).GetMembers(BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static); 97 | string missing = ""; 98 | if (systemIoFileMembers.Length != fileMembers.Length) 99 | { 100 | IEnumerable systemIoFileMemberNames = systemIoFileMembers.OrderBy(e => e.Name).Select(e => MemberToMethodString(e)); 101 | missing = systemIoFileMemberNames.Aggregate((c, n) => c + ", " + n); 102 | IEnumerable fileMemberNames = fileMembers.OrderBy(e => e.Name).Select(e => MemberToMethodString(e)); 103 | missing = fileMemberNames.Aggregate((c, n) => c + ", " + n); 104 | IEnumerable missingCollection = fileMemberNames.Except(systemIoFileMemberNames); 105 | IEnumerable missingCollection2 = systemIoFileMemberNames.Except(fileMemberNames); 106 | missing = (!missingCollection2.Any() ? "" : ("missing: " + missingCollection2.Aggregate((c, n) => c + ", " + n) + Environment.NewLine)) + 107 | (!missingCollection.Any() ? "" : ("extra: " + missingCollection.Aggregate((c, n) => c + ", " + n))); 108 | } 109 | Assert.AreEqual(systemIoFileMembers.Length, fileMembers.Length, missing); 110 | } 111 | 112 | [Test] 113 | public void DirectoryClassIsComplete() 114 | { 115 | MemberInfo[] systemIoDirectoryMembers = typeof(System.IO.Directory).GetMembers(BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static); 116 | MemberInfo[] directoryMembers = typeof(Directory).GetMembers(BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static); 117 | string missing = ""; 118 | if (systemIoDirectoryMembers.Length != directoryMembers.Length) 119 | { 120 | IOrderedEnumerable systemIoDirectoryMembersOrdered = systemIoDirectoryMembers.OrderBy(e => e.Name); 121 | IEnumerable systemIoDirectoryMemberNames = systemIoDirectoryMembersOrdered.Select(e => MemberToMethodString(e)); 122 | IOrderedEnumerable directoryMembersOrdered = directoryMembers.OrderBy(e => e.Name); 123 | IEnumerable directoryMemberNames = directoryMembersOrdered.Select(e => MemberToMethodString(e)); 124 | IEnumerable missingCollection = directoryMemberNames.Except(systemIoDirectoryMemberNames); 125 | IEnumerable missingCollection2 = systemIoDirectoryMemberNames.Except(directoryMemberNames); 126 | missing = (!missingCollection2.Any() ? "" : ("missing: " + missingCollection2.Aggregate((c, n) => c + ", " + n) + Environment.NewLine)) + 127 | (!missingCollection.Any() ? "" : ("extra: " + missingCollection.Aggregate((c, n) => c + ", " + n))); 128 | } 129 | Assert.AreEqual(systemIoDirectoryMembers.Length, directoryMembers.Length, missing); 130 | } 131 | 132 | [Test] 133 | public void FileInfoClassIsComplete() 134 | { 135 | MemberInfo[] systemIoFileInfoMembers = typeof(System.IO.FileInfo).GetMembers(BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static); 136 | MemberInfo[] FileInfoMembers = typeof(FileInfo).GetMembers(BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static); 137 | string missing = ""; 138 | if (systemIoFileInfoMembers.Length != FileInfoMembers.Length) 139 | { 140 | IOrderedEnumerable systemIoFileInfoMembersOrdered = systemIoFileInfoMembers.OrderBy(e => e.Name); 141 | IEnumerable systemIoFileInfoMemberNames = systemIoFileInfoMembersOrdered.Select(e => MemberToMethodString(e)); 142 | IOrderedEnumerable FileInfoMembersOrdered = FileInfoMembers.OrderBy(e => e.Name); 143 | IEnumerable FileInfoMemberNames = FileInfoMembersOrdered.Select(e => MemberToMethodString(e)); 144 | IEnumerable missingCollection = FileInfoMemberNames.Except(systemIoFileInfoMemberNames); 145 | IEnumerable missingCollection2 = systemIoFileInfoMemberNames.Except(FileInfoMemberNames); 146 | missing = (!missingCollection2.Any() ? "" : ("missing: " + missingCollection2.Aggregate((c, n) => c + ", " + n) + Environment.NewLine)) + 147 | (!missingCollection.Any() ? "" : ("extra: " + missingCollection.Aggregate((c, n) => c + ", " + n))); 148 | } 149 | Assert.LessOrEqual(systemIoFileInfoMembers.Length, FileInfoMembers.Length, missing); 150 | } 151 | 152 | [Test] 153 | public void DirectoryInfoClassIsComplete() 154 | { 155 | MemberInfo[] systemIoDirectoryInfoMembers = typeof(System.IO.DirectoryInfo).GetMembers(BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static); 156 | MemberInfo[] DirectoryInfoMembers = typeof(DirectoryInfo).GetMembers(BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static); 157 | string missing = ""; 158 | if (systemIoDirectoryInfoMembers.Length != DirectoryInfoMembers.Length) 159 | { 160 | IOrderedEnumerable systemIoDirectoryInfoMembersOrdered = systemIoDirectoryInfoMembers.OrderBy(e => e.Name); 161 | IEnumerable systemIoDirectoryInfoMemberNames = systemIoDirectoryInfoMembersOrdered.Select(e => MemberToMethodString(e)); 162 | IOrderedEnumerable DirectoryInfoMembersOrdered = DirectoryInfoMembers.OrderBy(e => e.Name); 163 | IEnumerable DirectoryInfoMemberNames = DirectoryInfoMembersOrdered.Select(e => MemberToMethodString(e)); 164 | IEnumerable missingCollection = DirectoryInfoMemberNames.Except(systemIoDirectoryInfoMemberNames); 165 | IEnumerable missingCollection2 = systemIoDirectoryInfoMemberNames.Except(DirectoryInfoMemberNames); 166 | missing = (!missingCollection2.Any() ? "" : ("missing: " + missingCollection2.Aggregate((c, n) => c + ", " + n) + Environment.NewLine)) + 167 | (!missingCollection.Any() ? "" : ("extra: " + missingCollection.Aggregate((c, n) => c + ", " + n))); 168 | } 169 | Assert.LessOrEqual(systemIoDirectoryInfoMembers.Length, DirectoryInfoMembers.Length, missing); 170 | } 171 | 172 | [Test] 173 | public void PathClassIsComplete() 174 | { 175 | MemberInfo[] systemIoPathMembers = typeof(System.IO.Path).GetMembers(BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static); 176 | MemberInfo[] PathMembers = typeof(Path).GetMembers(BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static); 177 | string missing = ""; 178 | if (systemIoPathMembers.Length != PathMembers.Length) 179 | { 180 | IOrderedEnumerable systemIoPathMembersOrdered = systemIoPathMembers.OrderBy(e => e.Name); 181 | IEnumerable systemIoPathMemberNames = systemIoPathMembersOrdered.Select(e => MemberToMethodString(e)); 182 | IOrderedEnumerable PathMembersOrdered = PathMembers.OrderBy(e => e.Name); 183 | IEnumerable PathMemberNames = PathMembersOrdered.Select(e => MemberToMethodString(e)); 184 | IEnumerable missingCollection = PathMemberNames.Except(systemIoPathMemberNames); 185 | IEnumerable missingCollection2 = systemIoPathMemberNames.Except(PathMemberNames); 186 | missing = (!missingCollection2.Any() ? "" : ("missing: " + missingCollection2.Aggregate((c, n) => c + ", " + n) + Environment.NewLine)) + 187 | (!missingCollection.Any() ? "" : ("extra: " + missingCollection.Aggregate((c, n) => c + ", " + n))); 188 | } 189 | Assert.AreEqual(systemIoPathMembers.Length, PathMembers.Length, missing); 190 | } 191 | 192 | [Test] 193 | public void FileSystemInfoClassIsComplete() 194 | { 195 | MemberInfo[] systemIoFileSystemInfoMembers = typeof(System.IO.FileSystemInfo).GetMembers(BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static); 196 | MemberInfo[] FileSystemInfoMembers = typeof(FileSystemInfo).GetMembers(BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static); 197 | string missing = ""; 198 | if (systemIoFileSystemInfoMembers.Length != FileSystemInfoMembers.Length) 199 | { 200 | IOrderedEnumerable systemIoFileSystemInfoMembersOrdered = systemIoFileSystemInfoMembers.OrderBy(e => e.Name); 201 | IEnumerable systemIoFileSystemInfoMemberNames = systemIoFileSystemInfoMembersOrdered.Select(e => MemberToMethodString(e)); 202 | IOrderedEnumerable FileSystemInfoMembersOrdered = FileSystemInfoMembers.OrderBy(e => e.Name); 203 | IEnumerable FileSystemInfoMemberNames = FileSystemInfoMembersOrdered.Select(e => MemberToMethodString(e)); 204 | IEnumerable missingCollection = FileSystemInfoMemberNames.Except(systemIoFileSystemInfoMemberNames); 205 | IEnumerable missingCollection2 = systemIoFileSystemInfoMemberNames.Except(FileSystemInfoMemberNames); 206 | missing = (!missingCollection2.Any() ? "" : ("missing: " + missingCollection2.Aggregate((c, n) => c + ", " + n) + Environment.NewLine)) + 207 | (!missingCollection.Any() ? "" : ("extra: " + missingCollection.Aggregate((c, n) => c + ", " + n))); 208 | } 209 | Assert.LessOrEqual(systemIoFileSystemInfoMembers.Length, FileSystemInfoMembers.Length, missing); 210 | } 211 | 212 | [TearDown] 213 | public void TearDown() 214 | { 215 | Directory.Delete(longPathRoot, true); 216 | Debug.Assert(!Directory.Exists(longPathDirectory)); 217 | } 218 | } 219 | } -------------------------------------------------------------------------------- /Tests/Util.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Diagnostics; 3 | using System.Globalization; 4 | using System.IO; 5 | using System.Linq; 6 | using System.Text; 7 | using File = Pri.LongPath.File; 8 | using FileMode=System.IO.FileMode; 9 | using FileAccess = System.IO.FileAccess; 10 | using FileShare = System.IO.FileShare; 11 | using FileOptions = System.IO.FileOptions; 12 | using Path = Pri.LongPath.Path; 13 | 14 | namespace Tests 15 | { 16 | internal static class Util 17 | { 18 | public static string MakeLongPath(string path) 19 | { 20 | var volname = new StringBuilder(261); 21 | var fsname = new StringBuilder(261); 22 | uint sernum, maxlen; 23 | NativeMethods.FileSystemFeature flags; 24 | if (!NativeMethods.GetVolumeInformation(System.IO.Path.GetPathRoot(path), volname, volname.Capacity, out sernum, out maxlen, out flags, fsname, 25 | fsname.Capacity)) 26 | maxlen = 255; 27 | var componentText = Enumerable.Repeat("0123456789", (int) ((maxlen + 10)/10)) 28 | .Aggregate((c, n) => c + n) 29 | .Substring(0, (int) maxlen); 30 | Debug.Assert(componentText.Length == maxlen); 31 | var directorySeparatorText = Path.DirectorySeparatorChar.ToString(CultureInfo.InvariantCulture); 32 | var endsWith = path.EndsWith(directorySeparatorText); 33 | var resultPath = new StringBuilder(path) 34 | .Append(endsWith ? String.Empty : directorySeparatorText) 35 | .Append(componentText) 36 | .Append(Path.DirectorySeparatorChar) 37 | .Append(componentText) 38 | .ToString(); 39 | Debug.Assert(resultPath.Length > 260); 40 | return resultPath; 41 | } 42 | 43 | public static string MakeLongComponent(string path) 44 | { 45 | var volname = new StringBuilder(261); 46 | var fsname = new StringBuilder(261); 47 | uint sernum, maxlen; 48 | NativeMethods.FileSystemFeature flags; 49 | NativeMethods.GetVolumeInformation(System.IO.Path.GetPathRoot(path), volname, volname.Capacity, out sernum, out maxlen, out flags, fsname, 50 | fsname.Capacity); 51 | var componentText = Enumerable.Repeat("0123456789", (int)((maxlen + 10) / 10)) 52 | .Aggregate((c, n) => c + n) 53 | .Substring(0, (int)maxlen); 54 | Debug.Assert(componentText.Length == maxlen); 55 | var directorySeparatorText = Path.DirectorySeparatorChar.ToString(CultureInfo.InvariantCulture); 56 | var endsWith = path.EndsWith(directorySeparatorText); 57 | var resultPath = new StringBuilder(path) 58 | .Append(endsWith ? String.Empty : directorySeparatorText) 59 | .Append(componentText) 60 | .Append(Path.DirectorySeparatorChar) 61 | .Append(componentText) 62 | .ToString(); 63 | Debug.Assert(resultPath.Length > 260); 64 | return resultPath; 65 | } 66 | 67 | public static string CreateNewFile(string longPathDirectory) 68 | { 69 | var tempLongPathFilename = CreateNewEmptyFile(longPathDirectory); 70 | using (var streamWriter = File.AppendText(tempLongPathFilename)) 71 | { 72 | streamWriter.WriteLine("beginning of file"); 73 | } 74 | 75 | return tempLongPathFilename; 76 | } 77 | 78 | public static string CreateNewEmptyFile(string longPathDirectory) 79 | { 80 | var tempLongPathFilename = new StringBuilder(longPathDirectory).Append(Path.DirectorySeparatorChar).Append(Path.GetRandomFileName()).ToString(); 81 | using (File.Create(tempLongPathFilename)) 82 | { 83 | } 84 | return tempLongPathFilename; 85 | } 86 | 87 | public static string CreateNewEmptyFile(string longPathDirectory, string filename) 88 | { 89 | var tempLongPathFilename = new StringBuilder(longPathDirectory).Append(Path.DirectorySeparatorChar).Append(filename).ToString(); 90 | using (File.Create(tempLongPathFilename)) 91 | { 92 | } 93 | return tempLongPathFilename; 94 | } 95 | 96 | public static bool VerifyContentsOfNewFile(string path) 97 | { 98 | string contents = File.ReadAllText(path); 99 | return "beginning of file" + Environment.NewLine == contents; 100 | } 101 | 102 | public static string CreateNewFileUnicode(string longPathDirectory) 103 | { 104 | var tempLongPathFilename = CreateNewEmptyFile(longPathDirectory); 105 | var fileStream = File.Open(tempLongPathFilename, FileMode.Create, FileAccess.Write, FileShare.Read, 4096, 106 | FileOptions.SequentialScan); 107 | using (var streamWriter = new StreamWriter(fileStream, Encoding.Unicode, 4096, false)) 108 | { 109 | streamWriter.WriteLine("beginning of file"); 110 | } 111 | return tempLongPathFilename; 112 | } 113 | } 114 | } -------------------------------------------------------------------------------- /Tests/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /Tests/tests.runsettings: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 14 | 20 | 21 | 28 | 29 | 30 | 31 | 32 | .*\.dll$ 33 | .*\.exe$ 34 | 35 | 36 | .*CPPUnitTestFramework.* 37 | .*/tests.dll 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | ^Fabrikam\.UnitTest\..* 46 | ^std::.* 47 | ^ATL::.* 48 | .*::__GetTestMethodInfo.* 49 | ^Microsoft::VisualStudio::CppCodeCoverageFramework::.* 50 | ^Microsoft::VisualStudio::CppUnitTestFramework::.* 51 | ^Pri.LongPath.Directory.ThrowIfError.* 52 | ^Pri.LongPath.File.ThrowIfError.* 53 | ^Pri.LongPath.File.SetSecurityInfo.* 54 | ^Pri.LongPath.NativeMethods.* 55 | ^Pri.LongPath.StringExtensions.* 56 | ^Pri.LongPath.Common.* 57 | ^Pri.LongPath.Common.Directory.<.* 58 | ^Pri.LongPath.Privilege.* 59 | ^Pri.LongPath.SafeTokenHandle.* 60 | ^Pri.LongPath.FileSystemInfo.GetObjectData.* 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | ^System.Diagnostics.DebuggerHiddenAttribute$ 69 | ^System.Diagnostics.DebuggerNonUserCodeAttribute$ 70 | ^System.Runtime.CompilerServices.CompilerGeneratedAttribute$ 71 | ^System.CodeDom.Compiler.GeneratedCodeAttribute$ 72 | ^System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverageAttribute$ 73 | 74 | 75 | 76 | 77 | 78 | 79 | .*\\atlmfc\\.* 80 | .*\\vctools\\.* 81 | .*\\public\\sdk\\.* 82 | .*\\microsoft sdks\\.* 83 | .*\\vc\\include\\.* 84 | 85 | 86 | 87 | 88 | 89 | 90 | .*microsoft.* 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | ^B77A5C561934E089$ 99 | ^B03F5F7F11D50A3A$ 100 | ^31BF3856AD364E35$ 101 | ^89845DCD8080CC91$ 102 | ^71E9BCE111E9429C$ 103 | ^8F50407C4E9E73B6$ 104 | ^E361AF139669C375$ 105 | 106 | 107 | 108 | 109 | 110 | True 111 | True 112 | True 113 | False 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | -------------------------------------------------------------------------------- /appveyor.yml: -------------------------------------------------------------------------------- 1 | version: 2.0.{build} 2 | branches: 3 | only: 4 | - master 5 | configuration: Release 6 | assembly_info: 7 | patch: true 8 | file: '**\AssemblyInfo.*' 9 | assembly_version: '{version}' 10 | assembly_file_version: '{version}' 11 | assembly_informational_version: '{version}' 12 | nuget: 13 | project_feed: true 14 | build: 15 | project: LongPath.sln 16 | verbosity: minimal 17 | after_build: 18 | - cmd: >- 19 | if not exist nuget\lib\net40 md nuget\lib\net40 20 | 21 | copy Pri.LongPath.net40\bin\Release\Pri.LongPath.dll nuget\lib\net40 22 | 23 | copy Pri.LongPath.net40\bin\Release\Pri.LongPath.pdb nuget\lib\net40 24 | 25 | copy Pri.LongPath.net40\bin\Release\Pri.LongPath.xml nuget\lib\net40 26 | 27 | if not exist nuget\lib\net20 md nuget\lib\net20 28 | 29 | copy Pri.LongPath.net20\bin\Release\Pri.LongPath.dll nuget\lib\net20 30 | 31 | copy Pri.LongPath.net20\bin\Release\Pri.LongPath.pdb nuget\lib\net20 32 | 33 | copy Pri.LongPath.net20\bin\Release\Pri.LongPath.xml nuget\lib\net20 34 | 35 | if not exist nuget\lib\net45 md nuget\lib\net45 36 | 37 | copy Pri.LongPath\bin\Release\Pri.LongPath.dll nuget\lib\net45 38 | 39 | copy Pri.LongPath\bin\Release\Pri.LongPath.pdb nuget\lib\net45 40 | 41 | copy Pri.LongPath\bin\Release\Pri.LongPath.xml nuget\lib\net45 42 | 43 | nuget.exe pack nuget\LongPath.nuspec -Symbols -version %APPVEYOR_BUILD_VERSION% 44 | artifacts: 45 | - path: '*.nupkg' 46 | name: nuget 47 | deploy: 48 | - provider: NuGet 49 | api_key: 50 | secure: K+dA1tJ3qNFfJ6G484VCFE9igeHb/BGa6tdJVKcaxvHYK6rwPsKrO/c5EahHvxsP 51 | artifact: nuget -------------------------------------------------------------------------------- /cleartestresults.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | if not exist TestResults goto :EOF 3 | setlocal 4 | set folder=TestResults 5 | set MT="%TEMP%\DelFolder_%RANDOM%" 6 | MD %MT% 7 | RoboCopy %MT% %folder% /MIR 8 | RD /S /Q %MT% 9 | RD /S /Q %folder% 10 | endlocal -------------------------------------------------------------------------------- /nuget/LongPath.nuspec: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Pri.LongPath 5 | $version$-alpha1 6 | Peter Ritchie 7 | 8 | Peter Ritchie 9 | LGPL-3.0-only 10 | https://github.com/peteraritchie/LongPath 11 | false 12 | Drop-in library to support long paths in .NET 13 | Fixes for mono 14 | Copyright 2019 15 | long path .NET Framework 16 | 17 | -------------------------------------------------------------------------------- /packages/NUnit.3.12.0/.signature.p7s: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/peteraritchie/LongPath/dc16477841aecc8a57093801dcf23f063fa447e9/packages/NUnit.3.12.0/.signature.p7s -------------------------------------------------------------------------------- /packages/NUnit.3.12.0/LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2019 Charlie Poole, Rob Prouse 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | 21 | -------------------------------------------------------------------------------- /packages/NUnit.3.12.0/NOTICES.txt: -------------------------------------------------------------------------------- 1 | NUnit 3.0 is based on earlier versions of NUnit, with Portions 2 | 3 | Copyright (c) 2002-2014 Charlie Poole or 4 | Copyright (c) 2002-2004 James W. Newkirk, Michael C. Two, Alexei A. Vorontsov or 5 | Copyright (c) 2000-2002 Philip A. Craig 6 | -------------------------------------------------------------------------------- /packages/NUnit.3.12.0/NUnit.3.12.0.nupkg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/peteraritchie/LongPath/dc16477841aecc8a57093801dcf23f063fa447e9/packages/NUnit.3.12.0/NUnit.3.12.0.nupkg -------------------------------------------------------------------------------- /packages/NUnit.3.12.0/build/NUnit.props: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /packages/NUnit.3.12.0/lib/net35/nunit.framework.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/peteraritchie/LongPath/dc16477841aecc8a57093801dcf23f063fa447e9/packages/NUnit.3.12.0/lib/net35/nunit.framework.dll -------------------------------------------------------------------------------- /packages/NUnit.3.12.0/lib/net40/nunit.framework.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/peteraritchie/LongPath/dc16477841aecc8a57093801dcf23f063fa447e9/packages/NUnit.3.12.0/lib/net40/nunit.framework.dll -------------------------------------------------------------------------------- /packages/NUnit.3.12.0/lib/net45/nunit.framework.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/peteraritchie/LongPath/dc16477841aecc8a57093801dcf23f063fa447e9/packages/NUnit.3.12.0/lib/net45/nunit.framework.dll -------------------------------------------------------------------------------- /packages/NUnit.3.12.0/lib/netstandard1.4/nunit.framework.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/peteraritchie/LongPath/dc16477841aecc8a57093801dcf23f063fa447e9/packages/NUnit.3.12.0/lib/netstandard1.4/nunit.framework.dll -------------------------------------------------------------------------------- /packages/NUnit.3.12.0/lib/netstandard2.0/nunit.framework.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/peteraritchie/LongPath/dc16477841aecc8a57093801dcf23f063fa447e9/packages/NUnit.3.12.0/lib/netstandard2.0/nunit.framework.dll -------------------------------------------------------------------------------- /packages/NUnit3TestAdapter.3.15.1/.signature.p7s: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/peteraritchie/LongPath/dc16477841aecc8a57093801dcf23f063fa447e9/packages/NUnit3TestAdapter.3.15.1/.signature.p7s -------------------------------------------------------------------------------- /packages/NUnit3TestAdapter.3.15.1/LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2011-2019 Charlie Poole, 2014-2019 Terje Sandstrom 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | 21 | -------------------------------------------------------------------------------- /packages/NUnit3TestAdapter.3.15.1/NUnit3TestAdapter.3.15.1.nupkg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/peteraritchie/LongPath/dc16477841aecc8a57093801dcf23f063fa447e9/packages/NUnit3TestAdapter.3.15.1/NUnit3TestAdapter.3.15.1.nupkg -------------------------------------------------------------------------------- /packages/NUnit3TestAdapter.3.15.1/build/net35/NUnit3.TestAdapter.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/peteraritchie/LongPath/dc16477841aecc8a57093801dcf23f063fa447e9/packages/NUnit3TestAdapter.3.15.1/build/net35/NUnit3.TestAdapter.dll -------------------------------------------------------------------------------- /packages/NUnit3TestAdapter.3.15.1/build/net35/NUnit3.TestAdapter.pdb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/peteraritchie/LongPath/dc16477841aecc8a57093801dcf23f063fa447e9/packages/NUnit3TestAdapter.3.15.1/build/net35/NUnit3.TestAdapter.pdb -------------------------------------------------------------------------------- /packages/NUnit3TestAdapter.3.15.1/build/net35/NUnit3TestAdapter.props: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | NUnit3.TestAdapter.dll 6 | PreserveNewest 7 | False 8 | 9 | 10 | NUnit3.TestAdapter.pdb 11 | PreserveNewest 12 | False 13 | 14 | 15 | nunit.engine.dll 16 | PreserveNewest 17 | False 18 | 19 | 20 | nunit.engine.api.dll 21 | PreserveNewest 22 | False 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /packages/NUnit3TestAdapter.3.15.1/build/net35/nunit.engine.api.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/peteraritchie/LongPath/dc16477841aecc8a57093801dcf23f063fa447e9/packages/NUnit3TestAdapter.3.15.1/build/net35/nunit.engine.api.dll -------------------------------------------------------------------------------- /packages/NUnit3TestAdapter.3.15.1/build/net35/nunit.engine.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/peteraritchie/LongPath/dc16477841aecc8a57093801dcf23f063fa447e9/packages/NUnit3TestAdapter.3.15.1/build/net35/nunit.engine.dll -------------------------------------------------------------------------------- /packages/NUnit3TestAdapter.3.15.1/build/netcoreapp1.0/NUnit3.TestAdapter.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/peteraritchie/LongPath/dc16477841aecc8a57093801dcf23f063fa447e9/packages/NUnit3TestAdapter.3.15.1/build/netcoreapp1.0/NUnit3.TestAdapter.dll -------------------------------------------------------------------------------- /packages/NUnit3TestAdapter.3.15.1/build/netcoreapp1.0/NUnit3.TestAdapter.pdb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/peteraritchie/LongPath/dc16477841aecc8a57093801dcf23f063fa447e9/packages/NUnit3TestAdapter.3.15.1/build/netcoreapp1.0/NUnit3.TestAdapter.pdb -------------------------------------------------------------------------------- /packages/NUnit3TestAdapter.3.15.1/build/netcoreapp1.0/NUnit3TestAdapter.props: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | NUnit3.TestAdapter.dll 6 | PreserveNewest 7 | False 8 | 9 | 10 | NUnit3.TestAdapter.pdb 11 | PreserveNewest 12 | False 13 | 14 | 15 | nunit.engine.dll 16 | PreserveNewest 17 | False 18 | 19 | 20 | nunit.engine.api.dll 21 | PreserveNewest 22 | False 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /packages/NUnit3TestAdapter.3.15.1/build/netcoreapp1.0/nunit.engine.api.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/peteraritchie/LongPath/dc16477841aecc8a57093801dcf23f063fa447e9/packages/NUnit3TestAdapter.3.15.1/build/netcoreapp1.0/nunit.engine.api.dll -------------------------------------------------------------------------------- /packages/NUnit3TestAdapter.3.15.1/build/netcoreapp1.0/nunit.engine.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/peteraritchie/LongPath/dc16477841aecc8a57093801dcf23f063fa447e9/packages/NUnit3TestAdapter.3.15.1/build/netcoreapp1.0/nunit.engine.dll -------------------------------------------------------------------------------- /packages/NUnit3TestAdapter.3.15.1/build/netcoreapp2.0/NUnit3.TestAdapter.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/peteraritchie/LongPath/dc16477841aecc8a57093801dcf23f063fa447e9/packages/NUnit3TestAdapter.3.15.1/build/netcoreapp2.0/NUnit3.TestAdapter.dll -------------------------------------------------------------------------------- /packages/NUnit3TestAdapter.3.15.1/build/netcoreapp2.0/NUnit3.TestAdapter.pdb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/peteraritchie/LongPath/dc16477841aecc8a57093801dcf23f063fa447e9/packages/NUnit3TestAdapter.3.15.1/build/netcoreapp2.0/NUnit3.TestAdapter.pdb -------------------------------------------------------------------------------- /packages/NUnit3TestAdapter.3.15.1/build/netcoreapp2.0/NUnit3TestAdapter.props: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | NUnit3.TestAdapter.dll 6 | PreserveNewest 7 | False 8 | 9 | 10 | NUnit3.TestAdapter.pdb 11 | PreserveNewest 12 | False 13 | 14 | 15 | nunit.engine.dll 16 | PreserveNewest 17 | False 18 | 19 | 20 | nunit.engine.api.dll 21 | PreserveNewest 22 | False 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /packages/NUnit3TestAdapter.3.15.1/build/netcoreapp2.0/nunit.engine.api.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/peteraritchie/LongPath/dc16477841aecc8a57093801dcf23f063fa447e9/packages/NUnit3TestAdapter.3.15.1/build/netcoreapp2.0/nunit.engine.api.dll -------------------------------------------------------------------------------- /packages/NUnit3TestAdapter.3.15.1/build/netcoreapp2.0/nunit.engine.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/peteraritchie/LongPath/dc16477841aecc8a57093801dcf23f063fa447e9/packages/NUnit3TestAdapter.3.15.1/build/netcoreapp2.0/nunit.engine.dll -------------------------------------------------------------------------------- /runtests.bat: -------------------------------------------------------------------------------- 1 | @choice /m "This will crash Visual Studio with Update 4 and prior, press Y to continue or N to abort" 2 | @if %errorlevel%==2 goto:eof 3 | "C:\Program Files (x86)\Microsoft Visual Studio 12.0\Common7\Tools\..\ide\commonextensions\microsoft\testwindow\vstest.console.exe" Tests\bin\Debug\Tests.dll 4 | @REM call cleartestresults.bat 5 | -------------------------------------------------------------------------------- /test.playlist: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /util/NuGet.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/peteraritchie/LongPath/dc16477841aecc8a57093801dcf23f063fa447e9/util/NuGet.exe --------------------------------------------------------------------------------