VS2008의 MFC 9.0에는 주로 Vista를 위한 메시지가 추가되었다.

그러나 아주아주 약간은 예전의 케케묵은 가려움을 긁어주는 메시지 핸들러가 추가되었는데,
그중 하나로 마우스 호버와 마우스 리브 메시지가 제공된다.

호버(HOVER)라 특정 윈도우에 마우스가 올라갔다는 메시지로 WM_MOUSEHOVER 를 발생시키며
리브(LEAVE)는 마우스가 떠날때 발생하는 메시지로 WM_MOUSELEAVE 메시지를 발생시킨다.


예전에는 이 두 메시지가 크게 알려지지 않았다.
그래서 SetCapture()를 이용해 마우스를 추적하거나 심지어 후킹(Hooking)까지 구현하여 원하는 기능을 만드는 사례도 심심찮게 보였다.

하지만 이 호버링을 이용하면 꽤 재미있는 예제들을 만들수가 있는데,
그중 하나가 페이드 윈도우(Fade Window)이다.

페이드 윈도우란 주로 메신저를 켤때 광고창으로 이용되는 사례가 많다.
아래 짤방처럼 창이 뜨고 몇초후면 서서히 엷여지며 사라지게 된다.
그러다가 마우스가 올라가면 다시 윈도우는 진해(?)진다.

MFC 9.0에 호버링 핸들러가 추가된 기념으로 MFC 페이드 윈도우를 만들어보자.
윈도우 기반 App라면 UI의 다양한 곳에서 적용될 수 있을 것이다.

타키의 페이드 윈도우

타키의 페이드 윈도우


1) 호버링 준비

먼저 MFC라면 WM_MOUSEMOVE, WM_MOUSEHOVER, WM_MOUSELEAVE 메시지를 함수로 만든다.
왜 WM_MOUSEMOVE까지 함수를 만들어야 할까?
아쉽게도 호버와 리브는 단순히 메시지 함수를 만들었다고 작동하지 않는다.

TRACKMOUSEEVENT 구조체에 TME_LEAVE|TME_HOVER 를 등록하고,
TrackMouseEvent() 함수를 호출해야 마우스 추적이 시작된다.
잊지말도록!

일단 이것으로 호버링은 구현되었다.
이제 점점 사라지는 페이드를 구현해보자.

2) 페이드 윈도우는 레이어드 윈도우

여기서 한가지 기억할 것은 페이드 기능은 원래 윈도우에 없으며,
윈도우 2000부터 지원되는 레이어드 윈도우(Layered Window) 속성을 이용하여 투명도를 점차 변화시키는 것이 핵심이다.
윈도우의 버전을 알기 위햐서는 GetVersionEx()을 이용하면 되며

참고로 윈도우 2000은 윈도우 버전 5.0이며 XP는 5.1 이다.
윈도우 Vista는 6.0.6000이니 윈도우2000에서 XP로 갈때에는 고작 0.1만큼 버전업되었지만
비스타는 1.0 이상 업그레이드 된셈이다.


이때 사용되는 함수가 바로 SetLayeredWindowAttributes()이다.
SetLayeredWindowAttributes()는 적어도 VC++ 6.0에는 제공되지 않아 USER32.DLL에서 GetProcAddress()로 꺼내와 사용했었다.

VS2008에서는 afxwin.h에서 당당히 제공되고 있지만 이 코드에서는 예전에 쓰던 방식을 그대로 사용하였다.
참고로 아래는 afxwin.h에서 SetLayeredWindowAttributes()의 함수 버전을 define 한 부분으로 윈도우 2000과 윈도우 XP에서는 인자의 전달방식이 미묘하게(포인터) 다름을 확인 할수 있다.

// Layered Window

#if(_WIN32_WINNT >= 0x0500)  // 윈도우 2000이라면

 BOOL SetLayeredWindowAttributes(COLORREF crKey, BYTE bAlpha, DWORD dwFlags);
 BOOL UpdateLayeredWindow(CDC* pDCDst, POINT *pptDst, SIZE *psize,
  CDC* pDCSrc, POINT *pptSrc, COLORREF crKey, BLENDFUNCTION *pblend, DWORD dwFlags);

#endif // _WIN32_WINNT >= 0x0500

#if(_WIN32_WINNT >= 0x0501)  // 윈도우 XP 이상이라면

 BOOL GetLayeredWindowAttributes(COLORREF *pcrKey, BYTE *pbAlpha, DWORD *pdwFlags) const;

#endif // _WIN32_WINNT >= 0x0501

아뭏든 USER32.DLL에서 SetLayeredWindowAttributes() 포인터를 얻어오자.

    SLWA pSetLayeredWindowAttributes = NULL; 
    HINSTANCE hmodUSER32 = LoadLibrary(_T("USER32.DLL"));
    pSetLayeredWindowAttributes=(SLWA)GetProcAddress(hmodUSER32, _T("SetLayeredWindowAttributes"));
    HWND hwnd = this->m_hWnd;
    SetWindowLong(hwnd, GWL_EXSTYLE,GetWindowLong(hwnd, GWL_EXSTYLE) | WS_EX_LAYERED);
    pSetLayeredWindowAttributes(hwnd, 0, (255 * m_iTransp) / 100, LWA_ALPHA);



위 코드에서 m_iTransp 가 바로 투명도에 해당한다. 100이면 다 보이는(?) 것이며 0이면 윈도우는 있지만 투명도 100%가 되어 보이지 않게 된다.
이 값이 점점 변해간다면 윈도우는 투명해지거나 불투명해질 것이다.

3) 타이머의 응용

타이머는 일단 세가지를 사용했다.
첫번째 타이머는 일단 페이드 윈도우가 원래 상태로 보여질 시간을 의미한다.
나타나자마자 그대로 투명화가 진행되면, 제대로 해당 윈도우를 볼 시간 조차 없으니까.

두번째 타이머는 윈도우 98을 위한 타이머이다.
윈도우 98은 레이어드 윈도우가 작동할 수 없으니 몇초후에 창을 종료해야 한다.

세번째 타이머는 실제 페이드를 진행시키는 함수이다.

그리고 중요한 것 하나.
마우스가 창위로 올라가게 되면(HOVER) 세번째 타이머를 종료시켜야 한다.
또한 마우스가 창을 떠나면(LEAVE), 첫번째 타이머는 다시 시작하게 된다.

또한 노파심에서 말하지만 타이머를 종료시키는 KillTimer()를 이용할때는
타이머 ID를 적어서는 안되며 SetTimer()의 리턴값을 이용해야 한다는 점이다.
(ID로 타이머를 죽이는 것은 상당히 위험한 코드이다.)

4) 주요 코드 보기

이제 모든 것이 합쳐진 부분이다.
이 코드는 최적화(?)라던가 모범 코드 사례는 결코 아니며 테스트를 위한 목적으로 되는대로 만든 소스라는 점을 잊지 말길 바란다.

void CMyDlg::OnMouseHover(UINT nFlags, CPoint point)
{
 // TODO: Add your message handler code here and/or call default

 m_iTransp = 100;

 SLWA pSetLayeredWindowAttributes = NULL; 
 HINSTANCE hmodUSER32 = LoadLibrary(_T("USER32.DLL"));
 pSetLayeredWindowAttributes=(SLWA)GetProcAddress(hmodUSER32, _T("SetLayeredWindowAttributes"));
 //함수포인터 얻음.
 HWND hwnd = this->m_hWnd;
 SetWindowLong(hwnd, GWL_EXSTYLE,GetWindowLong(hwnd, GWL_EXSTYLE) | WS_EX_LAYERED);
 pSetLayeredWindowAttributes(hwnd, 0, (255 * m_iTransp) / 100, LWA_ALPHA);
 
 KillTimer(m_iTimer1);
 KillTimer(m_iTimer2);
 KillTimer(m_iTimer3);

 CDialog::OnMouseHover(nFlags, point);
}

void CMyDlg::OnMouseLeave()
{
 // TODO: Add your message handler code here and/or call default
 if ( m_bIsOver2000 == TRUE )
  m_iTimer1 = SetTimer(TIMER_1, 2000, NULL);
 else
  m_iTimer2 = SetTimer(TIMER_2, 3000, NULL);
 CDialog::OnMouseLeave();
}

void CMyDlg::OnMouseMove(UINT nFlags, CPoint point)
{
 // TODO: Add your message handler code here and/or call default
 TRACKMOUSEEVENT tme;
 tme.cbSize = sizeof(tme);
 tme.hwndTrack = m_hWnd;
 tme.dwFlags = TME_LEAVE|TME_HOVER;
 tme.dwHoverTime = 1;
 TrackMouseEvent(&tme);

 CDialog::OnMouseMove(nFlags, point);
}

실제 실행결과

실제 실행결과


실제 위 테스트 코드가 사용된 예는 아래 짤방.
4월 22일 정기점검때 피망 웹게임에 반영될 것이다.

쪽지 수신시

쪽지 수신시


공지사항 티커

공지사항 티커


다이렉트X를 이용한 게임 프로그램이라도 알파값을 변경하는 방법이 다를뿐, 기본 원리는 같음을 기억하자.

혹시 VS2005 이하를 사용하는 개발자라면 ON_WM_MOUSEHOVER 같은 매크로가 없다는데 좌절할지도 모르겠다. 하지만 매크로가 VS2008에서 추가되었을뿐, 메시지는 존재한다.

헤더파일에는

     afx_msg LRESULT OnMouseHover(WPARAM wparam, LPARAM lparam);
     afx_msg
LRESULT OnMouseLeave(WPARAM wparam, LPARAM lparam);


와 같이 함수를 선언하고,

  ON_MESSAGE(WM_MOUSEHOVER, OnMouseHover)
  ON_MESSAGE(WM_MOUSELEAVE, OnMouseLeave)

처럼 메시지 맵안에서 핸들러를 등록하고

LRESULT ContainerNoticeCtrl::OnMouseHover(WPARAM wparam, LPARAM lparam)
{

 return TRUE;
}

LRESULT  ContainerNoticeCtrl::OnMouseLeave(WPARAM wparam, LPARAM lparam)
{

 return TRUE;
}

로 몸체를 구현하면 된다.
사실, VS2008  이전까지는 이렇게 사용해 왔었다.

유용한 팁이 되길~~*


크리에이티브 커먼즈 라이선스
Creative Commons License
Posted by Rhea君
일반적으로 DirectX를 이용한 게임 애플리케이션 개발은 VC++에서 Win32로 개발한다.
그런데 Rhea君과 Rhea君 주위의 모임에는 게임 코드에 MFC를 많이 사용한다.

Win32로 개발하더라도 MFC를 사용하는 것은 아주 쉽다.

1) 우선 stdafx.h 에

#include <afx.h>
#include <afxwin.h>


이 두 헤더파일을 추가한다.
사실 afx.h 만 있어도 되지만 AfxMessageBox()같은 함수를 사용하기 위해서는 afxwin.h 을 추가해주자.
(실제 최종 아웃풋에 사용되지 않더라도 개발시 많은 도움을 준다.)

중요한 것은 MFC 헤더 파일들이 반드시 기존

#include <windows.h>

보다 먼저 선언 되어야 한다는 점이다.

이 순서가 틀리면 afxv_w32.h 의

#ifdef _WINDOWS_
 #error WINDOWS.H already included.  MFC apps must not #include <windows.h>
#endif

에서 걸리게 된다.

2) 그리고 프로젝트 속성으로 들어가
일반 탭에서 윈도우 표준 Windows 라이브러리가 아닌 MFC 사용으로 설정해주자.
(MFC가 사실상의 표준 Windows 라이브러리라니까!!)
이때에도 일반적인 MFC 프로젝트와 마찬가지로 Shard DLL과 Static Library 를 고를수 있다.
이 과정은 Link빌드 명령줄에서 /ENTRY:"wWinMainCRTStartup"  을 추가해주는 것과 같은 효과를 가진다.
이제 Win32 프로젝트에도 CString, AfxMessageBox() 같은 MFC 클래스를 사용할 수 있고
추가되는 컨트롤은 해당 MFC 헤더를 추가해주면 된다.

이 과정은 사실 순수 Win32 애플리케이션을 MFC 애플리케이션으로 바꿔 버리므로
Release 버전 배포시 MFC DLL을 함께 배포하거나 Static 으로 빌드하는 것을 잊지말자.

...제대로 MFC를 공부했다면 저절로 알 내용이지만 분명 검색어에 걸려 이 포스트를 읽는 분이 많으리라 믿는다.
크리에이티브 커먼즈 라이선스
Creative Commons License
Posted by Rhea君
TAG MFC, Win32