同期 I/O 用にオープンされた HANDLE を、存続期間中に非同期 I/O 用にオープンするように変更することはできますか?
-
21-09-2019 - |
質問
現在、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()関数呼び出しを使用しようとしました。
一つの文で:私の目的のために、それは無用である。
。他のヒント
私は 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()
を返すfalse
とGetLastError()
を返す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で開かれたと仮定します。