2.화면보호기,트레이 아이콘,스플래쉬 윈도우

본장에서는 화면보호기 프로그램을 제작하는 방법과 작업표시줄 우측 영역에 아이콘 형태로 표시되는 트레이 아이콘 만들기, 그리고 보통 로고화면이라고 불리우는 스플래쉬 윈도우를 만드는 방법에 대해서 설명합니다. 본장에서 설명하는 3개의 팁은 제작방법이 매우 간단합니다.

대상자: 중급,고급

Chapter 등급: 선택

공부방법:

본항목에서는 꼭 배워야 할 이론이 없습니다. 필요한 부분이 있을경우 나중에 참조하는 부분으로 이용하시기 바랍니다.

1. 화면 보호기 만들기

화면 보호기 프로그램의 실행파일을 제작하고  확장자를 SCR 로 바꾸어주어야 합니다. 그리고 이 프로그램은 \windows\system 디렉토리에 프로그램이 설정되어 있어야 합니다. 이렇게 설정되면 제어판의 display항목을 이용하여 화면 보호기를 설정할수가 있습니다. 화면 보호기를 만들때는 일반적으로 MFC에서 대화상자 형태의 프로젝트를 만들고 이 프로젝트를 수정하여 만듭니다.

<1>화면보호기의 3가지 항목 설정

화면 보호기를 만드는 방법은 그림 2-2-1과 같습니다.

(그림 파일)

(그림 2-2-1)화면 보호기 만드는 방법

화면 보호기를 만들고자 할때는 3가지 항목이 필요합니다. 그림 2-2-1의 ⓐ항목은 제어판에서 Display 항목을 선택하고 화면보호기 항목을 선택하였을때의 화면입니다. 이화면에서 □설정□ 항목을 선택하면 대화상자가 나타나고 이때 화면보호기의 여러 기능을 수정또는 설정할수있게 됩니다. ⓑ항목이 □설정□ 버튼을 클릭하였을때 나타나는 대화상자입니다. 두번재로는 그림  ⓐ에서 모니터 화면에 출력되는 내용 항목입니다. 3번째 항목으로는 실제 화면 보호기가 실행되었을때의 항목입니다.  그림 2-2-1의 ⓒ 항목을 의미합니다.

이 3가지 항목으로 분기되는 부분은 어플리케이션의 InitInstance함수에서 입니다. 화면 보호기 실행파일을 구동할때 명령행 인자를 이용하여 3개로 분리합니다. 다음은 명령행 인자를 이용하여 화면 보호기를 실행한 예입니다.

//대화상자 설정 시 실행,그림 2-2-1의  ①항목

ScrSave.scr -c

ScrSave.scr /c

//모니터 화면에 설정시 실행 ,그림 2-2-1의  ②항목

ScrSave.scr -p 13413 <- 모니터 윈도우 핸들 포인터

ScrSave.scr /p 321431 <- 모니터 윈도우 핸들 포인터

//실제 프로그램 실행 ,그림 2-2-1의   ③항목

ScrSave.scr -s

ScrSave.scr /s

화면보호기 프로그램이 ScrSave.scr이라고 할경우 이프로그램을 실행하고자 할때 명령행인자를 위의 3가지 방법으로 하면 각기 다르게 분기되어 프로그램이 실행된다는 의미입니다. 이렇게 분기하기 위해서는 명령행 인자의 문자 배열을 얻어야 합니다.  이문자배열을 얻고자할때는 __argv[1],__argv[2] 등으로 사용하면 됩니다. __argv[1]이라는 의미는 프로그램 명과 함께 넘어온 첫번째 문자열을 의미합니다. 그림 2-2-1의 ⓓ항목은 어플리케이션의 InitInstance를 수정한 형태입니다. __argv[1]+1이라는 의미는 첫번재 문자열을 무시하고 두번째 부터 문자열을 유효화 하는 형태입니다. 첫번째 문자열을 □-□ 이거나 또는 □/□ 이기 때문에 무시하고 두번째 문가가 'c','p','s' 로 분리하여 각각의 항목대로 프로그램을 하면 된다는 것입니다. □c'일경우 (그림 2-2-1의 ①항목) 에는 옵션을 설정하는 항목이기때문에 대화상자를 만들고 이것을 실행하는 프로그램을 만드는것이 일반적입니다. □p'옵션일경우에는 모니터 화면에 출력해야 함으로 모니터 윈도우 핸들을 얻고 이 윈도우클래스에서 OnPaint함수를 만든다음 이함수에서 그래픽을 출력합니다. □c'옵션일경우에는 그냥 간단한 CWnd윈도우를 만들고 이윈도우를 스크린 크기로 설정한다음 이윈도우에서 OnPaint함수를 받은 다음 이곳에 그래픽을 출력하면 됩니다. ①항목에서는 CDialog를 상속받은 클래스를 사용하고 ,②와 ③항목에서는 CWnd를 상속받은 클래스를 사용하면 됩니다. 지금까지 설명한 내용만 이해하셔도 □아하 어떻게 화면 보호기를 만드는구나□ 하는 것을 알수 있을것입니다.

<2>모니터 윈도우 얻기

화면 보호기를 만들때 꼭 알아야 하는 부분은 그림 2-2-1이며 그다음으로는 모니터에 내용을 출력할때 입니다. 이때는 윈도우 크기와 핸들을 얻어야 합니다. 이렇게 윈도우 크기를 얻은 다음 모니터 윈도우 위에 또하나의 윈도우를 설정한다음 이윈도우에 그림을 그립니다. 다음은 display 대화상자에서 모니터 크기를 얻고 이 모니터 위에 윈도우를 설정하는 예입니다.

if (strcmp(__argv[1]+1,"p")==0)

{

//모니터 윈도우 핸들로 부터 CWnd를 만든다.

①CWnd* pParent = CWnd::FromHandle((HWND)atol(__argv[2]));

//CWnd를 상속받은 CTestWin를 만들고 이 클래스 변수를

//메모리에 할당한다.

②CTestWin* pWnd = new CTestWin;

//모니터 윈도우의 클라이언트 영역을 얻은다음

CRect rect;

③pParent->GetClientRect(&rect);

//CTestWin *pWnd를 WS_VISIBLE|WS_CHILD 형태로 클라이언트 영역

//크기로 부모 윈도우를 pParent로 만든다.

④pWnd->Create(NULL,NULL, WS_VISIBLE|WS_CHILD, rect, pParent, NULL);

//메인 윈도우를 pWnd로 설정한다.

⑤m_pMainWnd = pWnd;

return TRUE;

}

모니터 윈도우를 얻고자 할때는 두번째 인자로 넘어온 __argv[2] 를 이용합니다. __argv[2]는 모니터 윈도우 핸들의 포인터 번호입니다. 이번호를 다시 long형으로 바꾸고 FromeHandle를 이용하여 윈도우 클래스로 모니터윈도우를 받은것입니다. 이것이 위의 소스중에 ①항목입니다.

모니터 윈도우를 받은후에 모니터 윈도우 크기와 같은 하나의 윈도우를 만들고 이 윈도우를 모니터 위에 덮어 씌어야 합니다. 이렇게 하고 새로만든 윈도우에서 OnPaint함수를 수행하여 새롭게 그래픽을 출력하면 됩니다. 모니터 윈도우 위에 덮어씌울 윈도우를 만들때 이이윈도우를 CWnd형으로 설정합니다. 따라서 CWnd를 상속받은 새로운 윈도우 클래스를 만들고 이 클래스를 Create 함수를 이용하여 윈도우를 만듭니다. 이때 모니터 윈도우안에 들어갈 차일드 윈도우는 WS_VISIBLE|WS_CHILD 형과 또하나는 현재 프로그램 윈도우 클래스를 얻어야 합니다. 또한 현재의 윈도우를 AfxRegisterWndClass 를 이용하여 윈도우 시스템에 등록해야 합니다. 이부분을 본다면 다음과 같습니다.

먼저 모니터 윈도우의 크라이언트 영역을 받은다음 ④항목 처럼 Create 함수를 실행하게 되면 이함수는 다시 CWnd를 상속받은 CTestWind의 Create가 수행되게 합니다. Create함수에서는 AfxRegisterWndClass를 이용하여 아무것도 없는 공백 커서(IDC_CURSOR1)을 로드하면서 윈도우 시스템에 클래스를 등록하고 m_szClassName을 받습니다. 이렇게 받은 클래스명과 클라이언트 영역 그리고 ④항목에서 설정한 윈도우 스타일을 이용하여 윈도우를 만든형태입니다.

이렇게 윈도우를 만들게 되면 모든 선처리 작업이 끝나게 됩니다. 나머지는 CTestWin의 내부 맴버 함수를 만들고 그안에서 프로그래밍 하면 됩니다. 만일 화면에 글자를 출력하고자 한다면 OnPaint함수에서 처리하고, 타이머를 걸고 이타이머가 수행될때의 내용을 OnTimer함수에서 프로그래밍 하면됩니다.  이렇게 하면 display 대화상자의 모니터에 그대로 내용이 출력되게 됩니다.

CTestWind의 OnPaint에 문자을 출력하고자 한다면 다음과 같이 할수 있습니다.

void CTestWin::OnPaint()

{

CPaintDC dc(this); // device context for painting

CString data=((CScrSaveApp *)AfxGetApp())->m_strData;

dc.TextOut(0,0,data);

}

위와 같이 하면 App클래스의 맴버중 m_strData의 문자열이 실행되게 됩니다.

m_strData의 값이 "Screen Saver" 일경우 display 대화상자에서는 그림 2-2-2와 같은 형태로 출력되게 됩니다.

(그림 파일)

(그림 2-2-2)모니터 출력 예제

<3>실제 보호기 윈도우

화면 보호기가 실행되는 방법은 현재 화면위에 스크린 크기만한 윈도우가 만들어지고 이 윈도우 위에 그림을 그리는 것입니다. 제어판의 display 항목을 실행시키고 특정 화면보호기를 설정하면, 일정 시간이 되도록 아무런 입력이 없을경우 바로 화면 보호기가 실행됩니다. 이때 CWnd를 상속받은 하나의 윈도우를 만들고 이 윈도우 에서 화면 보호기 그래픽을 설정하면 됩니다.

실제 보호기 윈도우를 만드는 형태는 그림 2-2-3과 같습니다.

(그림 파일)

(그림 2-2-3)실제 보호기 윈도우 만들기

실제 보호기 윈도우를 만들때는 먼저 스크린 크기를 얻어야 합니다. 그림 2-2-3의  ⓐ에서 ①항목이 전체 스크린 크기를 얻어서 rect에 저장하는 내용입니다. 이렇게 저장한후에 AfxRegisterWndClass 함수를 이용하여 윈도우 시스템에 현재 윈도우 클래스를 등록하고,등록하면서 리턴된 클래스 이름 m_szClassName 를 이용하여 CreateEx함수를 호출시켜서 윈도우를 만들면 됩니다. 윈도우를 만들때 확장 스타일 옵션을 WS_EX_TOPMOST 로 설정합니다. 또한 윈도우 스타일은 WS_VISIBLE|WS_POPUP 으로 설정하면 그냥 박스 형태의 CWnd가 발생됩니다. 이렇게 하면 화면 보호기 윈도우가 만들어 집니다. 이렇게 한것이 그림 2-2-3의 ②항목입니다.

화면 보호기 윈도우가 만들어지면 실제 보호기 그래픽이 출력되어야 합니다. 이것은 여러 방법이 있습니다. 이 여러 방법들은 지금까지 설명한 그래픽 출력의 모든 방법을 다이용할수가 있다는 의미입니다. 비트맵을 출력할수도 있으며, 움직이는 라인을 그릴수도 있습니다. 이런 그래픽 출력을 하는 하나의 예를 만들어 보겠습니다. 본 예는 단순하게 "Screen Saver" 라는 문자열을 윈도우 영역의 임의의 위치에 출력하도록 해보겠습니다.

이렇게 출력하기 위해서 보호기 윈도우의 OnPaint에 다음과 같이 하엿습니다.

void CRealSave::OnPaint()

{

CPaintDC dc(this); // device context for painting

// TODO: Add your message handler code here

CRect rect;

CBrush cBrush,*oldBrush;

CPen cPen,*oldPen;

CFont cFont,*oldFont;

//폰트를 만들고

cFont.CreateFont(-96,0,0,700,FW_DONTCARE,FALSE,

FALSE,FALSE,DEFAULT_CHARSET,OUT_DEFAULT_PRECIS,

CLIP_DEFAULT_PRECIS,DEFAULT_QUALITY,DEFAULT_PITCH,

"바탕체");

//검정색 펜과 검정색 브러쉬를 만든다.

cPen.CreatePen(PS_SOLID,1,RGB(0,0,0));

cBrush.CreateSolidBrush(RGB(0,0,0));

oldFont=dc.SelectObject(&cFont);

oldPen=dc.SelectObject(&cPen);

oldBrush=dc.SelectObject(&cBrush);

//클라이언트 영역을 얻은다음

GetClientRect(rect);

//박스를 칠해서 화면을 검게 만든다.

dc.Rectangle(rect);

//App클래스의 m_strData를 화면임의의 위치에 출력한다.

CString data=((CScrSaveApp *)AfxGetApp())->m_strData;

int x,y;

//임의의 위치 만들기

x=rand()%rect.Width();

y=rand()%rect.Height();

//문자 배경색은 검정색

dc.SetBkColor(RGB(0,0,0));

//출력색은 임의의 색

dc.SetTextColor(RGB(rand()%255,rand()%255,rand()%255));

//문자 출력

dc.TextOut(x,y,data);

//이전 그리기 객체로 복귀

dc.SelectObject(oldFont);

dc.SelectObject(oldPen);

dc.SelectObject(oldBrush);

cFont.DeleteObject();

cBrush.DeleteObject();

cPen.DeleteObject();

// Do not call CWnd::OnPaint() for painting messages

}

위에서 설명한 소스의 내용은 지금까지 책을 읽어보신 분이라면 이해가 되실것입니다. 간단하게 화면을 검정색 배경으로 만든다음, 적당한 크기의 폰트를 만들고 이폰트로 문자를 화면에 출력하는 것입니다. OnPaint에서 이렇게 문자를 출력할때 수시로 문자의 위치가 변경되도록 만들고자 한다면 타이머를 가동시키고 OnTimer에서 Invalidate(FALSE);함수를 호출시켜 계속적으로 OnPaint함수를 호출하면 됩니다. 그림 2-2-3의 ③항목에서 SetTiemr함수를 이용한것이 바로 이이유입니다. 이렇게 하고 OnTimer함수를 다음과 같이 하면 0.5초마다 임의의 위치에 문자를 출력하게 됩니다.

void CRealSave::OnTimer(UINT nIDEvent)

{

Invalidate(FALSE);

CWnd::OnTimer(nIDEvent);

}

화면 보호기는 마우스 버튼이 눌려지거나 또는 키가 눌려지면 종료되게 되어 있습니다. 그외 여러 응답이 있을경우에도 화면 보호기는 중단해야 합니다. 화면 보호기가 끝나게 하는것은 매우 간단합니다. SendMessage(WM_CLOSE,0,0)을 하거나 또는 PostMessage(WM_CLOSE); 함수를 사용하면 됩니다. 다음은 화면보호기에서 마우스를 클릭하거나 또는 키가 눌려졌을때 화면 보호기가 중단되는 예입니다.

//키와 마우스눌려 졌을때 종료

void CRealSave::OnLButtonDown(UINT nFlags, CPoint point)

{

PostMessage(WM_CLOSE);

CWnd::OnLButtonDown(nFlags, point);

}

void CRealSave::OnLButtonUp(UINT nFlags, CPoint point)

{

PostMessage(WM_CLOSE);

CWnd::OnLButtonUp(nFlags, point);

}

void CRealSave::OnRButtonDown(UINT nFlags, CPoint point)

{

PostMessage(WM_CLOSE);

CWnd::OnRButtonDown(nFlags, point);

}

void CRealSave::OnRButtonUp(UINT nFlags, CPoint point)

{

PostMessage(WM_CLOSE);

CWnd::OnRButtonUp(nFlags, point);

}

void CRealSave::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags)

{

PostMessage(WM_CLOSE);

CWnd::OnKeyDown(nChar, nRepCnt, nFlags);

}

void CRealSave::OnKeyUp(UINT nChar, UINT nRepCnt, UINT nFlags)

{

PostMessage(WM_CLOSE);

CWnd::OnKeyUp(nChar, nRepCnt, nFlags);

}

이외에도 현재 윈도우가 비활성화 될경우에도 종료를 시킵니다. 비활성화 된것을 알수 있는 방법과 이때 종료하는 내용은 다음과 같습니다.

void CRealSave::OnActivate(UINT nState, CWnd* pWndOther, BOOL bMinimized)

{

CWnd::OnActivate(nState, pWndOther, bMinimized);

if (nState == WA_INACTIVE)

PostMessage(WM_CLOSE);

}

OnActivate 함수를 만들면 nState에 WA_INACTIVE라고 설정되었을경우가 비활성 입니다. 이때 WM_CLOSE 메시지를 수행시켜서 프로그램을 종료 하면 됩니다. CWnd를 사용하여 윈도우를 만들었을때는 WM_CLOSE와 함께 OnDestroy 함수에서 PostQuitMessage함수를 더사용합니다. 다음은 OnDestroy 함수를 만들고 PostQuitMessage 함수를 사용한 것입니다.

void CRealSave::OnDestroy()

{

CWnd::OnDestroy();

PostQuitMessage(0);

}

<4>대화상자 만들기

대화상자를 만드는 방법은 일반 대화상자를 만들듯이 하면 됩니다. 본장에서 만든 ScrSave예제에서 만든 대화상자는 그림 2-2-4와 같습니다.

(그림 파일)

(그림 2-2-4)ScrSave의 대화상자 폼

그림 2-2-4와 같이 폼을 만들고 이폼과 연결되는 클래스를 CScrSaveDlg 로 만들고 대화상자를 실행시키면 됩니다. 다음은 어플리케이션 클래스의 InitInstance 함수에서 □c' 옵션일때 대화상자를 실행하는 예입니다.

CScrSaveDlg dlg;

m_pMainWnd = &dlg;

if(dlg.DoModal()==IDOK)

m_strData=dlg.m_strData;

간단하게 대화상자에 입력된 데이터를 m_strData 에 설정한 예입니다.

<5>화면 보호기 예제 ScrSave

ScrSave는 지금까지 설명한 예를 이용하여 하나의 화면 보호기를 만든 예제입니다. 먼저 어플리케이션 클래스의  InitInstance를 보시겠습니다.

BOOL CScrSaveApp::InitInstance()

{

SetRegistryKey("Screen Save");

//옵션 설정 대화상자

if (strcmp(__argv[1]+1,"c")==0)

{

CScrSaveDlg dlg;

m_pMainWnd = &dlg;

if(dlg.DoModal()==IDOK)

m_strData=dlg.m_strData;

}

//대화상자에 출력되는 모니터에 출력하기

else if (strcmp(__argv[1]+1,"p")==0)

{

//모니터 윈도우 핸들로 부터 CWnd를 만든다.

CWnd* pParent = CWnd::FromHandle((HWND)atol(__argv[2]));

//CWnd를 상속받은 CTestWin를 만들고 이 클래스 변수를

//메모리에 할당한다.

CTestWin* pWnd = new CTestWin;

//모니터 윈도우의 클라이언트 영역을 얻은다음

CRect rect;

pParent->GetClientRect(&rect);

//CTestWin *pWnd를 WS_VISIBLE|WS_CHILD 형태로 클라이언트 영역

//크기로 부모 윈도우를 pParent로 만든다.

pWnd->Create(NULL,NULL, WS_VISIBLE|WS_CHILD, rect, pParent, NULL);

//메인 윈도우를 pWnd로 설정한다.

m_pMainWnd = pWnd;

return TRUE;

}

//실제 화면 출력하기

else

{

CRealSave* pWnd = new CRealSave;

pWnd->Create();

m_pMainWnd = pWnd;

return TRUE;

}

return FALSE;

}

옵션에 따라서 3개의 항목을 각각 출력하는 예제를 작성한것입니다. 여기에서 대화상자를 출력하면 , 즉 제어판의 display  에서 화면 보호기 항목을 선택하고 setting 버튼을 클릭하였을때 (그림 2-2-1의 ⓑ) 대화상자가 그림 2-2-4와 같이 출력되고 입력된 데이터가 App 클래스의 m_strData로 저장됩니다. 그러나 이렇게 입력된 데이터가 화면 보호기에는 변경된 문자열로 출력되지 않습니다. 이이유는 화면보호기가 실행되거나 또는 모니터 윈도우에 실행될때, 대화상자가 실행될때 이 3개가 서로 독립된 프로그램이 동작하기 때문입니다. 화면 보호기는 보통 출력할 옵션을 윈도우 레지스트리에 저장하고 이값을 로드하여 화면 보호기가 구동됩니다. 본장에서 설명한 화면 보호기 내용과 본 파트 4자에서 설명하는 윈도우 레지스트르리에 데이터 저장과 로드방법을 이용하면 입력된 문자열로 바꾸어 출력할수 있습니다.

간단하게 설명하면 그림 2-2-4의 대화상자에서 입력된 문자열을 윈도우 레지스트리에 기록한다음, 화면보호기 윈도우에서는 윈도우 레지스트리에 입력된 문자열을 읽어서 이것을 화면에 출력하면 됩니다.

나머지 방법은 본항목에서 설명한 방법대로 코딩하였습니다. 지면을 조금이나마 줄이기 위해서 소스는 생략합니다. 본 파트를 읽으시는 분들은 중급자 수준이라 생각되므로 지금까지 설명한 방법만 이해하시면 소스가 필요없을 것이라고 봅니다. 소스는 제공되는 CD의 ScrSave를 참조하시기 바랍니다.

2. 트레이 아이콘

트레이 아이콘 이란 작업표시줄 우측의 작은 영역에 아이콘형태로 위치하고 이아이콘을 클릭하면 프로그램이 실행되는 형태를 의미합니다. 그림 2-2-5는 트레이 아이콘을 만들고 설정한 예제입니다.

(그림파일)

(그림 2-2-5)트레이 아이콘 예제 ExTrayIcon

그림 2-2-5의 ③항목이 트레이 아이콘 입니다. 보통 현재 시스템에서 특정 상태를 설정할때 주로 많이 사용하는 형태입니다. 쉽게 보면 제어기 프로그램들이 트레이 아이콘 형태로 설정됩니다. 이아이콘의 좌측 마우스 버튼을 클릭하게 되면 그림 2-2-5의  ①항목 같이 대화상자가같은 윈도우가 출력되고 (또는 일반프로그램이 동작될수도 있습니다.) ②항목 같이 우측 마우스 버튼을 누르면 작은 팝업메뉴가 나타납니다. 이런 형태의 프로그램을 제작하는 방법을 그냥 트레이 아이콘 만들기라고 합니다.

<1>트레이 아이콘 만들기 전체 구조

트레이 아이콘을 만들기 위한 전체 구조는 그림 2-2-6과 같습니다.

(그림 파일)

(그림 2-2-6)트레이 아이콘 만들기 전체 구조

그림 2-2-6 의 ⓐ항목이 트레이 아이콘을 만드는 기본 형태입니다. 트레이 아이콘을 만들고자 할경우에는 NOTIFYICONDATA 구조체에 값을 넣고 Shell_NotifyIcon 함수를 이용하여 NOTIFYICONDTA 구조체의 정보를 설정하면 됩니다. NOTIFYICONDATA 구조체는 다음과 같습니다.

typedef struct _NOTIFYICONDATA {

DWORD cbSize; //NOTIFYICONDATA 구조체 크기

HWND hWnd; //윈도우 핸들

UINT uID; //윈도우 아이디

UINT uFlags; // 옵션 플래그

UINT uCallbackMessage; //콜백 메시지 ID

HICON hIcon; //아이콘 플러그

char szTip[64]; //풍선 도움말 문자열

} NOTIFYICONDATA, *PNOTIFYICONDATA;

uFlags는 3개의 플러그를 사용할수 있습니다.NIF_ICON  이면 아이콘 형태로 출력되는 것을 활성화 하며,NIF_MESSAGE  는 아이콘에서 발생하는 메시지가 활성하되고 uCallbackMessage로 설정한 메시지 ID로 메시지를 보내게되며, NIF_TIP 은 풍선도움말이 가능하여 아이콘위에 마우스가 위치하면 szTip에 설정한 문자열이 풍선 도움말로 나타나게 됩니다. ⓐ항목의 ①~⑥ 항목까지가 NOTIFYICONDATA의 변수 data를 설정하고 이 변수에 값을 설정한 예입니다. 이렇게 한후 Shell_NotifyIcon 함수를 이용하여 data를 설정하면 트레이 아이콘이 만들어지게 됩니다. Shell_NotifyIcon 함수의 형태는 다음과 같습니다.

WINSHELLAPI BOOL WINAPI Shell_NotifyIcon(

DWORD dwMessage, //명령 메시지

PNOTIFYICONDATA pnid//NOTIFYICONDATA 변수 포인터

);

dwMessage는 3가지 명령 플래그를 사용할수 있습니다.NIM_ADD  이면 새롭게 트레이 아이콘을 설정하는 것이며,NIM_DELETE 이면 현재 설정된 트레이 아이콘을 삭제하는 것이며 , NIM_MODIFY 는 트레이 아이콘을 수정하는 것입니다. ⓐ항목의 ⑧ 에서는 NIM_ADD를 사용하였기 때문에 새롭게 아이콘을 등록한 형태가 되고, ⓓ 항목 에서는 NIM_DELETE 를 사용하였기 때문에  등록된 아이콘이 제거됩니다.

트레이 아이콘에서 좌측,우측 마우스 버튼을 클릭하면 이때 이메시지를 받아야 합니다. 이메시지를 받기 위해서 NOTIFYICONDATA 의 맴버 uCallbackMessage 에 사용자 메시지를 설정한것입니다. 그림 2-2-6의 ⓑ항목의 ①은 사용자 메시지를 정의한 내용입니다. WM_USER+100을 ID_TRAYICON_NOTIFY 라고 설정하고 이값을 uCallBackMessage에 설정하면 아이콘에서 메시지가 발생되었을대 ID_TRAYCION_NOTIFY가 발생됩니다. 이메시지가 발생될때 수행하는 함수를  ⓑ항목의 ②와 ③과 같이 설정한후에  ⓒ항목처럼 실제 메시지처리를 하는 함수를 만들면 됩니다. ⓒ항목처럼 ID_TRAYCION_NOTIFY메세지에 의해서 수행하는 함수의 인자중 lParam에는 현재 발생된 윈도우 메시지가 설정됩니다. 이메시지에서 WM_LBUTTONDOWN이면 그에 해당하는 내용을 코깅하고 WM_RBUTTONDOWN이면 그에 대한 내용을 코딩하면 됩니다.

그림 2-2-6의 형태가 실제 트레이 아이콘을 만드는 기본구조 입니다. 이구조와 함께 사용되는 추가적인 함수가 있다면 이것은 HideApplication(); 함수입니다. 이함수를 실행하면 작업표시줄에 현재 프로그램의 이름이 설정되지 않습니다. 보통 트레이 아이콘이 생기면 작업표시줄에는 해당 프로그램의 이름을 설정하지 않습니다. 이때 HideApplication(); 함수를 사용하시면 됩니다.

<2>트레이 아이콘 만들기 예제 ExTrayIcon

ExTrayIcon예제는 간단하게 트레이 아이콘을 만드는 예제입니다.

ExTrayIcon는 SDI형태로 프로젝트를 만듭니다. SDI형태로 프로그램을 만들때 메인 프레임의 클래스명을 CMainFrame에서 CHideMainFrame 으로 만들었습니다. 또는 대화상자 형태의 프로젝트를 만든다음 ClassWizard를 이용하여 CFrameWnd를 상속받은 CHideMainFrame를 만들어도 됩니다. 이렇게 만들고 어플리케이션의  InitInstance에서 다음과 같이 단순하게 프레임 윈도우를 로드합니다.

BOOL CExTrayIconApp::InitInstance()

{

AfxEnableControlContainer();

#ifdef _AFXDLL

Enable3dControls();

#else

Enable3dControlsStatic();

#endif

m_pMainWnd = new CHideMainFrame;

m_nCmdShow = SW_SHOWMINIMIZED;

m_pMainWnd->ShowWindow(m_nCmdShow);

m_pMainWnd->UpdateWindow();

HideApplication();

return TRUE;

}

메인 프레임을 SW_SHOWMINIMIZED  즉 아이콘 형태로 만든다음 HideApplication 함수를 이용하여 작업표시줄에 이름이 나타나지 않도록 하였습니다. 이렇게 한후에 메인 프레임이 만들어질때  즉 OnCreate에서 트레이 아이콘을 등록합니다. 이렇게 등록하는 OnCreate 함수는 다음과 같습니다.

int CHideMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)

{

if (CFrameWnd::OnCreate(lpCreateStruct) == -1)

return -1;

NOTIFYICONDATA data;

data.cbSize = sizeof(NOTIFYICONDATA);

data.hWnd = m_hWnd;

data.uID = IDR_MAINFRAME;

data.uFlags = NIF_ICON | NIF_MESSAGE | NIF_TIP;

data.uCallbackMessage = ID_TRAYICON_NOTIFY;

data.hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);

strcpy(data.szTip,"ExTrayIcon");

Shell_NotifyIcon(NIM_ADD, &data);

pDlg =NULL;

pMenu=new CMenu;

pMenu->LoadMenu(IDR_MENU1);

return 0;

}

이때 pDlg와 pMenu라는 두개의 맴버가 보일것입니다. 이두개의 변수는 헤더에 다음과 같이 정의해놓았습니다.

CExTrayIconDlg *pDlg;

CMenu *pMenu;

pDlg는 좌측 마우스 버튼을 클릭했을때 나타나는 대화상자포인터를 설정할 변수이며 pMenu는 자원에서 하나의 메뉴를 만들고 이메뉴를 로드한 변수입니다. 이렇게 로드된 pMenu를 아이콘에서 우측 마우스 버튼을 클릭하면 실행하기 위함입니다. 그림 2-2-7은 IDR_MENU1와 프레임 윈도우와 연결된 함수입니다.

(그림 파일)

(그림 2-2-7)IDR_MENU1 와 프레임 윈도우와 연결된 함수

그림 2-2-7과 같이 메뉴를 만들고 이메뉴와 연결되는 함수를 프레임 윈도우에 만듭니다.

이렇게 한후에 그림 2-2-6의 ⓑ항목처럼 트레이 아이콘에서 발생된 이벤트 메시지를 수행하는 함수를 선언하고 그림 2-2-6의 ⓒ항목처럼 이함수를 만듭니다. 이렇게 만든 OnTrayIconNotify 함수는 다음과 같습니다.

LONG CHideMainFrame::OnTrayIconNotify(WPARAM wParam, LPARAM lParam)

{

POINT point;

CMenu *sMenu;

switch(lParam)

{

//좌측 마우스 버튼을 클릭하면 대화상자가 실행된다.

case WM_LBUTTONDOWN :

//현재 대화상자가 실행되면 아무일도 하지 않는다.

if(pDlg)

break;

pDlg= new CExTrayIconDlg;

//대화상자에서 EXIT버튼을 클릭하면 종료한다.

if(pDlg->DoModal()==IDC_EXIT)

{

SendMessage(WM_CLOSE,0,0);

return TRUE;

}

//대화상자 삭제

delete pDlg;

pDlg=NULL;

break;

//우측 마우스 버튼을 클릭하면

case WM_RBUTTONDOWN:

//pMenu의 서브 메뉴를 얻고

sMenu=pMenu->GetSubMenu(0);

//마우스 위치를 얻는다.

GetCursorPos(&point);

//TrackPopupMenu함수를 실행하여 팝업 메뉴를 구동한다.

sMenu->TrackPopupMenu(TPM_RIGHTBUTTON|TPM_LEFTALIGN	,point.x,point.y,this,CRect(point.x-100,point.y-100,point.x,point.y));

break;

}

return TRUE;

}

lParam 이 WM_LBUTTONDOWN이면 트레이 아이콘에서 좌측 마우스 버튼을 클릭한 상태입니다. 이때에는 대화상자를 구동하고 대화상자에서 IDC_EXIT 버튼을 클릭하면 대화상자가 종료 되면서  IDC_EXIT를 리턴하도록 하였습니다. 이렇게 되면 WM_CLOSE메시지를 발생시켜 프로그램을 종료 시킵니다. lParam 이 WM_RBUTTONDOWN이면 우측 마우스 버튼이 눌려진 상태이므로 이때는 컨텍스트 메뉴를 구동시킵니다. 이렇게 구동하여 메뉴에서 항목을 선택하면 그림 2-2-7에서 와 같이 두개의 함수가 실행되게 됩니다. 첫번째 항목은 대화상자 실행이며 두번째 항목은 프로그램 종료 입니다.이내용은 다음과 같습니다.

//컨텍스트메뉴에서 "끝내기" 선택

void CHideMainFrame::OnExit()

{

SendMessage(WM_CLOSE,0,0);

}

//컨텍스트 메뉴에서 "대화상자" 선택

void CHideMainFrame::OnShowdialog()

{

if(pDlg)

return;

pDlg= new CExTrayIconDlg;

if(pDlg->DoModal()==IDC_EXIT)

{

SendMessage(WM_CLOSE,0,0);

return;

}

delete pDlg;

pDlg=NULL;

}

위와 같이 하면 □끝내기□ 에서는 프로그램이 종료 되고 "대화상자□에서는 대화상자가 화면에 나타나게 됩니다.

프로그램이 종료 될때에는 트레이 아이콘을 해제해야 합니다.(그림 2-2-6의 ⓓ항목 참조)

본예제에서는 프레임 윈도우의 OnClose함수에서 트레이 아이콘을 삭제하였습니다. 이내용은 다음과 같습니다.

void CHideMainFrame::OnClose()

{

NOTIFYICONDATA data;

data.cbSize = sizeof(NOTIFYICONDATA);

data.hWnd = m_hWnd;

data.uID = IDR_MAINFRAME;

Shell_NotifyIcon(NIM_DELETE, &data);

CFrameWnd::OnClose();

}

이렇게 하면  ExTrayIcon 예제가 완성됩니다. 다음은 ExTrayIcon의 프레임 윈도우 헤더와 소스 그리고 어플리케이션 클래스의 소스부분입니다.

(1)프레임 윈도우 전체 소스

(프로그램 소스)

#if !defined(AFX_HIDEMAINFRAME_H__E1EAEB1F_3DAA_11D2_8B94_006097AD9DDC__INCLUDED_)

#define AFX_HIDEMAINFRAME_H__E1EAEB1F_3DAA_11D2_8B94_006097AD9DDC__INCLUDED_

#include "ExTrayIconDlg.h"

#if _MSC_VER > 1000

#pragma once

#endif // _MSC_VER > 1000

// HideMainFrame.h : header file

//

/////////////////////////////////////////////////////////////////////////////

// CHideMainFrame frame

#define ID_TRAYICON_NOTIFY WM_USER+100

class CHideMainFrame : public CFrameWnd

{

DECLARE_DYNCREATE(CHideMainFrame)

protected:

// Attributes

public:

CHideMainFrame();           // protected constructor used by dynamic creation

CExTrayIconDlg *pDlg;

CMenu *pMenu;

// Operations

public:

// Overrides

// ClassWizard generated virtual function overrides

//{{AFX_VIRTUAL(CHideMainFrame)

//}}AFX_VIRTUAL

// Implementation

protected:

virtual ~CHideMainFrame();

// Generated message map functions

//{{AFX_MSG(CHideMainFrame)

afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct);

afx_msg void OnClose();

afx_msg void OnExit();

afx_msg void OnShowdialog();

//}}AFX_MSG

afx_msg LONG OnTrayIconNotify(WPARAM wParam, LPARAM lParam);

DECLARE_MESSAGE_MAP()

};

/////////////////////////////////////////////////////////////////////////////

//{{AFX_INSERT_LOCATION}}

// Microsoft Visual C++ will insert additional declarations immediately before the previous line.

#endif // !defined(AFX_HIDEMAINFRAME_H__E1EAEB1F_3DAA_11D2_8B94_006097AD9DDC__INCLUDED_)

// HideMainFrame.cpp : implementation file

//

#include "stdafx.h"

#include "ExTrayIcon.h"

#include "HideMainFrame.h"

#ifdef _DEBUG

#define new DEBUG_NEW

#undef THIS_FILE

static char THIS_FILE[] = __FILE__;

#endif

/////////////////////////////////////////////////////////////////////////////

// CHideMainFrame

IMPLEMENT_DYNCREATE(CHideMainFrame, CFrameWnd)

CHideMainFrame::CHideMainFrame()

{

Create(NULL,"ExTrayIcon", WS_OVERLAPPED);

}

CHideMainFrame::~CHideMainFrame()

{

}

BEGIN_MESSAGE_MAP(CHideMainFrame, CFrameWnd)

//{{AFX_MSG_MAP(CHideMainFrame)

ON_WM_CREATE()

ON_WM_CLOSE()

ON_COMMAND(ID_EXIT, OnExit)

ON_COMMAND(ID_SHOWDIALOG, OnShowdialog)

//}}AFX_MSG_MAP

ON_MESSAGE(ID_TRAYICON_NOTIFY, OnTrayIconNotify)

END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////

// CHideMainFrame message handlers

int CHideMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)

{

if (CFrameWnd::OnCreate(lpCreateStruct) == -1)

return -1;

NOTIFYICONDATA data;

data.cbSize = sizeof(NOTIFYICONDATA);

data.hWnd = m_hWnd;

data.uID = IDR_MAINFRAME;

data.uFlags = NIF_ICON | NIF_MESSAGE | NIF_TIP;

data.uCallbackMessage = ID_TRAYICON_NOTIFY;

data.hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);

strcpy(data.szTip,"ExTrayIcon");

Shell_NotifyIcon(NIM_ADD, &data);

pDlg =NULL;

pMenu=new CMenu;

pMenu->LoadMenu(IDR_MENU1);

return 0;

}

void CHideMainFrame::OnClose()

{

NOTIFYICONDATA data;

data.cbSize = sizeof(NOTIFYICONDATA);

data.hWnd = m_hWnd;

data.uID = IDR_MAINFRAME;

Shell_NotifyIcon(NIM_DELETE, &data);

CFrameWnd::OnClose();

}

LONG CHideMainFrame::OnTrayIconNotify(WPARAM wParam, LPARAM lParam)

{

POINT point;

CMenu *sMenu;

switch(lParam)

{

//좌측 마우스 버튼을 클릭하면 대화상자가 실행된다.

case WM_LBUTTONDOWN :

//현재 대화상자가 실행되면 아무일도 하지 않는다.

if(pDlg)

break;

pDlg= new CExTrayIconDlg;

//대화상자에서 EXIT버튼을 클릭하면 종료한다.

if(pDlg->DoModal()==IDC_EXIT)

{

SendMessage(WM_CLOSE,0,0);

return TRUE;

}

//대화상자 삭제

delete pDlg;

pDlg=NULL;

break;

//우측 마우스 버튼을 클릭하면

case WM_RBUTTONDOWN:

//pMenu의 서브 메뉴를 얻고

sMenu=pMenu->GetSubMenu(0);

//마우스 위치를 얻는다.

GetCursorPos(&point);

//TrackPopupMenu함수를 실행하여 팝업 메뉴를 구동한다.

sMenu->TrackPopupMenu(TPM_RIGHTBUTTON|TPM_LEFTALIGN ,point.x,point.y,this,CRect(point.x-100,point.y-100,point.x,point.y));

break;

}

return TRUE;

}

//컨텍스트메뉴에서 "끝내기" 선택

void CHideMainFrame::OnExit()

{

SendMessage(WM_CLOSE,0,0);

}

//컨텍스트 메뉴에서 "대화상자" 선택

void CHideMainFrame::OnShowdialog()

{

if(pDlg)

return;

pDlg= new CExTrayIconDlg;

if(pDlg->DoModal()==IDC_EXIT)

{

SendMessage(WM_CLOSE,0,0);

return;

}

delete pDlg;

pDlg=NULL;

}

(프로그램 소스끝)

(2)어플리케이션의 소스 부분

(프로그램 소스)

// ExTrayIcon.cpp : Defines the class behaviors for the application.

//

#include "stdafx.h"

#include "ExTrayIcon.h"

#include "ExTrayIconDlg.h"

#include "HideMainFrame.h"

#ifdef _DEBUG

#define new DEBUG_NEW

#undef THIS_FILE

static char THIS_FILE[] = __FILE__;

#endif

/////////////////////////////////////////////////////////////////////////////

// CExTrayIconApp

BEGIN_MESSAGE_MAP(CExTrayIconApp, CWinApp)

//{{AFX_MSG_MAP(CExTrayIconApp)

// NOTE - the ClassWizard will add and remove mapping macros here.

//    DO NOT EDIT what you see in these blocks of generated code!

//}}AFX_MSG

ON_COMMAND(ID_HELP, CWinApp::OnHelp)

END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////

// CExTrayIconApp construction

CExTrayIconApp::CExTrayIconApp()

{

// TODO: add construction code here,

// Place all significant initialization in InitInstance

}

/////////////////////////////////////////////////////////////////////////////

// The one and only CExTrayIconApp object

CExTrayIconApp theApp;

/////////////////////////////////////////////////////////////////////////////

// CExTrayIconApp initialization

BOOL CExTrayIconApp::InitInstance()

{

AfxEnableControlContainer();

// Standard initialization

// If you are not using these features and wish to reduce the size

//  of your final executable, you should remove from the following

//  the specific initialization routines you do not need.

#ifdef _AFXDLL

Enable3dControls();	// Call this when using MFC in a shared DLL

#else

Enable3dControlsStatic();	// Call this when linking to MFC statically

#endif

m_pMainWnd = new CHideMainFrame;

m_nCmdShow = SW_SHOWMINIMIZED;

m_pMainWnd->ShowWindow(m_nCmdShow);

m_pMainWnd->UpdateWindow();

HideApplication();

return TRUE;

}

(프로그램 소스끝)

3.스플래쉬 윈도우 만들기

스플래쉬 윈도우란 프로그램이 구동되면 프로그램이 구동되는동안 윈도우가 하나 출력되고 이윈도우에 현재 프로그램을 로고가 출력되는 형태를 말합니다. 그림 2-2-8은 스플래쉬 윈도우가 출력된 예제 프로그램입니다.

(그림 파일)

(그림 2-2-8) 스플래쉬 윈도우가 출력된 예제

그림 2-2-8의  ① 항목과 같은 형태가 바로 스플래쉬 윈도우 입니다. 이윈도우를 만드는 방법은 매우 간단합니다. 이 방법은 그림 2-2-9와 같습니다.

(그림 파일)

(그림 2-2-9)스플래쉬 윈도우 만드는 방법

그림 2-2-9와 같이 프로젝트를 만든다음 Project의 메뉴에서 그림 2-2-9의  ⓐ 항목 처럼 메뉴를 클릭하게 되면  ⓑ항목과 같은 화면이 나타납니다. ⓑ항목에서 Visual C++ Components 항목을 선택하게 되면 ⓒ 항목이 나옵니다. ⓒ항목 상태는 Visual C++에서 제공하는 다양한 클래스를 쉽게 설치하도록 만들어놓은 콤포넌트 들입니다. 이 콤포넌트에서 ③항목처럼 Splash screen을 선택합니다. 이렇게 선택하면 그림 2-2-9의 ⓓ와 같은 항목이 나오게 됩니다. ⓓ화면에서 ④항목은 프로젝트에 삽입되는 스플래쉬 윈도우 클래스 명이며 ⑤항목은 화면 로고 그림입니다. 그림 2-2-9처럼 하여 스플래쉬 윈도우를 만들고 ⑤항목이 에 설정되어 있는 비트맵 그림 ID를 자원에서 열어 보면 그림 2-2-10과 같이 나타납니다.

(그림 파일)

(그림 2-2-10)로고 비트맵 그림

그림 2-2-10의 그림을 원하는 형태로 바꾸게 되면 그림 2-2-8과 같은 스플래쉬 윈도우가만들어지게 됩니다. 스플래쉬 윈도우를 만들때 코딩보다는 항목에서 버튼만 누르면 자동적으로 만들어지고 비트맵을 원하느 형태로 바꾸어주는 것이 전부입니다. 스플래쉬 윈도우에서 코딩을 하는 부분은 딱 한군데 입니다. 바로 스플래쉬 윈도우가 출력되는 시간 조정입니다. 이조정은 그림 2-2-9의 ④항목에 나타난 윈도우클래스의 OnCreate에서 시간을 조정하시면 됩니다.

int CSplashWnd::OnCreate(LPCREATESTRUCT lpCreateStruct)

{

if (CWnd::OnCreate(lpCreateStruct) == -1)

return -1;

// Center the window.

CenterWindow();

// Set a timer to destroy the splash screen.

SetTimer(1, 750, NULL);// <--이부분에서 750의 값을 수정한다.

return 0;

}

지금까지 설명한 부분으로 스플래쉬윈도우를 만들수가 있습니다.