2017-11-16 6 views
0

、私はWaitForSingleObject()で同期Io()をキャンセルする方法stdinを待っていますか? Windowsの10で

WaitForSingleObject(GetStdHandle(STD_INPUT_HANDLE), ...)

CancelSynchronousIo()を使用して、この待機をキャンセルするを使用して、コンソールからの入力を待っています。

ただし、キャンセルは何も行いません(0GetLastError()ERROR_NOT_FOUNDです)。

私が間違っている可能性がありますか?
これをキャンセルして、stdinの新しい入力を待つべきですか?

(私は実際にだけでなく、標準入力、そのGetFileType()FILE_TYPE_CHARある任意のHANDLEでこれをやりたいが、標準入力は確かに最も重要なユースケースであるとしてテストする最も簡単)。


関連ディスカッション私が見つけた:

しかし残念ながら、彼らは唯一の議論ReadFile()、ないWaitForSingleObject()。私もWaitForMultipleObjects()(配列内のオブジェクトは1つ)と同じ問題を試しました。

背景:私はimprove input handling in the GHC Haskell compiler runtimeにしようとしているを。)

+2

WaitForSingleObjectはI/O要求ではなく、もちろんキャンセルできません。 – RbMm

答えて

1

コンソールI/Oを非同期的に使用することは困難であり、それは単にそれのために設計されていません。考えられる回避策については、IO Completion Ports (IOCP) and Asynchronous I/O through STDIN, STDOUT and STDERRを参照してください。

それはあなたのためのオプションではない場合、あなたはどちらかになります:ループ内

  1. 使用WaitForSingleObject()短いタイムアウトで。フラグが設定されていればループを中断するためにループが各反復で見ることができるフラグ変数を作成します。

  2. WaitForMutipleObjects()を使用してください。コンソールには1つ(または何でも)、1つはCreateEvent()からのイベントオブジェクト用に1つを待機するように2 HANDLEを指定します。その後、待機を解除する場合は、SetEvent()でイベントを通知できます。戻り値WaitForMutipleObjects()は、どの信号がHANDLEに送られたかを示します。

+0

コンソールには、異なるウィンドウバージョンで異なる実装があります。 win8の前に - コンソールハンドル - ファイルハンドルではなく、結果として非同期入出力を使用することはできません。しかし、これは実際のファイルハンドルであり、非同期モードでも開くことができます(これはmsdnにもかかわらず無視されません)。正式な非同期モードでも非同期モードでもオープンできますコンソールでも勝利8/10。 – RbMm

+1

@RbMm、Windows 8以降でコンソールホストで使用されている "\ Device \ ConDrv"バックエンド通信チャネルは、他の標準NT入出力デバイスと同様に、 'CancelSynchronousIo'を介して同期入出力を取り消すことができます。 'NtDeviceIoControlFile'を呼び出すコンソールのI/Oループは、' ReadFile'または 'ReadConsoleW'から調理された読み込みをキャンセルすることをサポートしていません。クライアント側の要求は取り消されますが、コンソールはcooked readに残され、ユーザーがenterを押すと破棄されます。このバグが解決されるまで同期型I/Oをキャンセルしないようにしてください。 – eryksun

+0

@eryksun - はい。面白い。私はクライアントのI/O要求をキャンセルすることができます(CancelIo [Ex]経由で同期または非同期) - この問題が発生した後、コンソールウィンドウに2回押してください - 最初のユーザ入力(入力)は失われ、よくしかし、私はこれについては言わない。単に非同期コンソールハンドルをwin8 +以降オープンでき、非同期読み取りを使用できることに注意してください(キャンセルについては何も言及していません)。もちろん、待機機能はio要求としてキャンセルできません – RbMm

1

CancelSynchronousIo指定されたスレッドによって発行されたI/O操作をキャンセルします。より具体的には、IoCancelIrpを介して指定されたスレッドに関連付けられたパケットIRPを取り消します。使用がNtCancelSynchronousIoFile文書化されていない場合、我々は、より選択的であり得る(CancelSynchronousIoを内部IoRequestToCancel = 0でそれを呼び出す) - これは私がない

が、WaitForSingleObjectIoRequestToCancel指定使用のみのI/O要求取り消し(Irp->UserIosb == IoRequestToCancelというシステムチェックをしてのみ、この要求を取り消します)/Oリクエスト。この呼び出しはキャンセル可能なIRPを作成しません。だから - これは絶対にしないでください。

しかし、あなたはTRUEbAlertableセットでWaitForSingleObjectExを使用している場合 - あなたはブレークQueueUserAPCを使用してスレッド化にキューAPCによってを待つことができます。 NtWaitForSingleObjectの代わりにWaitForSingleObjectExを使用する場合は、文書化されていない電話NtAlertThreadを使用してスレッドに警告することもできます。再びNtWaitForSingleObjectを実行します - - 結果として、我々はコールNtAlertThreadWaitForSingleObjectExを破ることはできませんが、NtWaitForSingleObjectはbreakedされます

。この場合には NtWaitForSingleObjectWaitForSingleObjectEx内部 STATUS_ALERTEDのためにと、この状態の場合には、特別なチェックを行う NtWaitForSingleObjectを呼び出していることに注意してください( STATUS_ALERTEDで壊れます

ので、あなたがSTD入力待ち壊す必要がある場合 - (あなたは待機のためNtWaitForSingleObjectを使用する場合のみ)ではないCancelSynchronousIo(この無意味な)が、QueueUserAPCまたはNtAlertThread呼び出す必要があり、追加のスレッドを作成し、入力されたスレッドがアラート可能状態で待機する必要があります。デモコードは次のようになります:

extern "C" NTSYSCALLAPI NTSTATUS NTAPI NtAlertThread(HANDLE ThreadHandle); 

VOID NTAPI OnApc(ULONG_PTR Parameter) 
{ 
    DbgPrint("OnApc(%p)\n", Parameter); 
} 

DWORD CALLBACK BreakWaitThread(HANDLE hThread) 
{ 
    switch (LONG status = MessageBoxW(0, L"Use Apc(yes) or Alert(No) ?", L"BreakWaitThread", 
     MB_ICONQUESTION|MB_YESNOCANCEL|MB_DEFBUTTON3)) 
    { 
    case IDYES: 
     if (!QueueUserAPC(OnApc, hThread, 0)) 
     { 
      DbgPrint("QueueUserAPC=%u\n", GetLastError()); 
     } 
     break; 
    case IDNO: 
     if (0 > (status = NtAlertThread(hThread))) 
     { 
      DbgPrint("AlertThread=%x\n", status); 
     } 
     break; 
    case IDCANCEL: 
     DbgPrint("canceled\n"); 
     break; 
    default: 
     DbgPrint("MessageBox=%x\n", status); 
    } 

    CloseHandle(hThread); 

    return 0; 
} 

void ConsoleLoop(HANDLE hStdIn) 
{ 
    ULONG NumberOfEvents, NumberOfEventsRead, n; 

    INPUT_RECORD buf[8], *p; 

    for (;;) 
    { 
     switch (ZwWaitForSingleObject(hStdIn, TRUE, 0)) 
     //switch (WaitForSingleObjectEx(hStdIn, INFINITE, TRUE)) 
     { 
     case WAIT_OBJECT_0: 

      while (GetNumberOfConsoleInputEvents(hStdIn, &NumberOfEvents) && NumberOfEvents) 
      { 
       do 
       { 
        NumberOfEventsRead = min(RTL_NUMBER_OF(buf), NumberOfEvents); 

        if (ReadConsoleInput(hStdIn, buf, NumberOfEventsRead, &NumberOfEventsRead) && NumberOfEventsRead) 
        { 
         n = NumberOfEventsRead; 
         p = buf; 
         do 
         { 
          if (p->EventType == KEY_EVENT) 
          { 
           DbgPrint("%u(%u) %C %x %x %x\n", 
            p->Event.KeyEvent.bKeyDown, 
            p->Event.KeyEvent.wRepeatCount, 
            p->Event.KeyEvent.uChar.UnicodeChar, 
            p->Event.KeyEvent.wVirtualKeyCode, 
            p->Event.KeyEvent.wVirtualScanCode, 
            p->Event.KeyEvent.dwControlKeyState); 

           if (VK_OEM_PERIOD == p->Event.KeyEvent.wVirtualKeyCode) 
           { 
            return ;//if user type '.' return for demo 
           } 
          } 
         } while (p++, --n); 
        } 
        else 
        { 
         FlushConsoleInputBuffer(hStdIn); 
         break; 
        } 

       } while (NumberOfEvents -= NumberOfEventsRead); 
      } 
      continue; 

     case STATUS_USER_APC: 
      DbgPrint("\nUSER_APC\n"); 
      return; 
     case STATUS_ALERTED: 
      DbgPrint("\nALERTED\n"); 
      return; 
     case WAIT_FAILED : 
      DbgPrint("\nWAIT_FAILED=%u\n", GetLastError()); 
      return; 
     default: 
      __debugbreak(); 
      return; 
     } 
    } 
} 

void SimpleDemo() 
{ 
    if (HANDLE hCurrentThread = OpenThread(THREAD_ALERT|THREAD_SET_CONTEXT , FALSE, GetCurrentThreadId())) 
    { 
     ULONG dwThreadId; 
     HANDLE hThread = CreateThread(0, 0, BreakWaitThread, hCurrentThread, 0, &dwThreadId); 

     if (hThread) 
     { 
      ConsoleLoop(GetStdHandle(STD_INPUT_HANDLE)); 
      PostThreadMessage(dwThreadId, WM_QUIT, 0, 0); 
      WaitForSingleObject(hThread, INFINITE); 
      CloseHandle(hThread); 
     } 
     else 
     { 
      CloseHandle(hCurrentThread); 
     } 
    } 
} 
+0

この非常に詳細な返信と背景情報をありがとう! – nh2

関連する問題