QA@IT
«質問へ戻る

質問を投稿

SSDかどうかをC#から判別する方法

現在のドライブがSSDかどうかを判別する方法について、NyaRuRuさんの以下の記事を拝見して、自分の勉強を兼ねて同じことをC#からできないかと考えています。

SSDなら動作を変えるアプリケーションを作る

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.");
                };
            };
        }
    }
}
現在のドライブがSSDかどうかを判別する方法について、NyaRuRuさんの以下の記事を拝見して、自分の勉強を兼ねて同じことをC#からできないかと考えています。

[SSDなら動作を変えるアプリケーションを作る](http://nyaruru.hatenablog.com/entry/2012/09/29/063829)

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.");
                    };
                };
            }
        }
    }