同期 I/O 用にオープンされた HANDLE を、存続期間中に非同期 I/O 用にオープンするように変更することはできますか?

StackOverflow https://stackoverflow.com/questions/2475713

質問

現在、Windows での私の毎日のプログラミング作業のほとんどは、あらゆる種類の I/O 操作 (パイプ、コンソール、ファイル、ソケットなど) に関連しています。私は、さまざまな種類のハンドル (同期、非同期イベントの完了待ち、ファイル ハンドルの待機、I/O 完了ポート、およびアラート可能な I/O) に対するさまざまな読み取りおよび書き込み方法をよく知っています。私たちはそれらの多くを使用しています。

一部のアプリケーションでは、すべてのハンドルを処理する方法が 1 つだけあると非常に便利です。つまり、プログラムは、どのような種類のハンドルを受け取ったのかを知らない可能性があり、たとえば、すべての I/O 完了ポートを使用したいと考えています。

そこでまず私はこう尋ねます。

ハンドルがあると仮定しましょう:

HANDLE h;

これは、私のプロセスがどこかから I/O のために受信したものです。どのフラグを使用して作成されたかを確認する簡単で信頼できる方法はありますか?問題の主なフラグは次のとおりです。 FILE_FLAG_OVERLAPPED.

これまで私が知っている唯一の方法は、そのようなハンドルを I/O 完了ポートに登録しようとすることです ( CreateIoCompletionPort())。それが成功すると、ハンドルは FILE_FLAG_OVERLAPPED で作成されます。ただし、ハンドルを閉じないとハンドルの登録を解除できないため、I/O 完了ポートのみを使用する必要があります。 HANDLE h 自体。

の存在を判断する簡単な方法があれば、 FILE_FLAG_OVERLAPPED, とすると、2 番目の質問になります。

このようなフラグを既存のハンドルに追加する方法はありますか?これにより、もともと同期操作用にオープンされていたハンドルが、非同期操作用にオープンされることになります。反対のものを作成する方法はありますか( FILE_FLAG_OVERLAPPED 非同期から同期ハンドルを作成するには)?

MSDN を読んだり、グーグルで何度も検索した結果、直接的な方法が見つかりませんでした。少なくとも同じことができるトリックはあるでしょうか?同じようにハンドルを再作成するのと同じように、 CreateFile() 機能かそれに似たものですか?部分的にでも文書化されているか、まったく文書化されていないものですか?

これが必要となる主な場所は、プロセスがサードパーティのアプリケーションによって送信されたハンドルから読み取り/書き込みを行う方法 (または方法を変更する) を決定することです。サードパーティ製品がどのようにハンドルを作成するかを制御することはできません。

親愛なる Windows の達人へ:助けてください!

よろしく

マーティン

役に立ちましたか?

解決 2

3年が経過し、Windows 8がリリースされました。 Windows 8の中で、コンソールの実装で導入された回帰のおかげで、私はこの質問を引き起こした問題について何かを行うようになりました。だから私は最終的にReOpenFile()関数呼び出しを使用しようとしました。

一つの文で:私の目的のために、それは無用である。

ReOpenFile()APIは、「既存のファイルハンドルを取得し、アクセス権の異なるセットを持っている別のハンドルを取得する」ために使用されています。少なくとも、それは元の記事をrel="nofollow"> href="http://msdn.microsoft.com/en-us/magazine/cc300448.aspx"

私は、コンソール入力ハンドルにReOpenFile()を使用してみました。

  stdin_in = GetStdHandle(STD_INPUT_HANDLE);
  stdin_in_operlapped = ReOpenFile(stdin_in, GENERIC_READ | GENERIC_WRITE,
                                   FILE_SHARE_READ, FILE_FLAG_OVERLAPPED);
  if (stdin_in_operlapped ==  INVALID_HANDLE_VALUE)
    {
      my_debug("failed to ReOpen stdin handle with OVERLAPPED flag: %d", GetLastError());
      exit(1);
    }

そして、何私が手には、次のとおりです。エラー1168:「要素が見つかりません」。 「あなたにマイクロソフトをありがとう」。私もマニュアルは述べているので、匿名のパイプのためにそれを使用しようとしません。

「非同期(重複)読み取りおよび書き込み動作は、匿名パイプによってサポートされていません。これは、あなたが匿名パイプとReadFileExとWriteFileEx関数を使用できないことを意味します。これらの機能は、匿名のパイプを使用する場合に加えて、ReadFile関数やWriteFile関数のlpOverlappedパラメータは無視されます。」

すべてのご提案のために、人々をありがとう。非同期ハンドルから読み込む場合、1は操作が同期的に完了する可能性があるという事実のためにも準備ができなければなりません。私が知っています。私はこの質問を求めていた主な理由はあります:

同期リードがいくつかのオブジェクト(少なくとも匿名パイプ、およびWindows 8でのコンソール入力)に発行されたとき、その後のReadFileまで、失敗、またはハングアップするか、同じハンドル上の別のスレッドからCloseHandleを()を呼び出します()完了;それはそれは多くの場合、無期限にハングアップすることを意味します。それは私が非同期と同期のハンドルを交換したかった理由です。

今では、それは簡単な方法でいくつかの読み取り操作をキャンセルするには、Windowsオペレーティングシステムでは、単純に不可能であることを私には明らかです。同期ハンドルから読み込む場合、確実に、このような読み出し動作を覚ますために、単純に不可能であるため、1だけ、のReadFile()がまだいくつかのスレッドでハンドルから読んでいる場合でも、アプリケーションを終了する必要があります。ノウハウで...新しいOSでその操作をキャンセルすることが可能です。しかし、スレッドはまだすでに、そうでないかのReadFile()の呼び出しである天候を知る方法はありません。 ReadFile()がまだ呼び出されていない場合は、キャンセルすると、後続の読み取りがハングする操作はありません。唯一の方法は、ハンドルを閉じることであろうが、その操作がハングアップ、または一部のオブジェクトの上に、いくつかのオペレーティング・システムで障害が発生しました。これに対する唯一の適切な解決策は、非同期I / Oです。私は冒頭で述べたように。しかし、私たちのAPPは、サードパーティのアプリケーションによって開始され、私たちは常に、標準入出力のための重複フラグを設定して、名前付きパイプを作成するために、それらすべてを強制することはできません。

私はあきらめと厄介な醜いハックを実装するつもりです...私たちは、OVERLAPPEDフラグを使用して作成されたハンドルからOVERLAPPED構造ずに読み取り、ハンドルとスレッドをリークし続ける必要があります....

他のヒント

私は MSDN の読者が苦手だったようです :/ 機能を完全に見逃していました ReOpenFile() これは、おそらく 2003 年 6 月には Windows Server 2003 に導入されていました (によれば、 この記事)。少なくとも少しは自分を守るために:私はそれを期待します CreateFile() 相互参照する説明 ReOpenFile() 説明。に参考文献があります ReOpenFile() ページへ CreateFile() ページに戻りますが、その逆はありません。

この関数は、私が必要なことを正確に有効にしているようです。の追加または削除 FILE_FLAG_OVELRAPPED 必要なプロパティを持つ新しいハンドルを作成することで、既存のハンドルとの間で相互にやり取りできます。:-D ただし、まだテストしていません。残念ながら、これは Windows 2003 Server、Windows Vista 以降でのみ使用できます。以前の OS バージョンに関する質問は回答済みです ここ. 。Windows 2003 Server以前のOSの公開APIにはこの機能は存在しません。これは基礎となる実装によって使用されますが、それらのシステムの開発者は利用できません (サポートされていません)。

これは事実上、古い Windows プラットフォームのサポートを終了するまで、少なくとも今後数年間は希望がないことを意味します。これは、Windows Vista よりも古い OS では I/O に関する状況が非常に悪かったことも意味します。完全に欠如していたもう 1 つの厄介な部分は、古いシステムで同期および非同期 I/O をキャンセルできる機能でした。

さらに、私はまだ答えの一部を見逃しています。フラグの存在を何らかの方法でテストできますか?それを行うための機能が見つかりませんでした。つまり、ファイル オブジェクトに何らかのフラグが存在することを保証したい場合は、ファイルを常に再度開く必要があります。

私はあなたが後にしているものを理解していれば、私は重複フラグ付きでオープンされたかどうか、あなたが気にしないことを示唆したいと思います。私はあなたが安全に同期と非同期の両方のケースでOVERLAPPED構造に渡すことができると信じています。あなたのコードはReadFile()を返すfalseGetLastError()を返すERROR_IO_PENDINGを処理できるようにする必要があります。あなたはまた、などGetOverlappedResult()WaitForSingleObject()に適切な呼び出しを追加する必要があります。

ReadFile()上のMSDNの記事は、「同期とファイルの位置」セクションの「非同期ファイルハンドルを操作するための考慮事項」を「同期ファイルハンドルを操作するための考慮事項」この下にいくつかの良い情報を持っている、と。

テストのフラグを扱うおそらくハンドルを使用して作成された権限をテストと同じ方法で行われるべきです。 それを試してみてください。 APIが失敗した場合は、フォールバックを試してみてください。それが失敗した場合、エラーを返します。

私は本当に伝える事はReadFile関数のドキュメントが言う方法だと思います「のhFileがFILE_FLAG_OVERLAPPEDで開かれている場合は、...の機能が正しく読み出し動作が完了したことを報告することができます。」

エラーの私の解釈は、ある(そして、あなたは自問する必要がある疑問がある):それはファイルハンドルの重複状態を確認することが可能であったならば、なぜそのチェックを行い、それに応じてOVERLAPPED構造を検証しませreadFileのでしょう重複ハンドルに非重複の方法で呼び出された場合、明示的に失敗する?

ハンドルのフラグと ReOpen API の使用による副作用を判断する方法はわかりませんが、あなたの目標は

すべてのハンドルを処理する方法が 1 つだけあれば非常に便利です

同期動作が必要な場合 (つまり、重複していないハンドルには同期 API を使用し、OVERLAPPED 構造体が供給された非同期 API はその後重複したイベントを待機することを意味します) ハンドルが非オーバーラップモードで開かれた場合でも、いつでも非同期 API を使用できます。 @Brettがすでに述べたように
これが機能することを確認できます(少なくとも名前付きパイプでは)。

void connectSynchronous(HANDLE hPipeThatWeDontKnowItsFlag){
    ...
    BOOL bRet = ::ConnectNamedPipe(hPipeThatWeDontKnowItsFlag, pOverlapped);

    if(bRet == FALSE){
        DWORD dwLastErr = ::GetLastError();

        if(dwLastErr == ERROR_IO_PENDING){
            //The handle was opened for asynchronous IO so we have to wait for the operation
            ...waitFor on the overlapped hEvent;

        }else if(dwLastErr == ERROR_PIPE_CONNECTED){
            //The handle was opened for synchronous IO and the client was already connected before this call: that's OK!
            return;
        }else{
            throw Error(dwLastErr);
        }
    }/*else{
        //The handle was opened for synchronous IO and the client has connected: all OK
    }*/
}

CreateIoCompletionPortハックの代わりに、NULL lpOverlappedでゼロバイトのReadFileを行うことです。それはERROR_INVALID_PARAMETERで失敗した場合、それがFILE_FLAG_OVERLAPPEDで開かれたと仮定します。

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top