2016-06-25 7 views
0

私はReadFile関数でエラー持ってasynchroneouslyC++でファイルをasynchroneusly読み込む方法は?

string read(string path) { 
      DWORD readenByte; 
      int t; 
      char* buffer = new char[512]; 
      HANDLE hEvent = CreateEvent(NULL, FALSE, FALSE, "read"); 
      OVERLAPPED overlap; 
      overlap.hEvent = hEvent; 
      HANDLE hFile = CreateFile(path.c_str(), GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); 
      if(!hFile) { 
       Debug::error(GetLastError(), "fileAsync.cpp::read - "); 
      } 
      t = ReadFile(hFile, buffer, MAX_READ - 1, &readenByte, &overlap); 
      if(!t) { 
       Debug::error(GetLastError(), "fileAsync.cpp::read - "); 
      } 
      t = WaitForSingleObject(hEvent, 5000); 
      if(t == WAIT_TIMEOUT) { 
       Debug::error("fail to read - timeout, fileAsync.cpp::read"); 
      } 
      buffer[readenByte] = '\0'; 
      string str = buffer; 
      return str; 
     } 

ファイルを読むために必要な - 38:asynchroneusly WINAPIを使用してC++でファイルを読み取る方法ファイル

の終わりに達しましたか?

+0

エラー検出のメッセージは、[MSDNの例](https://msdn.microsoft.com/en-us/library/windows/desktop/aa365690.aspx)の最後にあります。 – theB

答えて

5

いくつか対処する必要がありますあなたのコード内のバグ、いくつかの原因故障、その他の致命的な障害があります。

  • 最初のバグは、あなたが得るエラーコードにつながる:あなたはに保存されたランダムなファイル位置から読み取るには、以下のReadFileの呼び出しを指示する、初期化されていないOVERLAPPED構造を持っているOffsetHighメンバーを相殺しました。これを修正するには、データを初期化してください:OVERLAPPED overlap = {0};

  • 次に、非同期アクセス用のファイルを開いていません。後でファイルから非同期に読み取るには、CreateFileのdwFlagsAndAttributesに渡してFILE_FLAG_OVERLAPPEDに渡す必要があります。もしあなたがいなければ、数ヶ月間バグを狩ることになります(What happens if you forget to pass an OVERLAPPED structure on an asynchronous handle?参照)。 lpNumberOfBytesReadパラメータは、非同期I/Oのために使用されていない、とあなたが代わりにNULLを渡す必要があることを

  • ReadFileのマニュアルに説明し、。転送されたバイト数がわかる前に、非同期のReadFile呼び出しが返るので、これは直ちに明らかになります。転送されたペイロードのサイズを取得するには、非同期I/Oが終了したらGetOverlappedResultにコールします。

  • 次のバグはメモリリークの原因となります。 bufferを動的に割り当てていますが、delete[] buffer;は決して呼び出しません。バッファを削除するか、自動保存期間(char buffer[MAX_READ] = {0};)のバッファを割り当てるか、C++コンテナ(たとえばstd::vector<char> buffer(MAX_READ);)を使用してください。あなたはbufferからstd::stringを構築しようとするところ

  • 別のバグは、次のとおりです。あなたが選択したコンストラクタが埋め込まNUL文字が何であるかに対処することはできません。あなたが持っているものは切り捨てます。あなたは明示的な長さの引数を取ってstd::string constructorを呼び出す必要があります。しかし、その場合でも、ファイルの文字エンコーディングとstd::stringが一致しない場合、ゴミで巻き上げることがあります。

  • 最後に、WaitForSingleObjectが後に続く非同期読み取りを発行することは、本質的に同期読み出しであり、何も購入しません。私はこれがテスト用であり、あなたの最終コードではないと仮定しています。これを終えるとき、非同期読み取り操作が飛行中である限り、OVERLAPPED構造体が生き続ける必要があることを覚えておいてください。

    • あなたはCreateFile呼び出しで使用されているあなたのread関数にstd::stringを渡している。すぐにバグに対処していない

    その他の推奨事項、。 Windowsでは、UTF-16LEエンコーディングが使用されています。これは、Visual Studioを使用するとwchar_t/std::wstringにマップされます(他のWindowsコンパイラも同様です)。文字列をUTF-16(またはその逆)にMBCSから変換されます

    1. ANSIのAPIを呼び出す:std::string/const char*を渡すと、2つの即時の欠点を有しています。これは、現在のロケールに依存するため、不必要にリソースを無駄にし、非常に微妙な方法で失敗します。
    2. すべてのUnicodeコードポイントがMBCSエンコーディングを使用して表現できるわけではありません。これは、MBCS文字エンコーディングを使用しているときには、一部のファイルを開くことができないことを意味します。

  • は全体のUnicode API( CreateFileW)とUTF-16の文字列( std::wstring/ wchar_t)を使用します。コンパイラのコマンドラインでプリプロセッサーシンボル UNICODE(Windows APIの場合)および _UNICODE(CRTの場合)を定義して、誤ってANSI APIを呼び出さないようにすることもできます。

  • イベントオブジェクトは、その名前ではなく、HANDLEの値でのみアクセスされます。 NULLlpName引数としてCreateEventに渡すことができます。これは潜在的な名前の衝突を防ぎます。これは、一般的な名前として"read"といっそう重要です。

+0

もう1つのバグ。使用後、リソースを解放するためにCloseHandle(hFile)を呼び出すことは明らかです。忘れた場合、同じファイルでCreateFileを再度使用することは不可能です。 – Nikita

+1

@Nikita:ハンドルを閉じるのを忘れても、互換性のあるアクセスと共有モードを使用している限り、ハンドルを開くことができます。しかしこれは珍しいことではないが、初めてハンドルを開くときにも同じ制限が適用される。リークしないようにしたら、ハンドルを閉じて、潜在的なアクセス制限や共有制限を解除する必要があります。 – IInspectable

2

1)CreateFileへの呼び出しの6番目の引数(dwFlagsAndAttributes)にフラグFILE_FLAG_OVERLAPPEDを含める必要があります。そのため、重複した読み込みが失敗する可能性が最も高いのです。

2)MAX_READの値は何ですか?私はファイルが512バイトよりも大きい場合、それが513未満であることを願っています。悪いことが起こります。重なった構造体ポインタがNULLないことと

3)ReadFileはあなたにReadFileを呼び出した後に期待されているので、あなたがtを評価することはできませんエラーコード997(ERROR_IO_PENDING)を得ます。

4)非同期操作の場合、ReadFile関数は、呼び出しで渡したポインタに読み込まれたバイトを格納しません。操作が完了したら、重複した結果を自分自身で照会する必要があります。ここで

は、小さな作業スニペットで、私はあなたがそれから構築することを願って:

#include <Windows.h> 

#include <iostream> 
#include <sstream> 

class COverlappedCompletionEvent : public OVERLAPPED 
{ 
public: 
    COverlappedCompletionEvent() : m_hEvent(NULL) 
    { 
     m_hEvent = CreateEvent(NULL, FALSE, FALSE, NULL); 
     if (m_hEvent == NULL) 
     { 
      auto nError = GetLastError(); 
      std::stringstream ErrorStream; 
      ErrorStream << "CreateEvent() failed with " << nError; 
      throw std::runtime_error(ErrorStream.str()); 
     } 
     ZeroMemory(this, sizeof(OVERLAPPED)); 
     hEvent = m_hEvent; 
    } 
    ~COverlappedCompletionEvent() 
    { 
     if (m_hEvent != NULL) 
     { 
      CloseHandle(m_hEvent); 
     } 
    } 

private: 
    HANDLE m_hEvent; 
}; 

int main(int argc, char** argv) 
{ 
    try 
    { 
     if (argc != 2) 
     { 
      std::stringstream ErrorStream; 
      ErrorStream << "usage: " << argv[0] << " <filename>"; 
      throw std::runtime_error(ErrorStream.str()); 
     } 
     COverlappedCompletionEvent OverlappedCompletionEvent; 
     char pBuffer[512]; 
     auto hFile = CreateFileA(argv[1], GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED, NULL); 
     if (hFile == NULL) 
     { 
      auto nError = GetLastError(); 
      std::stringstream ErrorStream; 
      ErrorStream << "CreateFileA() failed with " << nError; 
      throw std::runtime_error(ErrorStream.str()); 
     } 
     if (ReadFile(hFile, pBuffer, sizeof(pBuffer), nullptr, &OverlappedCompletionEvent) == FALSE) 
     { 
      auto nError = GetLastError(); 
      if (nError != ERROR_IO_PENDING) 
      { 
       std::stringstream ErrorStream; 
       ErrorStream << "ReadFile() failed with " << nError; 
       throw std::runtime_error(ErrorStream.str()); 
      } 
     } 
     ::WaitForSingleObject(OverlappedCompletionEvent.hEvent, INFINITE); 
     DWORD nBytesRead = 0; 
     if (GetOverlappedResult(hFile, &OverlappedCompletionEvent, &nBytesRead, FALSE)) 
     { 
      std::cout << "Read " << nBytesRead << " bytes" << std::endl; 
     } 
     CloseHandle(hFile); 
    } 
    catch (const std::exception& Exception) 
    { 
     std::cout << Exception.what() << std::endl; 
     return EXIT_FAILURE; 
    } 
    return EXIT_SUCCESS; 
} 
+0

'GetLastError'へのあなたの呼び出しはすべて遅すぎます。 'GetLastError'は、それを呼び出すための述語が真になった後に即座に**呼び出さなければなりません。散らばっているコンストラクタとストリーム演算子は、異なるエラーコードを設定することができ、無意味な赤身のタイプのエラーログにつながります。 – IInspectable

+0

@IInspectableが記載され、修正されました。 –

+0

'CreateEvent'への呼び出しとc'torへの呼び出しの間に(見えない)コードがあるかもしれません。 'CreateEvent'への呼び出しをc'tor本体に移さなければなりません。 – IInspectable

関連する問題