├── .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 | [](https://ci.appveyor.com/project/peteraritchie/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
--------------------------------------------------------------------------------