Quick.Console.pas 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810
  1. { ***************************************************************************
  2. Copyright (c) 2016-2018 Kike Pérez
  3. Unit : Quick.Console
  4. Description : Console output with colors and optional file log
  5. Author : Kike Pérez
  6. Version : 1.8
  7. Created : 10/05/2017
  8. Modified : 09/03/2018
  9. This file is part of QuickLib: https://github.com/exilon/QuickLib
  10. ***************************************************************************
  11. Licensed under the Apache License, Version 2.0 (the "License");
  12. you may not use this file except in compliance with the License.
  13. You may obtain a copy of the License at
  14. http://www.apache.org/licenses/LICENSE-2.0
  15. Unless required by applicable law or agreed to in writing, software
  16. distributed under the License is distributed on an "AS IS" BASIS,
  17. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  18. See the License for the specific language governing permissions and
  19. limitations under the License.
  20. *************************************************************************** }
  21. unit Quick.Console;
  22. {$i QuickLib.inc}
  23. {$IFDEF CONDITIONALEXPRESSIONS}
  24. {$ifndef VER140}
  25. {$ifndef LINUX}
  26. {$define WITHUXTHEME}
  27. {$endif}
  28. {$endif}
  29. {$IFDEF DELPHI2005_UP}
  30. {$DEFINE INLINES}
  31. {$ENDIF}
  32. {$IF RTLVersion >= 14.0}
  33. {$DEFINE HASERROUTPUT}
  34. {$ENDIF}
  35. {$ENDIF}
  36. interface
  37. uses
  38. Classes,
  39. Windows,
  40. Messages,
  41. SysUtils,
  42. Quick.Commons,
  43. Quick.Log;
  44. type
  45. //text colors
  46. TConsoleColor = (
  47. ccBlack = 0,
  48. ccBlue = 1,
  49. ccGreen = 2,
  50. ccCyan = 3,
  51. ccRed = 4,
  52. ccMagenta = 5,
  53. ccBrown = 6,
  54. ccLightGray = 7,
  55. ccDarkGray = 8,
  56. ccLightBlue = 9,
  57. ccLightGreen = 10,
  58. ccLightCyan = 11,
  59. ccLightRed = 12,
  60. ccLightMagenta = 13,
  61. ccYellow = 14,
  62. ccWhite = 15);
  63. TConsoleProperties = record
  64. LogVerbose : TLogVerbose;
  65. Log : TQuickLog;
  66. end;
  67. {$IFNDEF FPC}
  68. TOutputProc<T> = reference to procedure(const aLine : T);
  69. TExecuteProc = reference to procedure;
  70. {$ELSE}
  71. TOutputProc<T> = procedure(const aLine : T) of object;
  72. TExecuteProc = procedure of object;
  73. {$ENDIF}
  74. TConsoleMenuOption = record
  75. private
  76. fCaption : string;
  77. fKey : Word;
  78. fOnKeyPressed : TExecuteProc;
  79. public
  80. property Caption : string read fCaption write fCaption;
  81. property Key : Word read fKey write fKey;
  82. property OnKeyPressed : TExecuteProc read fOnKeyPressed write fOnKeyPressed;
  83. procedure DoKeyPressed;
  84. end;
  85. TConsoleMenu = class
  86. private
  87. fConsoleMenu : array of TConsoleMenuOption;
  88. fMenuColor : TConsoleColor;
  89. fIsActive : Boolean;
  90. procedure WriteMenu;
  91. public
  92. constructor Create;
  93. property MenuColor : TConsoleColor read fMenuColor write fMenuColor;
  94. property IsActive : Boolean read fIsActive;
  95. procedure AddMenu(const cMenuCaption : string; const cMenuKey : Word; MenuAction : TExecuteProc); overload;
  96. procedure AddMenu(MenuOption : TConsoleMenuOption); overload;
  97. procedure Refresh(aClearScreen : Boolean = False);
  98. procedure WaitForKeys;
  99. end;
  100. procedure cout(const cMsg : Integer; cEventType : TLogEventType); overload;
  101. procedure cout(const cMsg : Double; cEventType : TLogEventType); overload;
  102. procedure cout(const cMsg : string; cEventType : TLogEventType); overload;
  103. procedure cout(const cMsg : string; cColor : TConsoleColor); overload;
  104. procedure cout(const cMsg : string; params : array of const; cEventType : TLogEventType); overload;
  105. procedure coutXY(x,y : Integer; const cMsg : string; cEventType : TLogEventType); overload;
  106. procedure coutXY(x,y : Integer; const cMsg : string; cColor : TConsoleColor); overload;
  107. procedure coutXY(x,y : Integer; const cMsg : string; params : array of const; cEventType : TLogEventType); overload;
  108. procedure coutXY(x,y : Integer; const cMsg : string; params : array of const; cColor : TConsoleColor); overload;
  109. procedure coutTL(const cMsg : string; cEventType : TLogEventType); overload;
  110. procedure coutTL(const cMsg : string; cColor : TConsoleColor); overload;
  111. procedure coutBL(const cMsg : string; cEventType : TLogEventType); overload;
  112. procedure coutBL(const cMsg : string; cColor : TConsoleColor); overload;
  113. procedure coutFmt(const cMsg : string; params : array of const; cEventType : TLogEventType);
  114. procedure TextColor(Color: TConsoleColor); overload;
  115. procedure TextColor(Color: Byte); overload;
  116. procedure TextBackground(Color: TConsoleColor); overload;
  117. procedure TextBackground(Color: Byte); overload;
  118. procedure ResetColors;
  119. procedure ConsoleResize(Width, Height : Integer);
  120. procedure ClearScreen;
  121. procedure ClearLine; overload;
  122. procedure ClearLine(Y : Integer); overload;
  123. procedure ShowCursor;
  124. procedure HideCursor;
  125. function GetCursorX: Integer; {$IFDEF INLINES}inline;{$ENDIF}
  126. function GetCursorY: Integer; {$IFDEF INLINES}inline;{$ENDIF}
  127. function GetCursorMaxBottom : Integer;
  128. procedure SetCursorPos(NewCoord : TCoord);
  129. procedure ProcessMessages;
  130. procedure ConsoleWaitForEnterKey;
  131. procedure RunConsoleCommand(const aCommand, aParameters : String; CallBack : TOutputProc<PAnsiChar> = nil; OutputLines : TStrings = nil);
  132. procedure InitConsole;
  133. var
  134. Console : TConsoleProperties;
  135. CSConsole : TRTLCriticalSection;
  136. LastMode : Word;
  137. DefConsoleColor : Byte;
  138. TextAttr : Byte;
  139. hStdOut: THandle;
  140. hStdErr: THandle;
  141. ConsoleRect: TSmallRect;
  142. ScreenBufInfo : TConsoleScreenBufferInfo;
  143. CursorInfo : TConsoleCursorInfo;
  144. implementation
  145. procedure cout(const cMsg : Integer; cEventType : TLogEventType);
  146. var
  147. FmtSets : TFormatSettings;
  148. begin
  149. try
  150. {$IFNDEF FPC}
  151. FmtSets := TFormatSettings.Create;
  152. {$ENDIF}
  153. FmtSets.ThousandSeparator := '.';
  154. FmtSets.DecimalSeparator := ',';
  155. cout(FormatFloat('0,',cMsg,FmtSets),cEventType);
  156. except
  157. cout(cMsg.ToString,cEventType);
  158. end;
  159. end;
  160. procedure cout(const cMsg : Double; cEventType : TLogEventType);
  161. var
  162. FmtSets : TFormatSettings;
  163. begin
  164. try
  165. {$IFNDEF FPC}
  166. FmtSets := TFormatSettings.Create;
  167. {$ENDIF}
  168. FmtSets.ThousandSeparator := '.';
  169. FmtSets.DecimalSeparator := ',';
  170. cout(FormatFloat('.0###,',cMsg,FmtSets),cEventType);
  171. except
  172. cout(cMsg.ToString,cEventType);
  173. end;
  174. end;
  175. procedure cout(const cMsg : string; cEventType : TLogEventType);
  176. begin
  177. if cEventType in Console.LogVerbose then
  178. begin
  179. EnterCriticalSection(CSConsole);
  180. try
  181. if hStdOut <> 0 then
  182. begin
  183. case cEventType of
  184. etError : TextColor(ccLightRed);
  185. etInfo : TextColor(ccWhite);
  186. etSuccess : TextColor(ccLightGreen);
  187. etWarning : TextColor(ccYellow);
  188. etDebug : TextColor(ccLightCyan);
  189. etTrace : TextColor(ccLightMagenta);
  190. else TextColor(ccWhite);
  191. end;
  192. Writeln(cMsg);
  193. TextColor(LastMode);
  194. end;
  195. finally
  196. LeaveCriticalSection(CSConsole);
  197. end;
  198. if Assigned(Console.Log) then Console.Log.Add(cMsg,cEventType);
  199. end;
  200. end;
  201. procedure cout(const cMsg : string; cColor : TConsoleColor);
  202. begin
  203. EnterCriticalSection(CSConsole);
  204. try
  205. if hStdOut <> 0 then
  206. begin
  207. TextColor(cColor);
  208. Writeln(cMsg);
  209. TextColor(LastMode);
  210. end;
  211. finally
  212. LeaveCriticalSection(CSConsole);
  213. end;
  214. end;
  215. procedure cout(const cMsg : string; params : array of const; cEventType : TLogEventType);
  216. begin
  217. cout(Format(cMsg,params),cEventType);
  218. end;
  219. function GetCursorX: Integer; {$IFDEF INLINES}inline;{$ENDIF}
  220. var
  221. BufferInfo: TConsoleScreenBufferInfo;
  222. begin
  223. GetConsoleSCreenBufferInfo(hStdOut, BufferInfo);
  224. Result := BufferInfo.dwCursorPosition.X;
  225. end;
  226. function GetCursorY: Integer; {$IFDEF INLINES}inline;{$ENDIF}
  227. var
  228. BufferInfo: TConsoleScreenBufferInfo;
  229. begin
  230. GetConsoleSCreenBufferInfo(hStdOut, BufferInfo);
  231. Result := BufferInfo.dwCursorPosition.Y;
  232. end;
  233. function GetCursorMaxBottom : Integer;
  234. var
  235. BufferInfo: TConsoleScreenBufferInfo;
  236. begin
  237. GetConsoleSCreenBufferInfo(hStdOut, BufferInfo);
  238. Result := BufferInfo.srWindow.Bottom;
  239. end;
  240. procedure SetCursorPos(NewCoord : TCoord);
  241. begin
  242. SetConsoleCursorPosition(hStdOut, NewCoord);
  243. end;
  244. procedure coutXY(x,y : Integer; const cMsg : string; cEventType : TLogEventType);
  245. var
  246. NewCoord : TCoord;
  247. LastCoord : TCoord;
  248. begin
  249. if hStdOut = 0 then Exit;
  250. LastCoord.X := GetCursorX;
  251. LastCoord.Y := GetCursorY;
  252. NewCoord.X := x;
  253. NewCoord.Y := y;
  254. ClearLine(Y);
  255. SetCursorPos(NewCoord);
  256. try
  257. cout(cMsg,cEventType);
  258. finally
  259. SetCursorPos(LastCoord);
  260. end;
  261. end;
  262. procedure coutXY(x,y : Integer; const cMsg : string; cColor : TConsoleColor); overload;
  263. var
  264. NewCoord : TCoord;
  265. LastCoord : TCoord;
  266. begin
  267. if hStdOut = 0 then Exit;
  268. LastCoord.X := GetCursorX;
  269. LastCoord.Y := GetCursorY;
  270. NewCoord.X := x;
  271. NewCoord.Y := y;
  272. ClearLine(Y);
  273. SetCursorPos(NewCoord);
  274. try
  275. cout(cMsg,cColor);
  276. finally
  277. SetCursorPos(LastCoord);
  278. end;
  279. end;
  280. procedure coutXY(x,y : Integer; const cMsg : string; params : array of const; cEventType : TLogEventType);
  281. begin
  282. coutXY(x,y,Format(cMsg,params),cEventType);
  283. end;
  284. procedure coutXY(x,y : Integer; const cMsg : string; params : array of const; cColor : TConsoleColor);
  285. begin
  286. coutXY(x,y,Format(cMsg,params),cColor);
  287. end;
  288. procedure coutTL(const cMsg : string; cEventType : TLogEventType);
  289. begin
  290. coutXY(0,0,cMsg,cEventType);
  291. end;
  292. procedure coutTL(const cMsg : string; cColor : TConsoleColor);
  293. begin
  294. coutXY(0,0,cMsg,cColor);
  295. end;
  296. procedure coutBL(const cMsg : string; cEventType : TLogEventType);
  297. begin
  298. coutXY(0,GetCursorMaxBottom - 1,cMsg,cEventType);
  299. end;
  300. procedure coutBL(const cMsg : string; cColor : TConsoleColor);
  301. begin
  302. coutXY(0,GetCursorMaxBottom - 1,cMsg,cColor);
  303. end;
  304. procedure coutFmt(const cMsg : string; params : array of const; cEventType : TLogEventType);
  305. begin
  306. cout(Format(cMsg,params),cEventType);
  307. end;
  308. procedure TextColor(Color: TConsoleColor);
  309. begin
  310. TextColor(Integer(Color));
  311. end;
  312. procedure TextColor(Color: Byte);
  313. begin
  314. if hStdOut = 0 then Exit;
  315. LastMode := TextAttr;
  316. TextAttr := (TextAttr and $F0) or (Color and $0F);
  317. if TextAttr <> LastMode then SetConsoleTextAttribute(hStdOut, TextAttr);
  318. end;
  319. procedure TextBackground(Color: TConsoleColor);
  320. begin
  321. TextBackground(Integer(Color));
  322. end;
  323. procedure TextBackground(Color: Byte);
  324. begin
  325. if hStdOut = 0 then Exit;
  326. LastMode := TextAttr;
  327. TextAttr := (TextAttr and $0F) or ((Color shl 4) and $F0);
  328. if TextAttr <> LastMode then SetConsoleTextAttribute(hStdOut, TextAttr);
  329. end;
  330. procedure ResetColors;
  331. begin
  332. SetConsoleTextAttribute(hStdOut, DefConsoleColor);
  333. TextAttr := DefConsoleColor;
  334. end;
  335. procedure ConsoleResize(Width, Height : Integer);
  336. var
  337. Rect: TSmallRect;
  338. Coord: TCoord;
  339. begin
  340. Rect.Left := 1;
  341. Rect.Top := 1;
  342. Rect.Right := Width;
  343. Rect.Bottom := Height;
  344. Coord.X := Rect.Right + 1 - Rect.Left;
  345. Coord.y := Rect.Bottom + 1 - Rect.Top;
  346. SetConsoleScreenBufferSize(GetStdHandle(STD_OUTPUT_HANDLE), Coord);
  347. SetConsoleWindowInfo(GetStdHandle(STD_OUTPUT_HANDLE), True, Rect);
  348. end;
  349. procedure ClearScreen;
  350. var
  351. stdout: THandle;
  352. bufinfo: TConsoleScreenBufferInfo;
  353. ConsoleSize: DWORD;
  354. NumWritten: DWORD;
  355. Origin: TCoord;
  356. begin
  357. stdout := GetStdHandle(STD_OUTPUT_HANDLE);
  358. if stdout<>INVALID_HANDLE_VALUE then
  359. begin
  360. GetConsoleScreenBufferInfo(stdout,bufinfo);
  361. ConsoleSize := bufinfo.dwSize.X * bufinfo.dwSize.Y;
  362. Origin.X := 0;
  363. Origin.Y := 0;
  364. FillConsoleOutputCharacter(stdout,' ',ConsoleSize,Origin,NumWritten);
  365. FillConsoleOutputAttribute(stdout,bufinfo.wAttributes,ConsoleSize,Origin,NumWritten);
  366. SetConsoleCursorPosition(stdout, Origin);
  367. end;
  368. end;
  369. procedure ClearLine;
  370. begin
  371. ClearLine(GetCursorY);
  372. end;
  373. procedure ClearLine(Y : Integer);
  374. var
  375. dwWriteCoord: TCoord;
  376. dwCount, dwSize: DWord;
  377. begin
  378. if hStdOut = 0 then Exit;
  379. dwWriteCoord.X := 0;
  380. dwWriteCoord.Y := Y;
  381. dwSize := ConsoleRect.Right + 1;
  382. FillConsoleOutputAttribute(hStdOut, TextAttr, dwSize, dwWriteCoord, dwCount);
  383. FillConsoleOutputCharacter(hStdOut, ' ', dwSize, dwWriteCoord, dwCount);
  384. end;
  385. procedure ShowCursor;
  386. begin
  387. GetConsoleCursorInfo(hStdOut,CursorInfo);
  388. CursorInfo.bVisible := True;
  389. SetConsoleCursorInfo(hStdOut,CursorInfo);
  390. end;
  391. procedure HideCursor;
  392. begin
  393. GetConsoleCursorInfo(hStdOut,CursorInfo);
  394. CursorInfo.bVisible := False;
  395. SetConsoleCursorInfo(hStdOut,CursorInfo);
  396. end;
  397. function ConsoleKeyPressed(ExpectedKey: Word): Boolean;
  398. var
  399. lpNumberOfEvents: DWORD;
  400. lpBuffer: TInputRecord;
  401. lpNumberOfEventsRead : DWORD;
  402. nStdHandle: THandle;
  403. begin
  404. Result := False;
  405. nStdHandle := GetStdHandle(STD_INPUT_HANDLE);
  406. lpNumberOfEvents := 0;
  407. GetNumberOfConsoleInputEvents(nStdHandle, lpNumberOfEvents);
  408. if lpNumberOfEvents <> 0 then
  409. begin
  410. PeekConsoleInput(nStdHandle, lpBuffer, 1, lpNumberOfEventsRead);
  411. if lpNumberOfEventsRead <> 0 then
  412. begin
  413. if lpBuffer.EventType = KEY_EVENT then
  414. begin
  415. if lpBuffer.Event.KeyEvent.bKeyDown and ((ExpectedKey = 0) or (lpBuffer.Event.KeyEvent.wVirtualKeyCode = ExpectedKey)) then Result := true
  416. else FlushConsoleInputBuffer(nStdHandle);
  417. end
  418. else FlushConsoleInputBuffer(nStdHandle);
  419. end;
  420. end;
  421. end;
  422. function GetConsoleKeyPressed : Word;
  423. var
  424. lpNumberOfEvents: DWORD;
  425. lpBuffer: TInputRecord;
  426. lpNumberOfEventsRead : DWORD;
  427. nStdHandle: THandle;
  428. begin
  429. Result := 0;
  430. nStdHandle := GetStdHandle(STD_INPUT_HANDLE);
  431. lpNumberOfEvents := 0;
  432. GetNumberOfConsoleInputEvents(nStdHandle, lpNumberOfEvents);
  433. if lpNumberOfEvents <> 0 then
  434. begin
  435. PeekConsoleInput(nStdHandle, lpBuffer, 1, lpNumberOfEventsRead);
  436. if lpNumberOfEventsRead <> 0 then
  437. begin
  438. if lpBuffer.EventType = KEY_EVENT then
  439. begin
  440. Result := lpBuffer.Event.KeyEvent.wVirtualKeyCode;
  441. FlushConsoleInputBuffer(nStdHandle);
  442. end
  443. else FlushConsoleInputBuffer(nStdHandle);
  444. end;
  445. end;
  446. end;
  447. procedure ProcessMessages;
  448. var
  449. Msg: TMsg;
  450. begin
  451. while integer(PeekMessage(Msg, 0, 0, 0, PM_REMOVE)) <> 0 do begin
  452. TranslateMessage(Msg);
  453. DispatchMessage(Msg);
  454. end;
  455. end;
  456. {$IFDEF MSWINDOWS}
  457. procedure ConsoleWaitForEnterKey;
  458. var
  459. msg: TMsg;
  460. begin
  461. while not ConsoleKeyPressed(VK_RETURN) do
  462. begin
  463. {$ifndef LVCL}
  464. {$IFDEF FPC}
  465. if GetCurrentThreadID = MainThreadID then
  466. begin
  467. CheckSynchronize;
  468. Sleep(1);
  469. end
  470. else
  471. {$ELSE}
  472. if GetCurrentThreadID = MainThreadID then CheckSynchronize{$IFDEF DELPHI7_UP}(1000){$ENDIF} else
  473. {$ENDIF}
  474. {$endif}
  475. WaitMessage;
  476. while PeekMessage(msg,0,0,0,PM_REMOVE) do
  477. begin
  478. if Msg.Message = WM_QUIT then Exit
  479. else
  480. begin
  481. TranslateMessage(Msg);
  482. DispatchMessage(Msg);
  483. end;
  484. end;
  485. end;
  486. end;
  487. {$ELSE}
  488. procedure ConsoleWaitForEnterKey;
  489. begin
  490. ReadLn;
  491. end;
  492. {$ENDIF}
  493. procedure RunConsoleCommand(const aCommand, aParameters : String; CallBack : TOutputProc<PAnsiChar> = nil; OutputLines : TStrings = nil);
  494. const
  495. CReadBuffer = 2400;
  496. var
  497. saSecurity: Windows.TSecurityAttributes;
  498. hRead: THandle;
  499. hWrite: THandle;
  500. suiStartup: TStartupInfo;
  501. piProcess: TProcessInformation;
  502. pBuffer: array [0..CReadBuffer] of AnsiChar;
  503. dBuffer: array [0..CReadBuffer] of AnsiChar;
  504. dRead: DWORD;
  505. dRunning: DWORD;
  506. dAvailable: DWORD;
  507. begin
  508. saSecurity.nLength := SizeOf(Windows.TSecurityAttributes);
  509. saSecurity.bInheritHandle := true;
  510. saSecurity.lpSecurityDescriptor := nil;
  511. if CreatePipe(hRead,hWrite,@saSecurity, 0) then
  512. begin
  513. try
  514. FillChar(suiStartup, SizeOf(TStartupInfo), #0);
  515. suiStartup.cb := SizeOf(TStartupInfo);
  516. suiStartup.hStdInput := hRead;
  517. suiStartup.hStdOutput := hWrite;
  518. suiStartup.hStdError := hWrite;
  519. suiStartup.dwFlags := STARTF_USESTDHANDLES or STARTF_USESHOWWINDOW;
  520. suiStartup.wShowWindow := SW_HIDE;
  521. if CreateProcess(nil,PChar(Format('%s %s',[aCommand,aParameters])),
  522. @saSecurity,
  523. @saSecurity,
  524. True,
  525. NORMAL_PRIORITY_CLASS,
  526. nil,
  527. nil,
  528. suiStartup,
  529. piProcess) then
  530. begin
  531. try
  532. repeat
  533. dRunning := WaitForSingleObject(piProcess.hProcess,100);
  534. PeekNamedPipe(hRead,nil,0,nil,@dAvailable,nil);
  535. if (dAvailable > 0) then
  536. begin
  537. repeat
  538. dRead := 0;
  539. ReadFile(hRead,pBuffer[0],CReadBuffer,dRead,nil);
  540. pBuffer[dRead] := #0;
  541. OemToCharA(pBuffer,dBuffer);
  542. if Assigned(CallBack) then CallBack(dBuffer);
  543. if Assigned(OutputLines) then OutputLines.Add(dBuffer);
  544. until (dRead < CReadBuffer);
  545. end;
  546. //Application.ProcessMessages;
  547. until (dRunning <> WAIT_TIMEOUT);
  548. finally
  549. CloseHandle(piProcess.hProcess);
  550. CloseHandle(piProcess.hThread);
  551. end;
  552. end;
  553. finally
  554. CloseHandle(hRead);
  555. CloseHandle(hWrite);
  556. end;
  557. end
  558. else raise Exception.Create('Can''t create pipe!');
  559. end;
  560. procedure InitConsole;
  561. var
  562. BufferInfo: TConsoleScreenBufferInfo;
  563. begin
  564. Console.LogVerbose := LOG_ALL;
  565. Rewrite(Output);
  566. hStdOut := TTextRec(Output).Handle;
  567. {$IFDEF HASERROUTPUT}
  568. Rewrite(ErrOutput);
  569. hStdErr := TTextRec(ErrOutput).Handle;
  570. {$ELSE}
  571. hStdErr := GetStdHandle(STD_ERROR_HANDLE);
  572. {$ENDIF}
  573. if not GetConsoleScreenBufferInfo(hStdOut, BufferInfo) then
  574. begin
  575. {$IFNDEF FPC}
  576. SetInOutRes(GetLastError);
  577. {$ENDIF}
  578. Exit;
  579. end;
  580. ConsoleRect.Left := 0;
  581. ConsoleRect.Top := 0;
  582. ConsoleRect.Right := BufferInfo.dwSize.X - 1;
  583. ConsoleRect.Bottom := BufferInfo.dwSize.Y - 1;
  584. TextAttr := BufferInfo.wAttributes and $FF;
  585. DefConsoleColor := TextAttr;
  586. LastMode := 3; //CO80;
  587. end;
  588. { TConsoleMenu }
  589. procedure TConsoleMenu.AddMenu(const cMenuCaption: string; const cMenuKey: Word; MenuAction: TExecuteProc);
  590. var
  591. conmenu : TConsoleMenuOption;
  592. begin
  593. conmenu.Caption := cMenuCaption;
  594. conmenu.Key := cMenuKey;
  595. conmenu.OnKeyPressed := MenuAction;
  596. {$IFDEF DELPHIXE7_UP}
  597. fConsoleMenu := fConsoleMenu + [conmenu];
  598. {$ELSE}
  599. SetLength(fConsoleMenu,High(fConsoleMenu)+1);
  600. fConsoleMenu[High(fConsoleMenu)] := conmenu;
  601. {$ENDIF}
  602. end;
  603. procedure TConsoleMenu.AddMenu(MenuOption: TConsoleMenuOption);
  604. begin
  605. {$IFDEF DELPHIXE7_UP}
  606. fConsoleMenu := fConsoleMenu + [MenuOption];
  607. {$ELSE}
  608. SetLength(fConsoleMenu,High(fConsoleMenu)+1);
  609. fConsoleMenu[High(fConsoleMenu)] := MenuOption;
  610. {$ENDIF}
  611. end;
  612. constructor TConsoleMenu.Create;
  613. begin
  614. fMenuColor := ccLightCyan;
  615. fIsActive := False;
  616. end;
  617. procedure TConsoleMenu.Refresh(aClearScreen: Boolean);
  618. begin
  619. if aClearScreen then ClearScreen;
  620. WriteMenu;
  621. end;
  622. procedure TConsoleMenu.WaitForKeys;
  623. var
  624. msg: TMsg;
  625. conmenu : TConsoleMenuOption;
  626. keypressed : Word;
  627. begin
  628. fIsActive := True;
  629. HideCursor;
  630. WriteMenu;
  631. while True do
  632. begin
  633. //check key pressed
  634. keypressed := GetConsoleKeyPressed;
  635. for conmenu in fConsoleMenu do
  636. begin
  637. if keypressed = conmenu.Key then
  638. begin
  639. ClearScreen;
  640. WriteMenu;
  641. conmenu.DoKeyPressed;
  642. end;
  643. end;
  644. if keypressed = VK_ESCAPE then
  645. begin
  646. coutXY(50,12,'Exiting...',etInfo);
  647. Exit;
  648. end;
  649. {$ifndef LVCL}
  650. if GetCurrentThreadID=MainThreadID then CheckSynchronize{$ifdef WITHUXTHEME}(1000){$endif} else
  651. {$endif}
  652. WaitMessage;
  653. while PeekMessage(msg,0,0,0,PM_REMOVE) do
  654. begin
  655. if Msg.Message = WM_QUIT then Exit
  656. else
  657. begin
  658. TranslateMessage(Msg);
  659. DispatchMessage(Msg);
  660. end;
  661. end;
  662. end;
  663. ShowCursor;
  664. fIsActive := False;
  665. end;
  666. function GetCharFromVirtualKey(Key: Word): string;
  667. var
  668. keyboardState: TKeyboardState;
  669. asciiResult: Integer;
  670. begin
  671. GetKeyboardState(keyboardState) ;
  672. SetLength(Result, 2) ;
  673. asciiResult := ToAscii(key, MapVirtualKey(key, 0), keyboardState, @Result[1], 0) ;
  674. case asciiResult of
  675. 0: Result := '';
  676. 1: SetLength(Result, 1) ;
  677. 2:;
  678. else
  679. Result := '';
  680. end;
  681. end;
  682. procedure TConsoleMenu.WriteMenu;
  683. var
  684. conmenu : TConsoleMenuOption;
  685. ckey : string;
  686. coord : TCoord;
  687. oldcoord : TCoord;
  688. begin
  689. oldcoord.X := GetCursorX;
  690. oldcoord.Y := GetCursorY;
  691. coord.X := 0;
  692. coord.Y := 0;
  693. SetCursorPos(coord);
  694. TextColor(fMenuColor);
  695. ClearLine(0);
  696. for conmenu in fConsoleMenu do
  697. begin
  698. case conmenu.Key of
  699. VK_F1 : ckey := 'F1';
  700. VK_F2 : ckey := 'F2';
  701. VK_F3 : ckey := 'F3';
  702. VK_F4 : ckey := 'F4';
  703. VK_F5 : ckey := 'F5';
  704. VK_F6 : ckey := 'F6';
  705. VK_F7 : ckey := 'F7';
  706. VK_F8 : ckey := 'F8';
  707. VK_F9 : ckey := 'F9';
  708. VK_F10 : ckey := 'F10';
  709. VK_F11 : ckey := 'F11';
  710. VK_F12 : ckey := 'F12';
  711. else ckey := GetCharFromVirtualKey(conmenu.Key);
  712. end;
  713. TextColor(ccWhite);
  714. Write(Format('[%s]',[ckey]));
  715. TextColor(Self.MenuColor);
  716. Write(Format(' %s ',[conmenu.Caption]));
  717. end;
  718. TextColor(ccWhite);
  719. Write('[ESC]');
  720. TextColor(Self.MenuColor);
  721. Write(' Exit');
  722. TextColor(LastMode);
  723. SetCursorPos(oldcoord);
  724. end;
  725. { TConsoleMenuOption }
  726. procedure TConsoleMenuOption.DoKeyPressed;
  727. begin
  728. if Assigned(fOnKeyPressed) then fOnKeyPressed;
  729. end;
  730. initialization
  731. InitializeCriticalSection(CSConsole);
  732. //init stdout if not a service
  733. if GetStdHandle(STD_OUTPUT_HANDLE) <> 0 then InitConsole;
  734. finalization
  735. DeleteCriticalSection(CSConsole);
  736. end.