LazarusCompleteGuide:4.2
콘솔 애플리케이션
콘솔 애플리케이션의 구조는 GUI 애플리케이션의 구조보다 훨씬 단순하다. 기본적으로 어떤 창도 포함하지 않고 (TForms와 같은) 시각적 컴포넌트도 포함하지 않는다. 물론 FCL로부터 비시각적 컴포넌트는 포함할 수 있다. 이는 주로 수동으로 정의 및 초기화된다.
라자루스는 일반 프리 파스칼 프로그램들을 처리할 수 있다. 그러한 경우 기본 연산은 GUI 애플리케이션에 사용된 프로젝트 파일과 다른 버전에서 (.lpr) 이루어진다.
모든 콘솔 애플리케이션은 텍스트를 읽고 쓰기 위해 표준 파스칼 입/출력 루틴을 사용하는 터미널(terminal) 창에서 통신한다. 터미널 창과 통신하기 위해 터미널 창에 텍스트가 순차적으로 작성된다. 사용자 입력은 텍스트로 읽히며, 보통은 [Enter] 키를 이용해 입력문장을 한 번에 완성한다. 좀 더 복잡한 텍스트 기반 사용자 인터페이스를 작성하는 데 사용할 수 있는 외부 라이브러리도 몇 가지 있다.
Linux/Unix 에서 선호되는 라이브러리는 ncurses 로, 다음 주소에서 이용 가능하다:
- http://www.freepascal.org/packages/ncurses.html (ncurses유닛에 헤더가 있는 C 라이브러리)
Free Vision은 다음 주소에서 이용할 수 있다:
- http://wiki.freepascal.org/Free_Vision (TurboPascal6과 7의 Turbo Vision에서 파생).
후자는 플랫폼 독립적 프리 파스칼 유닛인 Mouse, Keyboard, Video를 사용한다.
컴파일러에게 입/출력 채널을 필요로 하는 콘솔 애플리케이션을 생성하고자 하는 뜻을 알리기 위해선 (델파이와 호환 가능한) 컴파일러 명령을 {$apptype console} 프로젝트 파일에 (라자루스에서 .lpr 확장자) 삽입하여야 하는데, 이 파일은 키보드 프로그램이 있는 Pascal 프로그램의 메인 본체(main body)도 포함한다.
최소 콘솔 프로그램은 다음과 같은 모양을 갖출 것이다:
program Consoledemo;
{$apptype console}
begin
WriteLn('This is a console program');
end.
이번 경우 {$apptype console} 를 사용하지만, {$apptype gui} 를 명시할 수도 있다. 하지만 GUI 애플리케이션의 경우라면 그 지시어는 선택적이다.
비 윈도우 플랫폼에서 {$apptype console} 지시어를 이용해 프로그램을 컴파일할 경우 컴파일러 지시어가 플랫폼에서 지원되지 않는다는 경고문이 뜰 것이다. 지시어는 윈도우 상에서 프로그램이 시작될 때 텍스트모드 콘솔을 열도록 컴파일러에게 명령한다. 또한 텍스트 모드에 올바른 폰트가 선택되고 (운영체제에 따라 좌우) 외부 세계와 통신을 위한 표준 입출력 채널이 준비된다. 표준 출력은 텍스트 콘솔이고, 표준 입력은 키보드이다. 둘 다 "<"와 ">" 기호를 이용해 재지정(redirect)할 수 있다.
윈도우에서 콘솔 프로그램을 생성할 때 {$apptype console} 컴파일러 지시어가 설정되어 있지 않을 경우, Write 혹은 WriteLn 을 이용해 콘솔로 데이터 출력을 시도 시 런타임 오류 103이 발생하고, 그에 해당하는 예외가 던져진다. 리눅스와 유닉스에선 항상 콘솔을 이용할 수 있다.
출력은 프로그램이 콘솔 창에서 시작되었을 때만 표시될 것이다.
WriteLn('This is a console program');
Windows GUI 애플리케이션을 충돌하게 만드는 위의 행은 리눅스와 유닉스에서 오류 및 정보 메시지를 표시하는 매우 유용한 방법이다. 윈도우 호환 문제를 피하기 위해선 {$ifndef} 지시어를 이용해 이러한 출력 명령어가 프로그램의 윈도우 버전으로 컴파일되지 않도록 한다:
{$ifndef Windows}
WriteLn('This console writing issue only appears in Windows programs');
{$endif}
위의 예제들은 리눅스와 유닉스에 혼합 모드 콘솔과 GUI 프로그램을 생성하는 것이 가능함을 보여준다. 하지만 {$apptype console} 은 윈도우 GUI 애플리케이션에 대한 프로젝트 파일에 포함할 수도 있다. 그러한 경우, 프로그램이 실행되면 일반 GUI 창(들)과 함께 콘솔 창도 열릴 것이다. 애플리케이션이 종료되면 추가 창도 자동으로 닫힐 것이다. 이렇게 콘솔 창을 자동으로 여는 옵션은 리눅스에선 이용할 수 없다.
결론: 어떤 플랫폼이든 텍스트 콘솔을 활용하도록 프로그램을 설계할 수 있다.
그렇지만 이것이 사실 올바른 방법은 아니다. 라자루스에서 새 콘솔 프로그램을 생성하기 위해선 File→New 선택 메뉴에서 열리는 대화창에서 Console application 또는 Program 을 선택해야 한다.
Program을 선택하면 일반 프리 파스칼 프로그램이 생성된다. .lpr 파일 내 소스 코드에 {$apptype console} 지시어를 포함시키는 방법과, Project→Project Options 선택 메뉴의 Options for project: xyz 대화상자를 열어 Linking 페이지에서 (Compiler Options 헤드의 옵션 중 하나) Win32 gui Application (-WG) 확인상자를 체크 해제하는 방법 중 하나를 선택해야 한다. 두 방법 모두 동일한 결과가 발생하며, 둘 다 윈도우에서 사용할 수 있다!
관리된 콘솔 애플리케이션
위의 초간단 콘솔 프로그램보다 더 나은 대안 방법으로, File ⇒ New 를 선택한 후 나타나는 대화창에서 Console application 을 선택하는 방법을 들 수 있다. 이러한 경우, 라자루스는 좀 더 복잡한 프로그램 구조를 생성한다. 이러한 변화를 기반으로 한 프로그램들은 TCustomApplication 에서 파생된 애플리케이션 클래스를 가진다. 메인 프로그램의 소스 코드는 begin...end 블록을 포함하진 않지만 해당 클래스에서 정의된 DoRun 메소드가 대신 포함될 것이다.
Console application 을 선택하고 나면 New console application 대화창이 열리면서 사용자가 프로그램 템플릿의 기본 설정을 조정하도록 해준다.
이는 애플리케이션 클래스와 (TMyApplication 으로 사정 정의된) 애플리케이션 제목을 (기본 값: My Application) 설정하도록 도와준다. 명령 행 옵션 -h 를 (주로 도움말 텍스트 표시에 사용되는) 지원할 것인지 여부를 명시할 수도 있다. 뿐만 아니라 예외가 발생 시 프로그램 실행을 중단할 것인지 명시하고 (명시하지 않을 시 오류 메시지만 표시됨) 생성자(constructor)와 소멸자(destructor)를 설치할 것인지 (따로 선택 가능) 설정할 수 있다. 마지막으로, 잘못된 명령 행 옵션으로 프로그램을 호출할 때 프로그램이 오류를 보고할 것인지 결정하도록 해준다.
TCustomApplication (CustApp 유닛에 정의됨) 은 콘솔 애플리케이션에 대한 기반 클래스로서, 다음 페이지의 리스트에서 볼 수 있듯이 도움말 텍스트를 표시하고 파라미터를 분석하는 데에 유용한 루틴들을 포함하고 있다. 아래 축약된 클래스 정의에서 private 및 protected 메소드와 프로퍼티는 생략되었다:
type TExceptionEvent = Procedure(Sender: TObject; E: Exception) of Object;
TCustomApplication = Class(TComponent)
Public
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
// Some Delphi methods
procedure HandleException(Sender: TObject); virtual;
procedure Initialize; virtual;
procedure Run;
procedure ShowException(E: Exception);virtual;
procedure Terminate; virtual;
// Extra methods
function FindOptionIndex(Const S : String;
Var Longopt : Boolean) : Integer;
Function GetOptionValue(Const S : String) : String;
Function GetOptionValue(Const C: Char; Const S : String) : String;
Function HasOption(Const S : String) : Boolean;
Function Hasoption(Const C : Char; Const S String) : Boolean;
Function CheckOptions(Const ShortOptions : String;
Const Longopts : TStrings;
Opts,NonOpts : TStrings) : String;
Function CheckOptions(Const ShortOptions : String;
Const Longopts : TStrings) : String;
Function CheckOptions(Const ShortOptions : String;
Const LongOpts : Array of string) : String;
Function CheckOptions(Const ShortOptions : String;
Const LongOpts : String) : String;
Procedure GetEnvironmentList(List : TStrings;NamesOnly : Boolean);
Procedure GetEnvironmentList(List : TStrings);
Procedure Log(EventType : TEventType; const Msg : String); virtual;
// Delphi properties
property ExeName: string read GetExeName;
property HelpFile: string read FHelpFile write FHelpFile;
property Terminated: Boolean read FTerminated;
property Title: string read FTitle write SetTitle;
property OnException: TExceptionEvent read FOnException
write FOnException;
// Extra properties
Property ConsoleApplication : Boolean Read GetConsoleApplication;
Property Location : String Read GetLocation;
Property Params [Index : integer] : String Read GetParams;
Property ParamCount : Integer Read GetParamCount;
Property EnvironmentVariable[envName : String] : String
Read GetEnvironmentVar;
Property OptionChar : Char Read FoptionChar Write FOptionChar;
Property CaseSensitiveOptions : Boolean Read FCaseSensitiveOptions
Write FCaseSensitiveOptions;
Property StopOnException : Boolean Read FStopOnException
Write FStopOnException;
end;
CheckOptions 메소드는 어떤 옵션이 선택되었는지 확인하며, 사용자가 애플리케이션에 매우 효과적인 오류 및 파라미터 처리방법을 포함하도록 해준다. 오류가 발생할 경우 사용자에게 유용한 메시지를 제공할 수 있다. HasOption 메소드는 옵션을 활성화해야 하는지 검사하고 사용자가 그 상태에 응답하도록 해준다.
라자루스는 콘솔 애플리케이션에 대해 아래와 같은 기본 구조를 생성한다:
program Project1;
{$mode objfpc}{$H+}
uses
{$IFDEF UNIX}{$IFDEF UseCThreads}
cthreads,
{$ENDIF}{$ENDIF}
Classes, SysUtils, CustApp
{ you can add units after this };
type
{ TMyApplication }
TMyApplication = class(TCustomApplication)
protected
procedure DoRun; override;
public
constructor Create(TheOwner: TComponent); override;
destructor Destroy; override;
procedure WriteHelp; virtual;
end;
{ TMyApplication }
procedure TMyApplication.DoRun;
var ErrorMsg: String;
begin
// quick check parameters
ErrorMsg:=CheckOptions('h','help');
if ErrorMsg <> '' then
begin
ShowException(Exception.Create(ErrorMsg));
Terminate;
Exit;
end;
// parse parameters
if HasOption('h','help') then
begin
WriteHelp;
Terminate;
Exit;
end;
{ add your program here }
// stop program loop
Terminate;
end;
constructor TMyApplication.Create(TheOwner: TComponent);
begin
inherited Create(TheOwner);
StopOnException:=True;
end;
destructor TMyApplication.Destroy;
begin
inherited Destroy;
end;
procedure TMyApplication.WriteHelp;
begin
{ add your help code here }
writeln('Usage: ',ExeName,' -h');
end;
var Application: TMyApplication;
{$R *.res}
begin
Application:=TMyApplication.Create(nil);
Application.Title:='My Application';
Application.Run;
Application.Free;
end.
터미널과 통신 시에는 로컬 시스템 문자 집합으로 된 표준 ANSI 문자열을 사용해야 한다. 콘솔 애플리케이션의 채널에 관한 상세한 정보는 8.2.1에서 찾을 수 있다.