none
DeviceIoControlで1306が発生する RRS feed

  • 質問

  • お世話になります。

    IntelのNVMe SSDのSMART値を取得するプログラムをC#で作成しているのですが、

    DeviceIoControlの結果で1306エラーが発生してしまいます。

    #ソースコード抜粋

    データ受け渡しに使用する構造体。

    [StructLayout(LayoutKind.Sequential)] struct SCSI_PASS_THROUGH { public ushort length; public byte scsiStatus; public byte pathId; public byte targetId; public byte lun; public byte cdbLength; public byte senseInfoLength; public byte dataIn; public uint dataTransferLength; public uint timeOutValue; public ulong dataBufferOffset; public uint senseInfoOffset; [MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)] public byte[] cdb; public void Initialize() { length = 0; scsiStatus = 0; pathId = 0; targetId = 0; lun = 0; cdbLength = 0; senseInfoLength = 0; dataIn = 0; dataTransferLength = 0; timeOutValue = 0; dataBufferOffset = 0; senseInfoOffset = 0; cdb = new byte[16]; } }; [StructLayout(LayoutKind.Sequential)] struct SCSI_PASS_THROUGH_WITH_BUFFERS24 { public SCSI_PASS_THROUGH spt; [MarshalAs(UnmanagedType.ByValArray, SizeConst = 24)] public byte[] senseBuf; [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4096)] public byte[] dataBuf; public void Initialize() { spt.Initialize(); senseBuf = new byte[24]; dataBuf = new byte[4096]; } };

    以下の部分がはまっている個所になります。

    IntPtr handle = CreateFile(physicalDriveId, FileAccess.ReadWrite, FileShare.ReadWrite, IntPtr.Zero, FileMode.Open, 0, IntPtr.Zero); if (handle != System.IntPtr.Zero && (uint)handle.ToInt32() != (uint)0xffffffff) { Console.WriteLine("hadle success open"); } else { throw new Exception("ボリュームデバイスのハンドルが開けませんでした。", new Win32Exception()); } uint returned = 0; SCSI_PASS_THROUGH_WITH_BUFFERS24 sptwb = new SCSI_PASS_THROUGH_WITH_BUFFERS24(); sptwb.Initialize(); sptwb.spt.length = 56; sptwb.spt.pathId = 0; sptwb.spt.targetId = 0; sptwb.spt.lun = 0; sptwb.spt.senseInfoLength = 24; sptwb.spt.dataIn = 0; sptwb.spt.dataTransferLength = 512; sptwb.spt.timeOutValue = 2; sptwb.spt.dataBufferOffset = 80; sptwb.spt.senseInfoOffset = 56; sptwb.spt.cdbLength = 12; sptwb.spt.cdb[0] = 0xA1; sptwb.spt.cdb[1] = 0x80; sptwb.spt.cdb[2] = 0; sptwb.spt.cdb[3] = 0; sptwb.spt.cdb[4] = 2; sptwb.spt.cdb[5] = 0; sptwb.spt.cdb[6] = 0; sptwb.spt.cdb[7] = 0; sptwb.spt.cdb[8] = 0; sptwb.spt.cdb[9] = 0; sptwb.spt.cdb[10] = 0; sptwb.spt.cdb[11] = 0; sptwb.dataBuf[0] = 0x4e; sptwb.dataBuf[1] = 0x56; sptwb.dataBuf[2] = 0x4d; sptwb.dataBuf[3] = 0x45; sptwb.dataBuf[8] = 0x02; sptwb.dataBuf[10] = 0x56; sptwb.dataBuf[12] = 0xFF; sptwb.dataBuf[13] = 0xFF; sptwb.dataBuf[14] = 0xFF; sptwb.dataBuf[15] = 0xFF; sptwb.dataBuf[0x21] = 0x40; sptwb.dataBuf[0x22] = 0x7A; sptwb.dataBuf[0x30] = 0x02; sptwb.dataBuf[0x32] = 0x7F; // sptwbPtr IntPtr sptwbPtr = Marshal.AllocCoTaskMem(Marshal.SizeOf(typeof(SCSI_PASS_THROUGH_WITH_BUFFERS24))); Marshal.StructureToPtr(sptwb, sptwbPtr, false); // length bool isSuccess = false;

    //// エラーの発生する箇所 isSuccess = DeviceIoControl(handle, 0x4d004, sptwbPtr, 592, sptwbPtr, 592, out returned, IntPtr.Zero); Console.Write("DeviceIoControl=" + isSuccess); if (!isSuccess) { Console.WriteLine("DeviceIoControl ErrorCd=" + Marshal.GetLastWin32Error() + " returned=" + returned); Marshal.FreeCoTaskMem(sptwbPtr); return; }

    なお、CreateFileとDeviceIoControlのDllImportは以下の通りです。

            [DllImport("kernel32.dll", SetLastError = true,
                CallingConvention = CallingConvention.StdCall,
                CharSet = CharSet.Auto)]
            public static extern IntPtr CreateFile(
                    [MarshalAs(UnmanagedType.LPTStr)] string filename,
                    [MarshalAs(UnmanagedType.U4)] FileAccess access,
                    [MarshalAs(UnmanagedType.U4)] FileShare share,
                    IntPtr securityAttributes,
                    [MarshalAs(UnmanagedType.U4)] FileMode creationDisposition,
                    UInt32 flagsAndAttributes,
                    IntPtr templateFile);
    
            [DllImport("kernel32.dll", SetLastError = true, CallingConvention = CallingConvention.StdCall)]
            [return: MarshalAs(UnmanagedType.Bool)]
            public static extern bool DeviceIoControl(
                IntPtr hDevice,
                UInt32 dwIoControlCode,
                IntPtr lpInBuffer,
                UInt32 nInBufferSize,
                IntPtr lpOutBuffer,
                UInt32 nOutBufferSize,
                [Out]out UInt32 lpBytesReturned,
                IntPtr lpOverlapped);

    ioのバッファに使用する構造体や設定する値はCrystalDiskInfoのソースコードを参考にしながら

    作成しており、同じプログラムをCやC++で作成すると問題なく動きます。

    DllImportの宣言がおかしいのか、DeviceIoControlに渡すIntPtr等の定義の仕方がおかしいのか

    と思ったのですが、なかなか情報が見つけられず詰まってしまっている状況です。

    何かご存じの方がいらっしゃったら是非ご教授いただけませんでしょうか。



    • 編集済み g.tomioka 2019年10月27日 11:03
    2019年10月27日 11:02

回答

  • dataBufferOffset が本来 IntPtr / UIntPtr であるべきところを、ulong にしているので、64bit ではたまたま合うのでしょう。
    質問者さんは C# のプロセスがどちらで実行されているかを気にした方が良いでしょうね。
    なお、最近の C# のプロジェクトは明示的に設定しない限り、x86(32bit) で実行されます。
    (C++ と sizeof が一致すると言うことは、C++ 側は x64 なのかな…?)
    2019年11月3日 12:41
    モデレータ

すべての返信

  • C++でのsizeof(SCSI_PASS_THROUGH)とC#でのMarshal.Sizeof<SCSI_PASS_THROUGH>()を比べるとわかりますが、定義が正しくありません。

    dataBufferOffsetをUIntPtrかIntPtrにすると一致するかも。

    • 回答の候補に設定 佐祐理 2019年11月4日 5:26
    2019年10月27日 12:16
  • g.tomiokaさん、こんにちは。フォーラムオペレーターのHarukaです。
    MSDNフォーラムにご投稿くださいましてありがとうございます。

    私の検索によれば、Intel NVMe SSDはソリッドステートドライブのようです。
    こちらでこのサポートは提供していません。 
    英語となりますが、次のフォーラムに投稿いただくことをご検討ください。
    Intel SSD Toolbox

    どうぞよろしくお願いいたします。

    MSDN/ TechNet Community Support Haruka
    ~参考になった投稿には「回答としてマーク」をご設定ください。なかった場合は「回答としてマークされていない」も設定できます。同じ問題で後から参照した方が、情報を見つけやすくなりますので、 ご協力くださいますようお願いいたします。また、MSDNサポートに賛辞や苦情がある場合は、MSDNFSF@microsoft.comまでお気軽にお問い合わせください。~

    2019年10月31日 7:22
    モデレータ
  • 佐祐理さん

    お返事が遅くなり申し訳ありません。

    私の環境で試した限りはどちらも0x38(56)となり違いは見られませんでした。

    定義の部分を再度見直してみることにします。

    ご指摘いただきありがとうございます。

    2019年11月3日 7:13
  • Haruka様

    自分としては、SSDに対する質問というよりもC#でのNativeMethodの利用法

    (特にDeviceIoControl)の使用法の質問のつもりだったのですが、

    投稿先が不適切だったようで申し訳ありません。

    ご指摘ありがとうございます。

    • 回答としてマーク g.tomioka 2019年11月3日 7:21
    • 回答としてマークされていない g.tomioka 2019年11月3日 7:21
    2019年11月3日 7:16
  • こちらは、Samsung SSD 970 EVO Plus (NVMe)ですが、ご提示のコードで
    hadle success open
    DeviceIoControl=True
    になりますよ(64bit環境)。

    ただし、CorFlagsで/32bitpref+ を付与して32bitで動かすと、
    hadle success open
    DeviceIoControl=FalseDeviceIoControl ErrorCd=1306 returned=0
    になります

    jzkey

    2019年11月3日 10:55
  • dataBufferOffset が本来 IntPtr / UIntPtr であるべきところを、ulong にしているので、64bit ではたまたま合うのでしょう。
    質問者さんは C# のプロセスがどちらで実行されているかを気にした方が良いでしょうね。
    なお、最近の C# のプロジェクトは明示的に設定しない限り、x86(32bit) で実行されます。
    (C++ と sizeof が一致すると言うことは、C++ 側は x64 なのかな…?)
    2019年11月3日 12:41
    モデレータ
  • Azuleanさんもコメントされていますが、

    C# 64bitでは正しいサイズとなります。そしてjzkeyさんがコメントされている通りだとすればC# 64bitでは動作するようです。
    対して、C# 32bitでは誤ったサイズとなっています。そしてjzkeyさんがコメントされている通りだとすればC# 32bitでは質問のようなエラーとなるようです。

    で、質問者さんは56バイトとのことで64bitで検証されているとお見受けしますが、そうであれば今の誤ったコードでも動作するようですが?

    2019年11月3日 21:54
  • jzkeyさん

    実際に試していただいたようでありがとうございます。

    プラットフォームの設定を見直したところ32bitで実行していたようで、

    64bitに変更したら無事実行されました。

    2019年11月4日 3:48
  • Azuleanさん

    まさにご指摘いただいた通りでした。

    自分では64bitで実行していたつもりでしたが(ソリューションプラットフォームでx64を選択)、

    それが間違えていたようで、32bitで実行されているようでした。不勉強です。

    64bitに変更したところ無事実行することができました!

    ありがとうございます!

    • 回答としてマーク g.tomioka 2019年11月4日 3:51
    • 回答としてマークされていない g.tomioka 2019年11月4日 3:55
    2019年11月4日 3:51
  • 佐祐理さん

    最初にご指摘いただいた内容をようやく理解することができました。

    自分はulongでsizeofが一致していたので問題ないと思っていましたが、

    ulongの場合プラットフォームによらないのでたまたま56というサイズが合っていただけだったのですね。

    IntPtr/UIntPtrにしたところ確かにサイズが異なり、32bitで実行されているようでした。

    64bitで実行させることで動作させることができました。

    大変勉強になりました。

    ありがとうございます!

    2019年11月4日 3:54