如何使控制台应用程序等待“Enter”键,但在一段时间后自动继续?

时间:2013-04-15 18:21:17

标签: delphi console-application delphi-7

我在delphi 7中创建了一个控制台应用程序,它应该在你按下回车键后显示消息:

begin
  writeln ('Press ENTER to continue');
  readln;
  writeln ('blablabla');
  writeln ('blablabla');
end;

问题是用户可以按任意按钮继续,这就是问题所在。如果用户按下键盘的输入按钮,我只希望程序继续。除此之外,我需要它在一段时间后自动继续,例如5秒,无需用户输入。

如何让控制台应用程序等待一段时间让用户按下Enter键,但如果用户没有,则会自动继续?

3 个答案:

答案 0 :(得分:9)

您可以尝试使用我们的 mORMot 框架中的SynCommons.pas单元修改此代码:

procedure ConsoleWaitForEnterKey(TimeOut: integer);
  function KeyPressed(ExpectedKey: Word):Boolean;
  var lpNumberOfEvents: DWORD;
      lpBuffer: TInputRecord;
      lpNumberOfEventsRead : DWORD;
      nStdHandle: THandle;
  begin
    result := false;
    nStdHandle := GetStdHandle(STD_INPUT_HANDLE);
    lpNumberOfEvents := 0;
    GetNumberOfConsoleInputEvents(nStdHandle,lpNumberOfEvents);
    if lpNumberOfEvents<>0 then begin
      PeekConsoleInput(nStdHandle,lpBuffer,1,lpNumberOfEventsRead);
      if lpNumberOfEventsRead<>0 then
        if lpBuffer.EventType=KEY_EVENT then
          if lpBuffer.Event.KeyEvent.bKeyDown and
             ((ExpectedKey=0) or (lpBuffer.Event.KeyEvent.wVirtualKeyCode=ExpectedKey)) then
            result := true else
            FlushConsoleInputBuffer(nStdHandle) else
          FlushConsoleInputBuffer(nStdHandle);
    end;
  end;
    var Stop: cardinal;
begin
  Stop := GetTickCount+TimeOut*1000;
  while (not KeyPressed(VK_RETURN)) and (GetTickCount<Stop) do 
    Sleep(50); // check every 50 ms
end;

请注意, mORMot 中嵌入的版本允许调用TThread.Synchronize()方法,并在必要时处理GDI消息循环。我希望这个程序符合你的需要。

答案 1 :(得分:6)

我之前做了几次类似的事情:

首先声明一些全局变量:

var
  hIn: THandle;
  hTimer: THandle;
  threadID: cardinal;
  TimeoutAt: TDateTime;
  WaitingForReturn: boolean = false;
  TimerThreadTerminated: boolean = false;

其次,添加功能

function TimerThread(Parameter: pointer): integer;
var
  IR: TInputRecord;
  amt: cardinal;
begin
  result := 0;
  IR.EventType := KEY_EVENT;
  IR.Event.KeyEvent.bKeyDown := true;
  IR.Event.KeyEvent.wVirtualKeyCode := VK_RETURN;
  while not TimerThreadTerminated do
  begin
    if WaitingForReturn and (Now >= TimeoutAt) then
      WriteConsoleInput(hIn, IR, 1, amt);
    sleep(500);
  end;
end;

procedure StartTimerThread;
begin
  hTimer := BeginThread(nil, 0, TimerThread, nil, 0, threadID);
end;

procedure EndTimerThread;
begin
  TimerThreadTerminated := true;
  WaitForSingleObject(hTimer, 1000);
  CloseHandle(hTimer);
end;

procedure TimeoutWait(const Time: cardinal);
var
  IR: TInputRecord;
  nEvents: cardinal;
begin

  TimeOutAt := IncSecond(Now, Time);
  WaitingForReturn := true;

  while ReadConsoleInput(hIn, IR, 1, nEvents) do
    if (IR.EventType = KEY_EVENT) and
      (TKeyEventRecord(IR.Event).wVirtualKeyCode = VK_RETURN)
      and (TKeyEventRecord(IR.Event).bKeyDown) then
      begin
        WaitingForReturn := false;
        break;
      end;

end;

现在您可以使用TimeoutWait等待Return,但不会超过给定的秒数。但是在使用此功能之前,您必须设置hIn并调用StartTimerThread

begin

  hIn := GetStdHandle(STD_INPUT_HANDLE);
  StartTimerThread;

  Writeln('A');
  TimeoutWait(5);

  Writeln('B');
  TimeoutWait(5);

  Writeln('C');
  TimeoutWait(5);

  EndTimerThread;

end.

你可以摆脱StartTimerThread,特别是如果你每次调用都启动一个线程,但是连续多次调用TimeoutWait可能会更棘手。

答案 2 :(得分:4)

单位控制台

unit Console;

interface

procedure WaitAnyKeyPressed(const TextMessage: string = ''); overload; inline;
procedure WaitAnyKeyPressed(TimeDelay: Cardinal; const TextMessage: string = ''); overload; inline;
procedure WaitForKeyPressed(KeyCode: Word; const TextMessage: string = ''); overload; inline;
procedure WaitForKeyPressed(KeyCode: Word; TimeDelay: Cardinal; const TextMessage: string = ''); overload;

implementation

uses
  System.SysUtils, WinAPI.Windows;

procedure WaitAnyKeyPressed(const TextMessage: string);
begin
  WaitForKeyPressed(0, 0, TextMessage)
end;

procedure WaitAnyKeyPressed(TimeDelay: Cardinal; const TextMessage: string);
begin
  WaitForKeyPressed(0, TimeDelay, TextMessage)
end;

procedure WaitForKeyPressed(KeyCode: Word; const TextMessage: string);
begin
  WaitForKeyPressed(KeyCode, 0, TextMessage)
end;

type
  TTimer = record
    Started: TLargeInteger;
    Frequency: Cardinal;
  end;

var
  IsElapsed: function(const Timer: TTimer; Interval: Cardinal): Boolean;
  StartTimer: procedure(var Timer: TTimer);

procedure WaitForKeyPressed(KeyCode: Word; TimeDelay: Cardinal; const TextMessage: string);
var
  Handle: THandle;
  Buffer: TInputRecord;
  Counter: Cardinal;
  Timer: TTimer;
begin
  Handle := GetStdHandle(STD_INPUT_HANDLE);
  if Handle = 0 then
    RaiseLastOSError;
  if not (TextMessage = '') then
    Write(TextMessage);
  if not (TimeDelay = 0) then
    StartTimer(Timer);
  while True do
    begin
      Sleep(0);
      if not GetNumberOfConsoleInputEvents(Handle, Counter) then
        RaiseLastOSError;
      if not (Counter = 0) then
        begin
          if not ReadConsoleInput(Handle, Buffer, 1, Counter) then
            RaiseLastOSError;
          if (Buffer.EventType = KEY_EVENT) and Buffer.Event.KeyEvent.bKeyDown then
            if (KeyCode = 0) or (KeyCode = Buffer.Event.KeyEvent.wVirtualKeyCode) then
              Break
        end;
      if not (TimeDelay = 0) and IsElapsed(Timer, TimeDelay) then
        Break
    end
end;

function HardwareIsElapsed(const Timer: TTimer; Interval: Cardinal): Boolean;
var
  Passed: TLargeInteger;
begin
  QueryPerformanceCounter(Passed);
  Result := (Passed - Timer.Started) div Timer.Frequency > Interval
end;

procedure HardwareStartTimer(var Timer: TTimer);
var
  Frequency: TLargeInteger;
begin
  QueryPerformanceCounter(Timer.Started);
  QueryPerformanceFrequency(Frequency);
  Timer.Frequency := Frequency div 1000
end;

function SoftwareIsElapsed(const Timer: TTimer; Interval: Cardinal): Boolean;
begin
  Result := (GetCurrentTime - Cardinal(Timer.Started)) > Interval
end;

procedure SoftwareStartTimer(var Timer: TTimer);
begin
  PCardinal(@Timer.Started)^ := GetCurrentTime
end;

initialization
  if QueryPerformanceCounter(PLargeInteger(@@IsElapsed)^) and QueryPerformanceFrequency(PLargeInteger(@@IsElapsed)^) then
    begin
      StartTimer := HardwareStartTimer;
      IsElapsed := HardwareIsElapsed
    end
  else
    begin
      StartTimer := SoftwareStartTimer;
      IsElapsed := SoftwareIsElapsed
    end

end.

测试或示例程序

program Test;

{$APPTYPE CONSOLE}
{$R *.res}

uses
  WinAPI.Windows,
  Console in 'Console.pas';

 begin
  Console.WaitAnyKeyPressed('Press any key to continue ...');
  WriteLn;
  Console.WaitAnyKeyPressed(5000, 'I''ll wait 5 seconds until You press any key to continue ...');
  WriteLn;
  Console.WaitForKeyPressed(VK_SPACE, 'Press [Space] key to continue ...');
  WriteLn;
  Console.WaitForKeyPressed(VK_ESCAPE, 5000, 'I''ll wait 5 seconds until You press [Esc] key to continue ...');
  WriteLn
end.