現在のドライブがSSDかどうかを判別する方法について、NyaRuRuさんの以下の記事を拝見して、自分の勉強を兼ねて同じことをC#からできないかと考えています。
Win32についてはほとんど素人ながらHasNoSeekPenalty関数の部分を書いてみたのですが、実行したところ、DeviceIoControlで(正確にはMarshal.ThrowExceptionForHRの行)で以下のエラーが出ます。
{"プログラムはコマンドを発行しましたが、コマンドの長さが間違っています。 (HRESULT からの例外: 0x80070018)"}
System.Exception {System.Runtime.InteropServices.COMException}
自力ではかなり詰まってしまったのですが、よろしければアドバイスをいただけないでしょうか。なお、Visual Studio 2012で対象のフレームワークを.NET Framework 4.0として作成し、Windows 8 Pro上で実行しています。
using Microsoft.Win32.SafeHandles;
using System;
using System.Runtime.InteropServices;
namespace DetectSsd
{
class Program
{
private const uint FILE_SHARE_READ = 1;
private const uint FILE_SHARE_WRITE = 2;
private const uint OPEN_EXISTING = 3;
private const uint FILE_ATTRIBUTE_NORMAL = 0x80;
[DllImport("kernel32.dll", ExactSpelling = true, SetLastError = true)]
private static extern SafeFileHandle CreateFileW(
[MarshalAs(UnmanagedType.LPWStr)] string lpFileName,
uint dwDesiredAccess,
uint dwShareMode,
IntPtr lpSecurityAttributes,
uint dwCreationDisposition,
uint dwFlagsAndAttributes,
IntPtr hTemplateFile);
private const uint FILE_DEVICE_MASS_STORAGE = 0x2d;
private const uint IOCTL_STORAGE_BASE = FILE_DEVICE_MASS_STORAGE;
private const uint METHOD_BUFFERED = 0;
private const uint FILE_ANY_ACCESS = 0;
public static uint CTL_CODE(uint DeviceType, uint Function, uint Method, uint Access)
{
return ((DeviceType << 16) | (Access << 14) | (Function << 2) | Method);
}
private const uint StorageDeviceSeekPenaltyProperty = 7;
private const uint PropertyStandardQuery = 0;
[StructLayout(LayoutKind.Sequential)]
private struct STORAGE_PROPERTY_QUERY
{
public uint PropertyId;
public uint QueryType;
}
[StructLayout(LayoutKind.Sequential)]
private struct DEVICE_SEEK_PENALTY_DESCRIPTOR
{
public uint Version;
public uint Size;
public bool IncursSeekPenalty;
}
[DllImport("kernel32.dll", ExactSpelling = true, SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool DeviceIoControl(
SafeFileHandle hDevice,
uint dwIoControlCode,
ref STORAGE_PROPERTY_QUERY lpInBuffer,
uint nInBufferSize,
ref DEVICE_SEEK_PENALTY_DESCRIPTOR lpOutBuffer,
uint nOutBufferSize,
ref uint lpBytesReturned,
IntPtr lpOverlapped);
[DllImport("kernel32", SetLastError = true)]
private static extern bool CloseHandle(SafeFileHandle hDevice);
static void Main(string[] args)
{
HasNoSeekPenalty("\\\\.\\PhysicalDrive0");
Console.ReadLine();
}
private static void HasNoSeekPenalty(string sDrive)
{
SafeFileHandle hDrive = CreateFileW(
sDrive,
0,
FILE_SHARE_READ | FILE_SHARE_WRITE,
IntPtr.Zero,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
IntPtr.Zero);
if (hDrive == null || hDrive.IsInvalid)
{
Console.WriteLine("CreateFile failed.");
}
else
{
Console.WriteLine("CreateFile suceeded.");
};
uint IOCTL_STORAGE_QUERY_PROPERTY = CTL_CODE(IOCTL_STORAGE_BASE, 0x500,
METHOD_BUFFERED, FILE_ANY_ACCESS);
STORAGE_PROPERTY_QUERY query_seek_penalty = new STORAGE_PROPERTY_QUERY()
{
PropertyId = StorageDeviceSeekPenaltyProperty,
QueryType = PropertyStandardQuery
};
DEVICE_SEEK_PENALTY_DESCRIPTOR query_seek_penalty_desc = new DEVICE_SEEK_PENALTY_DESCRIPTOR();
uint returned_query_seek_penalty_size = 0;
bool query_seek_penalty_result = DeviceIoControl(
hDrive,
IOCTL_STORAGE_QUERY_PROPERTY,
ref query_seek_penalty,
(uint)Marshal.SizeOf(query_seek_penalty),
ref query_seek_penalty_desc,
(uint)Marshal.SizeOf(query_seek_penalty_desc),
ref returned_query_seek_penalty_size,
IntPtr.Zero);
CloseHandle(hDrive);
if (query_seek_penalty_result == false)
{
Console.WriteLine("DeviceIoControl failed.");
Marshal.ThrowExceptionForHR(Marshal.GetHRForLastWin32Error());
}
else
{
if (query_seek_penalty_desc.IncursSeekPenalty == false)
{
Console.WriteLine("No seek penalty exists.");
}
else
{
Console.WriteLine("Seek penalty exists.");
};
};
}
}
}