none
APIのReadFile関数を使ってデータを読み込む方法 RRS feed

  • 質問

  • こんにちは、C#でAPIのReadFile関数を使ったデータの読み込み方法について質問させていただきます。


    MSDNプログラミングガイド等を参考にしてReadFile関数を使ったプログラムを以下のように記述しました(一部抜粋)。
    デバッグ中にReadFile関数の第四引数を見ると、たしかにデータが受信されていることがわかったのですが、
    第二引数に肝心のデータが格納されていませんでした。



        [System.Runtime.InteropServices.DllImport("kernel32", SetLastError = true)]
        static extern unsafe bool ReadFile
        (
            System.IntPtr hFile,      // handle to file
            void* pBuffer,            // data buffer
            int NumberOfBytesToRead,  // number of bytes to read
            int* pNumberOfBytesRead,  // number of bytes read
            int Overlapped            // overlapped buffer
        );


                int n = 0;
                byte[] buffer = new byte[128];

                fixed (byte* p = buffer)
                {
                    ReadFile(readhandle, p, lpNumberOfBytesRead, &n, 0);
                }



    以上がソースの主要部分を抜粋したものです。

    ビルドエラー等は出ない状態ですが、第二引数に肝心のデータが格納されず困っています。
    よろしければご回答ください。



    [参考]MSDNプログラミングガイド: http://msdn.microsoft.com/ja-jp/library/2d9wy99d(VS.80).aspx
    2009年3月19日 8:33

回答

  • ハンドルは SafeHandle(SafeFileHandle)に、overlapped は(NULL 渡すの前提なら) IntPtr にすべき、と少し気になるところはありますが問題ない宣言及び使用方法ですね。
    ReadFile の返値はどうなっていますか?
    pNumberOfBytesRead に返ってきてる値は NumberOfBytesToRead に渡したのと同じ(か読み取り先の EOF までの長さ)になっていますか?
    hFile に何を渡しているのか知りませんが、FileStream で開いたファイルの Handle プロパティを使った場合はどうですか?
    • 回答としてマーク sk7474 2009年4月1日 7:27
    2009年3月19日 10:26
  •  こんにちは!(^^)!ふ~です。

    取りあえず。ReadFile()の実行した後に、GetLastErr()で、エラーが取得できると思います。
    bufferの作成する場所がセーフ側のstaticである。バッファをクリアするなど、確認して見てください。
    また、エラー番号を取得してメッセージに変換できると思います。

    • 回答としてマーク sk7474 2009年4月1日 7:26
    2009年3月20日 9:42
  • 3052F の発言:

    ・hFileにはUSB通信のためのPipeハンドラを渡しています。

    で、

    Hongliang の発言:

    hFile に何を渡しているのか知りませんが、FileStream で開いたファイルの Handle プロパティを使った場合はどうですか?

    はどうなんですか?

    また、FileStream コンストラクタには IntPtr(または SafeFileHandle)を渡すこともできます(デフォルトでは FileStream を Close すれば自動的に CloseHandle Win32API 関数によって渡したハンドルがクローズされますが、引数次第でそうさせないこともできます)。こちらではどうでしょうか?

    3052F の発言:

    >!(^^)!ふ~さん
    以下のコードでエラー取得しました。

    [DllImport("kernel32.dll", SetLastError=true)] // SetLastErrorフィールドにtrueを設定しMarshal.GetLastWin32Errorメソッドを有効化

    if (lpNumberOfBytesRead != 0)      // 受信データが存在する場合
    {
     int er = 0;    // Win32エラーコード取得用変数
     er = Marshal.GetLastWin32Error(); // Win32エラーコード取得
    }

    GetLastError が有効なのは(同期 Read の場合)ReadFile が false を返したときのみです。
    lpNumberOfBytesRead は既に EOF に到達していた場合でも 0 を返すので、関数呼び出しの成否の判定に使ってはいけません。

    • 回答としてマーク sk7474 2009年4月1日 7:26
    2009年3月23日 4:34
  • 明後日な方向な回答として…

    FileStreamコンストラクタを使用してFileStreamクラスでラップしてしまうのはどうでしょう?
    そうすれば他のFileStreamとして簡単に扱えるようになります。
    もちろんちゃんと扱えるようになったらSafeFileHandle版に切り替えましょう。
    • 回答としてマーク sk7474 2009年4月1日 7:26
    2009年3月23日 23:29

すべての返信

  • ハンドルは SafeHandle(SafeFileHandle)に、overlapped は(NULL 渡すの前提なら) IntPtr にすべき、と少し気になるところはありますが問題ない宣言及び使用方法ですね。
    ReadFile の返値はどうなっていますか?
    pNumberOfBytesRead に返ってきてる値は NumberOfBytesToRead に渡したのと同じ(か読み取り先の EOF までの長さ)になっていますか?
    hFile に何を渡しているのか知りませんが、FileStream で開いたファイルの Handle プロパティを使った場合はどうですか?
    • 回答としてマーク sk7474 2009年4月1日 7:27
    2009年3月19日 10:26
  •  こんにちは!(^^)!ふ~です。

    取りあえず。ReadFile()の実行した後に、GetLastErr()で、エラーが取得できると思います。
    bufferの作成する場所がセーフ側のstaticである。バッファをクリアするなど、確認して見てください。
    また、エラー番号を取得してメッセージに変換できると思います。

    • 回答としてマーク sk7474 2009年4月1日 7:26
    2009年3月20日 9:42
  •  

    お返事ありがとうございます。C#を始めたばかりだったのでお返事いただけて助かります。

    >Hongliangさん
    ・実はハンドルはUSB通信のために取得しており、SafeHandleも検討していたのですが
     ここがReadFile関数の第二引数に受信したデータが格納されない重要な原因ではないと判断し見送っていました。
    ・第五引数overlappedはIntPtrにさせていただきました。
    ・ReadFileの戻り値ですが受信がないときはfalse, 受信があったときはtrueで見ています。
    ・ターゲットから送信したデータと、ホストで受け取ったデータ長は同じ長さであることをReadFile関数の第四引数の中身で確認しています。
    ・hFileにはUSB通信のためのPipeハンドラを渡しています。

    そのほかに第二引数にデータが格納されない(バッファの設定がよくない)原因がありますでしょうか?よろしくお願いします。



    >!(^^)!ふ~さん
    以下のコードでエラー取得しました。

    [DllImport("kernel32.dll", SetLastError=true)] // SetLastErrorフィールドにtrueを設定しMarshal.GetLastWin32Errorメソッドを有効化

    if (lpNumberOfBytesRead != 0)      // 受信データが存在する場合
    {
     int er = 0;    // Win32エラーコード取得用変数
     er = Marshal.GetLastWin32Error(); // Win32エラーコード取得
    }

    このコードを用いて取得されたエラーコードは1008でした(Dec)。
    エラーコード1008は以下の通りでした。

    ERROR_NO_TOKEN 1008 0x000003F0 存在しないトークンを参照しようとしました。

    このエラーはバッファの設定がよくないのでしょうか?よろしくお願いします。








     

    2009年3月23日 2:06
  • 3052F の発言:

    ・hFileにはUSB通信のためのPipeハンドラを渡しています。

    で、

    Hongliang の発言:

    hFile に何を渡しているのか知りませんが、FileStream で開いたファイルの Handle プロパティを使った場合はどうですか?

    はどうなんですか?

    また、FileStream コンストラクタには IntPtr(または SafeFileHandle)を渡すこともできます(デフォルトでは FileStream を Close すれば自動的に CloseHandle Win32API 関数によって渡したハンドルがクローズされますが、引数次第でそうさせないこともできます)。こちらではどうでしょうか?

    3052F の発言:

    >!(^^)!ふ~さん
    以下のコードでエラー取得しました。

    [DllImport("kernel32.dll", SetLastError=true)] // SetLastErrorフィールドにtrueを設定しMarshal.GetLastWin32Errorメソッドを有効化

    if (lpNumberOfBytesRead != 0)      // 受信データが存在する場合
    {
     int er = 0;    // Win32エラーコード取得用変数
     er = Marshal.GetLastWin32Error(); // Win32エラーコード取得
    }

    GetLastError が有効なのは(同期 Read の場合)ReadFile が false を返したときのみです。
    lpNumberOfBytesRead は既に EOF に到達していた場合でも 0 を返すので、関数呼び出しの成否の判定に使ってはいけません。

    • 回答としてマーク sk7474 2009年4月1日 7:26
    2009年3月23日 4:34
  • こんにちは!(^^)!ふ~です。

    仮に、確かなエラーメッセージとしますと、下記のような関連でしょうか? 

    すべてのユーザーへのアクセスを匿名パイプを作成する方法
    http://support.microsoft.com/kb/813414/ja
    2009年3月23日 5:12
  • 明後日な方向な回答として…

    FileStreamコンストラクタを使用してFileStreamクラスでラップしてしまうのはどうでしょう?
    そうすれば他のFileStreamとして簡単に扱えるようになります。
    もちろんちゃんと扱えるようになったらSafeFileHandle版に切り替えましょう。
    • 回答としてマーク sk7474 2009年4月1日 7:26
    2009年3月23日 23:29
  • こんにちは。中川俊輔です。

    皆様、回答ありがとうございます。

    3052Fさん、フォーラムのご利用ありがとうございます。
    その後いかがでしょうか?問題は解決しましたか?

    勝手ながら、有用な情報と思われる回答へ回答マークをつけさせていただきました。

    今後ともフォーラムをよろしくお願いします。
    それでは!
    マイクロソフト株式会社 フォーラム オペレータ 中川 俊輔
    2009年4月1日 7:29