트레이아이콘어플리케이션의제작 (Creating Tray Icon Application) 윈도우 95 와 NT 4.0 에는작업바의우측에작업트레이라는부분이있다. 여기에있는아이콘은현재데스크탑에서실행되고있는윈도우를가지고있는어플리케이션이라기보다는도스시절에있었던램상주프로그램과비슷한역할을하는것이많다. 이번장에서는트레이에상주하는어플리케이션을제작하는방법에대해서알아보자. Shell_NotifyIcon 트레이아이콘을사용하는어플리케이션에서는 Shell_NotifyIcon 이라는 API 함수를사용하게된다. 이함수에는파라미터가둘있는데, TNotifyIconData 구조체에대한포인터와트레이에작업을원하는플래그를설정한다. 플래그에는아이콘을추가, 삭제, 수정등의기능을설정할수있다. TNotifyIconData 구조체는다음과같이정의되어있다. type TNotifyIconData = record cbsize: DWORD; Wnd: HWND; uid: UINT; uflags: UINT; ucallbackmessage: UINT; hicon: HICON; sztip: array[0..63] of AnsiChar; 여기서 cbsize 는구조체의크기이며, Wnd 는트레이아이콘이메시지를보낼윈도우에대한핸들이다. uid 는아이콘을확인하기위한것으로어플리케이션에트레이아이콘이여러개있을때사용된다. uflag 필드에는세가지를사용할수있다. NIF_MESSAGE, NIF_ICON, NIF_TIP 이그것으로어느것이유효한것인지결정하는플래그이다. ucallbackmessage 필드는사용자의아이콘에대한동작에대해윈도우 (hwnd) 로보내어지는사용자정의메시지의갯수를나타내며, hicon 은트레이영역안에표시할아이콘의핸들이다. 마지막으로 sztip 필드에는작업표시줄의아이콘위로마우스커서가놓일때보여주는툴팁문자열에대한텍스트이다.
이구조체는 ShellAPI.pas 유닛에선언되어있으므로, 실제사용을하기위해서 interface 섹션의 uses 문에 ShellAPI 를추가해서사용한다. 기본원칙 트레이아이콘어플리케이션이동작하는방식에대해생각해보자. 제일먼저폼이생성될때작업트레이부분에 TNotifyIconData 구조체의내용을적절히채워서아이콘을등록해야할것이다. 그리고, 아이콘이나툴팁등의변화가있을경우이를적절하게반영해주어야한다. 그리고, 콜백메시지를등록해서일단트레이아이콘어플리케이션에해당되는메시지일경우에는이메시지를처리해주는루틴을만들어주어야한다. 이때트레이아이콘을더블클릭할경우에는디자인한폼을보여주도록해야할것이며, 폼이보여진후최소화버튼을눌렀을때에는다시트레이아이콘으로복귀하도록해야할것이다. 그리고, 트레이아이콘에서오른쪽버튼을클릭했을때에는디자인한팝업메뉴를보여주고메뉴를실행할수있어야한다. 또한, 작업이끝나면트레이아이콘을제거해야한다. 마지막으로염두에두어야할것은어플리케이션이처음시작할때어플리케이션폼이바로트레이아이콘으로최소화해서위치하게해야한다. 이를간략하게정리하면다음과같다. 1. 트레이아이콘등록 2. 트레이아이콘변화를반영 3. 메시지처리루틴제작 4. 팝업메뉴와폼의최소화 (minimize), 복귀 (restore) 메시지처리 5. 트레이아이콘제거 6. 어플리케이션시작시트레이아이콘으로시작할것 먼저트레이아이콘을등록하는부분에대해서알아보자. 다음의프로시저는전형적인트레이아이콘을등록하는코드를담고있다. procedure AddTrayIcon; var IconData: TNotifyIconData; with IconData do cbsize := SizeOf(IconData);
Wnd := Handle; uid := 0; uflags := NIF_ICON or NIF_MESSAGE or NIF_TIP; ucallbackmessage := WM_MyCallBack; hicon := LoadIcon(hInstance, MyIcon ); sztip := 시계 ; Shell_NotifyIcon(NIM_ADD, @IconData); 내용은앞에서설명한 TNotifyIcon 형의구조체변수에적절한데이터필드를입력하고, 마지막에 Shell_NotifyIcon API 함수를호출하는것이다. 여기서주의깊게보아야할부분이세군데있다. 첫째는 uflags 필드로여기에서추가또는수정을할때이용할플래그를결정한다. NIF_ICON 은트레이아이콘에대한정보를, NIF_TIP 은트레이아이콘에대한툴팁힌트에대한정보를나타낸다. 마지막으로 NIF_MESSAGE 는사용되는사용자정의메시지에대한것이다. 보통처음트레이아이콘을추가할때에는이세가지정보를모두등록해야하므로 NIF_ICON, NIF_TIP, NIF_MESSAGE 를사용하게된다. 둘째는 ucallbackmessage 필드로여기에는메시지처리를하게될사용자정의메시지를대입한다. 보통처음에상수선언을해서대입을하게된다. 세째로는 Shell_NotifyIcon API 함수를호출할때사용하는파라미터로, 지금과같이아이콘을추가할때에는 NIM_ADD, 아이콘에대한정보를수정할때에는 NIM_MODIFY, 아이콘을제거할때에는 NIM_DELETE 를각각호출한다. 이렇게등록된트레이아이콘에대한정보를수정할때에는다음과같이하면된다. Shell_NotifyIcon(NIM_MODIFY, @IconData); 즉, 바뀌게되는내용을 TNotifyIconData 형변수의필드에대입하고 NIM_MODIFY 로 Shell_NotifyIcon API 함수를호출한다. 트레이아이콘을제거할때에도다음과같이해주면해결된다. Shell_NotifyIcon(NIM_DELETE, @IconData); 메시지처리 메시지를처리하기위해서먼저사용할사용자정의메시지와이를처리할콜백함수를선
언해야한다. const WM_CallbackMessage = WM_User + 100; procedure WndProc(var Message: TMessage); 이함수는어플리케이션에대한메시지를전담해서처리하게된다. 이함수를다음과같이사용해서메시지를처리한다. 다음의코드는필자가제작한트레이아이콘제작컴포넌트 (TTrayIcon) 소스의일부이다. procedure TTrayIcon.WndProc(var Message: TMessage); try with Message do case Msg of WM_QueryEndSession: Message.Result := 1; WM_EndSession: if TWmEndSession(Message).EndSession then EndSession; WM_CallbackMessage: case Message.lParam of WM_LBUTTONDBLCLK: DblClick; WM_LBUTTONUP: Click(mbLeft); WM_RBUTTONUP: Click(mbRight); else Result := DefWindowProc(FHandle, Msg, wparam, lparam); except Application.HandleException(Self); 즉, 반드시처리해주어야하는메시지는자신이 TNotifyIconData 형의변수필드에등록한메시지로여기서는 WM_CallbackMessage 가된다. 보통그중에서도마우스더블클릭과좌우버튼클릭에대한메시지만처리하면된다. 그밖의메시지중 WM_EndSession 은트레이아이콘어플리케이션을종료하고자할때
발생하는메시지이다. 여기서는필자가따로제작한 EndSession 이라는프로시저를호출한다. EndSession 프로시저에서는다음과같이등록된트레이아이콘을제거하는코드를사용한다. procedure TTrayIcon.EndSession; Shell_NotifyIcon(NIM_DELETE, @FIconData); 트레이아이콘컴포넌트 (TTrayIcon) 의제작 트레이아이콘어플리케이션에대한확실한이해를돕기위해델파이폼에올려놓고모든어플리케이션을바로트레이아이콘어플리케이션으로전환시킬수있는컴포넌트를하나제작해보았다. 이컴포넌트를사용하면쉽게트레이아이콘어플리케이션을만들수있다. 지면관계상모든소스를다설명할수는없지만중요한부분을골라서설명하겠다. 기본적인테크닉은앞에서설명한 Shell_NotifyIcon 함수를이용해서 TNotifyIconData 구조체를등록, 수정, 삭제하고메시지를처리하는것이다. 먼저컴포넌트를설계하도록하자. 이컴포넌트에서는트레이아이콘의형태를지정할수있는 Icon, 툴팁을지정할수있는 Hint, 그리고트레이아이콘을오른쪽버튼클릭할때보여줄팝업메뉴를설정할때사용할 PopupMenu 프로퍼티를제공하도록하자. 또한이벤트로는트레이아이콘을클릭할때발생하는 OnClick, 더블클릭할때발생하는 OnDblClick, 그리고폼을트레이아이콘에최소화하거나정상형태로복귀할때각각발생하는 OnHide, OnShow 이벤트를제공하도록한다. 다음은 TTrayIcon 컴포넌트의선언부분이다. TTrayIcon = class(tcomponent) private FHandle: HWnd; FIconData: TNotifyIconData; FIcon: TIcon; FHint: string; FPopupMenu: TPopupMenu; FOnClick: TMouseEvent; FOnDblClick: TNotifyEvent; FOnHide: TNotifyEvent;
FOnShow: TNotifyEvent; procedure SetHint(const Hint: string); virtual; procedure SetIcon(Icon: TIcon); virtual; procedure WndProc(var Message: TMessage); protected procedure DoMenu; virtual; procedure Click(Button: TMouseButton); virtual; procedure DblClick; virtual; procedure EndSession; virtual; procedure Changed; virtual; public constructor Create(Owner: TComponent); override; destructor Destroy; override; procedure Hide(Sender: TObject); virtual; procedure Show(Sender: TObject); virtual; published property Hint: string read FHint write SetHint; property Icon: TIcon read FIcon write SetIcon; property PopupMenu: TPopupMenu read FPopupMenu write FPopupMenu; property OnClick: TMouseEvent read FOnClick write FOnClick; property OnDblClick: TNotifyEvent read FOnDblClick write FOnDblClick; property OnHide: TNotifyEvent read FOnHide write FOnHide; property OnShow: TNotifyEvent read FOnShow write FOnShow; 한번쯤컴포넌트를직접만들어보신분이라면그다지어렵지않게이해할것이다. 그렇지만, 앞에서설명한 3 개의프로퍼티와 4 개의이벤트와직접연관되지는않지만내부적으로사용되는데이터필드와메소드를소개하면다음과같다. FHandle 은 WndProc 콜백함수가사용될어플리케이션윈도우에대한핸들을저장한다. 또한, FIconData 는트레이아이콘을등록, 수정, 삭제할때사용할 TNotifyIconData 구조체변수이다. protected 섹션에정의된메소드들중 Click 과 DblClick 은 OnClick, OnDblClick 이벤트를실제구현할메소드이다. EndSession 은앞에서잠깐설명한바있으므로생략한다. Changed 메소드는 Set 메소드에서프로퍼티에저장된 TNotifyIconData 구조체필드의내용을실제로반영하는메소드로이메소드와 Set 메소드들은다음과같이구현된다.
procedure TTrayIcon.Changed; if not (csdesigning in ComponentState) then Shell_NotifyIcon(NIM_MODIFY, @FIconData); procedure TTrayIcon.SetHint(const Hint: string); if FHint <> Hint then FHint := Hint; StrPLCopy(FIconData.szTip, Hint, SizeOf(FIconData.szTip) - 1); if Hint <> '' then FIconData.uFlags := FIconData.uFlags or NIF_TIP else FIconData.uFlags := FIconData.uFlags and not NIF_TIP; Changed; procedure TTrayIcon.SetIcon(Icon: TIcon); if FIcon <> Icon then FIcon.Assign(Icon); FIconData.hIcon := Icon.Handle; Changed; 즉, Changed 메소드는어플리케이션이델파이의디자인모드에있지않으면 FIconData 변수의필드값을 Shell_Notify 함수를이용해서반영하는것이다. 그리고, 각각의 Set 메소드들은먼저 FIconData 변수에해당필드와프로퍼티로나타내기위해서필드데이터 (FIcon, FHint) 에값을저장하고 Changed 메소드를호출한다. DoMenu 메소드는팝업메뉴가선택되었을때메뉴를보여주는메소드로 Click 메소드에의해서호출되며다음과같이구현된다.
procedure TTrayIcon.DoMenu; var Pt: TPoint; if (FPopupMenu <> nil) and not IsWindowVisible(Application.Handle) then GetCursorPos(Pt); FPopupMenu.Popup(Pt.X, Pt.Y); procedure TTrayIcon.Click(Button: TMouseButton); var MousePos: TPoint; GetCursorPos(MousePos); if (Button = mbright) then DoMenu; if Assigned(FOnClick) then FOnClick(Self, Button, [], MousePos.X, MousePos.Y); 그다지어렵지않은코드이므로자세한설명은생략하도록한다. 컴포넌트를구현한다른부분들은이달에디스켓으로제공되는소스를직접참고하기바라 며, 마지막으로 Create 생성자를구현하는코드에대해서살펴보자. constructor TTrayIcon.Create(Owner: TComponent); inherited Create(Owner); FIcon := TIcon.Create; FIcon.Assign(Application.Icon); FHandle := AllocateHwnd(WndProc); if not (csdesigning in ComponentState) then FillChar(FIconData, SizeOf(FIconData), 0); with FIconData do
cbsize := SizeOf(FIconData); Wnd := FHandle; hicon := Icon.Handle; uflags := NIF_ICON or NIF_MESSAGE; ucallbackmessage := WM_CallbackMessage; StrPLCopy(FIconData.szTip, Application.Title, SizeOf(FIconData.szTip) - 1); if Application.Title <> '' then FIconData.uFlags := FIconData.uFlags or NIF_TIP; if not Shell_NotifyIcon(NIM_ADD, @FIconData) then raise EOutOfResources.Create(' 트레이아이콘을생성할수없습니다!'); Application.OnMinimize := Hide; Application.OnRestore := Show; 즉, 초기아이콘으로어플리케이션객체의아이콘을그대로사용하게하였다. 또한, 등록하는윈도우를 AllocateHwnd 함수를이용해서이컴포넌트의 WndProc 함수가소속된윈도우를찾는다. 그리고, 에러처리부분을추가했으며어플리케이션객체가최소화하거나복귀할때에 TTrayIcon 컴포넌트의 Hide, Show 메소드에서처리하도록한다. TTrayIcon 컴포넌트를이용한트레이아이콘어플리케이션의제작 그럼이제 TTrayIcon 컴포넌트를이용해서실제로트레이아이콘어플리케이션을제작해보자. 먼저이달의디스켓으로제공되는 TrayIcon.zip 파일의압축을풀고 TrayIcon.pas 유닛을이용해서컴포넌트를설치한다. 그러면 Samples 팔레트에 TTrayIcon 컴포넌트가추가될것이다. 새로운어플리케이션을시작하고, 폼을다음과같이디자인한다. 즉, TPopupMenu, TPanel, TTimer, TTrayIcon 컴포넌트를하나씩올려놓는다. Panel1 의 Align 프로퍼티를 alclient 로설정해서폼에꽉차도록한다. 또한, Caption 프 이때
로퍼티는시간을알려주게할것이므로그냥비워둔다. 그리고팝업메뉴에는 시계보기 와 닫기 를다음그림과같이설정한다. 이제 TrayIcon1 컴포넌트의프로퍼티를설정해보자. 디자인시에는 PopupMenu 프로퍼티만콤보박스를이용해서 PopupMenu1 으로설정하면된다. 그리고팝업메뉴들에대한이벤트핸들러를다음과같이작성한다. procedure TForm1.N1Click(Sender: TObject); Application.Restore; procedure TForm1.N2Click(Sender: TObject); Form1.Close; 즉, 시계보기 메뉴일경우에는 Application 객체의 Restore 메소드를사용해서폼을보여주게하고, 닫기 일경우에는폼의 Close 메소드를호출해서어플리케이션을종료한다. 이때 Application.Restore 메소드는 TTrayIcon 컴포넌트의 Show 메소드를호출하도록변경되어있으므로 TrayIcon1.Show 와같은기능을하게된다. 그리고, Timer 의 OnClick 이벤트핸들러를다음과같이작성한다. procedure TForm1.Timer1Timer(Sender: TObject);
Panel1.Caption := TimeToStr(Time); TrayIcon1.Hint := TimeToStr(Time); 현재시각을문자열로변환해서 Panel1 과트레이아이콘의툴팁으로설정한다. 이제사용할어플리케이션아이콘과초기툴팁을결정하기위해 Project Options... 메뉴를클릭한후 Application 탭을선택하고, 다음그림과같이설정한다. 이때사용한아이콘파일은필자가이미지에디터로급히그린것이라그다지아름답지못하므로독자여러분들의너그러운양해를바란다. 원래계획으로는필자가트레이아이콘을이용해서움직이는공룡캐릭터와타이머를이용해서자그마한다마고치예제를제공하려고했으나, 필자의조악한그림실력으로인해이렇게시계예제로전환하였다. 관심있는분들은 TTrayIcon 컴포넌트를이용해서쉽게구현할수있을것이다. 이제마지막으로처음에도언급했듯이어플리케이션이시작할때트레이아이콘상태로시작하도록하자. 필자도이방법을찾기위해별별방법을다써보았는데, 해답은생각보다가까운곳에있었다. 즉, 폼의 OnActivate 이벤트에서 Application.Minimize 를호출해
주면된다. procedure TForm1.FormActivate(Sender: TObject); Application.Minimize; 이제시계예제가완성되었다. 얻을수있을것이다. 프로그램을실행하면다음그림과같이동작하는화면들을 정리 (Summary) 트레이아이콘어플리케이션을만드는방법은아마도윈도우 95 와윈도우 NT 4.0 쉘을수정해서사용하는방법중에서가장유용하고간단하다고말할수있다. 특히도스시절의램상주프로그램처럼간단한유틸리티를제작할때에는그활용도가매우높다고할수있다. 이미많은수의유틸리티프로그램들이제공되고있는데, 이들중에는시스템을모니터하거나, 경고를주는등의유용한유틸리티들이있으며대부분의경우트레이아이콘어플리케이션의형태로제공되고있다.