2010년 4월 27일 화요일

프로세스감추기 2

이번엔 프로세스를 숨기는 방법에 대해 말씀드리려고 합니다.

윈도우에는 유저영역과 커널영역이 있습니다.

SDK를 이용해서 만들어지는 모든 응용 프로그램은 유저 영역에서만

다루어집니다.

아니, 윈도우에서는 응용 프로그램이 커널 영역을 건드리는 것을 막아 놓았습니다.

사실, 핸들이라는 것을 통해서 응용 프로그램은 간접적으로 커널 영역을 건드리는데요,

커널을 만지지 않고서는 아무 작업도 할 수 없기 때문에

윈도우가 고안한 방법일 겁니다.

커널을 직접 건드리기 위해선 DDK라는 것을 통해 시스템 프로그램 내지는

드라이버(확장자가 .sys입니다)를 만드는게 보통인데요,

제가 설명하고자 하는 방법은 SDK만을 이용한 방법입니다.

프로세스를 숨기는 기본적인 원리는 이렇습니다.

윈도우에는 커널영역이 있다고 했죠?

그리고 그 영역에 접근하기 위한 핸들이 있다고 했습니다.

각각의 핸들에는 대응되는 오브젝트가 있습니다.

예를 들어 윈도우 핸들에는 윈도우 정보를 담고 있는 구조체가,

프로세스 핸들에는 프로세스 정보를 담고 있는 구조체가 있습니다.

이 프로세스의 정보를 담은 구조체의 이름은 EPROCESS입니다.

EPROCESS에는 엄청나게 많은 멤버가 있는데요, 그 중 이 테마에 중요한 것은

ActiveProcessLinks라는 멤버 하나 뿐입니다.

이름에서 대략 눈치채셨겠지만, 프로세스들은 연결리스트 구조로 연결되어있습니다.

때문에 목표하는 프로세스를 연결리스트에서 끊기만 하면 감쪽같이

목록에서 사라지게 되지요.

혹시 CPU 사용 권한을 잃게 될 까 걱정하지 않아도 됩니다. 왜냐면,

작업은 쓰레드를 기반으로 이루어지기 때문에, 프로세스는 이름일 뿐입니다.

윈도우즈의 버전에 따라 ActiveProcessLinks의 오프셋값이 다른데요,

XP의 경우 구조체의 시작 번지로 부터 0x088만큼 떨어진(오프셋된) 위치에

ActiveProcessLinks가 있습니다. 이 멤버의 자료형은 LIST_ENTRY인데, 이는

SDK플랫폼에도 정의가 되어있는 구조체로, 다음 두 멤버를 가집니다.

PLIST_ENTRY Flink;
PLIST_ENTRY Blink;

그러므로,
ActiveProcessLinks.Filnk->Blink = ActiveProcessLinks.Blink;
ActiveProcessLinks.Bilnk->Flink = ActiveProcessLinks.Flink;
의 작업을 거쳐주면 되는 겁니다.

그럼 문제는 EPROCESS의 주소를 어떻게 알아내는가 인데요..

바로 여기에 여러가지 테크닉이 존재합니다.

어떤 방법을 사용하든지 Native API를 사용하게 될 텐데요,

이는 SDK에 정의되어있지 않으므로, GetProcAddress를 통해 직접 주소를 얻어내야 합니다.

제가 설명할 방법에 쓰이는 API함수는 다음 두 개입니다.

NTSTATUS __stdcall ZwQuerySystemInformation(IN SYSTEM_INFORMATION_CLASS SystemInformationClass, IN OUT PVOID SystemInformation, IN ULONG SystemInformationLength, OUT PULONG ReturnLength OPTIONAL );

NTSTATUS __stdcall ZwSystemDebugControl(IN SYSDBG_COMMAND SysDbgChunks, IN OUT PVOID pQueryBuff, DWORD dwSize, DWORD, DWORD, NTSTATUS *pResult);

NTSTATUS는 DDK에서 쓰이는 자료형으로, SDK에서 사용하려면 LONG형을 typedef해야 합니다.

(아시는 분은 알겠지만, 참고로 예기해 드립니다. IN, OUT, OPTIONAL은 아무 의미없는 #define으로, 인자가 입력인지, 출력인지, 그리고 사용하지 않으므로 NULL을 넣어도 되는가 등을 예기해주는 겁니다)

SYSTEM_INFORMATION_CLASS 는 enum으로 정의된 자료형으로, 여러가지 값이

정의되어 있습니다. 어떤 분이 찾아내셨는지는 모르겠지만;;

그 목록에 나와있지 않은 값들 중에서 16을 넣게 되면, 재미있는 일이 벌어지는데요,

이걸 이용할 것입니다. 사용할 값은 16뿐이므로 SYSTEM_INFORMATION_CLASS를

다음과 같이 정의해서 사용하면 됩니다.

typedef enum _SYSTEM_INFORMATION_CLASS
{
SystemHandleInformation = 16
} SYSTEM_INFORMATION_CLASS;

너무 길어지네요;; 2부에서 계속합니다.

 

 

SystemHandleInformation이란 이름에서 알 수 있듯,

16은 시스템 상에 로드되어있는 모든 핸들에 대한 정보를 얻어오게 합니다.

그 정보는 다음과 같은 구조체의 배열에 저장됩니다.

typedef struct _SYSTEM_HANDLE_INFORMATION
{
ULONG ProcessId;
UCHAR ObjectTypeNumber;
UCHAR Flags;
USHORT Handle;
PVOID Object;
ACCESS_MASK GrantedAccess;
} SYSTEM_HANDLE_INFORMATION, *PSYSTEM_HANDLE_INFORMATION;

눈여겨 보아야 할 것은 첫번째, 두번째, 다섯번째 인자인데요,

첫번째 인자는 이 핸들을 소유한 프로세스의 아이디입니다. 두번째 인자는

이 핸들의 타입인데요, 핸들에는 여러 종류가 있으니 이를 구분해 주는 겁니다.

윈도우 핸들, 쓰레드 핸들 등 많은 종류가 있지만, 프로세스는 5번입니다.

즉, 이 핸들이 우리가 찾고자 하는 프로세스인지 아닌지 조사하려면,

우선 첫번째 인자와 찾고자 하는 프로세스의 아이디를 조사하고,

두번째 인자가 5인지를 조사하면 되는 겁니다.

그리고 대망의 다섯번째 인자는 바로 EPROCESS구조체의 주소입니다.

이제 ZwQuerySystemInformation의 각 인자에 대해 설명드리겠습니다.

첫번째 인자는 어떤 종류의 정보를 얻어올 것인가로, 16을 입력할 경우 시스템 핸들 정보를

얻어온다고 했습니다.

두번째 인자는 정보를 입력받을 버퍼의 포인터입니다.

세번째 인자는 버퍼의 크기입니다.

네번째 인자는 정보의 크기입니다.

시스템 상에 핸들이 얼마나 많은지 모르기 때문에, 적당한 버퍼의 크기를 알 수가 없습니다.

그래서 1바이트 부터 시작해서 계속 2씩 곱해가면서 적당한 사이즈를 찾는데요,

버퍼의 크기가 부적절한 경우 ZwQuerySystemInformation은

STATUS_INFO_LENGTH_MISMATCH라는 값을 반환합니다.

이 값 역시 DDK에서 사용하는 매크로로, ((NTSTATUS)0xC0000004L)와 같이 정의하면 됩니다.

사이즈가 적당치 않은 경우는 버퍼를 free하고 크기를 두배로 늘린 뒤 다시 시도하는

반복문을 돌려서, 적당한 크기를 찾습니다.

크기가 충분해서 Query Information이 성공하면,

ZwQuerySystemInformation은 0이상의 값을 반환합니다.

네번째 인자는 OPTIONAL이므로 그냥 0을 주면 됩니다.

아무튼 이렇게 해서 얻어낸 정보의 앞 4바이트는 배열의 인자 개수를 나타냅니다.

다시말하면 시스템상에 존재하는 핸들의 숫자입니다.

이제 이 숫자만큼 루프를 돌면서 원하는 핸들을 찾아내면 됩니다.

헥헥.; 여기까지가 EPROCESS의 주소를 알아내는 방법입니다.

이제 다 끝난게 아닌가 하시겠지만, 사실 더 있습니다.

이렇게 얻어낸 EPROCESS의 주소는 선형 주소라는 것으로, 직접 접근할 수가 없습니다.

주소에는 물리 주소, 선형 주소, 논리 주소 이렇게 3개가 있는데요,

보통 그냥 사용해 왔던 포인터 변수는 논리 주소입니다.

선형 주소는 가상 주소라고도 하는데요, ZwSystemDebugControl은 여기서 쓰입니다.

이녀석도 첫번째 인자로 enum자료형을 받는데요, 여기서 사용할 값은

8, 9 두 개이므로, 다음과같이 정의해서 쓰면 됩니다.

typedef enum _SYSDBG_COMMAND
{
SysDbgCopyMemoryChunks_0 = 0x08, SysDbgCopyMemoryChunks_1 = 0x09
} SYSDBG_COMMAND;

두번째 인자는 데이터를 받을 구조체의 포인터입니다.

이 구조체는 MEMORY_CHUNKS라는 구조체로, 다음과 같이 정의됩니다.

typedef struct _MEMORY_CHUNKS
{
PVOID pVirtualAddress;
PVOID pBuffer;
DWORD dwBufferSize;
} MEMORY_CHUNKS, *PMEMORY_CHUNKS;

첫번째 멤버로 접근하고자 하는 메모리의 주소를 입력하고,

두번째 멤버와 세번째 멤버에 데이터를 저장할 버퍼의 주소와 그 크기를 입력합니다.

가상메모리를 읽는 경우(SysDbgCopyMemoryChunks_0),

이 버퍼로 가상 메모리의 데이터가 쓰여집니다.

가상메모리를 쓰는 경우(SysDbgCopyMemoryChunks_1),

이 버퍼에 쓰인 내용이 가상 메모리에 쓰여집니다.

한편 ZwSystemDebugControl의 세번째 인자는 읽고자 하는 데이터의 크기입니다.

포인터를 읽을 것이기 때문에 여기에는 4를 지정하면 됩니다.

네번째와 다섯번째 인자는 여기서 중요하지 않기 때문에 그냥 비워두었습니다.

그리고 여섯번째 인자로 성공의 여부가 들어오게 됩니다.

이렇게 해서 가상메모리를 읽거나 쓸 수가 있습니다.

그럼 3부에서 계속하겠습니다.

 

 

이제 진짜 끝인가 하시겠지만 사실 조금 더 있습니다.

가상메모리는 그냥 읽고 쓸 수 없습니다. 이걸 읽고 쓰려면 그에 맞는 권한(Privilege)

를 획득해야 하는데, 이 권한을 조절할 줄 알면 많은 것을 할 수 있습니다(^^훗)

이 권한을 통해 할 수 있는 일에 비해 이를 얻는 방법은 매우 쉽습니다.

Native API따위를 이용하지 않아도 됩니다.

이부분은 인터넷에 자료가 많이 있으므로 소스만 올리겠습니다.

TOKEN_PRIVILEGES priv = { 1, {0, 0, SE_PRIVILEGE_ENABLED} };
LookupPrivilegeValue(0, lpName, &priv.Privileges[0].Luid);
HANDLE hToken;
OpenProcessToken(hProcess, TOKEN_ADJUST_PRIVILEGES, &hToken);
AdjustTokenPrivileges(hToken, FALSE, &priv, sizeof(TOKEN_PRIVILEGES), 0, 0);
CloseHandle(hToken);

권한에는 여러 종류가 있는데, 그 중에는 셧다운 권한, 디버그 권한 등이 있습니다.

이름에서 예측할 수 있지만, 셧다운 권한은 컴퓨터를 시스템 종료할 수 있는 권한입니다.

이를 획득한 후 ExitWindows와 같은 함수를 이용하면 간단히 시스템 종료를 할 수 있습니다.

중요한 것은 디버그 권한입니다.

이 권한은 매우 강력한 권한으로, 이 권한을 획득해야만 가상메모리를 읽거나 쓸 수 있습니다.

(여담이지만)추가로, 이 권한을 얻으면 작업관리자도 종료하지 못하는 중대한

프로세스들을 죽일 수 있습니다.

이러한 프로세스들이 그냥은 Terminate되지 않는 이유는 OpenProcess함수가 NULL을

리턴하기 때문인데요, 그 이유는 PROCESS_ALL_ACCESS권한으로 프로세스를 열려면

디버그 권한이 필요하기 때문입니다.

따라서 디버그 권한을 얻은 뒤 OpenProcess를 하면 정상적으로 핸들을 얻게 되고,

TerminateProcess도 정상적으로 동작합니다.

이 방법을 이용하면 smss.exe, winlogon.exe등을 죽일 수가 있는데,

실험해 본 결과 이럴 경우 블루스크린이 뜨면서 바로 재부팅됩니다;;



어떠셨는지요;;;; 아마 프로세스 숨기는 법에 대해 궁금해 하셨던 분이 많을 겁니다.

저도 그렇습니다.

드라이버 개발자도 아니면서 이걸 궁금해 하는 이유는 하나 뿐이죠..

악성프로그램 제작.

그 증거로 이건 제가 중국 해커싸이트 여기저기 돌아다니면서 종합한 겁니다.

저라고 남말할 처지는 아니지만 .. 악용하지 말아주셨으면 합니다;;

그런 의미에서 전체 소스는 제시하지 않습니다.

(아니..사실 권한을 얻는 소스만 해도 충분히 악용될 수 있겠군요..;;)

죄송합니다; 앞으로는 소스가 제공될 수 없게 됬습니다;; 사정이 생겨서;;

#pragma 의 쓰임새

아직 #pragma까진 써본적 없지만, 앞으로 필요할것 같아서 퍼왔음

#pragma는 define 이나 include와 같이 #으로 시작하는 전처리구문(precompiler)의 하나이다.
컴파일러에 종속적인 구문이라 컴파일러가 변경되었을 경우 제대로된 동작을 보장하지 못하므로 프로젝트 진행중에 서로 다른 컴파일러를 사용한다면 사용하지 않음이 바람직 하겠다.
- 대신 대체하는 문법을 사용해야 되겠다.

#pragma once
이것은 "컴파일러에게 한번만 컴파일해!" 라고 명령한다.
헤더의 중복을 막아준다.
무슨말인가 하면

a.h를 구현한 a.cpp, a.h는 독립적이다.(include가 없다.)
b.h를 구현한 b.cpp, c.h, a.h순서로 include
c.h를 구현한 c.cpp, a.h를 include

컴파일하면 b.h에서 c.h를 포함시키라고 되어있네? 하고 c.h에 들어가고 어? a.h를 포함하라고 그러네? 이러고 a.h를 포함한 c.h가 b.h로 돌아온다 그리고 a.h를 포함하라는 명령을 받고 a.h를 추가하다보면 같은 변수와 함수선언이 되어있다. 에러에러~
같은 선언이 두 번 반복되니 당연히 충돌이 난다. 컴파일러가 똑똑하여 단순히 경고 처리만 해주고 알아서 하나로 종합해줄 수도 있지만 대부분의 기본적인 컴파일러는 이건 아니잖아~ 한다.

이럴 때 써주는 것이다. pragma once
이는 c기본문법을 사용하여 구현할 수 있다.

#ifdef _MYCOMPILECK
#define _MYCOMPILECK
// 헤더 파일의 내용 선언
#endif



#pragma comment()
기본적인 pragma comment()의 형식은 다음과 같다.

#pragma comment( comment-type, ["comment string"] )

[] 안의 구문은 comment-type에 따라 필요할 경우 사용하는 것이다.
comment type에는 compiler, exestr, lib, linker, user 등이 올 수 있다.

#pragma comment( linker, "/subsystem:windows" )
#pragma comment( linker, "/subsystem:console" )

linker 를 사용하면 프로젝트를 console application인지 win32 application인지 명시해줄 수 있다.

또한 섹션의 설정을 할 수 있다.

#pragme comment( linker, "SECTION:.SHAREDATA,RWS" )

#pragma data_seg("SHAREDATA") 와 함께 사용하여 공유 메모리를 생성한다.
위의 명령어 대신 def 파일 안에 아래와 같이 해주어도 된다.

SECTIONS
SHAREDATA READ WRITE SHARED


이 중 가장 대표적인 사용법은 명시적인 라이브러리의 링크이다.

#pragma comment(lib, "xxxx.lib")

와 같이 사용하여 해당 라이브러리를 링크시켜 준다.
여러사람이 같이 수행하는 프로젝트의 경우 이와 같은 방법을 사용하여 lib를 링크하는 것이 라이브러리가 링크되어있다는 사실을 알기에도 좋고 굳이 주석다라 설명할 필요도 없어 좋지 않나 싶다. (있다는 사실은 알지만 아직 프로젝트 수행중 실제로 사용해 본적은 없음)



#pragma data_seg()
pragma data_seg()의 형식은 다음과 같다.

#pragma data_seg( ["section-name"[, "section-class"] ] )

[]는 사용하지 않아도 된다는 의미이다.

#pragma data_seg( "SHAREDATA" )
int x;
char y;
#pragma data_seg()

DLL 파일을 만들어보면서 제일 많이 사용해 보았고 가장 헷갈려 했던 부분이기도 하다.
DLL의 데이터 공유를 하기 위해 사용한다.
공유할 섹션을 만드는 것이다. 위의 명령어는 필수적으로 위에서 사용된 두 가지중 한가지 방법과 함께 사용 되어야 한다.

#pragme comment( linker, "SECTION:.SHAREDATA,RWS" )
SECTIONS
SHAREDATA READ WRITE SHARED

둘 다 해당 SECTION(SHAREDATA)의 허용 범위(?속성?)를 설정하는 것이다. READ, WRITE, SHARED 세 가지를 쓴다는 의미~
해당 사항에 대해 msdn에서 자세한 정보를 발견하지 못해 적지 못하였다(검색능력의 부족!!)
이제 변수 x와 y는 해당 dll을 사용하는 외부 파일과 같이 공유할 수 있는 변수가 되었다.(외부에서 접근 가능하게 되었다.)

이렇게 공유하는 변수는 물론 new로 메모리를 할당한 변수도 공유 가능하다.
특히 new 나 memalloc(이건 아직 미확인이지만 같은 메모리 할당이므로 가능할 것으로 본다)으로 메모리할당한 변수들은 dll외부에서도 해제(delete) 가능하다.



#pragma warning
특정 경고를 끄고 싶을 때 사용한다.
비 쥬얼 스튜디오의 버전이 다르기 때문에 뜨는 경고는 더더욱이 귀찮은 존재이다.(하지만 수정해서 손해볼 것은 없다. 그것이 곧 버그로 이어질 수 있기 때문이다. 특히 형변환의 경우 강제 캐스팅하여 확실히 명시해주는 것이 좋다. 일부러 그 값을 떼어낸다는 프로그래머의 의지를 컴파일러에게 보여주자. 부지런할수록 후에 손이 가는 일이 적어진다. 노력하자~)

형식은 이와 같다.

#pragma warning( warning-specifier : warning-number-list [; warning-specifier : warning-number-list...] )
#pragma warning( push[ ,n ] )
#pragma warning( pop )

실제 사용은 아래와 같이 한다.

#pragma warning( disable:4996 )




#pragma message()
컴파일 중에 메세지를 뿌려준다.
말이 필요없다-.-/

#pragma message("merong")

AfxGetApp() 와 AfxGetMainWnd()의 차이점

exe 파일을 클릭하는 순간 하나의 프로세스가 동작하게 됩니다.

 프로그램당 하나의 프로세스가 시작이 되는 것이며..
 
 최초로 하는 짓은 프로세스의 진입점을 찾는 것입니다.

 즉 main 함수를 찾으며.. mfc에서는 winmain을 참조하게 되겠지요..

 그렇게 되면서 최초에 프로세스가 하나의 쓰레드를 참조하게 되는데..
 
 님이 생성하신 CXXXApp 입니다.

 이녀석의 상속 구조를 보면 CWinApp로 부터 상속했으며..
 
 또 CWinApp는 다음과 같이 선언된것을 볼수 있습니다.

 class CWinApp : public CWinThread

 즉 쓰레드에서 상속받은 것입니다.

 하나의 프로세스에서는 여러개의 쓰레드를 생성 시킬수 있는데..
 
 바로 app라는 의미는 이렇게 생성시킨 쓰레드를 의미합니다.

 그렇다면.. AfxGetApp()의 의미는 가장 먼저 생성시킨 app를 반환한다라는 의미이죠..

 즉 님이 최초 생성시킨.. CXXXApp의 포인터가 반환되는 것입니다.

 따라서 CXXXApp * pApp = (CXXXApp*)AfxGetApp(); // 형변환을 해야 하는 이유는 아시겠죠??

 

 이렇게 생성시킨 쓰레드에 가장 중심이 되는 윈도우를 생성 시키고 등록 시킵니다.

 app에 있는

 BOOL CXXXApp::InitInstance()
 {
  ...
  CMainFrame* pMainFrame = new CMainFrame;
  if (!pMainFrame->LoadFrame(IDR_MAINFRAME))
   return FALSE;

     m_pMainWnd = pMainFrame; // 이렇게 설정하는 과정이 현 쓰레드에 MainWnd를 설정하는 과정입니다.
  ....
 }
 만일 다이얼로그 베이스면
 m_pMainWnd = dlg;
 이렇게 다이얼로그를 메인 윈도우로 등록시키겠지요..

 afx라는 접두어를 주목하세요..
 mfc에서는 모든 전역 자원에는 afx라는 접두어를 붙였습니다.
 이는 모든 위치에서 이 전역자원을 쉽게 콜 할수 있으며..
 님이 가장 중심이 되는 쓰레드나 메인 윈도우를 부를때.. 쉽게 사용 가능합니다.


 따라서 님이 어느 장소에서나

 CMainFrame * pMain = (CMainFrame *)AfxGetMainWnd();
 이렇게 사용함으로서 쉽게 메인 윈도우를 부를수 있습니다.

 

 

 결국.. 모든 프로그램은 하나의 프로세스로 부터 기본이 되는 쓰레드가 존재하며..
 mfc에서 그 중심 쓰레드는 CWinApp에서 관리하게 되고..
 그 쓰레드에서 창을 생성하여...
 가장 기본이 되는 창을 그 쓰레드에 메인 윈도우로 설정하는 과정에 대해서
 이해를 하신다면 쉽게 해결되는 문제입니다.


 메인윈도우가 생성된뒤.. SDI 구조의 경우 또다시
 여러개의 차일드 프레임과 여러개으 뷰, 그리고 도큐먼트등을 둘수 있습니다.

 이들은 한개가 아니기 때문에 Active된 녀석을 얻는 함수를 디폴트로 제공하죠

AfxGetApp()

MFC Programming을 하다 보면 Dialog나 임의의 클래스 에서 Main Frame 이나 View Class의 Method를 사용하여야 할 경우가 있다. 이러한 경우 AfxGetApp() 라는 API가 유용하게 쓰인다.

 

즉, (CMainFrame *)AfxGetApp()->m_pMainWnd) 를 하게 되면 임의의 클래스 에서 Main Frame의 Member 및 Method의 접근을 할 수 있다.

 

또한 Main Frame에서 View나 Document를 접근하는 경우에는 아래와 같이 쉽게 할 수 있다.

 

CMainFrame *pFrame;

pFrame = AfxGetApp()->m_pMainWnd;

CSDIView *pView;

pView = pFrame->GetActiveView;

 

CSDIDoc *pDoc;

pDoc = pView->GetDocument();

 

이처럼 MFC를 사용하여 동등 Level의 클래스에 접근하는 방법은 우선 Application의 Class에 접근한 후 Application의 Main Frame, View로 접근 하면 된다.

CreateProcess 와 핸들

CreateProcess를 호출하고 나면, LPPROCESS_INFORMATION lpProcessInformation  에 있는 Handle 들을 닫아줘야 합니다.

이거 안 해주면... Task Manager에서 확인하면 Handle 값이 계속해서 2개씩 증가하는 것을 확인할 수 있습니다.

MSDN에 보면... 이런 글이 있습니다.
Handles in PROCESS_INFORMATION must be closed with CloseHandle when they are no longer needed.

물론, 이 CreateProcess를 호출하는 Process가 한 번 실행 되었다가 종료되면 모르지만, 계속 실행하는 Service나 기타 다른 프로그램이라면, 주의할 대목입니다.

그리고 보면, 참 생각없이 코딩했다는 생각이 드네요...

MSDN의 Sample을 볼까요?

#include <windows.h>
#include <stdio.h>
#include <tchar.h>

void _tmain( int argc, TCHAR *argv[] )
{
    STARTUPINFO si;
    PROCESS_INFORMATION pi;

    ZeroMemory( &si, sizeof(si) );
    si.cb = sizeof(si);
    ZeroMemory( &pi, sizeof(pi) );

    if( argc != 2 )
    {
        printf("Usage: %s [cmdline]\n", argv[0]);
        return;
    }

    // Start the child process. 
    if( !CreateProcess( NULL,   // No module name (use command line)
        argv[1],        // Command line
        NULL,           // Process handle not inheritable
        NULL,           // Thread handle not inheritable
        FALSE,          // Set handle inheritance to FALSE
        0,              // No creation flags
        NULL,           // Use parent's environment block
        NULL,           // Use parent's starting directory 
        &si,            // Pointer to STARTUPINFO structure
        &pi )           // Pointer to PROCESS_INFORMATION structure
    ) 
    {
        printf( "CreateProcess failed (%d)\n", GetLastError() );
        return;
    }

    // Wait until child process exits.
    WaitForSingleObject( pi.hProcess, INFINITE );

    // Close process and thread handles. 
    CloseHandle( pi.hProcess );
    CloseHandle( pi.hThread );
}


afx_msg 설명

메시지 맵( Message Map )

  SDK 프로그램에서는 윈도우 시스템에서 들어온 메시지를 switch문을 사용하여 처리하였다. 그러나 MFC에서는 메시지 처리를 위해 message map이라는 mechanism을 사용하고 있다. Message map은 메시지 번호와 메시지가 발생하였을 때 호출되는 함수의 포인터 등의 정보를 갖고 있는 테이블로 프로그램에 전달된 메시지와 메시지 핸들러 함수를 연결하는데 사용한다.



MFC에서 message를 처리하기 위해서는 다음의 세 가지 단계가 필요하다.

1.        윈도우 클래스의 멤버 함수로 메시지 핸들러 함수를 선언한다.



2.        message map에 message와 message handler 함수를 묶는 message의 매크로를 추가한다. (.cpp file에 존재한다. )



3.        메시지 핸들러 함수의 기능을 구현한다.



위의 세 단계중에서 1, 2 단계는 Class Wizard로 구현이 되고, 마지막 함수의 기능 구현만 구현하면 된다.



● 메시지 핸들러( Message Handler ) 함수

  윈도우로부터 애플리케이션에 메시지가 전달될 때 해당 메시지를 처리하는 멤버 함수이다.함수이름은 WM_를 떼고, 대신 On을 붙여서 시작하고 함수 선언 시에 afx_msg를 붙여 메시지 핸들러 함수라는 것을 나타낸다.



MFC를 처음에 시작하면 많이 만나게 되는 afx_msg에 관한 설명이다.
물론 이해는 잘 안되지만......
대충 보게 되면 어떤 메세지를 위한 구조체가 결정되어있고 그것을 불러다 쓰면 된다는 말이다.
위저드를 통해서 모든것은 가능하기 때문에 어렵지는 않지만 나중에 디버그나 코딩을 수월하게 하려면 이것에 대한 개념을 잡는것이 중요하다 하겠다. 

리스트컨트롤 체크박스 통지받기

void DemoDlg::OnItemchangedLinksList(NMHDR* pNMHDR, LRESULT* pResult)
{
         NM_LISTVIEW* pNMListView = (NM_LISTVIEW*)pNMHDR;
         *pResult = 0;
 
         if (pNMListView->uOldState == 0 && pNMListView->uNewState == 0)
                 return;  // No change
 
         BOOL bPrevState = (BOOL)(((pNMListView->uOldState &
                                   LVIS_STATEIMAGEMASK)>>12)-1);   // Old check box state
         if (bPrevState < 0)       // On startup there's no previous state 
                 bPrevState = 0; // so assign as false (unchecked)
 
         // New check box state
         BOOL bChecked=(BOOL)(((pNMListView->uNewState & LVIS_STATEIMAGEMASK)>>12)-1);
         if (bChecked < 0) // On non-checkbox notifications assume false
                 bChecked = 0;
 
         if (bPrevState == bChecked) // No change in check box
                 return;
 
         // Now bChecked holds the new check box state
 
         // ....
}

 

Setting the check box state of an item

void SetLVCheck (WPARAM ItemIndex, BOOL bCheck)
{
         ListView_SetItemState (m_lvTestList.m_hWnd, ItemIndex,
                 UINT((int(bCheck) + 1) << 12), LVIS_STATEIMAGEMASK);
}

삼바서버 설정(smb.conf)

출처: http://cafe.naver.com/devctrl/4267

삼버 서버를 구동하기 위해서는 먼저 네트워크 환경에 맞게 설정을 해야 한다.

삼바의 설정 파일은 /etc/samba/ 아래에 있으며 smb.conf가 기본 설정 파일이다.

삼바의 설정은 매우 복잡하며 환경에 따라서 다양한 설정 방법 등이 존재하므로 처음부터 모든 것을 설정하기 보다는 가장 기본적인 것부터 설정해 나가는 것이 효율적이다.

 

# testparm

smb.conf 파일의 구문이 정확한가를 검증한다. 삼바 데몬을 실행하기에 앞서 이 유틸리티로 문법 적합성 검사를 해야 한다.

 

# service smb restart (/etc/init.d/smb restart)

smb.conf 파일을 변경한 후에는 반드시 데몬을 재시작 해야 한다.

 

[global]

삼바 서버가 공유하는 자원들에 공통적으로 적용할 기본 값을 설정하는 곳이다.

 

[homes]

리눅스 계정 사용자들의 홈 디렉토리를 로그인 홈으로 사용하기 위한 설정이다.

 

[printers]

네트워크 공유 프린트에 대한 설정 부분이다.

 

 

=== Global Settings  ===========================================================

[global]

# 한국어 지원을 위한 설정

dos charset = cp949

unix charset = cp949 (utf8, euc-kr)

 

# 윈도우즈의 작업 그룹과 같다. 공유하고자 하는 작업 그룹 이름을 작성한다.

# 삼바 서버와 클라이언트의 작업 그룹 명은 반드시 일치해야 한다.

workgroup = RND

 

# 윈도우 네트워크 환경에서 보여줄 삼바 서버에 대한 설명이다.

server string = Samba Server

 

# 삼바 서버에 접속을 허용할 IP 지정.

# IP 주소 또는 네트워크 / 넷마스크 형태로도 지정할 수 있다.

hosts allow = 192.168.41. 127.

--> 192.168.41.0 네트워크에 속한 모든 호스트들과 로컬 시스템(127.0.0.1)만 허용

hosts allow = 192.168.41. except 192.168.41.100  192.168.41.101

--> 192.168.41.100번과 101번을 제외한 192.168.124의 모든 호스트들은 접속 허용

hosts allow = 192.168.41.0/255.255.255.0

--> 192.168.41.0 네트워크에 속한 모든 사용자들을 허용하고 넷마스크를 병용하여 사용

hosts allow = wow19, wow20

--> wow19와 wow20라는 호스트들만 서버 접속 허용

 

# 삼버 서버의 printcap에 정의된 모든 프린터 목록을 자동적으로 로딩되게 할 것인가를 지정

# 네트워크 프린터를 삼바 서버에 연결하여 사용하고자 한다면 선택해야 한다.

load printers = yes

 

# 서버에 의해 사용되는 printcap name을 겹쳐쓰기 하고자 할 때 그 위치를 지정

printcap name = /etc/printcap

 

# 현재 사용하는 프린터 시스템 종류 지정

# 비표준 프린터 시스템이 아니면 지정할 필요가 없다.

printing = lprng

 

# 삼바 서버의 guest 사용자를 허용하고자 할 때에는 이 주석을 제거한다.

# 즉, 이 옵션은 삼바 서버에 손님 권한으로 접속하였을 때에 어떤 권한을 부여할 것인가를 설정하는 것이다.

# 가능한 시스템에 특별한 권한이 없는 nobody와 같은 권한으로 설정하기 바란다.

#guest account = nobody

 

# 삼바 서버로 접속하는 개별 사용자드의 호스트 정보를 %m으로 받아서 개별 로그 파일을 생성하도록 한다.

log file = /var/log/samba/%m.log

 

# 접속한 호스트별로 로그 파일을 사용하지 않는다면 하나의 로그 파일을 사용할 수도 있다.

log file = /var/log/samba/smbd.log

 

# 로그 파일 최대 크기 지정

# 제한을 두었을 경우에 이를 초과하게 되면 .old 확장자를 가진 파일로 저장되고 새로운 파일이 생성된다.

# 0으로 설정하면 파일의 크기에 제한을 두지 않는다.

max log size = 0

 

# 클라인언트가 삼바 서버에  접속할 때 인증 레벨을 부여하는 옵션

# user, share, server, domain 4가지 보안 모드가 있다.

# user:

#    삼바 서버에 접속하는 사용자는 반드시 윈도우에서 사용하는 로그인 ID와 동일해야 한다.

# share:

#    공유 디렉토리 등에 설정하는 것으로써 ID와 패스워드의 인증없이 접속하는 것을 허용한다.

# server:

#    별도의 인증 서버에 ID와 패스워드 인증을 받도록 한다.

# domain

#    윈도우 NT 서버가 있어야 가능하며, 삼바 서버가 사용자 명과 패스워드를 윈도우 NT의 도메인 컨트롤러(Domain Controller)에

#    전달하여 유효한지 확인 하는 방법이다. 아예 Windows NT 도메인으로 삼바 서버를 참가시키는 방법을 사용한다.

security = user

 

# security 값이 server로 설정되었을 때에만 설정할 수 있는 옵션으로 인증 서버로 사용할 서버를 지정한다.

password server =

 

# 패스워드 문자로 대소문자를 조합하여 사용할 문자의 개수를 지정한다.

password level = 8

 

# 삼바 사용자 명을 대소문자 조합하여 사용할 문자의 개수를 지정한다.

username level = 8

 

# 삼바 서버에 클라이언트의 접속이 이뤄지는 과정에서 인증을 위하여 암호화된 패스워드 옵션을 사용할 수 있다.

# 기본 패스워드 모드는 encrypted 모드이다.

encrypt passwords = yes

 

# encrypt passwords 항목과 함께 사용되는 것으로 암호화된 삼바 사용자의 아이디와 패스워드가 기록되는 파일이다.
# 삼바의 암호 모드가 윈도우와 호환되도록 설정한다.
# 이 파일에 패스워드를 추가하는 명령은 smbpasswd이다.
smb passwd file = /etc/samba/smbpasswd

 

# 클라이언트 호스트에서 사용자의 패스워드를 변경할 수 있도록 해주는 옵션이다.
# 단, 이 경우에는 encrypt password, smb passwd file 두 옵션을 반드시 사용해야 한다.

unix password sync = Yes

passwd program = /usr/bin/passwd %u

passwd chat = *New*UNIX*password* %n\\n *ReType*new*UNIX*password* %n\\n

*passwd:*all*authentiction*tokens*updated*successfully*

 

# 대부분 삼바에서 사용하는 ID와 리눅스 계정 ID는 동일하게 사용한다. 만약 삼바 사용자 명과 리눅스 계정 사용자 명을 다르게 사용할 경우

# 이를 매칭할 수 있도록 하기 위한 옵션으로써 매칭 테이블 파일을 설정한다.

username map = /etc/samba/smbusers

 

# 접속하는 각 클라이언트마다 서로 다른 설정을 사용할 수 있게 해주는 것으로 %m은 접속하는 NetBIOS 이름으로 대치된다.

include = /etc/samba/smb.conf.%m

 

# 사용자의 로컬 네트워크 상에서 삼바 서버가 최적의 성능을 발휘할 수 있도록 튜닝할 때 사용한다.

# IPTOS_LOWDELAY, IPTOS_THROUGHPUT 등의 옵션이 있다.

socket options = TCP_NODELAY SO_RCVBUF=8192 SO_SNDBUF=8192

 

# 삼바 서버에서 두 개 이상의 네트워크 인터페이스(NIC)를 사용하도록 지원하기 위한 옵션이다.

# 삼바 서버가 사용하는 모든 인터페이스의 네트워크 대역을 설정해 두어야만 그 네트워크 인터페이스를 사용할 수 있다.

# 삼바에 연결된 네트워크 인터페이스를 설정하는 것으로 인터페이스는 IP / netmask 조합으로 지정할 수 있다.

interfaces = 192.168.41.3/24 192.168.124.33/24

 

# 지역 서브넷에서 삼바 서버를 잘 인식하도록 하기 위하여 자기 자신을 알리도록 한다.

remote browse sync =

remote announce =

 

# 삼바 서버가 nmbd에 의해 서브넷 상에서 로컬 마스터 브라우저가 될 수 있도록 허용하는 것으로

# no라고 설정하면 nmbd 데몬은 서브넷 상에서 로컬 마스터 브라우저가 되지 않는다.

local master = no

 

# 삼바 서버가 브라우저 선거에 있어서 자신을 알릴 수 있는 레벨을 설정하는 것으로
# 이 값에 의해서 nmbd 데몬이 로컬 브로드캐스트 지역에서 WORKGROUP에 대해 로컬마스터 브라우저가 될 수 있는지 결정된다.
# 이 값을 0으로 설정하면, nmbd 데몬은 윈도우 머신에 대해서 선거권을 상실하므로 로컬마스터 브라우저가 되지 못한다.

os level = 33

 

# 삼바가 도메인 마스터 브라우저가 되도록 해준다. 이것은 삼바와 서브넷 간의 브라우저 리스트를 모집할 수 있게 해준다.

# 만일 NT 도메인 컨트롤러를 가지고 있다면 이 기능을 사용해서는 안된다.

domain master = yes

 

# 윈도우 계열 사용자들도 도메인 로그인 가능하게 할 것인가 지정

domain logons = yes

 

# 삼바 구동시 로컬 마스터 선거를 강요하여 선거에서 이길 수 있게 보다 많은 가능성을 부여해 주는 옵션이다.

# 이 옵션은 domain master = yes 옵션과 같이 사용하여 nmbd 데몬에 의해 도메인 마스터가 될 수 있도록 해준다.

preferred master = yes

 

# 사용자가 성공적으로 로그인을 하였을 때 다운로드하여 작동할 수 있도록 배치파일(*.bat) 또는 NT 명령 파일(.cmd)을 지시해 주는 옵션이다.

# 배치 파일은 마지막 줄에 cr/lf가 들어있어야 하므로 도스편집기에서 만드는 것을 권장한다.

# 내용은 사용자가 임의대로 지정할 수 있다.

# 예) 보통 모든 클라이언트 머신들이 서버와 똑같은 시간에 시간을 맞추도록 할 수 있다.

# NET TIME \\\\SERVER /SET /YES

logon script = %m.bat

logon script = %U.bat

 

# 윈도우98 및 NT에서 user.dat과 같은 로우밍 프로파일(roaming profile)을 어디에 지정할 것인가를 지정해 주는 옵션이다.

# %L은 서버의 NetBIOS이름으로 대치되고, %U는 사용자 이름으로 대치된다.

# 이 옵션을 사용할 때 [Profile]공유 항목에서 주석을 풀어 주어야 한다.

logon path = \\\\%L\\Profiles\\%U

 

# 삼바 서버에서 nmbd 데몬이 wins 서버의 역할을 할 수 있는지 여부를 지정한다.

# 만일 이 옵션을 선택하기 위해서는 반드시 다중 서브넷 네트워크를 가지고 있어야 하며 wins서버로 될 특정 nmbd 데몬이 있어야 한다.

wins support = yes

 

(참고) WINS(Windows Internet Naming Service)
마이크로소프트 윈도우NT 서버의 일부인 WINS는 각 구성 변경에 수반되는 사용자 또는 관리자가 없는 IP 주소들과 컴퓨터 이름 및 위치들과의 결합을 관리한다. WINS는 컴퓨터 이름과 IP 주소를 서로 매칭시켜 데이터를 테이블 내에 자동으로 만드는데, 이 이름들은 다른 사람의 컴퓨터 이름과 중복되지 않도록 고유한 이름으로 견지한다. 컴퓨터가 다른 장소로 옮겨지면, IP 주소의 서브넷 부분이 변경될 수 있다. WINS를 사용하면 새로운 서브넷 정보가 WINS 테이블 내에서 자동으로 갱신된다. WINS는 어떤 컴퓨터가 네트워크에 처음 정의될 때 IP 주소를 협상하는 NT서버의 DHCP를 보충하여 완전하게 한다. 예를 들면 같은 네트워크에서 사람이 많아지면 네트워크에 부하도 많이 발생한다. 이 경우 WINS를 사용하면 컴퓨터 이름과 IP 목록을 관리해주기 때문에 동보 통신에 의한 부담을 줄일 수 있다.

 

# WINS 서버 사용할시 IP 지정

wins server = host

 

# nmbd에 의해서 wins기능을 갖추지 못한 호스트들을 대신하여 브로드캐스트 이름 질의를 대신 응답해 줄 수 있도록 지정해 주는 옵션이다.

# 이것을 사용하려면 네트워크 상에 최소 하나 이상의 WINS 서버가 있어야 한다.

wins proxy = yes

 

# nmbd 데몬이 wins server 역할을 하고 등록되지 않는 NetBIOS 이름을 찾아줄 때
# DNS server를 사용하여 NetBIOS 이름을 찾아줄 것인지의 여부를 지정하는 옵션이다.
dns proxy = no

 

# 대소문자를 유지 보존할 것인가를 지정하는 옵션이다. 시스템의 기본 값은 no이다.

preserve case = no

short preserve case = lower

 

# DOS 파일들의 기본 문자는 대문자로 인식한다. 만약 lower로 설정한다면 소문자로 인식한다.

default case = lower

 

# 대소문자 구분을 할 것인가 말 것인가를 지정하는 옵션이다.

case sensitive = no

 

 

=== Share Definitions ==========================================================

# 사용자들의 홈 디렉토리 서비스에서 사용하는 기본적인 설정 값이다.
[homes]
# 공유 자원에 대한 설명 필드와 같다.
comment = Home Directories
# 윈도우 네트워크 브라우저에서 디렉토리를 보일 것인지 결정한다.
browseable = no
# 사용자에게 쓰기 권한을 준다.
writable = yes

# 도메인 로그온을 사용하고자 할 때 사용한다. 일반적으로 사용하지 않는다.
[netlogon]
comment = Network Logon Service
path = /home/netlogon
guest ok = yes
writable = no
share modes = no

# 특정한 프로파일을 지정할 때 사용한다. 일반적으로 사용하지 않는다.
[Profiles]
path = /home/profiles
browseable = no
guest ok = yes

# 삼바 프린터를 네트워크 공유 프린터로 사용할 때
[printers]
comment = All Printers
# 프린터 위치
path = /var/spool/samba
browseable = no
# Set public = yes to allow user 'guest account' to print
# guest 계정으로 지정한 사용자, 즉 nobody가 printing을 할 수 있다
guest ok = yes
# 쓰기 설정
writable = no
# 프린트 사용 여부
printable = yes

# 여러 사람들이 파일을 공유할 목적으로 유효하게 사용할 수 있다.
# 현재 기본 값인 /tmp는 임시 작업 공간 디렉토리이므로 /var/tmp처럼 다른 디렉토리를 만들어서 사용하도록 한다.
[tmp]
comment = Temporary file space
path = /tmp
read only = no
public = yes

# 윈도우에서 보이는 공유 자원(폴더) 이름이지만 staff 그룹에 있는 사용자들을 제외한 사용자들은 오직 읽기만 사용 가능하다.
[public]
# 공유 자원에 대한 설명 필드와 같다.
comment = Public Stuff
# 삼바 서버로 공유할 실제 공유 자원(폴더)이다.
path = /home/samba/public
# 손님 사용자에게 접근을 허용한다.
public = yes
read only = no

printable = no
write list = @staff

# 사용자 정의 섹션
[posein]
comment = shared-files in posein directory
# 공유 디렉토리의 경로를 지정
path = /home/posein/pds
# 공유 디렉토리를 읽기만 가능하게 할지를 지정
read only = no
writable = yes
# 서비스 디렉토리에 사용 가능한 사용자를 말하며, 만약 이 옵션이 생략되면 모든 사용자가 접근할 수 있다.
valid user = posein xitem prehee
# guest ok와 같은 옵션으로 no로 설정하면 다른 사용자들은 이용할 수 없고 개인 사용자만 사용할 수 있게 된다.
public = no
# 이용 가능한 공유 리스트를 보여줄 것인가를 지정하는 것으로 no로 지정하면 리스트를 보여주지 않는다.
browseable = no
# 서비스로 지정된 디렉토리에 스풀 파일을 지정할 것인가를 지정하는 것으로, 프린터 공유 디렉토리가 아니므로 대부분 no로 설정한다.
printable = no
# create mode와 같은 옵션으로 파일을 생성할 때 사용되는 모드를 나타낸다.
create mask = 0644 
# 'no'로 하면 심볼릭 링크를 따르는 것이 방지된다. follow symlinks를 끄는 것은 잠재적인 보안 구멍을 제거한다.
follow symlinks = no

* 옵션
- read only : 공유 디렉토리를 읽기만 가능하다.
- writeble users : 공유 디렉토리를 쓰기 가능하다.
- valid users : 공유 디렉토리에 로그인 할 수 있도록 한다.
- public, guest ok : 다른 사용자들이 이용할 수 있도록 한다.
- browseable : 공유 디렉토리의 리스트를 보여준다.
- printable : 공유 디렉토리에 스풀 파일을 저장할 것인지를 지정한다.
- path : 공유할 디렉토리의 절대 경로이다.
- comment : 코멘트를 뜻한다.
- create mask, creat mode : 파일을 생성할 때 사용되는 모드를 의미한다.
- write list : 쓰기가 가능한 특정 사용자를 지정한다.

윈도우 메시지 정리

WM_ACTIVATE : LOWORD(wParam) : 윈도우가 활성화되었는지 비활성화 되었는지를 표현한다.
어떤 시점에서 활성화 상태의 윈도우는 오직 하나만 존재할 수 있다. 활성화되어 있다는 것은 현재 사용자가 사용하고 있는 윈도우라는 뜻이다. 탑 레벨(오버랩드, 팝업)윈도우만 활성화 될 수 있으며 차일드 윈도우는 활성화될 수 없다. 활성화된 부모 윈도우에 속한 차일드 윈도우는 포커스를 가질 수 있다. 이 메시지는 윈도우의 활성화 상태가 변경될 때 보내지는데 새로 활성화되는 윈도우와 활성 상태를 잃는 윈도우에게 동시에 보내진다. 예를 들어 A윈도우가 활성화된 상태에서 사용자가 B윈도우를 선택했다면 A윈도우에게는 비활성화된다는 메시지가 전달되며 B윈도우에게는 새로 활성화 된다는 메시지가 전달된다.

두 윈도우가 같은 스레드에 소속되어 있으면 즉, 동일한 메시지 큐를 사용하고 있으면 메시지는 동기적으로 전달된다. 비활성화되는 윈도우에게 메시지가 먼저 전달되고 이 메시지가 처리된 후 활성화된 윈도우에게 메시지가 이어서 전달된다. 따라서 비활성화되는 윈도우가 이 메시지에 늦게 응답하면 활성화되는 윈도우도 그만큼 늦게 활성화된다. 두 윈도우가 다른 메시지 큐를 사용하고 있으면 이 메시지는 비동기적으로 전달되므로 비활성화되는 윈도우의 응답 여부에 상관없이 새로 활성화 되는 윈도우는 윈도우는 즉시 사용 가능한 상태가 된다.

DefWindowProc은 최소화되지 않은 윈도우가 활성화될 때 키보드 포커스를 전달한다. 사용자가 마우스로 윈도우를 클릭하여 활성화했다면 WM_MOUSEACTIVATE 메시지가 먼저 전달되고 이 메시지의 응답 결과에 따라 활성화 여부가 결정된다.

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

WM_ACTIVATEAPP : wParam : 이 인수가 TRUE이면 윈도우가 활성화된 것이고 FALSE이면 비활성화된 것이다.
다른 프로세스에 소속된 윈도우로 활성 상태가 이동될 때 이 메시지가 발생한다. 이 메시지는 새로 활성화되는 윈도우와 비활성화되는 윈도우에 동시에 전달된다. 그러나 같은 프로세스내의 윈도우로 포커스가 이동할 때는 이 메시지가 발생하지 않으며 WM_ACTIVATE 메시지가 대신 발생한다. 응용 프로그램 전체의 활성 상태를 프로그래밍할 때만 이 메시지를 사용하며 윈도우 단위의 활성화는 WM_ACTIVATE 메시지를 대신 사용한다.

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

WM_APP : wParam, lParam 모두 의미가 정해져 있지 않다. 응용 프로그램이 의미를 정해서 사용할 수 있다.
WM_APP는 응용 프로그램을 위한 고유의 메시지를 정의하기 위한 상수값이며 이 범위 이후부터 윈도우 클래스의 사용자 정의 메시지를 만들 수 있다. 이 값은 0x8000으로 정의되어 있으며 보통 WM_APP+n으로 사용자 정의 메시지를 정의한다. 이때 n은 1보다 큰 정수이며 사용자 정의 메시지간의 구분을 위해 사용된다. 여러 개의 사용자 정의 메시지가 필요하다면 WM_APP+1, WM_APP+2, WM_APP+3,... 식으로 계속 n을 증가시켜 가며 메시지를 정의할 수 있다. 윈도우즈는 WM_APP이후 0xBFFF까지 사용자 정의 메시지 영역으로 정의하고 있으므로 n은 최대 0x4000까지 가능하다. WM_APP+n을 곧바로 사용할 수도 있으며 자주 사용할 경우 다음과 같이 매크로를 정의하여 별도의 메시지를 만들 수 있다.

#define WM_MYMESSAGE WM_APP+1

이렇게 매크로를 정의해 놓고 이후부터 WM_MYMESSAGE라는 명칭을 대신 사용하면 된다. WM_USER도 사용자 정의 메시지를 정의하는 용도로 사용되지만 표준 컨트롤중에 이미 WM_USER를 사용하는 컨트롤이 있으므로 중복될 위험성이 있다. 반면 WM_APP는 시스템이 전혀 이 영역을 사용하지 않고 있으므로 중복될 위험이 전혀 없으며 응용 프로그램간의 통신에 사용하기에 적합하다. 두 응용 프로그램의 약속에 의해 WM_APP+n 메시지를 정의하여 사용하면 된다.

WM_USER는 윈도우 클래스를 위한 사용자 정의 메시지이며 WM_APP는 응용 프로그램을 위한 사용자 정의 메시지라는 점이 다르다. 그러나 이 구분은 어디까지나 권장 사항일 뿐이지 강제 사항은 아니다. WM_USER를 응용 프로그램간의 통신에 사용하더라도 충돌이 없다는 확신만 있다면 가능하다. 다만 잠재적인 충돌 가능성이 있을 수 있다는 것을 고려할 때 바람직하지는 않다.

예를 들어 이런 상황을 고려해 보자. MyApp에서 Con1이라는 커스텀 컨트롤을 사용하는데 이 컨트롤은 자신에게 변화가 있을 때 WM_USER+1이라는 통지 메시지를 부모 윈도우로 보내도록 되어 있다. 이런 상황에서 MyApp가 자신의 고유 용도로 WM_USER+1을 다시 정의한다면 메시지간의 충돌이 발생하게 된다. 이런 상황을 방지하기 위해 운영체제는 WM_USER를 내부적인 용도로 WM_APP를 응용 프로그램간의 통신에 사용하도록 권장하는 것이다. 하지만 WM_APP도 여전히 충돌이 발생할 가능성이 있으므로 좀 더 안전한 방법으로 메시지를 정의하고자 한다면 RegisterWindowMessage 함수로 문자열 메시지를 등록하여 사용하는 것이 좋다.

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

WM_CHAR : wParam : 입력된 문자 코드이다. 일반적으로 이 코드는 아스키 코드이며 곧바로 문자열 출력에 사용할 수 있다.

lParam : 눌러진 키와 키보드 상태에 대한 여러 가지 정보를 가지는 비트 필드값이며 각 비트별로 다음과 같은 정보가 전달된다. 그러나 WM_CHAR 메시지에서는 이 정보를 사용하지 않는다. 왜냐하면 키 하나와 문자 하나의 대응 방식이 일정하지 않기 때문에 이 정보는 항상 유효하지 않기 때문이다.

키보드로부터 문자키가 입력되었을 때 이 메시지가 보내진다. 여기서 문자키는 화면으로 출력 가능한 문자인 알파벳, 숫자, 기호 등을 의미하며 커서 이동키나, PgUp, PgDn 등의 기능키들은 제외된다. 이 메시지는 TranslateMessage 함수에 의해 생성되어 메시지 큐에 덧붙여진다. 사용자가 키보드를 누를 때 TranslateMessage 함수는 이 키가 현재 키보드에서 대응되는 문자가 있는지를 점검하고 Caps Lock, Shift 키의 상태 등을 참고하여 적절한 WM_CHAR 메시지를 보내준다. 이 메시지를 받으려면 메시지 루프는 반드시 다음과 같이 TranslateMessage 함수를 포함하고 있어야 한다.

while(GetMessage(&Message,0,0,0)) {
TranslateMessage(&Message);
DispatchMessage(&Message);
}

키보드의 문자키를 눌렀다 뗄 때 WM_KEYDOWN, WM_CHAR, WM_KEYUP 메시지가 순서대로 전달된다. DBCS 문자 코드(유니코드가 아닌 한글)인 경우 이 메시지는 상하위 바이트에 대해 두번 전달되며 이 두 바이트를 합치면 한글 한 문자의 코드가 얻어진다.

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

WM_CLEAR : 인수없음
에디트 컨트롤(또는 콤보 박스의 에디트)로 보내지는 메시지이며 선택 영역을 삭제하도록 한다. 삭제만 될 뿐이며 클립보드로 텍스트가 복사되지는 않는다. 이 메시지를 보내 문자열을 삭제하는 동작은 사용자가 직접 하는 것이 아니므로 실행 취소(EM_UNDO)는 하지 못한다. 에디트 컨트롤이 없는 CBS_DROPDOWNLIST 콤보 박스에는 아무런 효과도 없다.

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

WM_CLOSE : 인수 없음
윈도우가 닫히기 전에 이 메시지가 전달되며 메인 윈도우인 경우는 응용 프로그램이 종료된다는 신호이다. 이 메시지를 처리하지 않고 DefWindowProc으로 보내면 DestroyWindow 함수를 호출하여 윈도우를 파괴하도록 한다. 이 메시지가 전달되었을 때는 아직 윈도우가 파괴된 것이 아니므로 윈도우가 파괴되는 것을 중간에 차단할 수 있다. 미저장 파일이 있거나 프로그램을 종료할 상황이 되지 않을 때 사용자에게 메시지 박스를 통해 종료 사실을 확인시킬 수 있으며 이 메시지를 가로채서 단순히 return하면 DestroyWindow가 호출되지 않도록 할 수 있다.

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

WM_COMMAND : LOWORD(wParam) : 통지 메시지를 보낸 항목의 ID이다. 메뉴의 ID, 액셀러레이터의 ID 또는 컨트롤의 ID가 전달된다.

HIWORD(wParam) : 컨트롤이 이 메시지를 보낼 때는 통지 코드가 전달된다. 통지 코드의 종류는 에디트, 리스트 박스 등의 컨트롤에 따라 다양하다. 메뉴 항목이 선택된 경우 이 값은 0이며 액셀러레이터가 선택된 경우 이 값은 1이다.

lParam : 통지 메시지를 보낸 컨트롤의 윈도우 핸들이 전달된다. 메뉴나 액셀러레이터로부터 이 메시지가 전달된 경우 이 값은 NULL이다.

메뉴, 액셀러레이터를 선택했을 때 이 메시지가 전달되며 차일드 컨트롤이 부모 윈도우로 통지 메시지를 전달할 때도 이 메시지 형태로 전달된다. 각종 컨트롤로부터 값이 전달되며 또한 각 컨트롤은 다양한 통지 메시지를 보내므로 이 메시지는 일반적으로 다음과 같은 이중 switch문으로 작성된다.

switch (LOWORD(wParam)) {
case ID:
switch (HIWORD(wParam))
case code: ........

컨트롤(또는 메뉴 항목의 ID)에 따라 먼저 분기를 하고 통지 메시지 별로 다시 분기를 한다. 메뉴와 액셀러레이터는 보통 같은 명령에 대해 같은 ID로 한쌍이 정의되며 둘 중 어떤 항목을 선택하더라도 프로그램의 동작은 동일하다. 그러나 만약 이 둘을 굳이 구분하려면 HIWORD(wParam)값을 참조하면 된다. 메뉴 항목과 대응되는 액셀러레이터는 윈도우가 최소화되어 있을 때는 사용 금지되지만 메뉴 항목과 무관하게 단독으로 정의된 액셀러레이터는 최소화 상태에서도 전달된다.

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

WM_COPY : 인수없음
에디트 컨트롤(또는 콤보 박스의 에디트)로 보내지는 메시지이며 선택 영역을 복사하도록 한다. 클립보드에는 CF_TEXT 포맷의 문자열이 들어간다. 에디트 컨트롤이 없는 CBS_DROPDOWNLIST 콤보 박스에는 아무런 효과도 없다.

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

WM_CREATE : wParam : 사용되지 않음

lParam : 윈도우 생성 정보인 CREATESTRUCT 구조체의 포인터이다. 이 구조체는 CreateWindow(Ex) 함수의 인수에 대한 정보를 가진다.

CreateWindow(Ex) 함수에 의해 윈도우가 생성될 때 보내진다. 메모리에 윈도우를 생성한 후 화면에 보이기 전에 보내지며 주로 윈도우에 관련된 초기화 작업을 할 때 사용된다. 윈도우 동작을 위한 메모리 할당, 리소스 생성, 차일드 컨트롤 생성, 윈도우 속성 초기화 작업에 이 메시지가 사용된다.

CreateWindow(Ex) 함수는 이 메시지를 완전히 처리한 후에 리턴한다. 만약 이 메시지 처리중에 차일드 윈도우를 생성했다면 각 차일드 윈도우로도 WM_CREATE 메시지가 전달되어 개별적인 초기화를 한다. 인수로 전달되는 LPCREATESTRUCT 구조체는 보통 사용하지 않으며 무시하나 이 구조체의 lParam 멤버는 CreateWindow 함수의 제일 마지막 인수를 전달하며 윈도우로 사용자 정의값을 전달하고자 할 때 사용할 수 있다.

참고:대화상자는 이 메시지 대신 WM_INITDIALOG 메시지를 받는다.

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

WM_CTLCOLORBTN : wParam : 버튼 컨트롤의 DC핸들. 이 DC에 전경색과 배경색 등을 설정한다.

lParam : 버튼 컨트롤의 핸들. 한 윈도우에 버튼이 여러 개 있을 경우 이 핸들값으로 원하는 버튼만 색상을 변경할 수 있다.

시스템이 오너 드로우 버튼을 그리기 전에 이 메시지를 보내 배경색상과 전경색 배경색 등을 질문한다. DefWindowProc은 시스템에 정의된 컨트롤 색상을 리턴하도록 되어 있으므로 이 메시지를 처리하지 않으면 디폴트 색상으로 버튼이 그려진다. 부모 윈도우가 이 메시지를 직접 처리하면 wParam으로 전달되는 DC에 전경색과 배경색을 설정할 수 있으며 배경 브러시 핸들을 리턴함으로써 버튼의 배경 색상을 변경할 수 있다.

배경 브러시를 변경하고자 할 경우 WM_CREATE 등의 메시지에서 미리 브러시를 만들어 놓고 이 메시지에서 브러시 핸들을 리턴해 주면 된다. 시스템은 이 메시지에서 리턴한 브러시 핸들로 오너 드로우 버튼의 배경을 채색한다. 이 브러시는 시스템이 자동으로 파괴해 주지 않으므로 더 이상 필요가 없어졌을 때(WM_DESTROY) 부모 윈도우가 직접 파괴해 주어야 한다.

오너 드로우 버튼에 대해서만 이 메시지가 전달되며 BS_PUSHBUTTON, BS_DEFPUSHBUTTON, BS_PUSHLIKE 스타일의 버튼에 대해서는 이 메시지가 전달되지 않는다. 이 메시지를 받으려면 버튼은 반드시 BS_OWNERDRAW 스타일을 가지고 있어야 한다.


이 메시지는 같은 스레드 내에서만 보내진다

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

WM_CTLCOLORDLG : wParam : 대화상자의 DC 핸들. 이 핸들값으로 전경색과 배경색을 변경한다.

lParam : 대화상자의 핸들.

시스템이 대화상자를 그리기 전에 이 메시지를 보내 배경색상을 질문한다. DefWindowProc은 시스템에 정의된 배경 색상을 리턴하도록 되어 있으나 이 메시지를 대화상자가 직접 처리하면 wParam으로 전달되는 DC에 배경색과 전경색을 설정할 수 있으며 배경 브러시 핸들을 리턴함으로써 대화상자의 배경색을 변경할 수 있다.

배경 브러시를 변경하고자 할 경우 WM_INITDIALOG 메시지에서 미리 브러시를 만들어 놓고 이 메시지에서 브러시 핸들을 리턴해 주면 된다. 대화상자는 이 메시지에서 리턴한 브러시 핸들로 배경을 채색하다. 이 브러시는 시스템이 자동으로 파괴해 주지 않으므로 더 이상 필요가 없어졌을 때(EndDialog 호출 직전)대화상자가 파괴해 주어야 한다.

이 메시지는 같은 스레드 내에서만 보내진다.

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

WM_CTLCOLOREDIT : wParam : 에디트 컨트롤의 DC핸들. 이 DC에 전경색과 배경색 등을 설정한다.

lParam : 에디트 컨트롤의 핸들. 한 윈도우에 에디트가 여러 개 있을 경우 이 핸들값으로 원하는 에디트만 색상을 변경할 수 있다

시스템이 에디트 컨트롤을 그리기 전에 이 메시지를 보내 배경색상과 전경색 배경색 등을 질문한다. DefWindowProc은 시스템에 정의된 컨트롤 색상을 리턴하도록 되어 있으므로 이 메시지를 처리하지 않으면 디폴트 색상으로 에디트가 그려진다. 부모 윈도우가 이 메시지를 직접 처리하면 wParam으로 전달되는 DC에 전경색과 배경색을 설정할 수 있으며 배경 브러시 핸들을 리턴함으로써 에디트의 배경 색상을 변경할 수 있다.

배경 브러시를 변경하고자 할 경우 WM_CREATE 등의 메시지에서 미리 브러시를 만들어 놓고 이 메시지에서 브러시 핸들을 리턴해 주면 된다. 시스템은 이 메시지에서 리턴한 브러시 핸들로 에디트 컨트롤의 배경을 채색한다. 이 브러시는 시스템이 자동으로 파괴해 주지 않으므로 더 이상 필요가 없어졌을 때(WM_DESTROY) 부모 윈도우가 직접 파괴해 주어야 한다.

읽기 전용이나 사용 금지된 에디트 컨트롤은 이 메시지 대신 WM_CTLCOLORSTATIC 메시지가 전달된다. 리치 에디트 컨트롤에 대해서는 이 메시지가 보내지지 않으므로 EM_SETBKCOLOR 메시지로 배경 색상을 바꾼다. 이 메시지를 사용하면 에디트 컨트롤의 폰트를 변경할 수도 있으나 폰트 변경은 통상 WM_SETFONT 메시지를 사용하는 것이 좋다.

이 메시지는 같은 스레드 내에서만 보내진다.

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

WM_CTLCOLORLISTBOX : wParam : 리스트 박스의 DC핸들. 이 DC에 전경색과 배경색 등을 설정한다.

lParam : 리스트 박스의 핸들. 한 윈도우에 여러 개의 리스트 박스가 있을 경우 이 핸들값으로 원하는 리스트 박스만 색상을 변경할 수 있다.

시스템이 리스트 박스 컨트롤을 그리기 전에 이 메시지를 보내 배경색상과 전경색 배경색 등을 질문한다. DefWindowProc은 시스템에 정의된 컨트롤 색상을 리턴하도록 되어 있으므로 이 메시지를 처리하지 않으면 디폴트 색상으로 리스트 박스가 그려지며 리스트 박스의 항목은 디폴트 전경색과 배경색으로 그려진다. 부모 윈도우가 이 메시지를 직접 처리하면 wParam으로 전달되는 DC에 전경색과 배경색을 설정할 수 있으며 배경 브러시 핸들을 리턴함으로써 리스트 박스의 배경 색상을 변경할 수 있다.

배경 브러시를 변경하고자 할 경우 WM_CREATE 등의 메시지에서 미리 브러시를 만들어 놓고 이 메시지에서 브러시 핸들을 리턴해 주면 된다. 시스템은 이 메시지에서 리턴한 브러시 핸들로 리스트 박스 컨트롤의 배경을 채색한다. 이 브러시는 시스템이 자동으로 파괴해 주지 않으므로 더 이상 필요가 없어졌을 때(WM_DESTROY) 부모 윈도우가 직접 파괴해 주어야 한다.

이 메시지는 같은 스레드 내에서만 보내진다.

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

WM_CTLCOLORSCROLLBAR : wParam : 스크롤 바의 DC핸들. 이 DC에 전경색과 배경색 등을 설정한다.

lParam : 스크롤 바의 핸들. 한 윈도우에 여러 개의 스크롤 바가 있을 경우 이 핸들값으로 원하는 스크롤 바만 색상을 변경할 수 있다

시스템이 스크롤 바를 그리기 전에 이 메시지를 보내 배경색상으로 사용할 브러시 핸들을 질문한다. DefWindowProc은 시스템에 정의된 컨트롤 색상을 리턴하도록 되어 있으므로 이 메시지를 처리하지 않으면 디폴트 색상으로 스크를 바가 그려진다. 부모 윈도우가 이 메시지를 직접 처리하여 배경 브러시 핸들을 리턴하면 스크롤 바의 배경 색상을 변경할 수 있다. wParam으로 DC의 핸들이 전달되므로 전경색과 배경색을 변경할 수는 있지만 스크롤 바는 텍스트를 출력하지 않기 때문에 배경 브러시를 바꾸는 것 외에는 별다른 의미가 없다.

배경 브러시를 변경하고자 할 경우 WM_CREATE 등의 메시지에서 미리 브러시를 만들어 놓고 이 메시지에서 브러시 핸들을 리턴해 주면 된다. 시스템은 이 메시지에서 리턴한 브러시 핸들로 스크롤 바의 몸체를 그린다. 이 브러시는 시스템이 자동으로 파괴해 주지 않으므로 더 이상 필요가 없어졌을 때(WM_DESTROY) 부모 윈도우가 직접 파괴해 주어야 한다.

이 메시지는 스크롤 바 컨트롤에게만 보내지며 WM_HSCROLL, WS_VSCROLL 스타일에 의해 윈도우에 부착된 표준 스크롤 바에는 보내지지 않는다. 이 메시지는 같은 스레드 내에서만 보내진다.

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

WM_CTLCOLORSTATIC : wParam : 스태틱 컨트롤의 DC핸들. 이 DC에 전경색과 배경색 등을 설정한다.

lParam : 스태틱 컨트롤의 핸들. 한 윈도우에 스태틱이 여러 개 있을 경우 이 핸들값으로 원하는 스태틱만 색상을 변경할 수 있다.

시스템이 스태틱 컨트롤을 그리기 전에 이 메시지를 보내 배경색상과 전경색 배경색 등을 질문한다. DefWindowProc은 시스템에 정의된 컨트롤 색상을 리턴하도록 되어 있으므로 이 메시지를 처리하지 않으면 디폴트 색상으로 스태틱이 그려진다. 부모 윈도우가 이 메시지를 직접 처리하면 wParam으로 전달되는 DC에 전경색과 배경색을 설정할 수 있으며 배경 브러시 핸들을 리턴함으로써 스태틱의 배경 색상을 변경할 수 있다.

배경 브러시를 변경하고자 할 경우 WM_CREATE 등의 메시지에서 미리 브러시를 만들어 놓고 이 메시지에서 브러시 핸들을 리턴해 주면 된다. 시스템은 이 메시지에서 리턴한 브러시 핸들로 스태틱 컨트롤의 배경을 채색한다. 이 브러시는 시스템이 자동으로 파괴해 주지 않으므로 더 이상 필요가 없어졌을 때(WM_DESTROY) 부모 윈도우가 직접 파괴해 주어야 한다.

읽기 전용 스타일을 가진 에디트 컨트롤과 사용 금지된 에디트도 이 메시지를 대신 받는다. 이 메시지는 같은 스레드 내에서만 보내진다.

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

WM_CUT : 인수 없음
에디트 컨트롤(또는 콤보 박스의 에디트)로 보내지는 메시지이며 선택 영역을 잘라내도록 한다. 잘라낸 문자열은 클립보드에 CF_TEXT포맷으로 들어가며 에디트에서는 삭제된다. 이 메시지를 보내 문자열을 잘라내는 동작은 사용자가 직접 하는 것이 아니므로 실행 취소(EM_UNDO)는 하지 못한다. 에디트 컨트롤이 없는 CBS_DROPDOWNLIST 콤보 박스에는 아무런 효과도 없다.

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

WM_DEADCHAR : wParam : 데드키에 의해 발생한 문자 코드

lParam : 반복 회수, 스캔 코드, 확장키 등에 대한 정보고 비트필드로 전달된다. WM_KEYDOWN의 lParam과 동일하다.

데드키란 단독으로 문자를 구성할 수 없는 키이며 이 키에 의해 발생하는 데드 문자는 다음에 입력되는 문자와 조합되어 하나의 문자를 만든다. 예를 들어 독일어 키보드의 경우 움라이트 키가 먼저 입력된 후 a,o,u 등이 입력되면 a,o,u위에 점 두 개가 찍히는 움라이트 문자가 입력되는데 이때 먼저 입력되는 움라이트가 데드 문자이다. 움라이트 문자를 입력했을 때 다음 메시지가 순서대로 전달된다.

WM_KEYDOWN
WM_DEADCHAR
WM_KEYUP
WM_KEYDOWN
WM_CHAR
WM_KEYUP

이 메시지는 데드키를 누를 때 TranslateMessage 함수에 의해 발생하며 포커스를 가진 윈도우에게 전달된다. 통상 이 메시지는 무시하며 움라우트 입력 결과는 최종적으로 WM_CHAR로 전달되므로 이 메시지만 처리하면 된다. 그러나 데드키 입력 사실을 사용자에게 분명히 알려주고 싶을 때는 이 메시지를 처리하여 다음 입력될 문자와 조합되어 한 문자가 됨을 표시할 수도 있다. 만약 데드키 다음에 입력된 문자가 데드키와 조합되지 못하는 글자일 경우는 데드키와 다음 문자 각각에 대해 두 개의 WM_CHAR 메시지가 전달된다. 독일, 폴란드, 그리스 등의 유럽 계통의 키보드에서 필요한 메시지이며 한국의 키보드에서는 이 메시지가 절대로 발생하지 않는다.

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

WM_DESTROY : 인수 없음
윈도우가 파괴될 때 이 메시지가 전달된다. 사용자가 Alt+F4 또는 닫기 버튼을 누를 경우 WM_CLOSE 메시지가 전달되며 이 메시지를 별도로 처리하지 않으면 DefWindowProc은 DestroyWindow 함수를 호출하여 윈도우를 파괴한다. 또는 프로그램 코드 내부에서 명시적으로 DestroyWindow 함수를 호출할 때도 윈도우가 파괴되는데 이 함수 호출 결과로 WM_DESTROY 메시지가 전달된다.

이 메시지를 받은 윈도우는 윈도우의 종료를 위한 처리를 해야 하는데 예를 들어 열어 놓은 파일을 닫고 할당한 메모리를 해제하는 등의 정리 작업을 한다. WM_CREATE에서의 초기화 처리의 반대 동작이 이 메시지에 작성되는 것이 일반적이며 그외 레지스트리에 미보관 정보를 저장하는 등의 작업을 할 수 있다. 만약 파괴되는 윈도우가 클립보드 체인에 속해 있으면 자신을 클립보드 체인에서 제거해야 한다.

DestroyWindow 함수는 파괴할 윈도우를 화면에서 숨긴 후 이 메시지를 보내므로 이 메시지를 받은 시점에서는 윈도우 자체가 파괴되지 않은 상태이다. 또한 DestroyWindow 함수는 자식 윈도우에게도 이 메시지를 차례대로 보내주는데 부모 윈도우가 먼저 이 메시지를 받고 자식 윈도우에게로 이 메시지가 보내진다. 따라서 부모 윈도우가 이 메시지를 처리하는 동안은 모든 자식 윈도우가 아직 파괴되기 전이므로 자식 윈도우를 프로그래밍할 수 있다.

파괴되는 윈도우가 메인 윈도우일 경우 PostQuitMessage 함수를 반드시 호출하여 프로세스의 메시지 루프를 종료하도록 해야 한다. 만약 이 처리를 생략하면 윈도우만 파괴되고 메시지 루프는 계속 실행중인 상태가 되므로 프로세스가 종료되지 않는다.

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

WM_DRAWITEM : wParam : 이 메시지를 보낸 컨트롤의 ID이다. 메뉴 항목의 경우는 0이 전달된다.

lParam : 그려질 항목에 대한 정보가 담긴 다음 구조체의 포인터이다. 오너는 이 구조체의 내용을 참조하여 컨트롤이나 메뉴 항목을 그린다

오너 드로우 버튼, 리스트 박스, 콤보 박스, 메뉴가 그려져야 할 필요가 있을 때 오너 윈도우에게 이 메시지가 전달된다. 오너는 lParam으로 전달된 컨트롤의 종류와 상태에 따라 컨트롤을 적절히 그려 주어야 할 책임이 있다. 만약 오너 드로우 리스트 박스를 가진 윈도우가 이 메시지를 처리하지 않고 DefWindowProc으로 보내면 포커스 사각형만 그려진다.

lParam의 DRAWITEMSTRUCT에는 항목 그리기에 사용할 DC의 핸들과 출력 영역이 전달되는데 이 정보를 참조하여 그리되 DC는 반드시 원래 상태대로 유지해 주어야 한다. DC에 커스텀 펜이나 브러시를 선택해 사용할 수 있지만 그 이전 객체를 반드시 복구시켜 주어야 한다.

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

WM_DROPFILES : wParam : 드롭된 파일에 대한 정보를 가지는 HDROP 내부 구조체의 포인터이다. 이 구조체로부터 드래그된 파일의 목록을 얻을 수 있다.

lParam : 사용되지 않는다.

DragAcceptFiles 함수로 파일을 드롭받겠다고 등록한 윈도우로 파일이 드롭될 때 이 메시지가 보내진다. wParam으로 전달된 HDROP으로부터 DragQueryFile 함수를 호출하면 드롭된 파일의 목록을 얻을 수 있다. 응용 프로그램은 이 메시지를 받았을 때 드롭된 파일로 원하는 작업을 하게 된다. 예를 들어 파일을 열거나 검색, 삭제 등을 할 수 있다.

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

WM_ENDSESSION : wParam : 셧다운되고 있는 중이면 TRUE이며 그렇지 않으면 FALSE이다. 이 값은 WM_QUERYENDSESSION 메시지의 리턴값과 같으며 TRUE이면 운영체제가 곧 종료된다는 뜻이다.

lParam : 로그오프를 하는 것인지 시스템 셧다운을 하는 것인지를 나타낸다. 이 값이 0이면 시스템을 완전히 종료하는 것이며 ENDSESSION_LOGOFF 플래그가 설정되어 있으면 로그오프만 하는 것이다. 이 값은 비트 필드이므로 반드시 & 연산자로 플래그의 존재 유무를 점검해야 한다.

if (lParam & ENDSESSION_LOGOFF) {
// 로그오프 처리
} else {
// 셧다운 처리
}

운영체제는 종료되기 전에 실행중인 모든 프로그램에게 WM_QUERYENDSESSION 메시지를 보내 종료 허가를 받는다. 각 프로그램이 종료를 허가하면 WM_ENDSESSION 메시지를 보내 운영체제가 종료된다는 사실을 알려준다. 즉 이 메시지를 받았을 때는 이미 셧다운이 결정된 상태이며 더 이상 운영체제 종료를 거부할 수 없다.

응용 프로그램은 이 메시지를 받았을 때 미저장 문서, 설정 상태의 저장 등 필요한 동작을 해야 한다. 그러나 DestroyWindow로 메인 윈도우를 파괴하거나 PostQuitMessage 함수로 메시지 루프를 종료하는 등의 처리는 굳이 할 필요가 없다. 왜냐하면 운영체제가 종료되는 특수한 상황이기 때문에 자원 해제를 할 필요가 없기 때문이다. 물론 자기 자신을 완전히 종료한다고 해서 시스템 종료에 문제가 생기는 것은 아니지만 셧다운 속도가 느려지게 된다. 어차피 전원이 꺼지는 상황이므로 RAM에 남아 있는 윈도우, 프로세스는 그대로 방치해도 상관이 없다.

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

WM_ERASEBKGND : wParam : 배경 채색에 사용될 DC의 핸들

lParam : 사용되지 않음

윈도우 크기 변경되었거나, 다른 윈도우에 가려진 부분이 드러났다거나 할 때 배경을 지우기 위해 이 메시지가 보내진다. WM_PAINT에서 작업 영역에 출력을 하기 전에 먼저 전에 그려져 있던 내용을 지워야 한다. 이 메시지를 처리하지 않을 경우 DefWindowProc은 윈도우 클래스에 정의된 배경 브러시로 작업 영역을 지운다. 그래서 별도의 처리를 하지 않더라도 윈도우를 새로 그릴 때는 항상 배경 브러시로 작업 영역을 지운 후 WM_PAINT에서 출력을 내보내게 된다.

만약 윈도우 클래스의 배경 브러시가 NULL이면 DefWindowProc은 아무것도 하지 않으며 따라서 배경은 지워지지 않는다. 이 경우 응용 프로그램이 직접 이 메시지를 처리하여 배경을 지워 주어야 한다. 별도의 브러시를 만들어 배경을 지울 수도 있고 비트맵이나 작도 함수로 커스텀 배경을 만들 수도 있다

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

WM_FONTCHANGE : wParam, lParam : 사용되지 않음
시스템의 폰트 구성이 변경되면 모든 탑 레벨 윈도우에게 이 메시지가 전달된다. 새로운 폰트가 설치되었거나 또는 기존의 폰트가 삭제되었을 때 이 메시지가 발생한다. 폰트 목록을 유지하고 있는 응용 프로그램은 이 메시지를 받았을 때 폰트 목록을 다시 조사해야 한다. 그렇지 않으면 새로 설치된 추가 폰트를 인식하지 못하거나 이미 삭제된 폰트를 사용할 위험이 있다.

시스템에 설치되어 있는 폰트 목록을 구하기 위해서는 EnumFontFamilies 함수로 폰트 열거를 해야 한다. 폰트 열거는 시스템의 모든 폰트 정보를 조사해야 하므로 다소 시간이 걸리며 따라서 이 작업은 응용 프로그램이 시작될 때 한번만 하며 그 결과를 전역 배열에 저장해 두고 계속 사용하게 된다. 다만 시스템의 폰트 구성이 실행중에 변경되었다면 다시 열거를 해야 하는데 그 시점이 바로 WM_FONTCHANGE 메시지를 받았을 때이다. 폰트 목록을 사용하는 프로그램은 이 메시지를 반드시 처리해야 한다.

응용 프로그램이 AddFontResource, RemoveFontResource 함수로 폰트를 추가 설치했다면 모든 탑 레벨 윈도우에게 이 메시지를 보내 주어 폰트 목록이 변경되었음을 알려 주어야 한다.

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

WM_GETDLGCODE : wParam : 사용되지 않음

lParam : 전달된 메시지인 MSG 구조체의 포인터이며 컨트롤은 전달된 메시지에 따라 다른 결과를 리턴할 수도 있다. 시스템이 단순한 질문을 하는 중에는 NULL이 전달된다.

이 메시지는 대화상자내의 컨트롤들에게 어떤 종류의 입력을 원하는지 질문하기 위해 보내진다. 대화상자에서 사용자의 모든 키 입력은 대화상자가 먼저 받으며 포커스를 가진 컨트롤이 원할 경우만 컨트롤에게 전달된다. 만약 컨트롤이 별다른 입력을 처리하지 않겠다고 응답하면 대화상자는 Tab, Enter, Esc, 커서 이동키 등에 대해 디폴트 처리를 한다. Tab키는 컨트롤간의 포커스 이동을 하며 Enter키는 디폴트 버턴을 누르는 것과 같아진다.

컨트롤들은 자신의 필요에 따라 이 메시지에 응답하여 어떤 입력을 원한다는 것을 대화상자에게 알려 주어야 한다. 예를 들어 ES_WANTRETURN 스타일을 가지는 에디트 컨트롤은 Enter 키 입력을 받아들여 개행해야 하며 이 경우 Enter키는 디폴트 버튼을 누르지 않게 된다. 컨트롤은 자신의 동작과 스타일, 그리고 lParam으로 전달된 메시지를 보고 원하는 키 입력에 대해 응답해야 한다. DefWindowProc은 이 메시지에 대해 항상 0을 리턴하도록 되어 있으므로 커스텀 컨트롤이 이 메시지를 처리하지 않으면 대화상자가 처리하는 키 입력은 받을 수 없다. 대화상자에서 사용될 컨트롤 또는 표준 컨트롤을 서브 클래싱할 때는 이 메시지에 대해 적절히 응답해야 한다.

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

WM_GETFONT : wParam, lParam : 사용되지 않음
컨트롤에 설정된 폰트를 조사한다. 즉 컨트롤이 어떤 폰트로 텍스트를 출력하고 있는지 조사한다.

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

WM_GETMINMAXINFO : wParam : 사용되지 않음

lParam : 윈도우의 최소, 최대 크기를 지정하는 MINMAXINFO 구조체의 포인터이다. 이 구조체값을 변경하면 최소, 최대 크기를 변경할 수 있다.

이 메시지는 윈도우의 크기나 위치가 변경되기 직전에 윈도우에게 보내진다. 응용 프로그램이 이 메시지의 등답하여 lParam으로 전달되는 MINMAXINFO 구조체를 변경하면 윈도우의 크기나 위치는 이 구조체의 값에 영향을 받게 된다. 이 메시지를 처리하지 않으면 윈도우의 크기는 자유롭게 조정할 수 있으나 이 메시지에서 최소, 최대 크기를 변경하면 그 범위내에서만 윈도우 크기 조정이 가능하다.

만약 작업 영역이 너무 좁으면 차일드 컨트롤을 배치하는데 문제가 있거나 텍스트 배치가 어려워진다면 이 메시지를 처리하여 최소 크기를 일정 폭으로 제한할 수 있다. MINMAXINFO 구조체의 멤버 전체를 다 수정할 필요는 없으면 원하는 멤버만 수정할 수 있다.

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

WM_GETTEXT : wParam : 복사될 TCHAR형 문자의 최대 길이이며 널 종료 문자도 포함해야 한다.

lParam : 문자열을 복사할 버퍼의 주소

이 메시지는 윈도우의 텍스트를 조사하기 위해 사용된다. 이 메시지를 처리하지 않고 DefWindowProc으로 보내면 윈도우 텍스트를 lParam이 지정하는 버퍼에 복사하고 복사된 문자 수를 리턴해 준다. 보통 컨트롤의 캡션을 구하기 위해 이 메시지를 보내는데 어떤 문자열이 윈도우의 캡션이 되는가는 컨트롤에 따라 다르다.

가장 일반적인 예는 에디트 컨트롤인데 편집중인 문자열이 조사된다. 리치 에디트 컨트롤도 이 메시지로 문자열을 조사할 수 있지만 길이가 64K를 넘을 경우는 EM_STREAMOUT 메시지나 EM_GETSELTEXT 메시지를 대신 사용해야 한다. 에디트에 편집중인 문자열은 길이가 길 수 있기 때문에 고정된 길이의 버퍼를 사용하는 것은 위험하며 반드시 WM_GETTEXTLENGTH 메시지를 보내 텍스트의 길이를 조사한 후 조사된 길이만큼의 버퍼를 할당해서 사용하는 것이 좋다.

콤보 박스의 경우 콤보 박스의 에디트 또는 스태틱 컨트롤의 텍스트가 조사되며 버튼의 경우 버튼의 이름 문자열이 조사된다. 일반 윈도우는 타이틀 바에 있는 캡션이 조사된다. 리스트 박스 항목의 텍스트는 이 메시지로 얻을 수 없으며 LB_GETTEXT 메시지를 사용해야 한다.

문자열 스타일의 스태틱 컨트롤은 캡션이 조사되지만 아이콘 스타일인 경우는 아이콘의 핸들이 lParam의 첫 4바이트에 조사된다. 단, 아이콘 설정을 위해 WM_SETTEXT 메시지를 사용한 경우에만 해당되며 2000/XP에서는 문자열 스타일이 아닌 스태틱은 무조건 0이 리턴된다. 2000이전 버전의 윈도우즈에서는 이 메시지를 사용하여 비 문자열 스태틱 컨트롤의 ID를 구할 수 있었으나 2000이상에서는 반드시 GetWindowLong 함수로 컨트롤의 ID를 구해야 한다.

보통 메시지를 직접 보내는 경우는 드물며 GetWindowText 함수를 많이 사용하는데 이 함수는 지정한 윈도우로 WM_GETTEXT 메시지를 보내 텍스트를 조사한다. 단, GetWindowText 함수는 다른 프로세스의 윈도우 텍스트트는 조사하지 못하므로 이때는 WM_GETTEXT 메시지를 사용해야 한다.

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

WM_GETTEXTLENGTH : 인수없음
윈도우의 텍스트 길이를 조사한다. 윈도우의 종류에 따라 윈도우 텍스트 종류는 달라지는데 에디트는 편집중인 문자열, 버튼은 이름, 일반 윈도우는 타이틀 바의 캡션이 윈도우 텍스트가 된다. 이 메시지를 보내서 조사한 텍스트 길이는 텍스트 조사를 위한 버퍼 할당에 사용된다. 에디트의 편집중인 문자열은 길이가 길 수 있으므로 고정 길이의 버퍼를 사용해서는 안되며 반드시 이 함수로 길이를 조사한 후 동적 할당한 버퍼를 사용해야 한다.

윈도우가 ANSI 문자와 유니코드 문자를 혼용하고 있을 경우 이 함수가 조사해 주는 텍스트 길이는 때로는 실제 문자열 길이보다 좀 더 클 수도 있다. 그러나 이 경우도 충분한 길이를 조사해 주므로 이 메시지로 조사한 길이만큼 버퍼를 할당하면 항상 안전하다.

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

WM_HSCROLL : LOWORD(wParam) : 사용자의 스크롤 요구를 전달하며 스크롤 바의 눌러진 위치값이 전달된다. 다음 값 중 하나가 전달된다.

HIWORD(wParam) : SB_THUMBPOSITION, SB_THUMBTRACK 메시지의 경우 스크롤 바의 현재 위치가 전달된다. 다른 메시지에서는 사용되지 않는다. 이 값은 16비트이나 스크롤 바는 32비트 범위를 스크롤 할 수 있는데 32비트의 스크롤 값을 얻고 싶을 경우 이 인수를 직접 사용하는 대신 GetScrollInfo 함수로 값을 직접 조사해 사용해야 한다. 만약 스크롤 범위가 음수를 가질 수 있다면 이 값을 int형으로 캐스팅한 후 읽어야 한다.

lParam : 스크롤 바 컨트롤로부터 이 메시지가 전달되었을 경우 스크롤 바 컨트롤의 윈도우 핸들이 전달된다표준 스크롤 바인 경우 이 인수는 NULL이다.

윈도우의 아래쪽에 부착되는 표준 수평 스크롤 바, 또는 SBS_HORZ 스타일을 가지는 수평 스크롤 바 컨트롤이 부모 윈도우로 스크롤 메시지를 보낼 때 이 메시지가 전달된다. 다른 컨트롤은 자신의 변화를 WM_COMMAND로 전달하지만 스크롤 바는 WM_COMMAND 대신 WM_HSCROLL, WM_VSCROLL 메시지를 보낸다. 또한 이 메시지는 트랙 바 컨트롤에 의해 사용되기도 한다.

윈도우는 이 메시지를 받았을 때 스크롤 바의 위치를 갱신해 주어야 하며 화면 스크롤 처리(또는 내부적인 값의 변경)를 해 주어야 한다. 사용자가 썸을 직접 드래그할 때 SB_THUMBTRACK 메시지가 발생하며 드래그를 종료할 때 SB_THUMBPOSITION 메시지가 발생하는데 이 두 메시지 중 하나만 처리해 주면 되며 둘 다 처리할 필요는 없다. 이때 HIWORD(wParam)으로 전달되는 값은 16비트 범위이므로 65535이상의 스크롤 위치값은 전달되지 않으므로 GetScrollInfo 함수로 직접 위치를 구해야 한다.

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

WM_ICONERASEBKGND : wParam : 아이콘 그리기에 사용할 DC 핸들

lParam : 사용되지 않음

이 메시지는 NT 3.51 이전 버전에만 적용되며 95이상에서는 사용되지 않는다. 16비트 윈도우즈에서는 아이콘이 최소화되면 작업 표시줄로 내려가지 않고 바탕 화면에 아이콘이 배치되었으며 이때 최소화된 아이콘에도 출력을 내 보낼 수 있었다. 이 메시지는 최소돠된 아이콘을 그를 때 보내지는 메시지이며 여기서 아이콘을 직접 그릴 수 있다. 윈도우 클래스의 아이콘이 정의되어 있어야만 이 메시지가 전달된다. 95이후에는 이 메시지를 처리할 필요가 없다.

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

WM_INITDIALOG : wParam : 키보드 포커스를 받을 컨트롤의 핸들. 통상 이 컨트롤은 탭 순서가 가장 빠르고 보이며 사용 금지 상태가 아니고 WS_TABSTOP 스타일을 가진 첫번째 컨트롤이다. 이 메시지에서 TRUE를 리턴하면 wParam으로 전달된 컨트롤에 포커스가 맞추어진다.

lParam : 대화상자 초기화 정보가 전달된다. DialogBox 함수로 대화상자를 호출한 경우는 0이 전달되며 DialogBoxParam, CreateDialogParam 등의 함수로 대화상자를 호출했을 때만 전달된다. 대화상자로 전달되는 인수값에 해당하며 이 인수에 따라 대화상자의 모양이나 동작을 다르게 정의할 수 있다. 프로퍼티 시트의 경우 lParam은 PROPSHEETPAGE 구조체의 포인터가 전달된다.

이 메시지는 대화상자가 메모리에 만들어지고 화면에 보이기 직전에 보내진다. 그래서 대화상자내의 모든 컨트롤을 참조할 수 있으며 아직 대화상자가 보이기 전이므로 컨트롤의 재배치, 생성, 삭제, 속성 변경 등을 자유롭게 할 수 있다. 오버랩드 윈도우의 WM_CREATE에 해당하는 함수이며 대화상자가 가장 먼저 받는 메시지이므로 주로 대화상자 초기화에 이 메시지가 사용된다.

대화상자에 속한 컨트롤을 초기화하는 것이 일반적이며 기타 대화상자 동작에 필요한 환경 설정, 메모리 할당, 대화상자 위치 및 속성 변경 등의 작업을 할 수 있다. 만약 초기화중에 실패를 하게 되면 이 메시지를 처리하는 동안에도 EndDialog를 호출하여 대화상자를 즉시 종료하는 것이 가능하다. WM_CREATE와는 달리 리턴값으로 에러 여부를 리턴할 수 없으며 EndDialog 함수로 에러 코드를 리턴해야 한다.

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

WM_INITMENU : wParam : 초기화될 메뉴의 핸들
사용자가 메뉴 바의 메뉴를 클릭하거나 메뉴 키를 눌러 메뉴가 열리기 직전에 이 메시지가 보내진다. 응용 프로그램은 이 메시지를 받았을 때 메뉴 항목에 대한 초기화나 수정을 한다. 선택된 메뉴 항목에 체크 표시를 하거나 사용 금지된 메뉴 항목을 Disable시킬 수 있으며 추가로 더 필요한 메뉴 항목을 AppendMenu 등의 함수로 만들 수 있다.

이 메시지는 메뉴가 활성화될 때 딱 한번만 보내지며 메뉴 바의 팝업 메뉴를 옮겨 다녀도 추가적인 메시지는 발생하지 않는다. 메뉴 항목에 대한 정보는 별도로 제공하지 않으므로 직접 구해서 사용해야 한다.

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

WM_INITMENUPOPUP : wParam : 드롭다운 메뉴 또는 서브 메뉴의 핸들

LOWORD(lParam) : 드롭다운 메뉴나 서브 메뉴를 연 항목의 위치값

HIWORD(lParam) : 드롭다운 메뉴가 윈도우 메뉴이면 TRUE가 되며 그렇지 않으면 FALSE가 된다.

드롭 다운 메뉴나 서브 메뉴가 열리기 직전에 보내진다. 이 메시지를 받았을 때는 아직 메뉴가 화면에 출력되기 전이므로 응용 프로그램은 메뉴를 수정할 수 있다. 각각의 팝업 메뉴에 대해 이 메시지가 전달되므로 전체 메뉴를 수정하지 않고도 원하는 서브 메뉴만 수정하고 싶을 때 이 메시지를 사용한다.

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

WM_KEYDOWN : wParam : 가상 키코드값이며 어떤 키가 눌러졌는지를 나타낸다. 가상 키코드는 키보드의 종류에 독립적인 키 코드값이다.

lParam : 눌러진 키와 키보드 상태에 대한 여러 가지 정보를 가지는 비트 필드값이며 각 비트별로 다음과 같은 정보가 전달된다.

키보드 포커스를 가진 윈도우에서 키보드를 누를 때 이 메시지가 전달된다. 단, Alt키와 함께 키를 눌렀을 때는 이 메시지 대신 WM_SYSKEYDOWN 메시지가 전달된다. wParam으로 눌러진 키에 대한 정보가 전달되며 lParam으로 반복 회수, 스캔코드 등의 추가 정보가 전달된다. 특정 키 입력시 어떤 작업을 하려면 이 메시지를 사용한다. 단, Ctrl+C, Ctrl+T 등의 조합키는 이 메시지에서 처리하는 것보다는 액셀러레이터를 사용하는 것이 더 좋다.

F10키가 눌러지면 DefWindowProc은 내부 플레그만 세트해 놓으며 이때 WM_KEYDOWN 메시지는 발생하지 않는다. 다음번에 F10키에 대해 WM_KEYUP 메시지를 받았을 때 내부 플레그가 세트되어 있으면 이 메시지를 WM_SYSCOMMAND의 SC_KEYMENU를 보내주어 메인 메뉴를 열도록 해준다.

키보드를 뗄 때는 WM_KEYUP 메시지가 전달되는데 키보드는 자동 반복 기능을 가지고 있기 때문에 WM_KEYDOWN이 여러번 발생하고 WM_KEYUP이 한번만 발생할 수도 있다. 이때 각 WM_KEYDOWN에서 이 키가 처음 눌러진 것인지 반복적으로 계속 눌러진 것인지는 lParam의 비트 30을 보면 알 수 있다.

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

WM_KEYUP : wParam : 떨어진 키를 나타내는 가상 키코드 값이다.

lParam : 눌러진 키와 키보드 상태에 대한 여러 가지 정보를 가지는 비트 필드값이다.

눌러진 키가 떨어질 때 이 메시지가 발생한다. 키보드를 계속 누르고 있다가 뗀 경우 반복 기능에 의해 여러번의 WM_KEYDOWN이 발생하므로 반드시 이 메시지가 WM_KEYDOWN과 일대일로 대응되는 것은 아니다. 만약 떨어진 키가 F10이고 WM_KEYDOWN에서 내부 플래그를 설정해 놓았으면 이 메시지는 WM_SYSCOMMAND의 SC_KEYMENU 메시지를 대신 보내 메인 메뉴를 열도록 한다.

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

WM_KILLFOCUS : wParam : 새로 키보드 포커스를 얻은 윈도우의 핸들이 전달된다. 포커스를 얻은 윈도우가 없으면 NULL이다.

키보드 포커스를 잃은 직후에 이 메시지가 전달된다. 이 메시지를 받았을 때는 이미 키보드 포커서가 이동 완료된 후이다. 주로 캐럿 처리를 위해 이 메시지를 프로그래밍하는데 이 메시지를 받았을 때 캐럿을 파괴한다. 이 메시지를 받았을 때 출력 함수나 활성화 상태를 변경하는 함수를 호출해서는 안된다.

키보드 포커스는 키보드 입력을 받을 수 있는 상태를 가리키며 한번에 하나의 윈도우만 포커스를 가질 수 있다. 포커스가 이동될 때는 포커스를 잃는 윈도우에게 WM_KILLFOCUS 메시지가 먼저 전달되며 이어서 포커스를 얻는 윈도우에게 WM_SETFOCUS 메시지가 전달된다.

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

WM_LBUTTONDBLCLK : wParam : 키보드와 다른 마우스 버튼의 현재 상태를 나타내는 값이며 다음 플레그들의 조합값이다.

LOWORD(lParam), HIWORD(lParam) : 마우스의 현재 X, Y좌표이다. 이 좌표는 작업 영역의 원점을 기준으로 한 좌표이다.

작업 영역 내부에서 마우스 왼쪽 버튼을 더블클릭할 때 이 메시지가 메시지 큐에 붙여진다. 마우스가 캡처되어 있으면 캡처한 윈도우로 메시지가 전달되며 그렇지 않으면 마우스 커서 아래의 윈도우로 전달된다. 이 메시지를 받기 위해서는 윈도우 클래스가 반드시 CS_DBLCLKS 스타일을 가져야 한다. 그렇지 않으면 단순히 마우스 누름 메시지만 두번 발생한다. 또한 두 마우스 클릭의 시간 간격은 시스템에 정의되어 있는 더블클릭 시간 간격내에 발생해야만 더블클릭으로 인정된다.

마우스 더블클릭시 WM_LBUTTONDOWN, WM_LBUTTONUP, WM_LBUTTONDBLCLK, WM_LBUTTONUP 네개의 메시지가 일련으로 발생한다. 두번째 WM_LBUTTONDOWN 메시지가 더블클릭으로 변경된다.

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

WM_LBUTTONDOWN : wParam : 키보드와 다른 마우스 버튼의 현재 상태를 나타내는 값이며 다음 플레그들의 조합값이다.

LOWORD(lParam), HIWORD(lParam) : 마우스의 현재 X, Y좌표이다. 이 좌표는 작업 영역의 원점을 기준으로 한 좌표이다.

작업 영역 내부에서 마우스 왼쪽 버튼을 누를 때 이 메시지가 메시지 큐에 붙여진다. 마우스가 캡처되어 있으면 캡처한 윈도우로 메시지가 전달되며 그렇지 않으면 마우스 커서 아래의 윈도우로 전달된다. 모든 메시지 중에 가장 쉽게 받을 수 있는 메시지이므로 실습용이나 간단한 테스트용으로 많이 사용된다.

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

WM_LBUTTONUP : wParam : 키보드와 다른 마우스 버튼의 현재 상태를 나타내는 값이며 다음 플레그들의 조합값이다.

LOWORD(lParam), HIWORD(lParam) : 마우스의 현재 X, Y좌표이다. 이 좌표는 작업 영역의 원점을 기준으로 한 좌표이다. 대부분의 경우 좌표는 양수값이지만 캡처된 특수한 상황에서는 음수일 수도 있는데 이 경우 반드시 (int)형으로 다시 한번 더 캐스팅해 주어야 부호를 제대로 얻을 수 있다.

마우스 왼쪽 버튼을 놓을 때 이 메시지가 큐에 붙여진다. WM_LBUTTONDOWN후에 연속적으로 이 메시지가 발생하는 것이 보통이나 마우스가 캡처되어 있지 않을 때 작업영역 밖에서 마우스 버튼을 놓으면 이 메시지가 전달되지 않을 수도 있다.

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

WM_MENUSELECT : LOWORD(wParam) : 선택된 메뉴 항목의 ID가 저달된다. 드롭다운 메뉴나 서브 메뉴의 경우 메인 메뉴에서의 서브 메뉴 인덱스가 전달되며 lParam은 메인 메뉴의 핸들이 전달된다. 이 인수로 GetSubMenu를 호출하면 열려진 서브 메뉴의 핸들을 얻을 수 있다.

HIWORD(wParam) : 메뉴 플래그값이며 다음 중 하나가 된다. 이 값이 0xFFFF이고 lParam이 NULL이면 시스템이 메뉴를 닫은 것이다.

lParam : 클릭된 메뉴의 핸들

사용자가 메뉴 항목을 선택할 때 메뉴의 소유자에게 보내진다. 이때 선택이란 메뉴 항목을 클릭한 것을 의미하는 것이 아니며 메뉴 항목 위로 마우스 커서나 반전 막대가 움직이고 있다는 뜻이다. 각각의 항목을 선택할 때마다 이 메시지가 전달되므로 개별 항목에 대한 처리가 필요할 때 이 메시지를 이용한다. 예를 들어 각 메뉴 항목에 대한 도움말을 보여 주고 싶다면 이 메시지를 받았을 때 선택된 메뉴 항목에 따른 도움말을 상태란 등에 출력한다

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

WM_MOUSEACTIVATE : wParam : 활성화된 탑 레벨 부모 윈도우의 핸들이다.

LOWORD(lParam) : 마우스를 클릭한 위치인 히트 테스트값이다. 작업 영역을 클릭했으면 HTCLIENT값이 전달된다.

HIWORD(lParam) : 이 메시지를 유발시킨 마우스 메시지의 ID가 전달된다. 이 메시지의 리턴값에 따라 마우스 메시지는 큐에 붙여지거나 버려진다.

사용자가 비활성화된 윈도우에서 마우스 버튼을 누를 때 이 메시지가 전달된다. 이미 활성화되어 있은 윈도우에서 마우스를 누를 때는 이 메시지가 전달되지 않는다. 윈도우는 이 메시지를 받았을 때 자신을 활성화할 것인지, 전달된 마우스 메시지는 어떻게 처리할 것인지 결정해야 한다. 이때 wParam과 lParam값을 읽어 사용자가 어떤 마우스 버튼을 화면의 어느 부분에서 눌렀는지를 조사할 수 있다. DefWindowProc으로 이 메시지를 보낼 경우 이 메시지는 부모 윈도우에게 전달되어 부모 윈도우가 차일드 윈도우의 활성화 여부를 결정한다.

부모 윈도우가 차일드를 활성화시킨다면 MA_NOACTIVATE(ANDEAT)를 리턴하여 시스템이 더 이상 이 메시지를 처리하지 않도록 해야 한다.

이 메시지에 의해 윈도우가 활성화되면 WM_ACTIVATE(APP), WM_NCACTIVATE, WM_SETFOCUS 메시지가 연속적으로 전달된다. 이 메시지는 마우스 버튼 누름 동작에 대해 포커스를 어떻게 처리할 것인가를 결정하기 위해 전달되는 것이다. 포커스가 이동된 후에 발생하는 것이 아니므로 포커스 변화에 따른 처리는 이 메시지에서 하지 않는 것이 옳다.

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

WM_MOUSEMOVE : wParam : 키보드와 다른 마우스 버튼의 현재 상태를 나타내는 값이며 다음 플레그들의 조합값이다.

LOWORD(lParam), HIWORD(lParam) : 마우스의 현재 X, Y좌표이다. 이 좌표는 작업 영역의 원점을 기준으로 한 좌표이다. 대부분의 경우 좌표는 양수값이지만 캡처된 특수한 상황에서는 음수일 수도 있는데 이 경우 반드시 (int)형으로 다시 한번 더 캐스팅해 주어야 부호를 제대로 얻을 수 있다.

마우스가 움직일 때 이 메시지가 메시지 큐에 붙여진다. 마우스가 캡처되어 있으면 캡처한 윈도우로 이 메시지가 전달되며 그렇지 않을 경우 커서 아래쪽에 있는 윈도우가 이 메시지를 받는다. 마우스가 계속 작업 영역 위에서 움직이고 있으면 이 메시지는 반복적으로 계속 전달된다.

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

WM_MOVE : wParam : 사용되지 않는다.

LOWORD(lParam) : 윈도우의 X좌표.

HIWORD(lParam) : 윈도우의 Y 좌표. 이 좌표는 오버랩드, 팝업 윈도우의 경우 화면 좌표이며 차일드 윈도우는 부모 윈도우의 작업 영역을 기준으로 한 좌표이다.

윈도우의 위치가 변경될 때마다 이 메시지가 보내진다. 일반적으로 윈도우의 위치 변경에 따른 처리는 하지 않기 때문에 이 메시지는 잘 사용되지 않지만 위치에 따라 윈도우의 모양이나 동작에 차이가 있거나 특별한 처리가 필요하다면 이 메시지를 사용한다. 이 메시지는 윈도우의 위치가 완전히 옮겨진 후에 보내지므로 인수로 전달되는 좌표는 이동 후의 좌표이다.

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

WM_NCACTIVATE : wParam : 이 값이 TRUE이면 활성화된 것이고 FALSE이면 비활성화된 것이다.

비작업 영역이 활성화 또는 비활성화되어 변경되어야 할 필요가 있을 때 보내진다. 이 메시지는 보통 응용 프로그램이 처리하지 않으며 DefWindowProc으로 보내준다. 이 메시지의 디폴트 처리는 wParam에 따라 타이틀 바를 활성/비활성화하도록 되어 있다. 그래서 활성화된 타이틀 바는 파란색으로 그려지며 비활성화되면 회색으로 그려진다. 만약 타이틀 바의 활성 여부를 다르게 프로그래밍하고 싶다면 이 메시지를 처리하도록 한다.

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

WM_NCCREATE : wParam : 사용되지 않음

lParam : CreateWindow(Ex) 함수의 마지막 인수로 지정한 CREATESTRUCT 구조체의 포인터이며 윈도우 생성에 필요한 추가 정보이다.

CreateWindow(Ex) 함수에 의해 윈도우가 만들어질 때 보내진다. 비작업 영역이 만들어진다는 의미를 가지고 있으며 WM_CREATE보다 먼저 이 메시지가 보내진다. 윈도우가 만들어질 때 가장 먼저 보내지는 메시지이며 윈도우가 제일 먼저 받는 메시지이기도 하다. 그러나 이 메시지는 일반적으로 사용되지 않으며 초기화를 할 필요가 있을 때는 통상 WM_CREATE 메시지가 대신 사용된다.

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

WM_NCDESTROY : 없음
비작업 영역이 파괴될 때 보내진다. 윈도우와 그 차일드들이 먼저 파괴된 후에 비작업 영역이 파괴되므로 이 메시지는 윈도우가 가장 마지막으로 받는 메시지이다. WM_DESTROY보다 뒤에 발생되며 이 메시지를 받았을 때는 모든 차일드가 이미 파괴된 후이다. 반면 WM_DESTROY 메시지는 차일드가 아직 파괴되기 전이다. 종료 처리가 필요할 경우는 일반적으로 WM_DESTROY 메시지에 코드를 작성하므로 이 메시지는 실용적인 가치가 거의 없는 셈이며 처리하는 경우가 극히 드물다.

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

WM_NCHITTEST : wParam : 사용되지 않는다.

lParam : 화면상의 커서 좌표가 전달된다. 하위 워드에 x좌표, 상위 워드에 y좌표가 전달된다

마우스를 이동하거나 버튼을 누르거나 놓을 때마다 이 메시지가 발생한다. 이 메시지는 커서가 있는 위치가 윈도우의 어디쯤인지를 윈도우에게 질문을 하며 운영체제는 이 메시지의 리턴값에 따라 마우스를 처리한다. 예를 들어 이 메시지가 HTBOTTOM을 리턴하면 아래쪽 경계선에 커서가 있는 것으로 판단하며 이 상태에서 마우스를 드래그하면 윈도우의 수직 크기를 변경한다.

DefWindowProc은 커서가 있는 위치를 정확하게 계산하여 적절한 위치값을 리턴해 준다. 이 메시지를 처리하여 리턴값을 조작하면 운영체제의 커서 관리 동작을 변경할 수 있다. 예를 들어 작업 영역에 커서가 있을 때 HTCLIENT 대신 HTCAPTION을 리턴해 주면 작업 영역을 드래그하여 윈도우의 위치를 변경한다. 이런 조작을 하려면 현재 위치를 먼저 조사해야 하므로 DefWindowProc을 먼저 호출한 후 현재 커서 위치를 파악하고 그 결과로부터 리턴값을 조작해야 한다.

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

WM_NEXTDLGCTL : wParam : 다음 포커스를 옮길 컨트롤을 지정한다. lParam이 TRUE일 경우 포커스를 받을 컨트롤의 핸들값을 지정하며 lParam이 FALSE일 경우 이전, 또는 다음 컨트롤을 지정한다. wParam이 0이면 다음 컨트롤로 포커스가 이동되며 0이외의 값이면 이전 컨트롤로 포커스를 이동한다. 포커스를 받을 컨트롤은 WS_TABSTOP 스타일을 가지고 있어야 한다.

lParam : 포커스를 이동시킬 방법을 지정한다. 이 값이 TRUE이면 wParam은 포커스를 받을 윈도우 핸들을 가리키며 FALSE이면 wParam값에 따라 이전/다음 컨트롤로 포커스를 옮긴다.

대화상자 컨트롤의 포커스를 이동시킨다. 어떤 컨트롤이 다음 포커스를 받을 것인가는 wParam와 lParam값에 따라 달라진다. 만약 포커스를 받을 컨트롤의 핸들을 알고 있다면 lParam에 TRUE를 주고 wParam에 컨트롤의 핸들값을 전달하면 된다. 현재 포커스를 가진 컨트롤의 이전 또는 다음 컨트롤로 이동하려면 lParam에 FALSE를 주고 wParam으로 이전(0이외) 또는 다음(0)을 지정한다.

이 메시지는 SetFocus로 단순히 입력 포커스를 옮기는 것보다 훨씬 더 많은 일을 한다. 새로 포커스를 받은 컨트롤이 디폴트 버튼일 경우 경계선을 두껍게 바꾸고 에디트 컨트롤이면 텍스트를 선택해 준다. 다른 작업을 하던 중에 포커스를 변경하고자 한다면 SendMessage 함수로 이 메시지를 보내서는 안되며 반드시 PostMessage 함수로 메시지를 붙여야 한다.

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

WM_NULL : wParam, lParam : 사용되지 않는다.

아무런 동작도 하지 않는 빈 메시지이다. DefWindowProc은 이 메시지를 받았을 때 아무 동작도 하지 않으며 곧바로 리턴한다. 이 메시지가 필요한 경우는 다음 두 경우이다.

1.응용 프로그램이 메시지에 잘 반응하는지 테스트한다. SendMessageTimeOut 함수로 WM_NULL을 보내 보고 곧바로 리턴되는지 검사함으로써 응용 프로그램이 동작중인지 아닌지를 확인할 수 있다. WM_NULL은 아무 동작도 하지 않는 메시지이므로 보내는 즉시 리턴해야 정상적이다. 만약 이 메시지를 보냈는데 리턴되지 않는다면 해당 응용 프로그램은 아주 바쁜 상태이거나 죽은 것으로 판단할 수 있다.

2.WH_GETMESSAGE 훅을 설치한 프로그램이 메시지를 무효화할 때 수신된 메시지를 WM_NULL로 변경함으로써 해당 메시지를 무시하도록 한다. 예를 들어 마우스 이동 메시지를 무시하도록 하고 싶다면 WM_MOUSEMOVE를 받았을 때 이 메시지를 WM_NULL로 바꾸면 된다.

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

WM_PAINT : wParam : 그리기에 사용할 DC 핸들이 전달되며 이 값이 NULL일 경우 디폴트 DC에 그려야 한다. 이 인수로 전달되는 DC는 일부 공통 컨트롤에 의해 사용될 뿐이며 일반적인 목적으로는 사용하지 않는 것이 안전하다. 이 DC 핸들보다는 BeginPaint 함수가 리턴하는 DC 핸들을 사용하는 것이 좋다.

윈도우의 작업 영역중 일부가 무효화되어 있을 때 시스템이 이 메시지를 큐에 넣어준다. 다음과 같은 경우에 무효 영역이 설정되며 이때마다 WM_PAINT메시지가 전달된다.

① 윈도우가 처음 생성되었을 때
② 윈도우의 위치가 이동되었을 때
③ 윈도우의 크기가 변경되었을 때. 최대, 최소화되었을 때
④ 다른 윈도우에 가려져 있다가 드러날 때
⑤ 스크롤 될 때

또는 응용 프로그램 내부에서 InvalidateRect 함수 호출에 의해 강제로 무효 영역을 설정할 수 있다. 윈도우는 이 메시지를 받았을 때 작업 영역 전체 또는 무효화된 부분을 다시 그려야 한다. 운영체제는 윈도우의 작업영역을 복구해 주지 않는 대신에 무효화될 때 이 메시지를 보내 줌으로써 윈도우에게 다시 그려야 할 시점을 알려 주기만 한다. 따라서 윈도우는 자신의 작업영역에 출력할 수 있는 모든 자료를 완벽하게 저장해 두어야 한다.

WM_PAINT 메시지는 모든 메시지 중에 우선 순위가 가장 늦다. GetMessage 함수는 메시지 큐에 WM_PAINT가 있더라도 다른 메시지가 대기중이면 이 메시지를 먼저 처리한다. WM_PAINT 메시지는 큐에 대기중인 다른 메시지가 없을 때, 그리고 무효 영역이 존재할 때만 윈도우 프로시저로 보내진다. 또한 WM_PAINT 메시지는 한번에 하나만 메시지 큐에 들어갈 수 있다. 만약 무효영역이 생겼을 때 WM_PAINT 메시지가 이미 메시지 큐에 있으면 기존의 무효영역과 새 무효영역의 합집합으로 새로운 무효영역이 설정된다.

윈도우가 이 메시지를 처리하지 않으면 이 메시지는 DefWindowProc 함수가 처리한다. 이 함수는 무효영역을 모두 유효화화기만 하며 다시 그리기는 하지 않는다. 만약 비작업 영역도 그려져야 한다면 WM_NCPAINT 메시지를 전달하며 또한 배경을 지워야 한다면 WM_ERASEBKGND 메시지를 전달한다.

WM_PAINT 메시지에서 그리기를 할 때는 BeginPaint 함수와 EndPaint 함수를 사용해야 한다. 이 두 함수는 WM_PAINT내에서만 사용되며 다시 그려야 할 영역에 대한 정확한 좌표를 조사하며 무효영역을 유효화하고 캐럿을 숨기거나 배경을 지우는 등의 꼭 필요한 동작을 한다.

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

WM_PASTE : 인수 없음
에디트 컨트롤(또는 콤보 박스의 에디트)로 보내지는 메시지이며 현재 캐럿 위치에 클립보드의 텍스트를 붙이도록 한다. 클립보드가 CF_TEXT 포맷의 데이터를 가지고 있을 때만 붙여지고 그렇지 않으면 이 메시지는 아무 동작도 하지 않는다. 에디트 컨트롤이 없는 CBS_DROPDOWNLIST 콤보 박스에는 아무런 효과도 없다.

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

WM_QUERYENDSESSION : wParam : 사용되지 않는다.

lParam : 로그오프를 하는 것인지 시스템 셧다운을 하는 것인지를 나타낸다. 이 값이 0이면 시스템을 완전히 종료하는 것이며 ENDSESSION_LOGOFF 플래그가 설정되어 있으면 로그오프만 하는 것이다. 이 값은 비트 필드이므로 반드시 & 연산자로 플래그의 존재 유무를 점검해야 한다.

if (lParam & ENDSESSION_LOGOFF) {
// 로그오프 처리
} else {
// 셧다운 처리
}

사용자가 운영체제를 종료 또는 로그 오프하고자 할 때, 또는 응용 프로그램이 ExitWindows 함수로 운영체제를 종료하고자 할 때(단, EXW_FORCE 플래그가 없어야 한다)모든 응용 프로그램으로 이 메시지가 보내진다. 이 메시지는 실행중인 각 프로그램에게 "지금 셧다운할건데 해도 괜찮습니까?" 라고 질문하는 것과 같다. 응용 프로그램은 이 메시지를 받았을 때 자신의 내부 상태를 보고 운영체제의 종료를 허가하거나 거부할 수 있다. 만약 한 프로그램이라도 0을 리턴하면 운영체제의 종료는 취소된다.

미저장 문서가 있다거나 또는 네트웍 연결을 함부로 끊을 수 없는 상황이거나 아니면 절대로 종료해서는 안되는 중요한 서버 프로그램인 경우는 이 메시지를 받았을 때 셧다운을 거부하는 것이 가능하다. 만약 TRUE를 리턴하여 종료를 허가하게 되면 운영체제를 종료한다는 WM_ENDSESSION 메시지가 전달된다. DefWindowProc은 이 메시지를 받았을 때 TRUE를 리턴하도록 되어 있으므로 이 메시지를 특별히 처리하지 않으면 운영체제의 종료를 항상 허가하는 것이 된다.

운영체제는 셧다운전에 실행중인 모든 프로그램에게 이 메시지를 보내 셧다운 허가를 받은 후 모든 프로그램이 허가할 때만 개별 프로그램을 종료시킨 후 셧다운한다. 이때 개별 프로그램의 종료 시점은 운영체제 버전에 따라 다르다. 95/98 계열은 모든 프로그램에게 셧다운 허가를 받은 후 각 프로그램을 종료하지만 NT/2000 계열은 개별 윈도우에게 허가를 받은 후 각각의 프로그램을 순서대로 종료시킨다. 그래서 만약 A,B,C 세개의 프로그램이 있고 B가 허가를 거부할 때 95/98계열은 모든 프로그램이 종료되지 않지만 NT/2000 계열은 일단 A가 먼저 종료되고 B의 거부를 받았을 때 셧다운을 중지한다.

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

WM_QUIT : wParam : 종료 코드이며 PostQuitMessage의 인수가 전달된다. 이 종료 코드는 응용 프로그램을 실행시킨 프로세스가 받으나 보통 무시한다.

응용 프로그램을 종료하라는 신호이다. PostQuitMessage 함수 호출에 의해 발생하며 GetMessage 함수가 0을 리턴하도록 함으로써 메시지 루프를 종료시키는 역할을 한다. GetMessage 함수는 WM_QUIT 이외의 모든 메시지에 대해 0이 아닌 값을 리턴하므로 계속 루프를 돌지만 WM_QUIT에 대해서만 0을 리턴한다. 그래서 메시지 루프는 통상 다음과 같이 작성된다.

while(GetMessage(&Message,0,0,0)) {
TranslateMessage(&Message);
DispatchMessage(&Message);
}
return (int)Message.wParam;

GetMessage 함수가 0이 아닌 값을 리턴하는 동안 무한히 이 루프를 도는데 단 WM_QUIT가 전달될 때는 while문이 종료되며 따라서 WinMain이 종료된다. 메인 윈도우의 WM_DESTROY에서는 반드시 PostQuitMessage 함수를 호출하여 메시지 루프가 종료될 수 있도록 해 주어야 한다. 그렇지 않으면 메인 윈도우는 파괴되었으나 프로세스는 계속 실행중인 상태가 된다.

PeekMessage 함수는 WM_QUIT 메시지와 상관없이 메시지 큐에 메시지가 있는지만 리턴하므로 메시지 루프를 구성할 때 따로 WM_QUIT 메시지를 점검해야 한다.

for (;;) {
if (PeekMessage(&Message,NULL,0,0,PM_REMOVE)) {
if (Message.message==WM_QUIT)
break;
TranslateMessage(&Message);
DispatchMessage(&Message);
}
else {
// 백그라운드 작업
}
}

조사한 메시지가 WM_QUIT이면 메시지 루프를 탈출하는 별도의 코드가 필요하다.

WM_QUIT는 윈도우에게 전달되는 메시지가 아니므로 윈도우 프로시저는 이 메시지를 받을 수 없다. 윈도우 프로시저까지 전달되기 전에 메시지 루프에서 이 메시지를 차단하여 루프를 탈출하게 된다.

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

WM_RBUTTONDOWN : wParam : 키보드와 다른 마우스 버튼의 현재 상태를 나타내는 값이며 다음 플레그들의 조합값이다.

LOWORD(lParam), HIWORD(lParam) : 마우스의 현재 X, Y좌표이다. 이 좌표는 작업 영역의 원점을 기준으로 한 좌표이다.

작업 영역 내부에서 마우스 오른쪽 버튼을 누를 때 이 메시지가 메시지 큐에 붙여진다. 마우스가 캡처되어 있으면 캡처한 윈도우로 메시지가 전달되며 그렇지 않으면 마우스 커서 아래의 윈도우로 전달된다. 이 메시지를 받았을 때 컨텍스트 메뉴를 열 수도 있으나 이 메시지는 현재 커서 위치를 작업영역 좌표로 전달해 주므로 불편하다. 이 메시지보다는 커서 위치를 화면 좌표로 전달해 주는 WM_COMTEXTMENU 메시지를 사용하는 것이 더 유리하다.

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

WM_RBUTTONUP : wParam : 키보드와 다른 마우스 버튼의 현재 상태를 나타내는 값이며 다음 플레그들의 조합값이다.

LOWORD(lParam), HIWORD(lParam) : 마우스의 현재 X, Y좌표이다. 이 좌표는 작업 영역의 원점을 기준으로 한 좌표이다. 대부분의 경우 좌표는 양수값이지만 캡처된 특수한 상황에서는 음수일 수도 있는데 이 경우 반드시 (int)형으로 다시 한번 더 캐스팅해 주어야 부호를 제대로 얻을 수 있다.

마우스 오른쪽 버튼을 놓을 때 이 메시지가 큐에 붙여진다. WM_RBUTTONDOWN후에 연속적으로 이 메시지가 발생하는 것이 보통이나 마우스가 캡처되어 있지 않을 때 작업영역 밖에서 마우스 버튼을 놓으면 이 메시지가 전달되지 않을 수도 있다.

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

WM_SETCURSOR : wParam : 커서 위치의 윈도우 핸들이다. 차일드 위에 커서가 있을 수도 있으므로 이 메시지를 받은 윈도우 핸들과는 다르다.

LOWORD(lParam) : 히트 테스트 코드이며 커서가 윈도우의 어디쯤에 있는지 알려 준다. 이 값에 대해서는 WM_NCHITTEST 메시지를 참조하기 바란다.

HIWORD(lParam) : 이 메시지를 보낼 때의 마우스 메시지 ID, 메뉴가 열려있는 상태이면 0이다.

커서가 윈도우 영역에서 이동될 때마다 이 메시지가 보내지며 새 위치에서 커서를 어떤 모양으로 바꿀 것인가를 질문한다. 단, 커서가 캡처되어 있을 때는 이 메시지가 보내지지 않는다. DefWindowProc은 이 메시지를 받았을 때 직접 처리하기 전에 부모 윈도우에게 이 메시지를 먼저 보내 처리하도록 한다. 그래서 차일드의 커서 모양은 부모 윈도우가 우선적으로 변경할 수 있는 기회를 준다. 부모 윈도우가 이 윈도우를 처리한 후 TRUE를 리턴하면 더 이상의 커서 관련 처리를 하지 않는다. 그렇지 않을 경우 DefWindowProc은 디폴트 처리한다.

디폴트로 커서는 작업 영역에 있을 때 윈도우 클래스에 등록된 커서로 변경되며 경계선이나 타이틀 바 등의 비작업 영역에 있을 때는 크기조절 모양이나 화살표 모양의 커서로 변경된다. 이 방식대로 커서를 처리하려면 WM_SETCURSOR를 처리하지 않고 DefWindowProc으로 보내주기만 하면 된다. 만약 커서를 조건에 따라 다른 모양으로 바꾸고 싶다면 이 메시지를 받았을 때 좌표, 커서 위치의 컨트롤 등을 참고하여 적절히 커서를 변경하고 TRUE를 리턴하면 된다.

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

WM_SETFOCUS : wParam : 키보드 포커스를 잃은 윈도우의 핸들이 전달된다. 포커스를 잃은 윈도우가 없으면 NULL이다.

이 함수는 키보드 포커스가 이동될 때 발생한다. 포커스를 가진다는 말은 키보드 입력을 받을 수 있다는 뜻이며 한번에 오직 하나의 윈도우만 포커스를 가질 수 있다. 포커스는 윈도우의 활성화 상태 변경에 따라 이동되는데 새로 활성화된 윈도우나 그 차일드가 포커스를 가진다. 또는 SetFocus 함수에 의해 명시적으로 포커스 이동이 발생할 수도 있다.

이 메시지는 윈도우가 키보드 포커스를 얻은 후에 전달된다. 즉, 이 메시지를 받았을 때는 이미 키보드 포커스가 이동 완료된 후이다. 만약 포커스를 다른 윈도우에게 양보하고자 한다면 이 메시지를 받자 마자 SetFocus 함수를 호출하여 다른 윈도우(주로 차일드 중 하나)에게 포커스를 건네 주어야 한다. 캐럿 처리를 위해 이 메시지를 프로그래밍하는데 이 메시지를 받았을 때 캐럿을 보여주면 된다.

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

WM_SETFONT : wParam : 폰트의 핸들. NULL일 경우 시스템 폰트를 사용한다.

lParam : 폰트를 변경한 후 컨트롤을 다시 그릴 것인지 아닌지를 지정한다. 이 값이 TRUE이면 컨트롤은 자기 자신을 다시 그린다. WM_CREATE나 WM_INITDIALOG에서 폰트를 변경한다면 굳이 다시 그릴 필요가 없으므로 이 인수는 FALSE로 준다.

컨트롤의 폰트를 변경한다. 표준 컨트롤 및 공통 컨트롤은 기본적으로 시스템 폰트로 텍스트를 출력하며 대화상자에 있는 컨트롤의 경우 대화상자의 폰트와 같은 폰트를 사용한다. 실행중에 컨트롤의 글꼴을 변경하고자 한다면 폰트 오브젝트를 먼저 만든 후 이 메시지로 폰트의 핸들을 전달하면 된다.

컨트롤의 폰트를 변경하는 가장 좋은 때는 대화상자가 WM_INITDIALOG 메시지를 받았을 때, 또는 오버랩드 윈도우의 경우 WM_CREATE 메시지를 받았을 때이다. 즉, 컨트롤이 생성된 직후, 화면에 보이기 직전에 폰트를 바꾸어야 화면 떨림도 없고 다시 그릴 필요도 없어 가장 좋다. 물론 실행중에라도 폰트를 자유롭게 변경할 수 있지만 폰트 변경에 의해 컨트롤이 다시 그려져야 하므로 보기에 좋지 않다. 컨트롤의 텍스트 출력에 사용할 폰트는 컨트롤의 부모가 생성해야 하며 다 사용하고 난 후에 직접 파괴해 주어야 한다.

실행중에 컨트롤의 폰트를 바꾼다고 해서 컨트롤의 크기가 바뀌는 것은 아니다. 따라서 폰트를 변경할 필요가 있는 컨트롤은 미리 변경될 폰트의 크기에 맞는 충분한 크기로 생성할 필요가 있다. 그렇지 않으면 컨트롤의 텍스트가 컨트롤 바깥으로 삐져 나와 잘릴 수가 있다.

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

WM_SETTEXT : lParam : 설정하고자 하는 텍스트이며 널 종료 문자열이다.
윈도우의 텍스트를 변경한다. 윈도우의 종류에 따라 윈도우 텍스트 종류는 달라지는데 에디트는 편집중인 문자열, 버튼은 이름, 일반 윈도우는 타이틀 바의 캡션이 윈도우 텍스트가 된다. 콤보 박스로 이 메시지를 보낸 경우 콤보의 에디트 컨트롤 내용만 바뀔 뿐 리스트 박스의 선택 상태가 변경되는 것은 아니다. 콤보의 선택 항목을 변경하려면 CB_SELECTSTRING 메시지를 보내 주어야 한다.

같은 프로세스에 속한 윈도우는 이 메시지를 보내는 대신 SetWindowText 함수를 대신 사용할 수도 있다. 다른 프로세스에 속한 윈도우의 텍스트를 변경할 때는 WM_SETTEXT 메시지를 보내야 한다.

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

WM_SHOWWINDOW : wParam : TRUE이면 윈도우가 보여지는 것이고 FALSE이면 윈도우가 숨겨지는 것이다.

lParam : 이 메시지가 전달된 이유이다. 0이면 ShowWindow 함수에 의해 이 메시지가 전달된 것이며 아니면 다음 값 중 하나가 된다.

윈도우의 보임 상태가 변경되기 직전에 보내진다. 즉 숨겨져 있던 윈도우가 보이게 되었거나 보이던 윈도우가 숨겨지게 되었을 때 이 메시지가 보내진다. ShowWindow나 ShowOwnedPopups 함수에 의해 명시적으로 보임 상태가 변경될 때는 물론이고 다른 윈도우의 상태 변화에 의해 보임 상태가 변경될 때도 이 메시지가 보내진다. WS_VISIBLE 스타일을 가진 윈도우가 생성될 때는 새로 만들어진 윈도우가 보이게 되므로 이 메시지가 보내진다.

보임 상태의 변화에 따라 어떤 처리를 하고 싶다면 이 메시지에서 처리하면 된다. 예를 들어 윈도우가 주기적으로 애니메이션을 재생하고 있다면 보이지 않는 상태에서는 애니메이션을 잠시 중지할 수 있다.

오버랩드 윈도우가 WS_MAXIMIZE 또는 WS_MINIMIZE 스타일을 가질 때, 즉 생성되자 마자 최대, 최소화될 때는 이 메시지가 발생하지 않으면 ShowWindow 함수를 SW_SHOWNORMAL 플래그로 호출할 때도 이 메시지는 발생하지 않는다.

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

WM_SIZE : wParam : 윈도우의 크기가 변경된 이유와 유형값을 가진다. 다음 중 하나의 값이 전달된다.

lParam : 윈도우의 작업 영역 크기이다. LOWORD(lParam)이 윈도우의 폭이며 HIWORD(lParam)이 윈도우의 높이이다. 윈도우의 폭과 높이는 32비트값으로 전달되지만 이 메시지로 전달되는 폭과 높이는 16비트 크기를 가진다.

윈도우의 크기가 변경될 때 이 메시지가 보내진다. 사용자가 윈도우의 경계선을 드래그해서 직접 크기를 변경할 때는 물론이고 프로그램 내부에서 MoveWindow, SetWindowPos 등의 함수로 윈도우의 크기를 변경할 때도 이 메시지가 전달된다. 윈도우의 크기에 따라 작업영역의 출력 내용이 달라지거나 차일드 윈도우를 재배치해야 할 경우 이 메시지에서 처리한다

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

WM_SYSCHAR : wParam : 윈도우 메뉴 케의 문자 코드

lParam : 눌러진 키와 키보드 상태에 대한 여러 가지 정보를 가지는 비트 필드값이다.

WM_SYSKEYDOWN 메시지가 TranslateMessage 함수에 의해 문자 코드로 번역될 때 포커스를 가진 윈도우에게 전달된다.

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

WM_SYSCOMMAND : wParam : 시스템 명령의 종류가 전달되며 사용자가 시스템 메뉴에서 어떤 항목을 선택했는지를 알 수 있다. 이 값의 하위 4비트는 시스템이 내부적으로 사용하는 값이므로 명령의 종류를 알고 싶으면 wParam을 0xFFF0와 AND연산해야 한다. 연산한 결과는 다음값 중의 하나가 된다.

lParam : 마우스로 윈도우 메뉴를 선택한 경우 커서의 좌표가 전달된다. 하위 워드에는 수평 좌표, 상위 워드에는 수직 좌표가 전달되는데 이 좌표는 화면 기준 좌표이다. 액셀러레이터에 의해 명령이 선택되었으면 -1이 되며 니모닉에 의해 선택되었으면 0이 된다.

시스템 메뉴에 있는 메뉴 항목을 선택하면 WM_COMMAND 메시지 대신 이 메시지가 전달된다. 시스템 메뉴를 직접 선택하는 동작 외에도 타이틀 바에 있는 최대, 최소, 닫기 버튼 등의 명령들도 이 메시지를 발생시킨다. 시스템 메뉴에 있는 명령들은 윈도우를 관리하기 위한 기본적인 명령이므로 응용 프로그램은 이 메시지를 직접 처리하지 않고 보통 DefWindowProc으로 그냥 보내 준다.

DefWindowProc은 wParam값에 따라 시스템에 미리 정의되어 있는 동작을 수행한다. 예를 들어 SC_MINIMIZE 시스템 명령이 전달되었으면 윈도우를 최소화하고 SC_CLOSE 명령이 전달되었으면 윈도우를 닫는다. 응용 프로그램이 직접 이 시스템 명령을 프로그래밍 하고 싶다면 이 메시지를 처리하며 자신이 처리한 시스템 명령은 DefWindowProc으로 보내지 말아야 한다. 그외의 시스템 명령은 모두 DefWindowProc으로 보내 주어 디폴트 처리를 하도록 해야 한다.

시스템 메뉴에는 이동, 최소화, 최대화, 크기 조정, 닫기 등의 표준 윈도우 관리 명령들만 들어 있다. GetSystemMenu, AppendMenu 등의 메뉴 관련 명령을 사용하면 시스템 메뉴에도 응용 프로그램 고유의 메뉴 항목을 추가할 수 있다. 이렇게 만들어진 메뉴 항목을 선택할 때는 WM_COMMAND 대신 WM_SYSCOMMAND 메시지가 대신 전달되므로 반드시 이 메시지를 처리해야 한다. 이 경우 직접 추가한 메뉴 항목 외의 시스템 명령은 모두 DefWindowProc으로 전달해 주어야 한다.

응용 프로그램이 시스템 명령을 직접 실행할 필요가 있다면 wParam에 원하는 시스템 명령을 대입하고 DefWindowProc으로 WM_SYSCOMMAND를 보내 준다. 예를 들어 윈도우를 닫고 싶으면 SendMessage(hWnd, WM_SYSCOMMAND, SC_CLOSE, 0); 명령을 보내 주면 된다.

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

WM_SYSDEADCHAR : wParam : 데드키에 의해 발생한 문자 코드

lParam : 반복 회수, 스캔 코드, 확장키 등에 대한 정보고 비트필드로 전달된다. WM_KEYDOWN의 lParam과 동일하다.

Alt키와 데드키를 함께 누를 때 이 메시지가 발생한다. 데드키에 대한 자세한 사항에 대해서는 WM_DEADCHAR 메시지를 참조하기 바란다. 통상 이 메시지는 처리하지 않는다.

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

WM_SYSKEYDOWN : wParam : 눌러진 가상 키 코드

lParam : 키보드의 상태에 대한 여러 가지 정보가 비트별로 전달된다.

F10키를 누르거나 Alt키와 함꼐 다른 키를 같이 누르면 포커스를 가진 윈도우에게 이 메시지가 전달된다. 포커를 가진 윈도우가 없을 경우 활성화된 윈도우로 전달되는데 이때는 lParam의 29번 비트인 컨텍스트 코드가 0이 된다. 컨텍스트 코드가 0인 경우 이 메시지는 TranslateAccelerator 함수로 전달될 수 있는데 포커스를 가지지 않은 활성화된 윈도우도 액셀러레이터를 처리할 수 있도록 한다.

키보드의 반복 입력 기능으로 인해 WM_SYSKEYUP이 오기 전에 복수 개의 WM_SYSKEYDOWN 메시지가 발생할 수도 있다. 반복 기능에 의해 전달된 메시지인지 처음 눌러진 키인지를 구분하려면 lParam의 비트 30을 점검해 본다.

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

WM_SYSKEYUP : wParam : 놓이진 키의 가상 키 코드

lParam : 키보드의 상태에 대한 여러 가지 정보가 비트별로 전달된다.

Alt키와 함께 눌러진 키가 놓아질 때 포커스를 가진 윈도우에게 발생한다. 키보드 포커스를 가진 윈도우가 없을 경우는 활성화된 윈도우로 이 메시지가 전달되는데 이때 lParam의 29번 비트인 컨텍스트 코드가 0이 된다. 컨텍스트 코드가 0인 경우 이 메시지는 TranslateAccelerator 함수로 전달될 수 있는데 포커스를 가지지 않은 활성화된 윈도우도 액셀러레이터를 처리할 수 있도록 한다.

DefWindowProc은 Alt키나 F10키가 놓아질 때 탑 레벨 윈도우에게 WM_SYSCOMMAND 메시지를 보내며 이때 wParam은 SC_KEYMENU가 된다.

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

WM_TIMECHANGE : 없음. wParam, lParam 모두 사용되지 않음
시스템 시간이 변경되면 이 메시지가 보내진다. 만약 응용 프로그램이 시스템 시간을 사용하고 있다면 이 메시지를 받았을 때 시간을 갱신해야 한다. 예를 들어 상태란에 현재 시간을 보여주고 있다면 이 메시지를 받는 즉시 상태란을 갱신할 필요가 있다.

SetSystemTime, SetLocalTime 등의 함수로 시스템 시간을 변경하는 프로그램은 시간 변경 후 모든 탑레벨 윈도우에게 이 메시지를 보내 시간이 변경되었음을 알려야 한다. SendMessage(HWND_TOPMOST, WM_TIMECHANGE,0,0); 함수를 호출하면 된다.

2000/XP는 시스템 시간이 변경될 경우 운영체제가 시간 변경을 각 프로그램에게 알려 주므로 응용 프로그램이 이 메시지를 보낼 필요가 없다. 그러나 95/98/ME와 NT 4.0이전 버전에서는 이 메시지를 반드시 보내주어야 한다.

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

WM_TIMER : wParam : 타이머의 ID가 전달된다. 이 ID는 SetTimer의 두번째 인수로 지정한 값이다.

lParam : 콜백 함수가 있을 경우 콜백 함수의 번지가 전달된다

SetTimer 함수로 타이머를 설치했을 경우 지정한 시간 간격으로 이 메시지가 반복적으로 큐에 붙여진다. 주기적으로 어떤 작업을 반복해야 한다면 타이머를 설치하고 이 메시지에서 작업을 처리하도록 한다. 두 개 이상의 타이머가 설치되어 있을 경우 각각의 타이머는 정해진 시간 간격으로 이 메시지를 큐에 붙이며 WM_TIMER에서는 wParam으로 어떤 타이머에 의해 이 메시지가 발생했는지 조사한다.

타이머 콜백 함수를 지정했을 경우는 이 메시지를 처리할 필요가 없으며 시스템이 콜백 함수를 주기적으로 호출해 준다.

이 메시지는 다른 메시지들에 비해 우선순위가 낮게 설정되어 있기 때문에 먼저 처리해야 할 메시지가 있을 경우 곧바로 윈도우 프로시저로 보내지지 않을 수 있다. 따라서 정확한 시간에 이 메시지가 전달되지 않는 경우도 있으므로 정확도를 요하는 작업에는 이 메시지를 사용하지 않는 것이 좋다.

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

WM_UNDO : 없음

에디트 컨트롤에게 최후의 편집 동작을 취소하라는 명령을 보낸다. 즉 마지막으로 삽입한 문자열을 삭제하거나 마지막으로 삭제한 문자열을 다시 삽입하도록 한다. 에디트 컨트롤은 1회의 실행 취소만 지원하므로 바로 직전에 한 편집만 취소할 수 있다. 리치 에디트 컨트롤은 이 메시지를 지원하지 않는다.

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

WM_USER : wParam, lParam 모두 의미가 정해져 있지 않다. 응용 프로그램이 의미를 정해서 사용할 수 있다.

WM_USER는 한 윈도우 클래스를 위한 고유의 메시지를 정의하기 위한 상수값이며 이 범위 이후부터 윈도우 클래스의 사용자 정의 메시지를 만들 수 있다. 이 값은 0x400으로 정의되어 있으며 보통 WM_USER+n으로 사용자 정의 메시지를 정의한다. 이때 n은 1보다 큰 정수이며 사용자 정의 메시지간의 구분을 위해 사용된다. 여러 개의 사용자 정의 메시지가 필요하다면 WM_USER+1, WM_USER+2, WM_USER+3,... 식으로 계속 n을 증가시켜 가며 메시지를 정의할 수 있다. 윈도우즈는 WM_USER이후 0x8000까지 사용자 정의 메시지 영역으로 정의하고 있으므로 n은 최대 0x7c00까지 가능하다. WM_USER+n을 곧바로 사용할 수도 있으며 자주 사용할 경우 다음과 같이 매크로를 정의하여 별도의 메시지를 만들 수 있다.

#define WM_MYMESSAGE WM_USER+1

이렇게 매크로를 정의해 놓고 이후부터 WM_MYMESSAGE라는 명칭을 대신 사용하면 된다. 표준 컨트롤 중 일부는 자신만의 사용자 정의 메시지를 정의하여 사용하고 있다. 따라서 WM_USER+n은 가급적이면 한 윈도우 클래스내에서만 정의하여 사용해야 하며 응용 프로그램간의 통신에는 사용하지 않는 것이 바람직하다. 표준 컨트롤을 서브클래싱했을 경우 함부로 WM_USER+n을 사용하면 표준 컨트롤 고유의 메시지와 충돌이 발생할 수 있다

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

WM_VSCROLL : LOWORD(wParam) : 사용자의 스크롤 요구를 전달하며 스크롤 바의 눌러진 위치값이 전달된다. 다음 값 중 하나가 전달된다.

HIWORD(wParam) : SB_THUMBPOSITION, SB_THUMBTRACK 메시지의 경우 스크롤 바의 현재 위치가 전달된다. 다른 메시지에서는 사용되지 않는다. 이 값은 16비트이나 스크롤 바는 32비트 범위를 스크롤 할 수 있는데 32비트의 스크롤 값을 얻고 싶을 경우 이 인수를 직접 사용하는 대신 GetScrollInfo 함수로 값을 직접 조사해 사용해야 한다. 만약 스크롤 범위가 음수를 가질 수 있다면 이 값을 int형으로 캐스팅한 후 읽어야 한다.

lParam : 스크롤 바 컨트롤로부터 이 메시지가 전달되었을 경우 스크롤 바 컨트롤의 윈도우 핸들이 전달된다표준 스크롤 바인 경우 이 인수는 NULL이다.

윈도우의 오른쪽에 부착되는 표준 수직 스크롤 바, 또는 SBS_VERT 스타일을 가지는 수직 스크롤 바 컨트롤이 부모 윈도우로 스크롤 메시지를 보낼 때 이 메시지가 전달된다. 다른 컨트롤은 자신의 변화를 WM_COMMAND로 전달하지만 스크롤 바는 WM_COMMAND 대신 WM_HSCROLL, WM_VSCROLL 메시지를 보낸다. 또한 이 메시지는 트랙 바 컨트롤에 의해 사용되기도 한다.

윈도우는 이 메시지를 받았을 때 스크롤 바의 위치를 갱신해 주어야 하며 화면 스크롤 처리(또는 내부적인 값의 변경)를 해 주어야 한다. 사용자가 썸을 직접 드래그할 때 SB_THUMBTRACK 메시지가 발생하며 드래그를 종료할 때 SB_THUMBPOSITION 메시지가 발생하는데 이 두 메시지 중 하나만 처리해 주면 되며 둘 다 처리할 필요는 없다. 이때 HIWORD(wParam)으로 전달되는 값은 16비트 범위이므로 65535이상의 스크롤 위치값은 전달되지 않으므로 GetScrollInfo 함수로 직접 위치를 구해야 한다.

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

WM_WINIINICHANGE : wParam : 사용되지 않는다.

lParam : 변경된 시스템 설정의 이름 문자열이며 레지스트리의 키 이름이나 Win.ini 의 섹션 이름이 전달된다. 그러나 레지스트리 키일 경우 전체 경로가 아니라 단순히 키의 이름만 전달되며 설정을 변경하는 프로그램이 이 인수에 정확하게 값을 대입해 주지 않기 때문에 이 인수로부터 어떤 설정이 변경되었는지 정확하게 알아내기는 어렵다. 따라서 이 메시지를 받았을 때 응용 프로그램이 참조하고 있는 모든 설정값을 다시 조사해야 한다.

이 메시지는 하위 호환성을 위해서만 제공되므로 Win32 응용 프로그램은 이 메시지 대신 WM_SETTINGCHANGE 메시지를 대신 사용해야 한다. 이 두 메시지는 이름만 다른 같은 메시지이다.

#if(WINVER >= 0x0400)
#define WM_SETTINGCHANGE WM_WININICHANGE
#endif /* WINVER >= 0x0400 */

Win.ini 파일은 운영체제의 중요한 설정 상태를 저장하는 정보 파일이다. 이 파일이 변경되었다는 것은 곧 시스템 설정에 중요한 변화가 있었다는 뜻이며 이때 WM_WININICHANGE 메시지가 모든 탑 레벨 윈도우에게 보내진다. Win.ini는 16비트 윈도우즈에서는 파일 형태로 존재하며 95이후부터 다음 레지스트리 키애 연결되어 있다.

HKEY_LOCAL_MACHINE\\Software\\Microsoft\\ Windows NT\\CurrentVersion\\IniFileMapping

Win.ini를 직접 편집하는 것뿐만 아니라 이 레지스트리 키를 편집하는 것도 동일하게 시스템 설정을 변경시킨다. 만약 Win.ini를 편집하여 시스템 설정을 변경하였다면 SendMessage 함수의 첫번째 인수로 HWND_BROADCAST를 주어 모든 탑 레벨 윈도우에게 이 메시지를 전달해야 한다.