■기타 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를 사용한다