12

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

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

だから最初に私は尋ねます:

私がハンドルを持っているとしましょう:

HANDLE h;

これは、どこかからの I/O のプロセスによって受信されました。作成されたフラグを簡単かつ確実に確認する方法はありますか? 問題の主なフラグはFILE_FLAG_OVERLAPPED.

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

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

そのようなフラグを既存のハンドルに追加する方法はありますか? これにより、同期操作用に最初に開いていたハンドルが、非同期操作用に開かれます。反対の方法を作成する方法はありますか (FILE_FLAG_OVERLAPPED非同期から同期ハンドルを作成するために削除します)?

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

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

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

よろしく

マーティン

4

7 に答える 7

5

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

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

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

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

于 2010-03-19T22:08:49.907 に答える
3

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

一言で言えば、私の目的にとっては役に立たない.

ReOpenFile() API は、「既存のファイル ハンドルを取得し、別のアクセス権セットを持つ別のハンドルを取得する」ために使用されます。少なくともそれは元の記事に記載されています。

コンソール入力ハンドルで 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 パラメータは、これらの関数が無名パイプで使用される場合は無視されます。」</p>

皆様、ご提案いただきありがとうございます。ハンドルから非同期的に読み取る場合、操作が同期的に完了する可能性があるという事実にも備える必要があります。私が知っています。私がこの質問をした主な理由は次のとおりです。

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

Windows オペレーティング システムでは、一部の読み取り操作を簡単な方法でキャンセルすることは不可能であることは明らかです。同期ハンドルから読み取る場合、 ReadFile() がまだスレッドのハンドルから読み取っていても、そのような読み取り操作を確実にウェイクアップすることは不可能であるため、アプリケーションを終了する必要があります。ご存知のように... 新しいOSでは、その操作をキャンセルすることができます。ただし、スレッドが ReadFile() 呼び出しに既にあるかどうかを知る方法はありません。ReadFile() がまだ呼び出されていない場合、キャンセルする操作はなく、後続の読み取りがハングします。唯一の方法はハンドルを閉じることですが、その操作はハングするか、一部のオブジェクトや一部のオペレーティング システムで失敗します。これに対する唯一の適切な解決策は、非同期 I/O です。しかし、冒頭で述べたように、

私はあきらめて、厄介な醜いハックを実装しようとしています... OVERLAPPED フラグで作成された HANDLE からの OVERLAPPED 構造、およびハンドルとスレッドのリークなしで読み続ける必要があります....

于 2013-05-10T13:17:37.337 に答える
2

If I understand what you're after, I would like to suggest that you don't care if was opened with the overlapped flag or not. I believe that you can safely pass in an OVERLAPPED structure in both the synchronous and asynchronous cases. Your code needs to be able to handle ReadFile() returning false and GetLastError() returning ERROR_IO_PENDING. You'll also need to add appropriate calls to GetOverlappedResult(), WaitForSingleObject(), etc.

The MSDN article on ReadFile() has some good info on this under "Considerations for working with synchronous file handles", and "Considerations for working with asynchronous file handles" in the "Synchronization and File Position" section.

于 2010-11-02T01:39:52.603 に答える
1

ハンドル フラグのテストは、おそらく、ハンドルが作成されたパーミッションのテストと同じ方法で行う必要があります。それを試してみてください。API が失敗した場合は、フォールバックを試してください。それが失敗した場合は、エラーを返します。

本当にわかりやすいのは、ReadFile のドキュメントに「hFile が FILE_FLAG_OVERLAPPED で開かれている場合、... 関数は読み取り操作が完了したことを誤って報告する可能性がある」ということだと思います。

私のエラーの解釈は次のとおりです (そして、自問する必要がある質問は次のとおりです): ファイル ハンドルのオーバーラップ ステータスを確認できた場合、なぜ ReadFile はそのチェックを行わず、それに応じて OVERLAPPED 構造を検証し、明示的にオーバーラップされたハンドルでオーバーラップしない方法で呼び出された場合、失敗しますか?

于 2010-03-19T11:02:13.597 に答える
1

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

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

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

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
    }*/
}
于 2013-01-29T17:36:33.410 に答える