DeviceIoControlを使ってコマンドを送るには

解決


蒼月  2008-07-25 10:58:11  No: 100943

下記の構造体を引数に、DeviceIoControlを使用してコマンドを送りたいのですが、各値はどのようにすればいいのかが良くわかりません。
コマンドを送信しても、戻り値が失敗になってしまいます。

Public Type SCSI_PASS_THROUGH_DIRECT
  length                As Integer
  ScsiStatus            As Byte
  PathId                As Byte
  TargetId              As Byte
  Lun                   As Byte
  CdbLength             As Byte
  SenseInfoLength       As Byte
  DataIn                As Byte
  DataTransferLength    As Long
  TimeOutValue          As Long
  DataBuffer          As Long
  SenseInfoOffset       As Long
  Cdb(15)          As Byte
End Type

Public Type SCSI_PASS_THROUGH_DIRECT_WITH_BUFFERS
  spt                   As SCSI_PASS_THROUGH
  Fill                As Long
  SenseBuf(31)     As Byte
End Type

DeviceIoControl(hDevice, _
                             IOCTL_SCSI_PASS_THROUGH_DIRECT, _
                             sptwb, _
                             length, _
                             sptwb, _
                             length, _
                             returned, _
                             OL)

構造体への値は、下記のように入れています。
cdbには、10byte分のコマンドが入っています。
それ以上はすべて0です。

    sptwb.spt.length = LenB(sptwb.spt)
    sptwb.spt.PathId = 0
    sptwb.spt.TargetId = 0
    sptwb.spt.Lun = 0
    sptwb.spt.CdbLength = 10
    sptwb.spt.SenseInfoLength = 24
    sptwb.spt.DataIn = 1
    sptwb.spt.DataTransferLength = 24
    sptwb.spt.TimeOutValue = 10  
    sptwb.spt.SenseInfoOffset = LenB(sptwb.spt) + Len(sptwb.Filler)
    sptwb.spt.DataBuffer = 0

    length = 4 + sptwb.spt.DataTransferLength

DeviceIoControl関数についての理解があいまいで、申し訳ないのですが、よろしくお願いします。


蒼月  2008-07-25 10:59:54  No: 100944

すいません、訂正です。

Public Type SCSI_PASS_THROUGH_DIRECT_WITH_BUFFERS
  spt                   As SCSI_PASS_THROUGH_DIRECT
  Fill                As Long
  SenseBuf(31)     As Byte
End Type


オショウ  2008-07-25 16:46:28  No: 100945

SCSIにどんな機器が繋がっているのか解りませんが・・・
どんなエラーですか?エラーコードで解るはずですが。

因みに、hDeviceにハンドル正しく入っているんですよね?
CreateDeviceかOpenDeviceでSCSIのデバイスを正しくオープン
できていれば、後は構造体のパラメータの内容が送信先機器に
マッチしていないと言うことになります。

その辺から調べてもらわないと・・・

以上。


蒼月  2008-07-25 19:25:20  No: 100946

オショウ 様

レスありがとうございます。

CreateDeviceは成功しています。
DeviceIoControlは戻り値は0が返ってきてしまうのですが、GetaLastErrorでエラーコードを確認すると、0(正常?)が戻ってきます。

関数の戻り値は失敗しているのに、GetLastErrorは正常とはどういうことなのでしょうか?
ちなみに、CreateDeviceを使用しないで、DeviceIoControlを使った場合は失敗し、GetLastErrorで6(ハンドルの異常)が返って来ます(当たり前ですが・・・)


魔界の仮面弁士  2008-07-25 21:55:21  No: 100947

> SCSI_PASS_THROUGH
こちらの定義は?

> GetaLastErrorでエラーコードを確認すると
GetLastError API ではなく、LastDllError プロパティを使ってください。


オショウ  2008-07-26 03:25:02  No: 100948

回答ではありませんが・・・
SCSIへの構造体宣言で、バイトのパッキングが原因になって
いるのでは?
随分昔にコーディングしたのですが、C言語でやった記憶が
あるので。

ttp://forums.technet.microsoft.com/ja-JP/Offtopic/thread/56529bff-ef02-4b67-8dae-bef7a7be0dfd/

ここでも似た投稿がありました。

※ VBのみで出来ればよいのですが・・・なんとも・・・

以上。


蒼月  2008-07-26 09:59:32  No: 100949

解決しました。
テキストボックスに構造体、変数の値を入れて、引数を自由に変更できるように、フォームを作り、実行を繰り返してみました。

結果的に、IOCTL_SCSI_PASS_THROUGH_DIRECTではなく、IOCTL_SCSI_PASS_THROUGHを使うことで解決することができました。
コマンド送信をする装置の仕様書にIOCTL_SCSI_PASS_THROUGH_DIRECTを使えと記載してあったため、そちらを使うものと思い込んでいたようです。

各値の設定値は以下のようになりました。
    sptwb.spt.length = LenB(sptwb.spt)
    sptwb.spt.PathId = 0
    sptwb.spt.TargetId = 0
    sptwb.spt.Lun = 0
    sptwb.spt.CdbLength = 10
    sptwb.spt.SenseInfoLength = 24
    sptwb.spt.DataIn = 1
    sptwb.spt.DataTransferLength = 1
    sptwb.spt.TimeOutValue = 10  
    sptwb.spt.SenseInfoOffset = 48
    sptwb.spt.DataBuffer = 80

    length = 4 + sptwb.spt.DataTransferLength

魔界の仮面弁士 様
SCSI_PASS_THROUGHは定義していませんでした。
SCSI_PASS_THROUGH_DIRECTのみを定義していました。

LastDllErrorについては初めて知りました。
ちょっと調べてみましたが、VBでこっちを使うのが常識のようですね・・・。
知らなかった自分が情けないです。

オショウ 様
バイトのパッキングというのは、構造体が全体で4バイトの倍数でないと引数のやり取りで不都合が起きるというやつでしょうか?
最初に投稿した時点で、一応、その辺も考慮してやってはみたのですが、解決できなかったので、インターネットの力を借りようと思った次第です。

お二人ともありがとうございました。
ただ、ちょっと気になるのは、IOCTL_SCSI_PASS_THROUGH_DIRECTと、IOCTL_SCSI_PASS_THROUGHの違いなのですが、一体何が違うのでしょうか?


オショウ  2008-07-26 17:31:47  No: 100950

解決後の書き込みになりますが
DDKの定義には・・・

SCSI_PASS_THROUGH
http://msdn.microsoft.com/en-gb/library/ms810309.aspx

SCSI_PASS_THROUGH_DIRECT
http://msdn.microsoft.com/en-gb/library/ms810301.aspx

見比べて下さい!
SCSIの機器側から見れば、違うのでしょう〜

以上。


魔界の仮面弁士  2008-07-27 00:44:12  No: 100951

> SCSI_PASS_THROUGHは定義していませんでした。
あれ? だとすると、最初の投稿にあった SCSI_PASS_THROUGH_DIRECT_WITH_BUFFERS という
ユーザ定義型の spt メンバがコンパイルエラーになるのでは無いでしょうか。

今までは一体、どのようにしてテストしていたのでしょうか?

> LastDllErrorについては初めて知りました。
VB も内部で各種の API を呼び出すため、Declare 処理に対して
正しい値を返せるよう、GetLastError API の戻り値が、
Err.LastDllError プロパティに保持されているというわけです。

ところで、Err.LastDllError に切り替えた後、
> エラーコードを確認すると、0(正常?)が戻ってきます。
の結果は変化したのでしょうか?

> バイトのパッキングというのは、構造体が全体で4バイトの倍数でないと
> 引数のやり取りで不都合が起きるというやつでしょうか?
最初の投稿にあった SCSI_PASS_THROUGH_DIRECT ユーザー定義型で言えば、
DataIn メンバの後に、3 バイト分の余白が生まれていることになりますね。

VB の場合、各メンバを 32bit 境界上に配置しようとするため、ユーザー定義型の
メンバ構造によっては、Len() と LenB() の結果が異なってしまう可能性があり、
API によっては、これが問題を引き起こす可能性があります。

たとえば SHFILEOPSTRUCT 構造体などは 1 バイトパッキングなので
ftp://ftp.grapecity.com/pdf/appleman98_06.pdf
にあるように、2 バイト分の「ズレ」を生じることが知られています。

ただし、SCSI_PASS_THROUGH_DIRECT が求めるパッキングサイズが、
1 バイトか 2 バイトか 4 バイトか 8 バイトなのかは、私は調べていません。

# 一応、C++ にて sizeof にて測定してみたところ、SCSI_PASS_THROUGH_DIRECT が
# (41 ではなく)3 バイト増えた「44」を返していた事を確認はしていますが。


蒼月  2008-07-27 10:28:37  No: 100952

オショウ 様
なんでだろ・・・。
リンク先が見られないです。
2つの構造体の違いは、片方は、ULONG_PTR型なのが、もう片方はPVOID型でしたよね。

魔界の仮面弁士 様
>今までは一体、どのようにしてテストしていたのでしょうか?
最初の書き込みは私のミスで、sptがSCSI_PASS_THROUGHになっていますが、次の投稿で、SCSI_PASS_THROUGH_DIRECTに訂正したんです。

>結果は変化したのでしょうか?
念のため、GetLastError & "/" & Err.LastDllError としてやってみたのですが、結果は変わらずでした。

パッキングサイズについて
構造体ごとに、パッキングサイズが違うのですね。
VBから、APIを呼び出すときは気をつけないとならないですね。
興味深い記事を紹介して頂き、ありがとうございます。
まだ、流し読み程度でしか読んでいませんが、時間があるときにきちんと読んでみたいと思います。


※返信する前に利用規約をご確認ください。

※Google reCAPTCHA認証からCloudflare Turnstile認証へ変更しました。






  このエントリーをはてなブックマークに追加