OFFICE/델파이

델파이 - 유용한 팁

gandus 2014. 6. 13. 17:00

■기타 Tips와 Tricks

■MDI

●MDI 애플리케이션에서 Focus를 가지고 있는 Form이 어느 것인지 알려면?

 TForm의 ActiveMDIChild Property을 이용한다.


if Application.MainForm.ActiveMDIChild is TeditForm then

  TeditForm(ActiveMDIChild).Save1Click(Sender);


●델파이의 MDI에서 Title Bar가 없는 Child Form을 만드는 방법은?

 다음과 같이 MDI Parent Form에 코드를 입력하면 가능하다.


type

 TForm1 = class(TForm) {TForm1는 child form의 이름이다.}

 procedure CreateParams(var Params : TCreateParams); override;

end;

procedure TForm1.CreateParams(var Parmas : TCreateParams);

begin

 inherited CreateParams(Params);

 Params.Style := Params.Style and not WS_OVERLAPPEDWINDOW or WS_BORDER;

end;


●MDI Application에서 특정 Child를 항상 일정한 크기/위치로 고정하려면?

 Child Form의 OnActivate Event에서 다음과 같이 크기와 위치를 정해 주면 된다.


procedure TeditForm.FormActivate(Sender : TObject);

begin

 Left := 200;

 Height := 140;

end;


 단, 이 이후에 생성하는 Child Form 은 윈도우의 기본 특성에 따라 다른 위치로 생성된다.


●사용자가 런타임시에 Child Windows를 열면 각각은 이전 윈도우의 약간 우측 아래에 위치하게 된다. 사용자의 문제는 어떤 Child 윈도우를 닫고, 새로운 것을 열 경우, 이 새로운 윈도우는 닫기 전의 윈도우의 우측 아래에 위치하게 된다. 이렇게 하고 싶지 않을 경우 어떻게 해야 하나?

 이것은 MDI 윈도우가 어떻게 작업하느냐이다. VCL은 이 상환에서 윈도우의 기본적인 행동을 무시하지 못한다.

 단, Position Property에서 poScreenCenter 값을 사용할 수 있다. 그러나 이 속성을 선택하면 Child Form이 항상 Parent Form의 가운데에 위치한다.


●MDIchild 폼을 실행시 Create하려면?

 폼 2개를 각각 MDIForm, MDIChild로 만든 후 다음과 같이 프로그램한다. 물론 Form2는 Project Option에서 Available Form으로 지정한다.


----------------Unit1.pas-----------------

uses Unit2;

{$R *.DFM}


procedure TForm1.New1Click(Sender : TObject);

var

 cForm : TForm2;

begin

 CForm := TForm2.Create(Application);

end;


----------------Unit2.pas------------------

procedure TForm2.FormClose(Sender : Tobject; var Action : TCloseAction);

begin

 Action := caFree;

end;


●MDI Child를 한 개만 띄우는 방법은?/

 메인 폼의 MDIChildCount 속성을 통해 Child 폼의 개수를 알 수 있으므로 다음과 같이 프로그램한다.


procedure TForm1.New(Sender : TObject);

var

 cForm : TForm2;

begin

 if MDIChildCount = 0 then

  cForm := TForm2.Create(Application);

end;


●MDI ChildForm이 이미 Create되어 있다면 다시 Create하지 못하게 하려면?

for I:= 0 to MDIChildCount -1 do

 if MDIChildren[i] is TForm1 then

  begin

   MDIChildren[i].show;

   exit;

  end;

Form1 := Form1.Create(Application);



■키보드

●Caps Lock 키를 조절하려면?

 SetKeyboardState 함수를 사용하면 된다. 다음은 버튼을 누를 때마다 Caps Lock 키의 상태가 변하게 하는 코드이다.


procedure TForm1.Button1Click(Sender : TObject);

var

 Lavel : Integer;

 KeyState : TKeyBoardState;

begin

 Level := GetKeyState(VK_CAPITAL); {Caps Lock 키를 검사한다}

 GetKeyboardState(KeyState);

 if Level = 0 then

  begin

   KeyState[VK_CAPITAL] := 1;

   setkeyboardState(KeyState);

  end

  else if Level = 1 then

  begin

   KeyState[VK_CAPITAL] := 0;

   SetKeyboardState(KeyState);

  end;

end;


●누른 Key가 무엇인지 확인하는 방법은?

 Form의 KeyPreview 속성을 True로 하고, KeyDown 이벤트에서 처리하면 된다.


다음은 Key를 처리하는 예제 Procedure이다.


procedure TForm1.Edit3KeyDown(Sender : TObject; var Key : Word; Shift :

                                                               TShiftState);

var

 tempstr : string; {used to spell out keys typed}

begin

 {be sure to set Form.KeyPreview to True}

 Edit3.Text := '';

 if (Shift = ([ssShift])) then Edit4.Text := Edit4.Text + 'Shift';

 if (Shift = ([ssShift, ssAlt])) then Edit4.Text := Edit4.Text + 'Shift+Alt';

 if (shift = ([ssShift, ssCtrl])) then Edit4.Text := Edit4.Text + 'Shift + Ctrl';

 if (Shift = ([ssShift, ssAlt, ssCtrl])) then Edit4.Text := Edit4.Text + 'Shift+Ctrl+Alt';

 if (Shift = ([ssAlt, ssCtrl])) then Edit4.Text := Edit4.Text + 'Ctrl + Alt';

 if (Shift = ([ssCtrl])) then Edit4.Text := Edit4.Text + 'Ctrl';


 tempstr := '';


 case Key of

  VK_CANCEL : tempstr := 'CANCEL';

  VK_BACK : tempstr := 'BACKSPACE';

  VK_TAB : tempstr := 'TAB';

  VK_CLEAR : tempstr := 'CLEAR';

  VK_RETURN : tempstr := 'RETURN';

  VK_PAUSE : tempstr := 'PAUSE';

  VK_CAPITAL : tempstr := 'CAPS LOCK';

  VK_ESCAPE : tempstr := 'ESC';

  VK_SPACE : tempstr := 'SPACEBAR';

  VK_PRIOR : tempstr := 'PAGE UP';

  VK_NEXT : tempstr := 'PAGE DOWN';

  VK_END : tempstr := 'END';

  VK_HOME : tempstr := 'HOME';

  VK_LEFT : tempstr := 'LEFT ARROW';

  VK_UP : tempstr := 'UP ARROW';

  VK_RIGHT : tempstr := 'RIGHT ARROW';

  VK_DOWN : tempstr := 'DOWN ARROW';

  VK_SELECT : tempstr := 'SELECT';

  VK_EXECUTE : tempstr := 'EXECUTE';

  VK_SNAPSHOT : tempstr := 'PRINT SCREEN';

  VK_INSERT : tempstr := 'INS';

  VK_DELETE : tempstr := 'DEL';

  VK_HELP : tempstr := 'HELP';


 {VK_1..VK_0 and VK_A..VK_Z are not defined so you have to use the Ord()

  function instead which yields the equivilent VK code}


  Ord('0') : tempstr := '0';

  Ord('1') : tempstr := '1';

  Ord('2') : tempstr := '2';

.

.

  Ord('A') : tempstr := 'A';

  Ord('B') : tempstr := 'B';

  Ord('C') : tempstr := 'C';

.

.

  VK_NUMPAD0 : tempstr := 'Numeric keypad 0');

  VK_NUMPAD1 : tempstr := 'Numeric keypad 1');

  VK_NUMPAD2 : tempstr := 'Numeric keypad 2');

.

.

  VK_MULTIPLY : tempstr := 'Multiply';

  VK_ADD : tempstr := 'Add';

  VK_SEPARATOR : tempstr := 'Separator';

  VK_SUBTRACTOR : tempstr := 'Subtract';

  VK_DECIMAL : tempstr := 'Decimal';

  VK_DIVIDE : tempstr := 'Devide';

  VK_F1 : tempstr := 'F1';

  VK_F2 : tempstr := 'F2';

  VK_F3 : tempstr := 'F3';

.

.

  VK_NUMLOCK : tempstr := 'NUM LOCK';

  VK_SCROLL : tempstr := 'SCROLL';

 end;

 if Edit4.Text = '' then Edit4.Text := tempstr

 else if tempstr <> '' then Edit4.Text := Edit4.Text + '+' + tempstr;

 Key := 0; {set key to 0 to send no key stroke}

end;


●특정 Component에서 Enter 키를 눌렀을 때 원하는 처리를 추가하려면?

 KeyDown Event Handler에서 Key가 VK_Return 값인지 체크하면 된다.

다음은 그러한 Event Handler의 간단한 예제이다.


procedure TForm1.Edit1KeyDown(Sender : TObject;var Key : Word; Shift : TShiftState);

begin

 if Key = VK_Return then

  Edit2.Text := FormatFloat('###.###', StrToFloat(Edit1.Text));

end;


●Task 전환 키인 ALT+TAB 키를 Disable시키려면?

 WM_KeyDown Message를 처리하거나 Form의 KeyDown Event Handler를 작성한다.

다음은 간단한 예제 코드이다.


procedure TForm1.WMKEYDOWN(var Message : TMKEYDOWN); mwssage

                                                       WM_KEYDOWN;

begin

 if ssAlt in KeyDataToShiftState(Message.KeyData) then

  if Message.CharCode = VK_TAB then

  begin

   Message.CharCode := 0;

   exit;

  end;

end;


procedure TForm1.FormKeyDown(Sender : TObject;var Key : Word; Shift : TShiftState);

begin

 if ssAlt in Shift then

  if Key = VK_TAB then

  begin

   Key := 0;

   exit;

  end;

end;


●Keyboard 명령을 사용하여 TForm Component에서 스크롤 기능을 어떻게 사용하나? 예를 들어, 폼을 위/아래로 스크롤하기 위해 PgUp과 PgDn 키를 사용하고 싶다.

 다음과 같이 프로그램한다.


procedure TForm1.FormCreate(Sender : TObject);

begin

 with VertScrollBar do

 begin

  Range := 500;

  Visible := true;

 end;

end;


procedure TForm1.FormKeyDown(Sender : TObject;var Key : Word; Shift :TShiftState);

const

 PageDelta = 100;

begin

 with VertScrollBar do

 if Key = VK_NEXT then Position := Position + PageDelta

 else if Key = VK_PRIOR then Position := Position - PageDelta;

end;


●Alt+Tab 키를 누르면 다른 윈도우가 보인다. 이를 방지할 수 있나?

 윈도우 95에서는 다음 코드와 같이 한다면 가능하다. 그러나 윈도우 NT에서는 Third Party Keyboard Device Driver를 이용해야 한다.


var

 OldValue : LongBool;

begin

 {turn the trap on}

 SystemParametersInfo(97, Word(True), @OldValue, 0);


 {turn the trap off}

 SystemParametersInfo(97, Word(False), @OldValue, 0);

end;


●Form에서 Hot Key를 사용하고 싶다. 그러나 Form의 KeyDown 이벤트가 작동하지 않는다. 어떻게 해야 하나?

 먼저 Form의 KeyPreview 속성을 True로 놓고, 다음 KeyDown 이벤트에서 다음과 같이 하면 된다. Key 값에 대한 정보는 Windows.pas를 참조하기 바란다. 주의할 것은 KeyPreview 속성은 Navigation 버튼(TAB, BackTAB, Up Arrow, Down Arrow, 등)은 동작하지 않는다.


procedure TForm1.FormKeyDown(Sender : TObject;var Key : Word; Shift : TShiftState);

begin

 if key = 112 then              //F1 key이다

  Button2Click(self);

end;



■File Handling

●파일을 만들려면?

 FileCreate 함수를 이용하면 된다.


●파일 크기를 알고 싶다면?


function FileGetSize(YourFile : String) : LongInt;

var

 F : integer;

begin

 F := FileOpen(YourFile,0); {Readonly Mode}

 FileGetSize := FileSeek(F, 0, 2);

 Filecolse(F);

end;


procedure TForm1.Button1Click(Sender : TObject);

begin

 Edit1.Text := IntToStr(FileGetSize('c:\bin\q.exe'));

end;


●파일을 복사하려면?

 다음 FileCopy 함수를 사용해 보기 바란다.


function FileCopy(source,dest : String): Boolean;

var

 fSrc,fDst,len : integer;

 size : LomgInt;

 buffer : packed array[0..2047] of byte;

begin

 Result := False; {Assume that it won't work}

 if source <> dest then begin

  fSrc := FileOpen(source, fmOpenRead);

  if fSrc >= 0 then begin

    size := FileSeek(fSrc, 0, 2);

    FileSeek(fSrc, 0 ,0);

    fDst := FileCreate(dest);

    if fDst >= 0 then begin

       while size > 0 do begin

        len := FileRead(fSrc, buffer, sizeof(buffer));

        FileWrite(fDst, buffer, len);

        size := size - len;

       end;

       FileSetDate(fDst, FileGetDate(fSrc));

       FileClose(fDst);

       FileSetAttr(dest, FileGetAttr(source));

       Result := true;

    end;

    FileClose(fSrc);

  end;

 end;

end;


●파일의 날짜와 시간을 설정하려면?

 SetFTime 함수를 사용한다.

다음은 파일의 날짜와 시간을 바꾸는 간단한 예제 코드이다.


var

 f:File;

begin

 Assign(f, DirInfo.Name);

 Reset(f);

 SetFTime(f, Time);

 Close(f);

end;


●현재 시스템의 날짜와 시간을 프로그램에서 바꾸려면?

unit Unit1;

interface

uses

 Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, ExtCtrls,

 StdCtrls;

type

 Ttm = class(TForm)

  Edit1 : TEdit;

  Edit2 : TEdit;

  Edit3 : TEdit;

  Edit4 : TEdit;

  btnSetDateTime : TButton;

  procedure btnSetDateTimeClick(Sender : TObject);

 private

  {            }

 public

  {            }

end;


var

 tm : Ttm;

implementation

{$R *.DFM}


procedure Ttm.btnSetDateTimeClick(Sender : TObject);

var

 lpSystemTime : TSystemTime;

 wDate, wTime : TDateTime;

 yy,mm,dd : Word;

 hh,mi,ss,ms : Word;

begin

 wDate := StrToDate(Edit1.Text);

 wTime := StrToTime(Edit2.Text);

 DecodeDate(wDate, yy, mm, dd);

 DecodeTime(wTime, hh, mi, ss, ms);

 with lpsystemtime do

  begin

   wYear := yy;

   WMonth := mm;

   wDayOfWeek := DayOfWeek(wDate);

   wDay := dd;

   wHour := hh;

   wMinute := mi;

   wSecond := ss;

   wMilliSeconds := ms;

  end;

end;

end.


●INI File 처리 방법은?

 TIniFile Object를 이용하면 응용프로그램에서 .INI 파일을 생성하여 조작할 수 있다. 중요한 Method로는 Create, ReadString, WriteString 등이 잇다.

 다음은 그러한 Method들의 간단한 예제 코드이다.


var

 IniFile : TIniFile;

begin

 IniFile := TIniFile.Create('C:\Windows\Delphi.InI');

 IniFile.ReadString('Library', 'SearchPath', 'C:\DelPhi\Bin');

 IniFile.WriteString('Library', 'SearchPath', 'C:\Delphi\Bin;C:\MyDir');

 IniFile.Free;

end;


●INI 파일을 통해 프로그램을 작성하려면?

 GetPrivateProfileString을 이용하여 INI 파일에 있는 특정한 키 값을 가져 오기 위해서는 다음과 같이 하면 된다.


procedure TForm1.Button1Click(Sender : TObject);

var

 buf : pchar;

begin

 GetPrivateProfileString('WS_FTP', 'GROUP','',buf,30,'win.ini');

 Button1.caption := StrPas(buf);

end;


WS_FTP :     win.ini 파일에 있는 section 이름

GROUP  :      WS_FTP section 의 key 값

‘’:              값이 없을 경우 return할 default 값

buf :           지정한 key에 해당하는 값을 return하게 될 버퍼

30 :            버퍼의 크기

win.ini :       ini 파일의 이름


 앞의 방법은 윈도우 API를 이용한 방법이다. 또한 델파이내의 함수를 이용해서도 구현할 수 있는데, 방법은 다음과 같다.


var

 MyIni : TIniFile;

 ReturnStr : String;

begin

 MyIni := TIniFile.Create('c:\windows\win.ini');

 ReturnStr := MyIni.ReadString('WS_FTP', 'GROUP', '');

 MyIni.Free;

end;


●특정 디렉토리에 있는 모든 파일을 다루는 방법은?

 FindFirst와 FindNext 함수를 사용한다.

다음은 특정 디렉토리에 있는 파일의 이름과 크기를 보여주는 간단한 예제이다.


var

 SearchRec : TSearchRec;


procedure TForm1.Button1Click(Sender : TObject);

begin

 FindFirst('c:\이\bin\*.*', faAnyFile, SearchRec);

 Label1.Caption := SearchRec.Name + 'is '+IntToStr(SearchRec.Size)+'bytes in size';

end;


●TextFile을 만들었는데, 이 File을 직접 Printing하는 방법은?

 AssignPrn을 이용하면 된다.


var

 pText : TextFile;

 printstring : string[30];

 s1,s2 : string;

begin

 AssignPrn(PText);

 rewrite(PText);

 s1 := 'test';

 s2 := 'ttt';

 printstring := s1 + s2;

 writeln(PText, printstring);

 closefile(PText);

end;


●Text File을 특정 문자 길이 만큼 가져 오려면?

 특정 길이 만큼의 Array Type을 선언한 후 Read Method를 사용하기 바란다.


var a : array[0..9] of char;

 f : textfile;

....

 assignfile(f, 'c:\test.txt');

 reset(f);

 read(f, a);

 ...

 closefile(f);


●환경변수의 값을 변경하거나 현재 설정되어 있는 값을 가져 오려면?

 실행시 현재 환경변수를 설정하거나 정보를 가져 오는 것은 GetEnvirobmentVariable 이나 SetEnvironmentVariable을 사용하면 된다. 자세한 것은 다음 코드를 참조하기 바란다.


 예 1 : 환경변수의 값을 가져 오기


procedure TForm1.Button1Click(Sender : TObject);

var

 evariable : array[0..50] of char;

 ebuffer : array[0..50] of char;

 esize : longint;

 s : string;

begin

 s := 'windir';  //환경변수 windir의 값 가져 오기

 StrPCopy(evariable, S);

 s := '';

 strpcopy(ebuffer, s);

 if GetEnvironmentVariable(evariable, ebuffer, esize) > 0 then

  showMessage(ebuffer);

end;


 예 2 : 환경변수의 값을 설정하기


procedure TForm1.Button1Click(Sender : TObject);

var

 evariable : array[0..50] of char;

 ebuffer : array[0..50] of char;

 s : string;

begin

 s := 'windir';

 StrPCopy(evariable, S);

 s := 'c:\winnt32';      //환경변수 windir의 값 변경하기

 strpcopy(ebuffer, s);

 if SetEnvirnmentVariable(evariable, ebuffer) then

  ShowMessage('success');

end;



■프린트, 주변장치

●인쇄 관리자에서 표시되는 인쇄 작업의 제목을 주고 싶을 때는?

 다음과 같은 코드를 쓰면 가능하다.


Printer.Title := 'Your title';


●프린터의 인치당 픽셀을 구하려면?

 윈도우 API 함수인 GetDeviceCaps을 사용하면 가능하다.

다음은 에디트박스에 프린터의 가로,세로 인치당 픽셀을 보여주는 간단한 코드이다.


procedure TForm1.Button2Click(Sender : TObject);

begin

 Edit1.Text := IntToStr(GetDeviceCaps(Printer.Handle, LogPixelsX));

 Edit2.Text := IntToStr(GetDeviceCaps(Printer.Handle, LogPixelsY));

end;


●프린터에 Escape 코드를 전달하는 방법은?

 다음은 삼보 KSSM 모드에서 축소 Escape 코드를 사용하는 예제이다.


procedure TForm1.Button1Click(Sender : TObject);

var

 MyFile : TextFile;

 s, s1 : string;

 ff : string;

begin

 {삼보 KSSM mode}

 s[0] := #1; {축소 지정}

 s[1] := #15;

 s1[0] := #1; {축소 해제}

 s1[1] := #18;

 ff[0] := #1; {Form Feed}

 ff[1] := #12;


 AssignFile(MyFile, 'PRN');

 Rewrite(MyFile);

 s := 'Normal font size'+ s + 'small font' + s1 + 'normal font size' + ff;

 Writeln(MyFile, s);

 System.CloseFile(MyFile);

end;


 또는 윈도우 API 함수 Escape()를 참조하기 바란다.


●특정 어드레스에 데이터를 저장하려면?

 Object Pascal에서는 사용자가 직접 메모리를 다룰 수 있도록 다음과 같은 함수를 제공한다.


Mem

MemW

MemL


 Mem의 각 요소는 Byte이고, MemW의 각 요소는 Word이며, MemL의 요소들은 LongInt이다. Mem 배열 함수는 인덱스로 특정한 형식을 취한다.

 다음은 예제 코드이다.


Mem[$0040:$0049] := 7;         {7을 $0040:$0049에 저장한다}

Data := Mem[Seg(V):Ofs(V)];    {변수 V의 첫 2바이트에 저장된 워드 값을 변수 데이터

                               로 옮긴다.}


●TPrinter Object를 사용하려면?

 다음 예제를 참고하기 바란다.


var

Printer : TPrinter;


FontDialog1.Font.Assign(Edit1.Font);

if Font1.Dialog.Execute then Edit1.Font.Assign(FontDialog1.Font);


printer.Canvas.TextOut(X, Y, '문자열‘);

Printer.Canvas.TextRect(TRect, X, Y, '문자열‘);

Printer.Canvas.TextHeight('문자열‘);

GetDeviceCaps(Printer.Handle, LOGPIXELSX);

GetDeviceCaps(Printer.Handle, LOGPIXELSY);


Printer.NewPage;


●Write 함수를 써서프린터로 보냈는데, 강제로 Page를 넘기려면?

 Form Feed Hexa Code를 Write를 이용해서 Print로 보낸다.



■Resource

●사용자가 .RES 파일을 추가했어도 왜 사용자의 프로그램은 Resource의 어느 것도 찾지 못하는가? 사용자의 폼에 Unit 이름과 .RES가 같아서 그런가?

 만약 포함된 .RES 파일의 이름이 .DPR 파일 이름과 같다면 델파이는 자신의 .RES 파일로 덮어쓰기를 할 것이다. 또한 Project RES 파일은 단지 Delphi Project 관리에 대한 것이다. 이 RES 파일에 Resource를 추가하거나 편집할 수 없다.


●RC File을 컴파일하는 방법과 Bitmap, Cursor, Icon 등을 사용하려면?

 DOS Prompt에서 Delphi\Bin Directory에 있는 BRCC.EXE를 이용하여 컴파일하면 된다. 컴파일한 RC File은 .RES 형태의 Binary File로 생성된다.

 생성된 .RES File을 다음과 같이 선언하여 Program에서 사용하면 된다.


implementation

{$R C:\Delphi\MyDir\MyRes}


위와 같이 선언한 후 사용시에는 윈도우 API를 이용하면 된다.


Image1.Picture.Bitmap.Handle := LoadBitmap(HInstance, 'BITMAP1');

Screen.Cursor[1] := LoadCursor(HInstance, 'CURSOR1');

Image1.Picture.Icon.Handle := LoadIcon(HInstance, 'ICON1');

LoadString(HInstance, 1000, Buffer, 255);


●애플리케이션에 링크된 리소스 파일로부터 JPEG 파일을 로드하려면?

 다음은 JPEG 이미지를 포함하는 리소스 파일을 만들고, 이 JPEG 파일을 읽어들이는 법을 보여준다. 결과 JPEG 이미지는 Image 컴포넌트에 표시된다.


 ‘.rc' 확장자를 가지는 텍스트 파일을 만든다. 이 텍스트 파일명은 컴파일러에게 혼란을 주지 않기 위해 작성하고 있는 애플리케이션의 프로젝트 이름이나 유닛 이름과 달라야 한다. 이 텍스트 파일에 다음 라인을 넣는다.


MYJEPG JPEG C:\Download\My.JPG


"MyJPEG"              이 리소스에 주고 싶은 이름.

“JPEG"                 사용자 정의 리소스 타입.

“C:\Download\My.JPG": JPEG 파일의 패스와 파일명.


이 예에서는 파일명으로 ‘foo.rc'를 준다.


 이제 Delphi/C++ Builder의 Bin 디렉토리에 있는 BRCC32.exe (Borland Resource CommandLine Compiler) 프로그램을 실행시킨다. (rc 파일명으로는 풀 패스 이름을 주어야 한다.)


C:\DelphiPath\BIN\BRCC32.EXE C:\ProjectPath\FOO.RC


 이제 ‘.rc' 파일과 같은 이름으로 된 ’.res' 확장자가 붙은 컴파일된 리소스 파일이 만들어진다.

 다음은 애플리케이션에 포함된 JPEG 파일을 어떻게 사용하는지의 예를 보여준다.


{link the res file}

{$R FOO.RES}


uses Jpeg;


procedure LoadJPEGFromRes(TheJPEG : string; ThePicture : TPicture);

var

 ResHandle : THandle;

 MemHandle : THandle;

 MemStream : TMemoryStream;

 ResPtr : PByte;

 ResSize : LongInt;

 JPEGImage : TJPEGImage;

begin

 ResHandle := FindResource(hInstance, PChar(TheJPEG), 'JPEG');

 MemHandle := LoadResource(hInstance, ResHandle);

 ResPtr := LockResource(MemHandle);

 MemStream := TMemoryStream.Create;

 JPEGImage := TJPEGImage.Create;

 ResSize := SizeOfResource(hInstance, ResHandle);

 MemStream.SetSize(ResSize);

 MemStream.Write(ResPtr^, ResSize);

 FreeResource(MemHandle);

 MemStream.Seek(0, 0);

 JPEGImage.LoadFromStream(MemStream);

 ThePicture.Assign(JPEGImage);

 JPEGImage.Free;

 MemStream.Free;

end;


procedure TForm1.Button1Click(Sender : TObject);

begin

 LoadJPEGFromRes('MYJPEG', Image1.Picture);

end;


■Error 처리

●사용자에게 에러를 보여주기 전에 가로채려면?

 그러한 역할을 하는 새로운 Exception을 다음과 같이 만들 수 있다.

다음은 프로시저를 Main 폼에 선언해준다.


type

 TForm1 = class(TForm)

  procedure FormCreate(Sender : TObject);


  {Exception 처리를 하기 위한 procedure를 선언한다}

  procedure MyException(Sender : TObject; E:Exception);

 private

  {            }

 public

  {            }

end;


procedure TMyForm.MyException(Sender : TObject; E:Exception);

begin

 if (E.ClassType.ClassName = 'EConvertError') then

 begin

  {사용자가 원하는 처리 내용}

 end

 else Application.ShowException(E);

end;


procedure TMyForm.FormCreate(Sender : TObject);

begin

 Application.OnException := MyException;

end;


●델파이에서 런타임 오류를 조정하려면?

 델파이에서 런타임 오류가 발생할 때에 Exception이 생성된다. 델파이에서 사용자의 프로그램이 실행되는 동안 Exception이 발생할 때, 개발환경에서 특수한 옵션이 설정된다면, 델파이는 발생한 Line에 커서를 위치시킬 것이다. 그래도 프로그램은 종료되지 않는다. 따라서 Exception이 생성되면 사용자의 애플리케이션은 자동적으로 종료되지 않는다.


●‘Stream Read Error'를 조정하려면?

 *.DSM을 삭제한 후 다시 Project를 컴파일한다.



■Cursor, Image, Glyph

●새로운 커서를 만들어 사용하려면?

 다음과 같이 Res 파일을 Project에 포함시킨다.


{$R c:\dl\testcur.res} {RES 파일의 이름과 경로를 명시}


procedure TForm1.Button1Click(Sender : TObject);

var

 ddd : PChar;

begin

 GetMem(ddd, 8);

 ddd := 'Cursor_1';

 SetClassWord(Form1.handle, GCW_HCURSOR, LoadCursor(hinstance, ddd);

end;


●Cursor 모양을 바꾸려면?

 다음은 어떤 작업시에 HourGlass 커서를 보여주고, 다시 정상적인 커서를 보여주는 예제이다.


begin

 try

  Screen.Cursor := crHourGlass;

  ReportForm := TReportForm.Create(Application);

  Screen.Cursor := crDefault;

  ReportForm.ShowModal;

  ReportForm.Free;

 finally

  Screen.Cursor := crDefault;

 end;

end;


●이미지의 크기를 조절하여 보여주려면?

 다음 예제를 참고하기 바란다.


procedure TForm.ZoomImage;

var

 Bitmap : TBitmap;

 DstRect : TRect;

begin

 Bitmap := TBitmap.Create;

 Bitmap.Width := { 원하는 길이}

 Bitmap.Height := { 원하는 높이}

 Bitmap.Canvas.StretchDraw(Bitmap.Canvas.ClipRect,{원하는 이미지});

 Image1.Picture.Graphic := Bitmap;

 Image1.Invalidate;

end;


●아이콘을 Glyphs으로 바꾸려면?

 다음 예제를 참고하기 바란다.


unit Procs;


interface

uses

 SysUtils, WinTypes, WinProcs, Messages, Classes, Graphics, Controls, forms, StdCtrls,

 Buttons, ExtCtrls, ShellAPI;


 procedure Llenaboton(boton : TSpeedButton; Programa : string);

 procedure LimpiaBoton(boton : TSpeedButton);


var

 Pic : TPicture;

 Fname : String;

 TempFile : array[0..255] of Char;

 Icon : TIcon;


implementation

uses ttotro;


procedure LlenaBoton(boton : TSpeedButton; Programa : string);

var

 NumFiles, NameLength : integer;

 nIconsInFile : word;

 rBoton : Trect;

 oBitmap : TBitmap;

 oBitmap2 : Tbitmap;

 NombreBitmap : string;

begin

 try

  screen.cursor := crHourglass;


  Icon := TIcon.Create;

  StrPCopy(TempFile, Programa);

  Icon.Handle := ExtracIcon(HInstance, TempFile, 0);


  Pic := TPicture.Create;


  Pic.Icon := Icon;


  oBitmap := TBitMap.Create;

  oBitmap2 := TBitMap.Create;

  oBitmap2.Width := Icon.Width;

  oBitmap2.Height := Icon.Height;

  oBitmap.Width := boton.Width-4;

  oBitmap.Height := boton.Height-4;

  oBitmap.Canvas.Draw(0, 0, Pic.Graphic);

  rBoton.Left := 1;

  rBoton.Top := 1;

  rBoton.right := boton.Width-6;

  rBoton.Bottom := boton.height-6;

  oBitmap.Canvas.StrectDraw(rBoton, oBitmap2);


  Boton.Hint := Programa;


  NombreBitmap := Copy(programa, 1, Length(programa)-3) + 'BMP';

  if not FileExists(NombreBitmap) then

  begin

   oBitmap.SaveToFile(ExtractFilePath(Application.ExeName)+

                       ExtractFileName(NombreBitmap));

   Boton.Glyph := oBitmap;

  end else

   oBitmap.Glyph.LoadFromFile(ExtractFilePath(Application.ExeName)+

                       ExtractFileName(NombreBitmap));

 finally

  Icon.Free;

  oBitmap.Free;

  oBitmap2.Free;

  Screen.cursor := crDefault;

 end;

end;


procedure LimpiaBoton(boton : TSpeedButton);

var

 oBitmap : TBitmap;

 rBoton : TRect;

begin

 try

  oBitmap := Tbitmap.Create;

  oBitmap.Width := boton.Width -4;

  oBitmap.Height := boton.Height-4;

  Boton.Glyph := oBitmap;

 finally

  oBitmap.Free;

 end;

end;


end.


●Image Component에 특정 Application의 Icon을 보이게 하려면?

 윈도우 API인 ExtractIcon Function을 이용하면 된다.


uses SHELLAPI;


image1.Picture.Icon.Handle := ExtractIcon(Handle, 'C:\Delphi\bin\Delphi.exe' , 0);


●Hint를 보여주는 HintBox의 크기를 변경하려면?

 HintBox의 크기는 변경할 수 없고, Hint의 글꼴을 크게 할 수는 있다.


 Source 디렉토리의 vcl에서 Controls.pas 파일에서 THint의 글꼴을 변경한 후 컴파일하고 생성된 .dcu 파일을 Lib 디렉토리로 옮기면 된다.


constructor THintWindow.Create(AQwner : TComponent);

begin

 inherited Create(AOwner);

 Color := $80FFFF;

 with Canvas do

 begin

  Font.Name := 'MS Sans Serif';

  Font.Size := 12; ==========>size 변경

  Brush.Style := bsClear;

 end;

end;


 그리고, 풍선 모양의 Hintbox 등을 원한다면 3rd party 컴포넌트를 사용하면 된다.


●교차된 Brush로 Paint하려면?

var

 TheMask : TBitmap;

 TheRect : TRect;

begin

 TheMask : Tbitmap.Create;

 TheRect := rect(0, 0, image1.width, image1.height);

 try

  with TheMask do begin

   Monochrome := False;

   Width := image1.Width;

   Height := image1.height;

   Canvas.brush.color := ClBlack;

   Canvas.brush.style := bsDiagCross;

   Canvas.Ellipse(0, 0, image1.Width, Image1.height);

   Canvas.CopyMode := cmSrcAnd;

   Canvas.Copyrect(TheRect, image1.canvas, TheRect);

  end;

  image1.canvas.CopyMode := cmSrcCopy;


  image1.Canvas.CopyRect(TheRect, TheMask.Canvas, TheRect);

 finally

  TheMask.free;

 end;

end;


●Canvas 대신 Pixel을 이용하여 String의 높이와 넓이를 구하는 방법은?

 TextHeight와 TextWidth를 사용하는데, 이는 Pixel의 String 높이와 넓이를 결정한다. TForm과 같은 Component는 Canvas를 가지고 있으나 TPanel은 Canvas를 사용할 수 없다.

 만약 Canvas Property를 가지고 있지 않으면 다음의 Function은 Font에 기초한 Text 넓이를 Return 한다.


function GetTextWidth(CanvasOWner : TForm; Text:String; TextFont : TFont): Integer;

var

 OldFont : Tfont;

begin

 OldFont := Tfont.Create;

 try

  OldFont.Assign(CanvasOwner.Font);

  CanvasOWner.Font.Assign(TextFont);

  Result := CanvasOWner.Canvas.TextWidth(Text);

  CanvasOWner.Font.Assign(OldFont);

 finally

  OldFont.Free;

 end;

end;



■형식

●날짜와 시간의 형식을 DD/MM/YY에서 DDMMYYYY로 바꾸려면?

 다음 예제를 참고하기 바란다.


LongDate := FormatDateTime('ddmmyyyy', StrToDate(ShortDate));


 앞의 코드는 DD/MM/YY와 같은 제어판의 날짜 형식을 형식 문자열(여기서는 DDMMYYYY)로 바꾸어 준다.


●Date 또는 시간 차이를 구하려면?

 다음은 시간 차이를 구하는 예제이다.


procedure Tform1.MaskEdit2Ecit(Sender : TObject);

begin

 MaskEdit2.Text := FormatDateTime('HH:MM', StrToTime(MaskEdit2.Text));

 Edit2.Text := FormatDateTime('HHMM', StrToTime(MaskEdit2.Text));

 if StrToTime(MaskEdit2.text) < StrTotime(MaskEdit1.text) then

  ShowMessage('Form 시간보다 작습니다.‘)

 else

  MaskEdit3.text := FormatDateTime('HH:MM', StrToTime(MaskEdit2.text) -

                                               StrToTime(MaskEdit1.text));

end;


다음은 날짜 차이를 구하는 예제이다.


procedure TForm1.MaskEdit5Exit(Sender : TObject);

begin

 MaskEdit5.Text := FormatDateTime('YYYY-MM-DD', StrToDate(MaskEdit5.Text));

 Edit5.Text := FormatDateTime('MMDDYYYY', StrToDate(MaskEdit5.Text));

 if StrToDate(MaskEdit5.text) < StrToDate(MaskEdit4.text) then

  ShowMessage('Form 날짜보다 작습니다‘);

 else begin

  MaskEdit6.Text := formatDateTime('YYYY-MM-DD', StrToDate(MaskEdit5.Text) -

                                                       StrToDate(MaskEdit4.text));

  Edit6.Text := FloatToStr(StrToDate(MaskEdit5.text) - StrToDate(MaskEdit4.text));

 end;

end;


●두 날짜 사이의 일(Day) 수를 계산하려면?

 윤년까지 고려하여 날짜 사이의 일 수를 계산해 주는 DateTimeToTimeStamp() 함수를 이용하면 된다(sysutils.pas에 정의되어 있음).


Function GetSubTotaldays(SDate, EDate : TDateTime) : Integer;

begin

 Result := DateTimeToTimeStamp(Sdate).Date - DateTimeToTimeStamp(EDate).Date;

end;


procedure TForm1.Button1Click(Sender : Tobject);

begin

 edit1.text := inttostr(GetSubTotaldays(strtodatetime(maskedit1.text),

                                       strtodatetime(maskedit2.text)));

end;


●두 시간의 차이를 알아내어 시,분,초로 나타내려면?

 다음 예제를 참고하기 바란다.


procedure TForm1.Button1Click(Sender : TObject);

var

 Present : TDateTime;

 Present1 : TDateTime;

 Present2 : TDateTime;

 Hour, Min, Sec, MSec : Word;

begin

 Present := Now;

 edit1.text := datetimetostr(Present);

 Present1 := Now;

 edit2.text := datetimetostr(Present1);

 Present2 := Persent - Present1;

 DecodeTime(Present2, hour, Min, Sec, MSec); //시,분,초로 나타내는 함수

 edit3.text := InttoStr(hour) + ' 시간‘+ IntToStr(Min)+ ' 분’ + IntToStr(Sec)+' 초';

end;


●Number를 천 단위로 분리해서 Comma를 넣으려면?

 다음 예제를 참고하기 바란다.


function FormatNumber(l : Longint) : string;

begin

 FormatNumber := formatFloat('#,##0', StrToFloat(IntToStr(l)));

end;


●Application에서 사용되는 날짜 형식을 특정한 Format으로 고정하려면?

 SysUtils Unit에 선언된 qsu수들을 원하는 특정 값으로 주면 된다.

 Application 전체에 원하는 형태로 날짜 Format을 지정하려면 Main Form의 onCreate Event에 다음과 같이 하면 된다.


ShortDateFormat := 'YY-MM-DD';

LongDateFormat := 'YYYY-MM-DD';


●윈도우/대화상자의 크기를 재지정 했을 때, 버튼을 윈도우내의 하단에 자동으로 정렬하려면?

 TPanel을 생성한 후, TPanel의 Align 특성을 사용하여 하단에 정렬하기 바란다.

 그리고 나서 이 Panel 위에 버튼을 위치시키면 된다. 만약 Button만 나타내고 싶은 경우 Panel의 특성 중 Ctl3D를 ‘Off'로 설정하고, Bevels를 ’none'으로, Parentcolor를 true로 설정하면 된다.



■기타

●파라독스의 계산 필드에서 25.55가 24.499999가 되는 이유는?

 숫자의 소수점 자릿수가 틀리면 잘못된 값을 준다.

 다음과 같이 Round 함수나 str 함수를 서서 자릿수를 맞추어 계산하기 바란다.


SalesIncVAT := round(SalesIncVAT*100) / 100; {소수점 둘째 자리 수}


혹은


var

 s :string;

begin

 str(SalesIncVat :10:2, s);{ 10 자릿수와 소수점 둘째자리}

 Label1.Caption := s;

end;


●금주가 몇 번째 주인지 구하려면(윤년 계산법)?

 다음 함수를 사용해 보기 바란다.


 윤년을 계산하는 함수

function kcIsLeapYear(nYear : Integer) : Boolean;

begin

 Result := (nYear mod 4 = 0) and ((nYear mod 100 <> 0) or (nYear mod 400 = 0));

end;


한 달에 며칠이 있는지를 계산하는 함수

function kcMonthDays(nMonth, nYear : Integer) : Integer;

const

 DaysPerMonth : array[1..12] of Integer = (31,28,31,30,31,31,31,31,30,31,30,31);

begin

 Result := DaysPerMonth[nMonth];

 if (nMonth = 2) and kcIsLeapYear(nYear) then Inc(Result);

end;


앞의 두 함수를 써서 몇 번째 주인지 계산하는 함수

function kcWeekOfYear(dDate : TDateTime) : Integer;

var

 X,nDayCount : Integer;

 nMonth, nDay, nYear : Word;

begin

 nDayCount := 0;

 deCodeDate(dDate, nYear, nMonth, nDay);

 for X := 1 to (nMonth - 1) do

  nDayCount := nDayCount + kcMonthDays(X, nYear);

 nDayCount := nDayCount + nDay;

 Result := ((nDayCount div 7) + 1);

end;


●Hint가 나타나는 속도를 빠르게 하려면?

 TApplication의 Property 중 HintPause Property를 이용하면 된다. 기본 값은 800 Millisecond이다.

 Application.HintPause := 100;


●실행중 Component를 생성하고 Property를 주려면?

 다음의 코드를 참고하기 바란다.


{component를 만든다}

procedure TForm1.Button1Click(Sender :TObject);

begin

 SomeComponent := TComponentClass(FindClass(Edit1.Text)).Create(Self);

 (SomeComponent as Tcontrol).Parent := Self;

end;


{만들 수 있는 component의 유형}

procedure TForm1.FormCreate(Sender : TObject);

begin

 RegisterClassess(TButton, TEdit, TMemo, TLabel]);

end;


{Component의 property를 주는 방법}

procedure TForm1.Button1Click(Sender : TObject);

var

 PropType : pTypeInfo;

 PropInfo : pPropInfo;

begin

 PropInfo := GetPropInfo(SomeComponent.ClassInfo, Edit2.Text);

 PropType := PropInfo^.PropType;

 case PropType^.Kind of

  tkInteger :

       SetOrdProp(SomeComponent, PropInfo, StrToInt(Edit3.Text));

  tkChar :

       SetOrdProp(SomeComponent, PropInfo, Ord(Edit3.text[1]));

  tkEnumeration:

       SetOrdProp(SomeComponent, PropInfo, GetEnumValue(PropType, Edit3.text));

  tkFloat :

       SetFloatProp(SomeComponent, PropInfo, StrToFloat(Edit3.Text));

  tkString :

       SetStrProp(SomeComponent, PropInfo, Edit3.Text);

 end;

end;


●실행중에 Component 하나를 만들었다. 그런데 사용한 기본 값이 설정되지 않은 것 같다. 어떻게 해야 하나?

 그 Component의 Create Constructor에서 Property의 기본값을 설정했는지 확인하기 바란다. 기존 Property 의 기본 값은 실행중에 만든 Component에는 적용되지 않는다.


●런타임시에 VCL 컴포넌트의 개체를 생성하려면?

 다음의 코드는 런타임시에 Form을 생성하는 것이다.


with TPasswordForm.Create(Application) do

begin                   {i.e TForm1, TPasswordForm etc.}

 ShowModal;           {Display form as a modal window}

 Free;                  {Free the from when it is closed}

end;


다음의 코드는 Button을 생성하는 것이다.


var

 TempButton : TButton; {This is only a pointer to a TButton}

begin

 TempButton := TButton.Create(Self);

 TempButton.Parent := Self;     {Must assign the Parent}

 TempButton.Caption := 'Run-time' ; {Assign proprties new}

 TempButton.Visible := true;      {Show to button}

end;


이 개체는 사용 후에 반드시 Free 시켜야 한다.


●‘Tab Order'를 설정하려면?

 Control을 정하기 위해 Form에서 클릭한 후 Object Inspector에서 TabOrder Property를 설정해 준다.


●ImLine Assembler 코드를 사용하려면?

 다음은 시스템의 시간을 맞추는 예제이다.


procedure TForm1.Button1click(Sender : TObject);

begin

 {set system time}

 asm

  mov Ah,2dh {function number}

  mov ch,1    {hours}

  mov cl,10    {minutes}

  mov dx,0    {seconds}

  INT 21h

 end;

end;


●숫자를 Editing, Display할 때 소수점 2자리까지 표현하려면?

 반올림을 적용시키려면 formatfloat 혹은 floattostrF을 이용하고, 그대로 잘라 사용하려면 먼저 100을 곱한 후 TRUNC 시킨 후 다시 100으로 나누면 된다.


formatfloat('#,##.##', strtofloat(edit1.text);

혹은 floattostrF(123.56789, ffFixed, 9, 2);

혹은 floattostr((trunc(strtoFloat(edit1.text) * 100)) / 100);


●프로그램에서 logo를 띄우는 wave 파일을 실행하려면?

 MediaPlayer를 이용하여 특정 .wav를 실행시킬 수 있다. 단, windows 제어판 멀티미디어에 해당 Device가 Add되어 잇어야 한다.


●오디오 CD 트랙의 실시간 트랙 선택사항과 현재의 플레이 시간을 표시하려면?

 다음 예는 오디오 CD의 현재 트랙과 플레이 시간을 타이머 이벤트를 이용해서 매초마다 표시해 준다.


uses

 MMSystem;


procedure TForm1.Timer1Timer(Sender : TObject);

var

 Trk : Word;

 Min : Word;

 Sec : Word;

begin

 with MediaPlayer1 do begin

  Trk := MCI_TMSF_TRACK(Position);

  Min := MCI_TMSF_MINUTE(Position);

  Sec := MCI_TMSF_SECOND(Position);

  Label1.Caption := Format('%.2d', [Trk]);

  Label2.Caption := Format('%.2d : %.2d', [Min, Sec]);

 end;

end;


●BreakPoint가 제대로 작동하지 않는 경우에는?

 디버깅시에 BreakPoint를 설정해 놓아도 Optimization을 위해서 BreakPoint를 사용 할 수 없다는 메시지를 종종 만나게 된다. 어떤 경우에 이런 현상이 발생하는가?


var

 I : Integer;

begin

 I := 10;

 DoSomething;

 DoSomethingElse;

end;


앞의 내용처럼 로컬 변수 I는 선언되어 있지만 결코 사용되지 않는다.


{doSomething, DoSomethingElse에서 사용되지 않는다고 가정}


 물론 I에 값이 할당되기는 하지만 실질적으로 그 I 가 사용이 되는 것은 아니다. 이런 경우에 I := 10이라는 곳에 BreakPoint를 설정하면 다음과 같은 메시지가 나온다.


“breakpoint is set on line that may have been removed by the optimizer

or constrains no debug information. Run anyway?"


 여기에서 Yes를 선택하면 BreakPoint를 무시하게 된다. 이런 메시지가 나온다면 다음과 같이 하면 된다.


1.그 변수가 한 번도 사용되지 않는 변수인지 확인한다.

2.Project|Option에서 Optimization을 Turn Off 한 다음 Rebuild를 한 후 BreakPoint를 사용한다