我在delphi 7中创建了一个控制台应用程序,它应该在你按下回车键后显示消息:
begin
writeln ('Press ENTER to continue');
readln;
writeln ('blablabla');
writeln ('blablabla');
end;
问题是用户可以按任意按钮继续,这就是问题所在。如果用户按下键盘的输入按钮,我只希望程序继续。除此之外,我需要它在一段时间后自动继续,例如5秒,无需用户输入。
如何让控制台应用程序等待一段时间让用户按下Enter键,但如果用户没有,则会自动继续?
答案 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.