【问题标题】:How to make a console application wait for the "Enter" key, but automatically continue after time?如何使控制台应用程序等待“Enter”键,但在一段时间后自动继续?
【发布时间】:2013-04-07 23:27:30
【问题描述】:

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

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

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

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

【问题讨论】:

  • Readln 阻止执行流程,直到用户按下 RETURN
  • 嗯,是的,这就是问题的症结不是吗
  • 无论你做什么,都不要编写等到按下 SHIFT 键的程序。这些键在您的键盘上不起作用。不过说真的,请使用大写字母并表明您关心这个问题。
  • 你读过我的cmets吗?我很生气我花时间改进你的问题,而你却放弃了我的改进。就好像你不在乎一样。
  • @LURD 这完全是一个不同的话题。

标签: delphi console-application delphi-7


【解决方案1】:

您可以试试这段代码(改编自我们的SynCommons.pas 单元,在我们的mORMot 框架内):

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 消息循环。我希望这个过程正好满足您的需要。

【讨论】:

  • 它不识别任何变量,库是什么?
  • 没有库,我猜只有普通的 Windows API 调用。
【解决方案2】:

我以前做过几次类似的事情:

首先声明几个全局变量:

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 等待返回,但不超过给定的秒数。但是在使用此功能之前,您必须设置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 可能会更加棘手。

【讨论】:

  • 好的,谢谢,这是有效的,但是你的代码中缺少一些运算符,我不得不输入,但它正在工作
【解决方案3】:

单位控制台

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.

【讨论】:

  • 感谢您发布此控制台单元!这将是有用的。它是从更大的单位或图书馆复制的吗?
猜你喜欢
  • 2015-07-27
  • 1970-01-01
  • 1970-01-01
  • 2019-01-15
  • 2023-03-20
  • 2012-06-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多