├── .gitattributes
├── .gitignore
├── LICENSE.md
├── README.md
├── Tests
├── DiskDetectorTests.cs
└── Tests.csproj
├── diskdetector-net.sln
└── diskdetector-net
├── Detector.cs
├── Exceptions
└── DetectionFailedException.cs
├── Models
├── DriveInfoExtended.cs
├── HardwareType.cs
└── QueryType.cs
├── Tools
└── Pathing.cs
└── diskdetector-net.csproj
/.gitattributes:
--------------------------------------------------------------------------------
1 | ###############################################################################
2 | # Set default behavior to automatically normalize line endings.
3 | ###############################################################################
4 | * text=auto
5 |
6 | ###############################################################################
7 | # Set default behavior for command prompt diff.
8 | #
9 | # This is need for earlier builds of msysgit that does not have it on by
10 | # default for csharp files.
11 | # Note: This is only used by command line
12 | ###############################################################################
13 | #*.cs diff=csharp
14 |
15 | ###############################################################################
16 | # Set the merge driver for project and solution files
17 | #
18 | # Merging from the command prompt will add diff markers to the files if there
19 | # are conflicts (Merging from VS is not affected by the settings below, in VS
20 | # the diff markers are never inserted). Diff markers may cause the following
21 | # file extensions to fail to load in VS. An alternative would be to treat
22 | # these files as binary and thus will always conflict and require user
23 | # intervention with every merge. To do so, just uncomment the entries below
24 | ###############################################################################
25 | #*.sln merge=binary
26 | #*.csproj merge=binary
27 | #*.vbproj merge=binary
28 | #*.vcxproj merge=binary
29 | #*.vcproj merge=binary
30 | #*.dbproj merge=binary
31 | #*.fsproj merge=binary
32 | #*.lsproj merge=binary
33 | #*.wixproj merge=binary
34 | #*.modelproj merge=binary
35 | #*.sqlproj merge=binary
36 | #*.wwaproj merge=binary
37 |
38 | ###############################################################################
39 | # behavior for image files
40 | #
41 | # image files are treated as binary by default.
42 | ###############################################################################
43 | #*.jpg binary
44 | #*.png binary
45 | #*.gif binary
46 |
47 | ###############################################################################
48 | # diff behavior for common document formats
49 | #
50 | # Convert binary document formats to text before diffing them. This feature
51 | # is only available from the command line. Turn it on by uncommenting the
52 | # entries below.
53 | ###############################################################################
54 | #*.doc diff=astextplain
55 | #*.DOC diff=astextplain
56 | #*.docx diff=astextplain
57 | #*.DOCX diff=astextplain
58 | #*.dot diff=astextplain
59 | #*.DOT diff=astextplain
60 | #*.pdf diff=astextplain
61 | #*.PDF diff=astextplain
62 | #*.rtf diff=astextplain
63 | #*.RTF diff=astextplain
64 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | ## Ignore Visual Studio temporary files, build results, and
2 | ## files generated by popular Visual Studio add-ons.
3 |
4 | # User-specific files
5 | *.suo
6 | *.user
7 | *.userosscache
8 | *.sln.docstates
9 |
10 | # User-specific files (MonoDevelop/Xamarin Studio)
11 | *.userprefs
12 |
13 | # Build results
14 | [Dd]ebug/
15 | [Dd]ebugPublic/
16 | [Rr]elease/
17 | [Rr]eleases/
18 | x64/
19 | x86/
20 | build/
21 | bld/
22 | [Bb]in/
23 | [Oo]bj/
24 |
25 | # Visual Studio 2015 cache/options directory
26 | .vs/
27 |
28 | # MSTest test Results
29 | [Tt]est[Rr]esult*/
30 | [Bb]uild[Ll]og.*
31 |
32 | # NUNIT
33 | *.VisualState.xml
34 | TestResult.xml
35 |
36 | # Build Results of an ATL Project
37 | [Dd]ebugPS/
38 | [Rr]eleasePS/
39 | dlldata.c
40 |
41 | # DNX
42 | project.lock.json
43 | artifacts/
44 |
45 | *_i.c
46 | *_p.c
47 | *_i.h
48 | *.ilk
49 | *.meta
50 | *.obj
51 | *.pch
52 | *.pdb
53 | *.pgc
54 | *.pgd
55 | *.rsp
56 | *.sbr
57 | *.tlb
58 | *.tli
59 | *.tlh
60 | *.tmp
61 | *.tmp_proj
62 | *.log
63 | *.vspscc
64 | *.vssscc
65 | .builds
66 | *.pidb
67 | *.svclog
68 | *.scc
69 |
70 | # Chutzpah Test files
71 | _Chutzpah*
72 |
73 | # Visual C++ cache files
74 | ipch/
75 | *.aps
76 | *.ncb
77 | *.opensdf
78 | *.sdf
79 | *.cachefile
80 |
81 | # Visual Studio profiler
82 | *.psess
83 | *.vsp
84 | *.vspx
85 |
86 | # TFS 2012 Local Workspace
87 | $tf/
88 |
89 | # Guidance Automation Toolkit
90 | *.gpState
91 |
92 | # ReSharper is a .NET coding add-in
93 | _ReSharper*/
94 | *.[Rr]e[Ss]harper
95 | *.DotSettings.user
96 |
97 | # JustCode is a .NET coding add-in
98 | .JustCode
99 |
100 | # TeamCity is a build add-in
101 | _TeamCity*
102 |
103 | # DotCover is a Code Coverage Tool
104 | *.dotCover
105 |
106 | # NCrunch
107 | _NCrunch_*
108 | .*crunch*.local.xml
109 |
110 | # MightyMoose
111 | *.mm.*
112 | AutoTest.Net/
113 |
114 | # Web workbench (sass)
115 | .sass-cache/
116 |
117 | # Installshield output folder
118 | [Ee]xpress/
119 |
120 | # DocProject is a documentation generator add-in
121 | DocProject/buildhelp/
122 | DocProject/Help/*.HxT
123 | DocProject/Help/*.HxC
124 | DocProject/Help/*.hhc
125 | DocProject/Help/*.hhk
126 | DocProject/Help/*.hhp
127 | DocProject/Help/Html2
128 | DocProject/Help/html
129 |
130 | # Click-Once directory
131 | publish/
132 |
133 | # Publish Web Output
134 | *.[Pp]ublish.xml
135 | *.azurePubxml
136 | ## TODO: Comment the next line if you want to checkin your
137 | ## web deploy settings but do note that will include unencrypted
138 | ## passwords
139 | #*.pubxml
140 |
141 | *.publishproj
142 |
143 | # NuGet Packages
144 | *.nupkg
145 | # The packages folder can be ignored because of Package Restore
146 | **/packages/*
147 | # except build/, which is used as an MSBuild target.
148 | !**/packages/build/
149 | # Uncomment if necessary however generally it will be regenerated when needed
150 | #!**/packages/repositories.config
151 |
152 | # Windows Azure Build Output
153 | csx/
154 | *.build.csdef
155 |
156 | # Windows Store app package directory
157 | AppPackages/
158 |
159 | # Visual Studio cache files
160 | # files ending in .cache can be ignored
161 | *.[Cc]ache
162 | # but keep track of directories ending in .cache
163 | !*.[Cc]ache/
164 |
165 | # Others
166 | ClientBin/
167 | [Ss]tyle[Cc]op.*
168 | ~$*
169 | *~
170 | *.dbmdl
171 | *.dbproj.schemaview
172 | *.pfx
173 | *.publishsettings
174 | node_modules/
175 | orleans.codegen.cs
176 |
177 | # RIA/Silverlight projects
178 | Generated_Code/
179 |
180 | # Backup & report files from converting an old project file
181 | # to a newer Visual Studio version. Backup files are not needed,
182 | # because we have git ;-)
183 | _UpgradeReport_Files/
184 | Backup*/
185 | UpgradeLog*.XML
186 | UpgradeLog*.htm
187 |
188 | # SQL Server files
189 | *.mdf
190 | *.ldf
191 |
192 | # Business Intelligence projects
193 | *.rdl.data
194 | *.bim.layout
195 | *.bim_*.settings
196 |
197 | # Microsoft Fakes
198 | FakesAssemblies/
199 |
200 | # Node.js Tools for Visual Studio
201 | .ntvs_analysis.dat
202 |
203 | # Visual Studio 6 build log
204 | *.plg
205 |
206 | # Visual Studio 6 workspace options file
207 | *.opt
208 |
209 | # LightSwitch generated files
210 | GeneratedArtifacts/
211 | _Pvt_Extensions/
212 | ModelManifest.xml
213 |
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2015 - 2019 Christian Hermann & Contributors
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # diskdetector-net [](https://www.nuget.org/packages/diskdetector-net/) [](https://github.com/bitbeans/diskdetector-net/blob/master/LICENSE.md)
2 |
3 |
4 | Class to detect the hardware type (SSD or HDD) of a hard disk on windows based systems.
5 |
6 | This implementation is based on [emoacht.wordpress.com](https://emoacht.wordpress.com/2012/11/06/csharp-ssd/).
7 |
8 |
9 |
10 | ## Installation
11 |
12 | There is a [NuGet package](https://www.nuget.org/packages/diskdetector-net/) available.
13 |
14 | ## Example
15 |
16 | ```csharp
17 | public void DetectFixedDrivesTest()
18 | {
19 | var detectedDrives = Detector.DetectFixedDrives(QueryType.SeekPenalty);
20 | if (detectedDrives.Count != 0)
21 | {
22 | foreach (var detectedDrive in detectedDrives)
23 | {
24 | Console.WriteLine("Drive {0}", detectedDrive.Name);
25 | Console.WriteLine(" File type: {0}", detectedDrive.DriveType);
26 |
27 | Console.WriteLine(" Volume label: {0}", detectedDrive.VolumeLabel);
28 | Console.WriteLine(" File system: {0}", detectedDrive.DriveFormat);
29 | Console.WriteLine(" Letter: {0}", detectedDrive.DriveLetter);
30 | Console.WriteLine(" HardwareType: {0}", detectedDrive.HardwareType);
31 | Console.WriteLine(" Id: {0}", detectedDrive.Id);
32 | Console.WriteLine(
33 | " Available space to current user:{0, 15} bytes",
34 | detectedDrive.AvailableFreeSpace);
35 |
36 | Console.WriteLine(
37 | " Total available space: {0, 15} bytes",
38 | detectedDrive.TotalFreeSpace);
39 |
40 | Console.WriteLine(
41 | " Total size of drive: {0, 15} bytes ",
42 | detectedDrive.TotalSize);
43 | }
44 | }
45 |
46 | /*
47 | Drive C:\
48 | File type: Fixed
49 | Volume label: Windows
50 | File system: NTFS
51 | Letter: C
52 | HardwareType: Ssd
53 | Id: 0
54 | Available space to current user: 23861460992 bytes
55 | Total available space: 23861460992 bytes
56 | Total size of drive: 255505461248 bytes
57 | Drive F:\
58 | File type: Fixed
59 | Volume label: Data
60 | File system: NTFS
61 | Letter: F
62 | HardwareType: Hdd
63 | Id: 1
64 | Available space to current user: 1250781491200 bytes
65 | Total available space: 1250781491200 bytes
66 | Total size of drive: 2000397791232 bytes
67 | */
68 |
69 | ```
70 |
71 | ## License
72 | [MIT](https://en.wikipedia.org/wiki/MIT_License)
--------------------------------------------------------------------------------
/Tests/DiskDetectorTests.cs:
--------------------------------------------------------------------------------
1 | using DiskDetector;
2 | using DiskDetector.Models;
3 |
4 | using Xunit;
5 | using Xunit.Abstractions;
6 |
7 | namespace Tests
8 | {
9 | ///
10 | /// Class to test the DiskDetector.
11 | /// Note: As every device has a different hardware specification,
12 | /// it`s currently hard to write usable tests.
13 | ///
14 | public class DiskDetectorTests
15 | {
16 | private readonly ITestOutputHelper output;
17 |
18 | // As recommended by https://xunit.net/docs/capturing-output
19 | public DiskDetectorTests(ITestOutputHelper output)
20 | {
21 | this.output = output;
22 | }
23 |
24 | [Theory]
25 | [InlineData("C")]
26 | [InlineData("F")]
27 | public void DetectFixedDriveTest(string driveName)
28 | {
29 | var detectedDrive = Detector.DetectFixedDrive(driveName, QueryType.RotationRate, true);
30 | output.WriteLine("Drive {0}", detectedDrive.Name);
31 | output.WriteLine(" File type: {0}", detectedDrive.DriveType);
32 | output.WriteLine(" Volume label: {0}", detectedDrive.VolumeLabel);
33 | output.WriteLine(" UNC Path: {0}", detectedDrive.UncPath);
34 | output.WriteLine(" File system: {0}", detectedDrive.DriveFormat);
35 | output.WriteLine(" Letter: {0}", detectedDrive.DriveLetter);
36 | output.WriteLine(" HardwareType: {0}", detectedDrive.HardwareType);
37 | output.WriteLine(" Id: {0}", detectedDrive.Id);
38 | output.WriteLine(
39 | " Available space to current user:{0, 15} bytes",
40 | detectedDrive.AvailableFreeSpace);
41 |
42 | output.WriteLine(
43 | " Total available space: {0, 15} bytes",
44 | detectedDrive.TotalFreeSpace);
45 |
46 | output.WriteLine(
47 | " Total size of drive: {0, 15} bytes ",
48 | detectedDrive.TotalSize);
49 |
50 | Assert.NotNull(detectedDrive);
51 | }
52 |
53 |
54 | [Fact]
55 | public void DetectFixedDrivesTest()
56 | {
57 | var detectedDrives = Detector.DetectFixedDrives(QueryType.RotationRate, true);
58 | if (detectedDrives.Count != 0)
59 | {
60 | foreach (var detectedDrive in detectedDrives)
61 | {
62 | output.WriteLine("Drive {0}", detectedDrive.Name);
63 | output.WriteLine(" File type: {0}", detectedDrive.DriveType);
64 | output.WriteLine(" Volume label: {0}", detectedDrive.VolumeLabel);
65 | output.WriteLine(" UNC Path: {0}", detectedDrive.UncPath);
66 | output.WriteLine(" File system: {0}", detectedDrive.DriveFormat);
67 | output.WriteLine(" Letter: {0}", detectedDrive.DriveLetter);
68 | output.WriteLine(" HardwareType: {0}", detectedDrive.HardwareType);
69 | output.WriteLine(" Id: {0}", detectedDrive.Id);
70 | output.WriteLine(
71 | " Available space to current user:{0, 15} bytes",
72 | detectedDrive.AvailableFreeSpace);
73 |
74 | output.WriteLine(
75 | " Total available space: {0, 15} bytes",
76 | detectedDrive.TotalFreeSpace);
77 |
78 | output.WriteLine(
79 | " Total size of drive: {0, 15} bytes ",
80 | detectedDrive.TotalSize);
81 | }
82 | }
83 | Assert.True(detectedDrives.Count > 0);
84 | /*
85 | Drive C:\
86 | File type: Fixed
87 | Volume label: Windows
88 | File system: NTFS
89 | Letter: C
90 | HardwareType: Ssd
91 | Id: 0
92 | Available space to current user: 23861460992 bytes
93 | Total available space: 23861460992 bytes
94 | Total size of drive: 255505461248 bytes
95 | Drive F:\
96 | File type: Fixed
97 | Volume label: Data
98 | File system: NTFS
99 | Letter: F
100 | HardwareType: Hdd
101 | Id: 1
102 | Available space to current user: 1250781491200 bytes
103 | Total available space: 1250781491200 bytes
104 | Total size of drive: 2000397791232 bytes
105 | Drive Z:\
106 | File type: Network
107 | Volume label: hubiC
108 | UNC Path: \\ExpanDrive\hubiC
109 | File system: EXFS
110 | Letter: Z
111 | HardwareType: Unknown
112 | Id: -1
113 | Available space to current user: 50000000000 bytes
114 | Total available space: 50000000000 bytes
115 | Total size of drive: 100000000000 bytes
116 | */
117 | }
118 |
119 | [Fact]
120 | public void DetectDriveTest()
121 | {
122 | var detectedDrive = Detector.DetectDrive("Z", QueryType.RotationRate, true);
123 | output.WriteLine("Drive {0}", detectedDrive.Name);
124 | output.WriteLine(" File type: {0}", detectedDrive.DriveType);
125 | output.WriteLine(" Volume label: {0}", detectedDrive.VolumeLabel);
126 | output.WriteLine(" UNC Path: {0}", detectedDrive.UncPath);
127 | output.WriteLine(" File system: {0}", detectedDrive.DriveFormat);
128 | output.WriteLine(" Letter: {0}", detectedDrive.DriveLetter);
129 | output.WriteLine(" HardwareType: {0}", detectedDrive.HardwareType);
130 | output.WriteLine(" Id: {0}", detectedDrive.Id);
131 | output.WriteLine(
132 | " Available space to current user:{0, 15} bytes",
133 | detectedDrive.AvailableFreeSpace);
134 |
135 | output.WriteLine(
136 | " Total available space: {0, 15} bytes",
137 | detectedDrive.TotalFreeSpace);
138 |
139 | output.WriteLine(
140 | " Total size of drive: {0, 15} bytes ",
141 | detectedDrive.TotalSize);
142 |
143 | Assert.NotNull(detectedDrive);
144 | }
145 |
146 | [Fact]
147 | public void DetectDrivesTest()
148 | {
149 | var detectedDrives = Detector.DetectDrives(QueryType.RotationRate, true);
150 | if (detectedDrives.Count != 0)
151 | {
152 | foreach (var detectedDrive in detectedDrives)
153 | {
154 | output.WriteLine("Drive {0}", detectedDrive.Name);
155 | output.WriteLine(" File type: {0}", detectedDrive.DriveType);
156 | output.WriteLine(" Volume label: {0}", detectedDrive.VolumeLabel);
157 | output.WriteLine(" UNC Path: {0}", detectedDrive.UncPath);
158 | output.WriteLine(" File system: {0}", detectedDrive.DriveFormat);
159 | output.WriteLine(" Letter: {0}", detectedDrive.DriveLetter);
160 | output.WriteLine(" HardwareType: {0}", detectedDrive.HardwareType);
161 | output.WriteLine(" Id: {0}", detectedDrive.Id);
162 | output.WriteLine(
163 | " Available space to current user:{0, 15} bytes",
164 | detectedDrive.AvailableFreeSpace);
165 |
166 | output.WriteLine(
167 | " Total available space: {0, 15} bytes",
168 | detectedDrive.TotalFreeSpace);
169 |
170 | output.WriteLine(
171 | " Total size of drive: {0, 15} bytes ",
172 | detectedDrive.TotalSize);
173 | }
174 | }
175 | Assert.True(detectedDrives.Count > 0);
176 | }
177 |
178 | [Fact]
179 | public void DetectHardwareTypeByRotationRate1Test()
180 | {
181 | // Note: Test requires administrator privileges.
182 | var ssd = Detector.DetectHardwareTypeByRotationRate(0);
183 | var hdd = Detector.DetectHardwareTypeByRotationRate(1);
184 | Assert.Equal(HardwareType.Ssd, ssd);
185 | Assert.Equal(HardwareType.Hdd, hdd);
186 | }
187 |
188 | [Fact]
189 | public void DetectHardwareTypeByRotationRate2Test()
190 | {
191 | var driveLetterStringSsd = "C";
192 | char driveLetterSsd = driveLetterStringSsd[0];
193 |
194 | var driveLetterStringHdd = "E";
195 | char driveLetterHdd = driveLetterStringHdd[0];
196 | // Note: Test requires administrator privileges.
197 | var ssd = Detector.DetectHardwareTypeByRotationRate(driveLetterSsd);
198 | var hdd = Detector.DetectHardwareTypeByRotationRate(driveLetterHdd);
199 | Assert.Equal(HardwareType.Ssd, ssd);
200 | Assert.Equal(HardwareType.Hdd, hdd);
201 | }
202 |
203 | [Fact]
204 | public void DetectHardwareTypeBySeekPenalty1Test()
205 | {
206 | var ssd = Detector.DetectHardwareTypeBySeekPenalty(0);
207 | var hdd = Detector.DetectHardwareTypeBySeekPenalty(1);
208 | Assert.Equal(HardwareType.Ssd, ssd);
209 | Assert.Equal(HardwareType.Hdd, hdd);
210 | }
211 |
212 | [Fact]
213 | public void DetectHardwareTypeBySeekPenalty2Test()
214 | {
215 | var driveLetterStringSsd = "C";
216 | char driveLetterSsd = driveLetterStringSsd[0];
217 |
218 | var driveLetterStringHdd = "E";
219 | char driveLetterHdd = driveLetterStringHdd[0];
220 | var ssd = Detector.DetectHardwareTypeBySeekPenalty(driveLetterSsd);
221 | var hdd = Detector.DetectHardwareTypeBySeekPenalty(driveLetterHdd);
222 | Assert.Equal(HardwareType.Ssd, ssd);
223 | Assert.Equal(HardwareType.Hdd, hdd);
224 | }
225 |
226 | [Fact]
227 | public void IsAdministratorTest()
228 | {
229 | var isAdministrator = Detector.IsAdministrator();
230 | Assert.True(isAdministrator);
231 | }
232 | }
233 | }
234 |
--------------------------------------------------------------------------------
/Tests/Tests.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | netcoreapp2.0
5 |
6 | false
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/diskdetector-net.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio 15
4 | VisualStudioVersion = 15.0.27130.2036
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{97365F76-5C9F-445C-BFBD-58B54F17ED53}"
7 | ProjectSection(SolutionItems) = preProject
8 | LICENSE.md = LICENSE.md
9 | README.md = README.md
10 | EndProjectSection
11 | EndProject
12 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "diskdetector-net", "diskdetector-net\diskdetector-net.csproj", "{8D92F7E8-ACB7-4711-8364-F62D2E3D0247}"
13 | EndProject
14 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tests", "Tests\Tests.csproj", "{A00FF10D-4EA9-4774-9332-D121BCEEC24F}"
15 | EndProject
16 | Global
17 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
18 | Debug|Any CPU = Debug|Any CPU
19 | Release|Any CPU = Release|Any CPU
20 | EndGlobalSection
21 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
22 | {8D92F7E8-ACB7-4711-8364-F62D2E3D0247}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
23 | {8D92F7E8-ACB7-4711-8364-F62D2E3D0247}.Debug|Any CPU.Build.0 = Debug|Any CPU
24 | {8D92F7E8-ACB7-4711-8364-F62D2E3D0247}.Release|Any CPU.ActiveCfg = Release|Any CPU
25 | {8D92F7E8-ACB7-4711-8364-F62D2E3D0247}.Release|Any CPU.Build.0 = Release|Any CPU
26 | {A00FF10D-4EA9-4774-9332-D121BCEEC24F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
27 | {A00FF10D-4EA9-4774-9332-D121BCEEC24F}.Debug|Any CPU.Build.0 = Debug|Any CPU
28 | {A00FF10D-4EA9-4774-9332-D121BCEEC24F}.Release|Any CPU.ActiveCfg = Release|Any CPU
29 | {A00FF10D-4EA9-4774-9332-D121BCEEC24F}.Release|Any CPU.Build.0 = Release|Any CPU
30 | EndGlobalSection
31 | GlobalSection(SolutionProperties) = preSolution
32 | HideSolutionNode = FALSE
33 | EndGlobalSection
34 | GlobalSection(ExtensibilityGlobals) = postSolution
35 | SolutionGuid = {9AE50727-AD0A-441C-AF8B-7F8A416278DA}
36 | EndGlobalSection
37 | EndGlobal
38 |
--------------------------------------------------------------------------------
/diskdetector-net/Detector.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.ComponentModel;
4 | using System.IO;
5 | using System.Runtime.InteropServices;
6 | using System.Security;
7 | using System.Security.Principal;
8 | using System.Text;
9 | using DiskDetector.Exceptions;
10 | using DiskDetector.Models;
11 | using DiskDetector.Tools;
12 | using Microsoft.Win32.SafeHandles;
13 |
14 | namespace DiskDetector
15 | {
16 | ///
17 | /// Class to detect the hardware type of a disk.
18 | ///
19 | ///
20 | public static class Detector
21 | {
22 | #region DeviceIoControl (nominal media rotation rate)
23 |
24 | private const uint AtaFlagsDataIn = 0x02;
25 |
26 | #endregion
27 |
28 | ///
29 | /// Check if the application is running as administrator.
30 | ///
31 | /// true if the application is running as administrator otherwise, false
32 | ///
33 | ///
34 | ///
35 | public static bool IsAdministrator()
36 | {
37 | var identity = WindowsIdentity.GetCurrent();
38 | if (identity == null) return false;
39 | var principal = new WindowsPrincipal(identity);
40 | return principal.IsInRole(WindowsBuiltInRole.Administrator);
41 | }
42 |
43 | ///
44 | /// Detect a fixed drive by letter.
45 | ///
46 | /// A valid drive letter.
47 | /// The QueryType.
48 | /// Use QueryType.SeekPenalty as fallback.
49 | /// A list of DriveInfoExtended.
50 | ///
51 | ///
52 | ///
53 | ///
54 | ///
55 | ///
56 | ///
57 | ///
58 | public static DriveInfoExtended DetectFixedDrive(string driveName, QueryType queryType = QueryType.SeekPenalty,
59 | bool useFallbackQuery = true)
60 | {
61 | var driveInfoExtended = new DriveInfoExtended();
62 | var logicalDrive = new DriveInfo(driveName);
63 | if (logicalDrive.DriveType == DriveType.Fixed)
64 | {
65 | if (logicalDrive.IsReady)
66 | {
67 | var tmp = new DriveInfoExtended
68 | {
69 | DriveFormat = logicalDrive.DriveFormat,
70 | VolumeLabel = logicalDrive.VolumeLabel,
71 | Name = logicalDrive.Name,
72 | UncPath = Pathing.GetUNCPath(logicalDrive.Name),
73 | DriveType = logicalDrive.DriveType,
74 | AvailableFreeSpace = logicalDrive.AvailableFreeSpace,
75 | TotalSize = logicalDrive.TotalSize,
76 | TotalFreeSpace = logicalDrive.TotalFreeSpace,
77 | RootDirectory = logicalDrive.RootDirectory,
78 | DriveLetter = logicalDrive.Name.Substring(0, 1).ToCharArray()[0]
79 | };
80 |
81 | var driveId = GetDiskId(tmp.DriveLetter);
82 | if (driveId != -1)
83 | {
84 | tmp.Id = driveId;
85 | if (queryType == QueryType.SeekPenalty)
86 | {
87 | tmp.HardwareType = DetectHardwareTypeBySeekPenalty(driveId);
88 | }
89 | else
90 | {
91 | if (IsAdministrator())
92 | {
93 | tmp.HardwareType = DetectHardwareTypeByRotationRate(driveId);
94 | }
95 | else
96 | {
97 | if (useFallbackQuery)
98 | {
99 | tmp.HardwareType = DetectHardwareTypeBySeekPenalty(driveId);
100 | }
101 | else
102 | {
103 | throw new SecurityException(
104 | "DetectHardwareTypeBySeekPenalty needs administrative access.");
105 | }
106 | }
107 | }
108 | if (tmp.HardwareType != HardwareType.Unknown)
109 | {
110 | driveInfoExtended = tmp;
111 | }
112 | }
113 | }
114 | }
115 | return driveInfoExtended;
116 | }
117 |
118 | ///
119 | /// Detect all fixed drives.
120 | ///
121 | /// The QueryType.
122 | /// Use QueryType.SeekPenalty as fallback.
123 | /// A list of DriveInfoExtended.
124 | ///
125 | ///
126 | ///
127 | ///
128 | ///
129 | ///
130 | ///
131 | ///
132 | public static List DetectFixedDrives(QueryType queryType = QueryType.SeekPenalty,
133 | bool useFallbackQuery = true)
134 | {
135 | var driveInfoExtended = new List();
136 | var logicalDrives = DriveInfo.GetDrives();
137 |
138 | foreach (var logicalDrive in logicalDrives)
139 | {
140 | if (logicalDrive.DriveType == DriveType.Fixed)
141 | {
142 | if (logicalDrive.IsReady)
143 | {
144 | var tmp = new DriveInfoExtended
145 | {
146 | DriveFormat = logicalDrive.DriveFormat,
147 | VolumeLabel = logicalDrive.VolumeLabel,
148 | Name = logicalDrive.Name,
149 | UncPath = Pathing.GetUNCPath(logicalDrive.Name),
150 | DriveType = logicalDrive.DriveType,
151 | AvailableFreeSpace = logicalDrive.AvailableFreeSpace,
152 | TotalSize = logicalDrive.TotalSize,
153 | TotalFreeSpace = logicalDrive.TotalFreeSpace,
154 | RootDirectory = logicalDrive.RootDirectory,
155 | DriveLetter = logicalDrive.Name.Substring(0, 1).ToCharArray()[0]
156 | };
157 |
158 | var driveId = GetDiskId(tmp.DriveLetter);
159 | if (driveId != -1)
160 | {
161 | tmp.Id = driveId;
162 | if (queryType == QueryType.SeekPenalty)
163 | {
164 | tmp.HardwareType = DetectHardwareTypeBySeekPenalty(driveId);
165 | }
166 | else
167 | {
168 | if (IsAdministrator())
169 | {
170 | tmp.HardwareType = DetectHardwareTypeByRotationRate(driveId);
171 | }
172 | else
173 | {
174 | if (useFallbackQuery)
175 | {
176 | tmp.HardwareType = DetectHardwareTypeBySeekPenalty(driveId);
177 | }
178 | else
179 | {
180 | throw new SecurityException(
181 | "DetectHardwareTypeBySeekPenalty needs administrative access.");
182 | }
183 | }
184 | }
185 | if (tmp.HardwareType != HardwareType.Unknown)
186 | {
187 | driveInfoExtended.Add(tmp);
188 | }
189 | }
190 | }
191 | }
192 | }
193 | return driveInfoExtended;
194 | }
195 |
196 | ///
197 | /// Detect a fixed or removable drive.
198 | ///
199 | /// A valid drive letter.
200 | /// The QueryType.
201 | /// Use QueryType.SeekPenalty as fallback.
202 | /// A list of DriveInfoExtended.
203 | ///
204 | ///
205 | ///
206 | ///
207 | ///
208 | ///
209 | ///
210 | ///
211 | public static DriveInfoExtended DetectDrive(string driveName, QueryType queryType = QueryType.SeekPenalty,
212 | bool useFallbackQuery = true)
213 | {
214 | var driveInfoExtended = new DriveInfoExtended();
215 | var logicalDrive = new DriveInfo(driveName);
216 | if (logicalDrive.DriveType == DriveType.Fixed)
217 | {
218 | if (logicalDrive.IsReady)
219 | {
220 | var tmp = new DriveInfoExtended
221 | {
222 | DriveFormat = logicalDrive.DriveFormat,
223 | VolumeLabel = logicalDrive.VolumeLabel,
224 | Name = logicalDrive.Name,
225 | UncPath = Pathing.GetUNCPath(logicalDrive.Name),
226 | DriveType = logicalDrive.DriveType,
227 | AvailableFreeSpace = logicalDrive.AvailableFreeSpace,
228 | TotalSize = logicalDrive.TotalSize,
229 | TotalFreeSpace = logicalDrive.TotalFreeSpace,
230 | RootDirectory = logicalDrive.RootDirectory,
231 | DriveLetter = logicalDrive.Name.Substring(0, 1).ToCharArray()[0]
232 | };
233 |
234 | var driveId = GetDiskId(tmp.DriveLetter);
235 | if (driveId != -1)
236 | {
237 | tmp.Id = driveId;
238 | if (queryType == QueryType.SeekPenalty)
239 | {
240 | tmp.HardwareType = DetectHardwareTypeBySeekPenalty(driveId);
241 | }
242 | else
243 | {
244 | if (IsAdministrator())
245 | {
246 | tmp.HardwareType = DetectHardwareTypeByRotationRate(driveId);
247 | }
248 | else
249 | {
250 | if (useFallbackQuery)
251 | {
252 | tmp.HardwareType = DetectHardwareTypeBySeekPenalty(driveId);
253 | }
254 | else
255 | {
256 | throw new SecurityException(
257 | "DetectHardwareTypeBySeekPenalty needs administrative access.");
258 | }
259 | }
260 | }
261 | driveInfoExtended = tmp;
262 | }
263 | }
264 | }
265 | else
266 | {
267 | if (logicalDrive.IsReady)
268 | {
269 |
270 | var tmp = new DriveInfoExtended
271 | {
272 | DriveFormat = logicalDrive.DriveFormat,
273 | VolumeLabel = logicalDrive.VolumeLabel,
274 | Name = logicalDrive.Name,
275 | UncPath = Pathing.GetUNCPath(logicalDrive.Name),
276 | DriveType = logicalDrive.DriveType,
277 | AvailableFreeSpace = logicalDrive.AvailableFreeSpace,
278 | TotalSize = logicalDrive.TotalSize,
279 | TotalFreeSpace = logicalDrive.TotalFreeSpace,
280 | RootDirectory = logicalDrive.RootDirectory,
281 | DriveLetter = logicalDrive.Name.Substring(0, 1).ToCharArray()[0],
282 | HardwareType = HardwareType.Unknown,
283 | Id = -1
284 | };
285 | driveInfoExtended = tmp;
286 | }
287 | }
288 | return driveInfoExtended;
289 | }
290 |
291 | ///
292 | /// Detect fixed and removable drives.
293 | ///
294 | /// The QueryType.
295 | /// Use QueryType.SeekPenalty as fallback.
296 | /// A list of DriveInfoExtended.
297 | ///
298 | ///
299 | ///
300 | ///
301 | ///
302 | ///
303 | ///
304 | ///
305 | public static List DetectDrives(QueryType queryType = QueryType.SeekPenalty,
306 | bool useFallbackQuery = true)
307 | {
308 | var driveInfoExtended = new List();
309 | var logicalDrives = DriveInfo.GetDrives();
310 |
311 | foreach (var logicalDrive in logicalDrives)
312 | {
313 | if (logicalDrive.DriveType == DriveType.Fixed)
314 | {
315 | if (logicalDrive.IsReady)
316 | {
317 | var tmp = new DriveInfoExtended
318 | {
319 | DriveFormat = logicalDrive.DriveFormat,
320 | VolumeLabel = logicalDrive.VolumeLabel,
321 | Name = logicalDrive.Name,
322 | UncPath = Pathing.GetUNCPath(logicalDrive.Name),
323 | DriveType = logicalDrive.DriveType,
324 | AvailableFreeSpace = logicalDrive.AvailableFreeSpace,
325 | TotalSize = logicalDrive.TotalSize,
326 | TotalFreeSpace = logicalDrive.TotalFreeSpace,
327 | RootDirectory = logicalDrive.RootDirectory,
328 | DriveLetter = logicalDrive.Name.Substring(0, 1).ToCharArray()[0]
329 | };
330 |
331 | var driveId = GetDiskId(tmp.DriveLetter);
332 | if (driveId != -1)
333 | {
334 | tmp.Id = driveId;
335 | if (queryType == QueryType.SeekPenalty)
336 | {
337 | tmp.HardwareType = DetectHardwareTypeBySeekPenalty(driveId);
338 | }
339 | else
340 | {
341 | if (IsAdministrator())
342 | {
343 | tmp.HardwareType = DetectHardwareTypeByRotationRate(driveId);
344 | }
345 | else
346 | {
347 | if (useFallbackQuery)
348 | {
349 | tmp.HardwareType = DetectHardwareTypeBySeekPenalty(driveId);
350 | }
351 | else
352 | {
353 | throw new SecurityException(
354 | "DetectHardwareTypeBySeekPenalty needs administrative access.");
355 | }
356 | }
357 | }
358 | driveInfoExtended.Add(tmp);
359 | }
360 | }
361 | }
362 | else
363 | {
364 | if (logicalDrive.IsReady)
365 | {
366 | var tmp = new DriveInfoExtended
367 | {
368 | DriveFormat = logicalDrive.DriveFormat,
369 | VolumeLabel = logicalDrive.VolumeLabel,
370 | Name = logicalDrive.Name,
371 | UncPath = Pathing.GetUNCPath(logicalDrive.Name),
372 | DriveType = logicalDrive.DriveType,
373 | AvailableFreeSpace = logicalDrive.AvailableFreeSpace,
374 | TotalSize = logicalDrive.TotalSize,
375 | TotalFreeSpace = logicalDrive.TotalFreeSpace,
376 | RootDirectory = logicalDrive.RootDirectory,
377 | DriveLetter = logicalDrive.Name.Substring(0, 1).ToCharArray()[0],
378 | HardwareType = HardwareType.Unknown,
379 | Id = -1
380 | };
381 | driveInfoExtended.Add(tmp);
382 | }
383 | }
384 | }
385 | return driveInfoExtended;
386 | }
387 |
388 | ///
389 | /// DeviceIoControl to get disk extents
390 | ///
391 | ///
392 | ///
393 | ///
394 | ///
395 | ///
396 | ///
397 | ///
398 | ///
399 | ///
400 | [DllImport("kernel32.dll", EntryPoint = "DeviceIoControl",
401 | SetLastError = true)]
402 | [return: MarshalAs(UnmanagedType.Bool)]
403 | private static extern bool DeviceIoControl(
404 | SafeFileHandle hDevice,
405 | uint dwIoControlCode,
406 | IntPtr lpInBuffer,
407 | uint nInBufferSize,
408 | ref VolumeDiskExtents lpOutBuffer,
409 | uint nOutBufferSize,
410 | out uint lpBytesReturned,
411 | IntPtr lpOverlapped);
412 |
413 |
414 | ///
415 | /// Gets the device ID by drive letter.
416 | ///
417 | /// A valid drive letter.
418 | /// The device ID.
419 | ///
420 | ///
421 | ///
422 | private static int GetDiskId(char driveLetter)
423 | {
424 | var di = new DriveInfo(driveLetter.ToString());
425 | if (di.DriveType != DriveType.Fixed)
426 | {
427 | throw new DetectionFailedException(string.Format("This drive is not fixed drive: {0}", driveLetter));
428 | }
429 |
430 | var sDrive = "\\\\.\\" + driveLetter + ":";
431 |
432 | var hDrive = CreateFileW(
433 | sDrive,
434 | 0, // No access to drive
435 | FileShareRead | FileShareWrite,
436 | IntPtr.Zero,
437 | OpenExisting,
438 | FileAttributeNormal,
439 | IntPtr.Zero);
440 |
441 | if (hDrive == null || hDrive.IsInvalid)
442 | {
443 | int lastError = Marshal.GetLastWin32Error();
444 | throw new DetectionFailedException(string.Format("Could not detect Disk Id of {0}",
445 | driveLetter),
446 | new Win32Exception(lastError)
447 | );
448 | }
449 |
450 | var ioctlVolumeGetVolumeDiskExtents = CTL_CODE(
451 | IoctlVolumeBase, 0,
452 | MethodBuffered, FileAnyAccess); // From winioctl.h
453 |
454 | var queryDiskExtents =
455 | new VolumeDiskExtents();
456 |
457 | uint returnedQueryDiskExtentsSize;
458 |
459 | var queryDiskExtentsResult = DeviceIoControl(
460 | hDrive,
461 | ioctlVolumeGetVolumeDiskExtents,
462 | IntPtr.Zero,
463 | 0,
464 | ref queryDiskExtents,
465 | (uint) Marshal.SizeOf(queryDiskExtents),
466 | out returnedQueryDiskExtentsSize,
467 | IntPtr.Zero);
468 |
469 | hDrive.Close();
470 |
471 | if (!queryDiskExtentsResult)
472 | {
473 | int lastError = Marshal.GetLastWin32Error();
474 | const int ERROR_MORE_DATA = 234; //(0xEA) More data is available.
475 | if (lastError != ERROR_MORE_DATA
476 | || (queryDiskExtents.Extents.Length < 1) // We need at least 1
477 | )
478 | {
479 | throw new DetectionFailedException(string.Format("Could not detect Disk Id of {0}",
480 | driveLetter),
481 | new Win32Exception(lastError)
482 | );
483 | }
484 | }
485 |
486 | return (int) queryDiskExtents.Extents[0].DiskNumber;
487 | }
488 |
489 | ///
490 | /// Detect the HardwareType by SeekPenalty.
491 | ///
492 | /// A valid drive letter.
493 | /// The detected HardwareType.
494 | public static HardwareType DetectHardwareTypeBySeekPenalty(char driveLetter)
495 | {
496 | try
497 | {
498 | return DetectHardwareTypeBySeekPenalty(GetDiskId(driveLetter));
499 | }
500 | catch (DetectionFailedException)
501 | {
502 | return HardwareType.Unknown;
503 | }
504 | }
505 |
506 | ///
507 | /// Detect the HardwareType by SeekPenalty.
508 | ///
509 | /// A valid drive Id.
510 | /// The detected HardwareType.
511 | public static HardwareType DetectHardwareTypeBySeekPenalty(int driveId)
512 | {
513 | var physicalDriveName = "\\\\.\\PhysicalDrive" + driveId;
514 |
515 | try
516 | {
517 | return HasDriveSeekPenalty(physicalDriveName) ? HardwareType.Hdd : HardwareType.Ssd;
518 | }
519 | catch (DetectionFailedException)
520 | {
521 | return HardwareType.Unknown;
522 | }
523 | }
524 |
525 | ///
526 | /// Detect the HardwareType by RotationRate.
527 | ///
528 | /// A valid drive letter.
529 | /// The detected HardwareType.
530 | public static HardwareType DetectHardwareTypeByRotationRate(char driveLetter)
531 | {
532 | try
533 | {
534 | return DetectHardwareTypeByRotationRate(GetDiskId(driveLetter));
535 | }
536 | catch (DetectionFailedException)
537 | {
538 | return HardwareType.Unknown;
539 | }
540 | }
541 |
542 | ///
543 | /// Detect the HardwareType by RotationRate.
544 | ///
545 | /// A valid drive Id.
546 | /// The detected HardwareType.
547 | /// Administrative privilege is required!
548 | public static HardwareType DetectHardwareTypeByRotationRate(int driveId)
549 | {
550 | var physicalDriveName = "\\\\.\\PhysicalDrive" + driveId;
551 |
552 | try
553 | {
554 | return HasDriveNominalMediaRotationRate(physicalDriveName) ? HardwareType.Hdd : HardwareType.Ssd;
555 | }
556 | catch (DetectionFailedException)
557 | {
558 | return HardwareType.Unknown;
559 | }
560 | }
561 |
562 | [DllImport("mpr.dll", CharSet = CharSet.Auto, SetLastError = true)]
563 | public static extern int WNetGetConnection([MarshalAs(UnmanagedType.LPTStr)] string localName, [MarshalAs(UnmanagedType.LPTStr)] StringBuilder remoteName, ref int length);
564 |
565 | private static object strToUnc(string path)
566 | {
567 | // This sample code assumes you currently have a drive mapped to p:
568 |
569 | // Find out what remote device a local mapping is to
570 |
571 | int rc = 0;
572 |
573 | // Size for the buffer we will use
574 |
575 | int bsize = 200;
576 |
577 | // Create a new stringbuilder, pre-sized as above
578 |
579 | StringBuilder rname = new StringBuilder(bsize);
580 |
581 | // Call the function
582 |
583 | rc = WNetGetConnection("Z:", rname, ref bsize);
584 |
585 | //https://stackoverflow.com/questions/1088752/how-to-programmatically-discover-mapped-network-drives-on-system-and-their-serve
586 | //http://www.pinvoke.net/default.aspx/mpr/WNetGetConnection.html
587 | int length = 255;
588 | /*2250 (0x8CA)
589 | This network connection does not exist.
590 | 1200 (0x4B0)
591 | The specified device name is invalid.*/
592 | System.Text.StringBuilder UNC = new System.Text.StringBuilder(length);
593 | int q = WNetGetConnection("Z:", UNC, ref length);
594 | return UNC.ToString();
595 | }
596 |
597 | //to get the UNC-Path of a network-drive use something like:
598 |
599 |
600 |
601 | ///
602 | /// CreateFile to get handle to drive.
603 | ///
604 | ///
605 | ///
606 | ///
607 | ///
608 | ///
609 | ///
610 | ///
611 | ///
612 | [DllImport("kernel32.dll", SetLastError = true)]
613 | private static extern SafeFileHandle CreateFileW(
614 | [MarshalAs(UnmanagedType.LPWStr)] string lpFileName,
615 | uint dwDesiredAccess,
616 | uint dwShareMode,
617 | IntPtr lpSecurityAttributes,
618 | uint dwCreationDisposition,
619 | uint dwFlagsAndAttributes,
620 | IntPtr hTemplateFile);
621 |
622 | private static uint CTL_CODE(uint DeviceType, uint Function,
623 | uint Method, uint Access)
624 | {
625 | return ((DeviceType << 16) | (Access << 14) |
626 | (Function << 2) | Method);
627 | }
628 |
629 | ///
630 | /// DeviceIoControl to check no seek penalty.
631 | ///
632 | ///
633 | ///
634 | ///
635 | ///
636 | ///
637 | ///
638 | ///
639 | ///
640 | ///
641 | [DllImport("kernel32.dll", EntryPoint = "DeviceIoControl",
642 | SetLastError = true)]
643 | [return: MarshalAs(UnmanagedType.Bool)]
644 | private static extern bool DeviceIoControl(
645 | SafeFileHandle hDevice,
646 | uint dwIoControlCode,
647 | ref StoragePropertyQuery lpInBuffer,
648 | uint nInBufferSize,
649 | ref DeviceSeekPenaltyDescriptor lpOutBuffer,
650 | uint nOutBufferSize,
651 | out uint lpBytesReturned,
652 | IntPtr lpOverlapped);
653 |
654 | ///
655 | /// DeviceIoControl to check nominal media rotation rate.
656 | ///
657 | ///
658 | ///
659 | ///
660 | ///
661 | ///
662 | ///
663 | ///
664 | ///
665 | ///
666 | [DllImport("kernel32.dll", EntryPoint = "DeviceIoControl",
667 | SetLastError = true)]
668 | [return: MarshalAs(UnmanagedType.Bool)]
669 | private static extern bool DeviceIoControl(
670 | SafeFileHandle hDevice,
671 | uint dwIoControlCode,
672 | ref AtaIdentifyDeviceQuery lpInBuffer,
673 | uint nInBufferSize,
674 | ref AtaIdentifyDeviceQuery lpOutBuffer,
675 | uint nOutBufferSize,
676 | out uint lpBytesReturned,
677 | IntPtr lpOverlapped);
678 |
679 | ///
680 | /// Check if the drive has a seek penalty.
681 | ///
682 | /// A valid physicalDriveName.
683 | /// true if the drive has a seek penalty otherwise, false
684 | /// Administrative privilege is required!
685 | ///
686 | private static bool HasDriveSeekPenalty(string physicalDriveName)
687 | {
688 | var hDrive = CreateFileW(
689 | physicalDriveName,
690 | 0, // No access to drive
691 | FileShareRead | FileShareWrite,
692 | IntPtr.Zero,
693 | OpenExisting,
694 | FileAttributeNormal,
695 | IntPtr.Zero);
696 |
697 | if (hDrive == null || hDrive.IsInvalid)
698 | {
699 | int lastError = Marshal.GetLastWin32Error();
700 | throw new DetectionFailedException(string.Format("Could not detect SeekPenalty of {0}",
701 | physicalDriveName),
702 | new Win32Exception(lastError)
703 | );
704 | }
705 |
706 | var ioctlStorageQueryProperty = CTL_CODE(
707 | IoctlStorageBase, 0x500,
708 | MethodBuffered, FileAnyAccess); // From winioctl.h
709 |
710 | var querySeekPenalty =
711 | new StoragePropertyQuery
712 | {
713 | PropertyId = StorageDeviceSeekPenaltyProperty,
714 | QueryType = PropertyStandardQuery
715 | };
716 |
717 | var querySeekPenaltyDesc =
718 | new DeviceSeekPenaltyDescriptor();
719 |
720 | uint returnedQuerySeekPenaltySize;
721 |
722 | var querySeekPenaltyResult = DeviceIoControl(
723 | hDrive,
724 | ioctlStorageQueryProperty,
725 | ref querySeekPenalty,
726 | (uint) Marshal.SizeOf(querySeekPenalty),
727 | ref querySeekPenaltyDesc,
728 | (uint) Marshal.SizeOf(querySeekPenaltyDesc),
729 | out returnedQuerySeekPenaltySize,
730 | IntPtr.Zero);
731 |
732 | hDrive.Close();
733 |
734 | if (querySeekPenaltyResult == false)
735 | {
736 | int lastError = Marshal.GetLastWin32Error();
737 | throw new DetectionFailedException(string.Format("Could not detect SeekPenalty of {0}",
738 | physicalDriveName),
739 | new Win32Exception(lastError)
740 | );
741 | }
742 | if (querySeekPenaltyDesc.IncursSeekPenalty == false)
743 | {
744 | //This drive has NO SEEK penalty
745 | return false;
746 | }
747 | //This drive has SEEK penalty
748 | return true;
749 | }
750 |
751 | ///
752 | /// Check if the drive has a nominal media rotation rate.
753 | ///
754 | /// A valid physicalDriveName.
755 | /// true if the drive has a media rotation rate otherwise, false
756 | /// Administrative privilege is required!
757 | ///
758 | private static bool HasDriveNominalMediaRotationRate(string physicalDriveName)
759 | {
760 | var hDrive = CreateFileW(
761 | physicalDriveName,
762 | GenericRead | GenericWrite, // Administrative privilege is required
763 | FileShareRead | FileShareWrite,
764 | IntPtr.Zero,
765 | OpenExisting,
766 | FileAttributeNormal,
767 | IntPtr.Zero);
768 |
769 | if (hDrive == null || hDrive.IsInvalid)
770 | {
771 | int lastError = Marshal.GetLastWin32Error();
772 | throw new DetectionFailedException(string.Format("Could not detect NominalMediaRotationRate of {0}",
773 | physicalDriveName),
774 | new Win32Exception(lastError)
775 | );
776 | }
777 |
778 | var ioctlAtaPassThrough = CTL_CODE(
779 | IoctlScsiBase, 0x040b, MethodBuffered,
780 | FileReadAccess | FileWriteAccess); // From ntddscsi.h
781 |
782 | var idQuery = new AtaIdentifyDeviceQuery {data = new ushort[256]};
783 |
784 | idQuery.header.Length = (ushort) Marshal.SizeOf(idQuery.header);
785 | idQuery.header.AtaFlags = (ushort) AtaFlagsDataIn;
786 | idQuery.header.DataTransferLength =
787 | (uint) (idQuery.data.Length*2); // Size of "data" in bytes
788 | idQuery.header.TimeOutValue = 3; // Sec
789 | idQuery.header.DataBufferOffset = Marshal.OffsetOf(
790 | typeof (AtaIdentifyDeviceQuery), "data");
791 | idQuery.header.PreviousTaskFile = new byte[8];
792 | idQuery.header.CurrentTaskFile = new byte[8];
793 | idQuery.header.CurrentTaskFile[6] = 0xec; // ATA IDENTIFY DEVICE
794 |
795 | uint retvalSize;
796 |
797 | var result = DeviceIoControl(
798 | hDrive,
799 | ioctlAtaPassThrough,
800 | ref idQuery,
801 | (uint) Marshal.SizeOf(idQuery),
802 | ref idQuery,
803 | (uint) Marshal.SizeOf(idQuery),
804 | out retvalSize,
805 | IntPtr.Zero);
806 |
807 | hDrive.Close();
808 |
809 | if (result == false)
810 | {
811 | int lastError = Marshal.GetLastWin32Error();
812 | throw new DetectionFailedException(string.Format("Could not detect NominalMediaRotationRate of {0}",
813 | physicalDriveName),
814 | new Win32Exception(lastError)
815 | );
816 | }
817 | // Word index of nominal media rotation rate
818 | // (1 means non-rotate device)
819 | const int kNominalMediaRotRateWordIndex = 217;
820 |
821 | if (idQuery.data[kNominalMediaRotRateWordIndex] == 1)
822 | {
823 | return false;
824 | }
825 | return true;
826 | }
827 |
828 | ///
829 | /// For DeviceIoControl to get disk extents
830 | ///
831 | [StructLayout(LayoutKind.Sequential)]
832 | private struct DiskExtent
833 | {
834 | public readonly uint DiskNumber;
835 | public readonly long StartingOffset;
836 | public readonly long ExtentLength;
837 | }
838 |
839 | [StructLayout(LayoutKind.Sequential)]
840 | private struct VolumeDiskExtents
841 | {
842 | public readonly uint NumberOfDiskExtents;
843 | [MarshalAs(UnmanagedType.ByValArray)] public readonly DiskExtent[] Extents;
844 | }
845 |
846 | [StructLayout(LayoutKind.Sequential)]
847 | private struct StoragePropertyQuery
848 | {
849 | public uint PropertyId;
850 | public uint QueryType;
851 | [MarshalAs(UnmanagedType.ByValArray, SizeConst = 1)] public readonly byte[] AdditionalParameters;
852 | }
853 |
854 | [StructLayout(LayoutKind.Sequential)]
855 | private struct DeviceSeekPenaltyDescriptor
856 | {
857 | public readonly uint Version;
858 | public readonly uint Size;
859 | [MarshalAs(UnmanagedType.U1)] public readonly bool IncursSeekPenalty;
860 | }
861 |
862 | [StructLayout(LayoutKind.Sequential)]
863 | private struct AtaPassThroughEx
864 | {
865 | public ushort Length;
866 | public ushort AtaFlags;
867 | public readonly byte PathId;
868 | public readonly byte TargetId;
869 | public readonly byte Lun;
870 | public readonly byte ReservedAsUchar;
871 | public uint DataTransferLength;
872 | public uint TimeOutValue;
873 | public readonly uint ReservedAsUlong;
874 | public IntPtr DataBufferOffset;
875 | [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)] public byte[] PreviousTaskFile;
876 | [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)] public byte[] CurrentTaskFile;
877 | }
878 |
879 | [StructLayout(LayoutKind.Sequential)]
880 | private struct AtaIdentifyDeviceQuery
881 | {
882 | public AtaPassThroughEx header;
883 | [MarshalAs(UnmanagedType.ByValArray, SizeConst = 256)] public ushort[] data;
884 | }
885 |
886 | #region CreateFile (get handle to drive)
887 |
888 | private const uint GenericRead = 0x80000000;
889 | private const uint GenericWrite = 0x40000000;
890 | private const uint FileShareRead = 0x00000001;
891 | private const uint FileShareWrite = 0x00000002;
892 | private const uint OpenExisting = 3;
893 | private const uint FileAttributeNormal = 0x00000080;
894 |
895 | #endregion
896 |
897 | #region Control Codes
898 |
899 | private const uint FileDeviceMassStorage = 0x0000002d;
900 | private const uint IoctlStorageBase = FileDeviceMassStorage;
901 | private const uint FileDeviceController = 0x00000004;
902 | private const uint IoctlScsiBase = FileDeviceController;
903 | private const uint MethodBuffered = 0;
904 | private const uint FileAnyAccess = 0;
905 | private const uint FileReadAccess = 0x00000001;
906 | private const uint FileWriteAccess = 0x00000002;
907 | private const uint IoctlVolumeBase = 0x00000056;
908 |
909 | #endregion
910 |
911 | #region DeviceIoControl (seek penalty)
912 |
913 | private const uint StorageDeviceSeekPenaltyProperty = 7;
914 | private const uint PropertyStandardQuery = 0;
915 |
916 | #endregion
917 | }
918 |
919 | internal class NativeMethods
920 | {
921 | ///
922 | /// The type of structure that the function stores in the buffer.
923 | ///
924 | public enum InfoLevel
925 | {
926 | ///
927 | /// The function stores a structure in the
928 | /// buffer.
929 | ///
930 | UniversalName = 1,
931 |
932 | ///
933 | /// The function stores a REMOTE_NAME_INFO structure in the buffer.
934 | ///
935 | ///
936 | /// Using this level will throw an .
937 | ///
938 | RemoteName = 2
939 | }
940 |
941 | ///
942 | /// The function
943 | /// takes a drive-based path for a network resource and returns an information
944 | /// structure that contains a more universal form of the name.
945 | ///
946 | /// A pointer to a constant null-terminated string that
947 | /// is a drive-based path for a network resource.
948 | /// The type of structure that the function stores in
949 | /// the buffer pointed to by the parameter.
950 | /// A pointer to a buffer that receives the structure
951 | /// specified by the parameter.
952 | /// A pointer to a variable that specifies the size,
953 | /// in bytes, of the buffer pointed to by the parameter.
954 | /// If the function succeeds, the return value is .
955 | [DllImport("mpr.dll", CharSet = CharSet.Auto)]
956 | public static extern int WNetGetUniversalName(
957 | string lpLocalPath,
958 | InfoLevel dwInfoLevel,
959 | ref UNIVERSAL_NAME_INFO lpBuffer,
960 | ref int lpBufferSize);
961 |
962 | ///
963 | /// The function
964 | /// takes a drive-based path for a network resource and returns an information
965 | /// structure that contains a more universal form of the name.
966 | ///
967 | /// A pointer to a constant null-terminated string that
968 | /// is a drive-based path for a network resource.
969 | /// The type of structure that the function stores in
970 | /// the buffer pointed to by the parameter.
971 | /// A pointer to a buffer that receives the structure
972 | /// specified by the parameter.
973 | /// A pointer to a variable that specifies the size,
974 | /// in bytes, of the buffer pointed to by the parameter.
975 | /// If the function succeeds, the return value is .
976 | [DllImport("mpr.dll", CharSet = CharSet.Auto)]
977 | public static extern int WNetGetUniversalName(
978 | string lpLocalPath,
979 | InfoLevel dwInfoLevel,
980 | IntPtr lpBuffer,
981 | ref int lpBufferSize);
982 |
983 | ///
984 | /// The structure contains a pointer to a
985 | /// Universal Naming Convention (UNC) name string for a network resource.
986 | ///
987 | [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
988 | public struct UNIVERSAL_NAME_INFO
989 | {
990 | ///
991 | /// Pointer to the null-terminated UNC name string that identifies a
992 | /// network resource.
993 | ///
994 | [MarshalAs(UnmanagedType.LPTStr)]
995 | public string lpUniversalName;
996 | }
997 | }
998 | }
--------------------------------------------------------------------------------
/diskdetector-net/Exceptions/DetectionFailedException.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace DiskDetector.Exceptions
4 | {
5 | public class DetectionFailedException : Exception
6 | {
7 | public DetectionFailedException()
8 | {
9 | }
10 |
11 | public DetectionFailedException(string message)
12 | : base(message)
13 | {
14 | }
15 |
16 | public DetectionFailedException(string message, Exception inner)
17 | : base(message, inner)
18 | {
19 | }
20 | }
21 | }
--------------------------------------------------------------------------------
/diskdetector-net/Models/DriveInfoExtended.cs:
--------------------------------------------------------------------------------
1 | using System.IO;
2 |
3 | namespace DiskDetector.Models
4 | {
5 | public class DriveInfoExtended
6 | {
7 | public string Name { get; set; }
8 | public char DriveLetter { get; set; }
9 | public DriveType DriveType { get; set; }
10 | public int Id { get; set; }
11 | public string VolumeLabel { get; set; }
12 | public string DriveFormat { get; set; }
13 | public long TotalFreeSpace { get; set; }
14 | public long TotalSize { get; set; }
15 | public long AvailableFreeSpace { get; set; }
16 | public HardwareType HardwareType { get; set; }
17 | public DirectoryInfo RootDirectory { get; set; }
18 | public string UncPath { get; set; }
19 | }
20 | }
--------------------------------------------------------------------------------
/diskdetector-net/Models/HardwareType.cs:
--------------------------------------------------------------------------------
1 | namespace DiskDetector.Models
2 | {
3 | ///
4 | /// Possible HardwareTypes.
5 | ///
6 | public enum HardwareType
7 | {
8 | ///
9 | /// Unknown hardware type.
10 | ///
11 | Unknown,
12 |
13 | ///
14 | /// Hard Disk Drive.
15 | ///
16 | Hdd,
17 |
18 | ///
19 | /// Solid State Drive.
20 | ///
21 | Ssd
22 | }
23 | }
--------------------------------------------------------------------------------
/diskdetector-net/Models/QueryType.cs:
--------------------------------------------------------------------------------
1 | namespace DiskDetector.Models
2 | {
3 | ///
4 | /// Possible QueryTypes.
5 | ///
6 | public enum QueryType
7 | {
8 | ///
9 | /// Detect the HardwareType by SeekPenalty.
10 | ///
11 | SeekPenalty,
12 |
13 | ///
14 | /// Detect the HardwareType by RotationRate.
15 | ///
16 | RotationRate
17 | }
18 | }
--------------------------------------------------------------------------------
/diskdetector-net/Tools/Pathing.cs:
--------------------------------------------------------------------------------
1 | using System.IO;
2 | using System.Runtime.InteropServices;
3 | using System.Text;
4 |
5 | namespace DiskDetector.Tools
6 | {
7 | public static class Pathing
8 | {
9 | [DllImport("mpr.dll", CharSet = CharSet.Unicode, SetLastError = true)]
10 | public static extern int WNetGetConnection(
11 | [MarshalAs(UnmanagedType.LPTStr)] string localName,
12 | [MarshalAs(UnmanagedType.LPTStr)] StringBuilder remoteName,
13 | ref int length);
14 |
15 | ///
16 | /// Given a path, returns the UNC path or the original. (No exceptions
17 | /// are raised by this function directly). For example, "P:\2008-02-29"
18 | /// might return: "\\networkserver\Shares\Photos\2008-02-09"
19 | ///
20 | /// The path to convert to a UNC Path
21 | ///
22 | /// A UNC path. If a network drive letter is specified, the
23 | /// drive letter is converted to a UNC or network path. If the
24 | /// originalPath cannot be converted, it is returned unchanged.
25 | ///
26 | ///
27 | public static string GetUNCPath(string originalPath)
28 | {
29 | var sb = new StringBuilder(512);
30 | var size = sb.Capacity;
31 |
32 | // look for the {LETTER}: combination ...
33 | if (originalPath.Length > 2 && originalPath[1] == ':')
34 | {
35 | // don't use char.IsLetter here - as that can be misleading
36 | // the only valid drive letters are a-z && A-Z.
37 | var c = originalPath[0];
38 | if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z'))
39 | {
40 | var error = WNetGetConnection(originalPath.Substring(0, 2),
41 | sb, ref size);
42 | if (error == 0)
43 | {
44 | var dir = new DirectoryInfo(originalPath);
45 |
46 | var path = Path.GetFullPath(originalPath)
47 | .Substring(Path.GetPathRoot(originalPath).Length);
48 | return Path.Combine(sb.ToString().TrimEnd(), path);
49 | }
50 | }
51 | }
52 |
53 | return originalPath;
54 | }
55 | }
56 | }
--------------------------------------------------------------------------------
/diskdetector-net/diskdetector-net.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | netstandard2.0
5 | DiskDetector
6 | DiskDetector
7 | 0.3.2.0
8 | 0.3.2.0
9 | Copyright © Christian Hermann 2015 - 2019
10 | diskdetector-net
11 | 0.3.3
12 | https://github.com/bitbeans/diskdetector-net/blob/master/LICENSE.md
13 | https://github.com/bitbeans/diskdetector-net
14 | https://github.com/bitbeans/diskdetector-net
15 | disk detection hdd ssd driveinfo
16 | diskdetector-net
17 |
18 | Christian Hermann & Contributors
19 | true
20 | Class to detect the hardware type (SSD or HDD) of a hard disk on windows based systems.
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
--------------------------------------------------------------------------------