- 윈도우가 종료 하기 바로 전에 해야 할일은 주로 WM_CLOSE 메시지 에서 처리한다.!!
'Study/API'에 해당되는 글 37건
- 2007/10/10 9.17(월) 이론-4( 종료과정이해 )
- 2007/10/10 9.17(월) 이론-3( 마우스 캡쳐, 좌표 )
- 2007/10/10 9.17(월) 이론-2(WM_NCHITTEST)
- 2007/10/10 9.17(월) 이론-1
- 2007/10/10 9.17(월) 실습
- 2007/10/10 9.13(목) 이론-3
- 2007/10/10 9.13(목) 이론-2
- 2007/10/10 9.13(목) 이론-1
- 2007/10/10 9.13(목) 실습-3(UNICODE)
- 2007/10/10 9.13(목) 실습-2
- 윈도우가 종료 하기 바로 전에 해야 할일은 주로 WM_CLOSE 메시지 에서 처리한다.!!
- 일반적으로 마우스 메시지는, 메시지가 발생할 당시 커서의 아래 있는 윈도우에게 전달된다. 하지만 SetCapture()함수를 사용하므로서 이런 행동을 변경 할 수 있다.
- 특정 윈도우가 SetCapture() 함수를 사용해서 마우스를 캡쳐할 경우, 모든 마우스 메시지는 마우스를 캡쳐한 윈도우에게로 전달된다.
2. 마우스 캡쳐하기
- 마우스 캡쳐는 아래의 3가지 경우에 해지된다.
1) RealeaseCapture() 를 호출 한 경우
2) 다른 윈도우가 마우스를 캡쳐한 경우
3) 사용자가 다른 스레드가 만든 윈도우를 클릭 한 경우
- 마우스 캡쳐가 해지될 경우, 캡쳐를 잃은 윈도우에 WM_CAPTURECHANGED 메시지가 전달된다.
이 때 lParam에는 새롭게 마우스를 캡쳐 한 윈도우의 핸들이 들어 있다.
3. 마우스가 캡쳐 되어 있을 경우 WM_NCHITTEST, WM_SETCURSOR 메시지는 발생되지 않는다.
4. 예제 코드( 다른 윈도우 캡쳐하기 )
more..
- 어디를 기준으로 보냐에 따라서 스크린 좌표계, 윈도우 좌표계, 클라이언트 좌표계로 나눌수 있다.
- ClinetToScreen | ScreenToClient ( 두개 외에 나머지 함수 만들기 )
void WindowToScreen(HWND hwnd, POINT* Point)
{
RECT Rect;
GetWindowRect( hwnd, &Rect );
Point->x = Point->x + Rect.left;
Point->y = Point->y + Rect.top;
}
void ScreenToWindow(HWND hwnd, POINT* Point)
{
RECT Rect;
GetWindowRect( hwnd, &Rect );
Point->x = Point->x - Rect.left;
Point->y = Point->y - Rect.top;
}
void WindowToClient(HWND hwnd, POINT* Point)
{
WindowToScreen( hwnd, Point );
ScreenToClient( hwnd, Point );
}
void ClientToWindow(HWND hwnd, POINT* Point)
{
ClientToScreen( hwnd, Point );
ScreenToWindow( hwnd, Point );
}
- Cursor가 움직이거나 마우스 버튼을 누르거나 놓을 때, System은 커서 아래 있는 윈도우(마우스를 캡쳐한 경우 캡쳐한 윈도우)에게 WM_NCHITTEST 메시지를 보낸다.(sent)
- 이때 대부분 윈도우 프로시저는 이 메세지를 직접 처리 하지 않고 DefWindowProc으로 보내는데 DefWindowProc는 커서의 좌표를 조사해서 커서가 현재 윈도우의 어느 부분에 커서가 있는지 나타내는
Hit Test Code 를 리턴한다.
- WM_NCHITTEST의 결과로 얻어진 HitTestCode가 다음 메시지를 결정하는데 사용된다.
- 예제 코드( Control키를 누른 상태에서 클라이언트 영역에서 마우스의 왼쪽 버튼을 누르면 윈도우를 이동 )
// WM_NCHITTEST 를 처리하는 방법
case WM_NCHITTEST:
{
// 1. 먼저 DefWindowProc()으로 전달해서 hit test code를 얻는다.
int code = DefWindowProc( hwnd, msg, wParam, lParam );
// 2. code를 조작한다.
if( code == HTCLIENT && GetKeyState( VK_CONTROL ) < 0 )
code = HTCAPTION;
// 3. code를 리턴한다. - 다음 메세지는 이 리턴값에 의해 결정된다.
return code;
}
- CreateWindowEx() 는 WM_CREATE메세지를 발생시킨다. 이때 이 메세지를 처리를 먼저하고 HANDLE값을 반환한다.
- DestroyWindow()는 WM_DESTROY를 발생하며 윈도우를 파괴시킨다.
- 비Q메시지는 API함수 호출시 주로 발생하며 메세지 처리 함수에 직접 전달한다.
- 입력 메시지(마우스 메세지, 키보드 메세지)를 제외한 대부분의 메시지가 non-queued Message 이다.
- SendInput을 통하여 마우스, 키보드 드라이버에서 SHIQ로 가상으로 키를 넣을 수 있다.(화상키보드)
- 키보드 : 활성화된 윈도우에 메시지를 입력한다. Raw가 담당..
- 스레드당 한개 씩 메세지 Q를 갖는다. GetMessage를 통해서 메시지를 끄집어 낸다.
- DispatchMessage는 WndProc에 메시지를 전달해주는 역할을 한다.
- WM_PAINT
=> GetMessage does not remove WM_PAINT messages from the queue. The messages remain in the queue until processed. 참고(페졸드책:108page)
또한 프로그램은 ValidateRect()를 호출함으로써 클라이언트 영역의 임의 직사각형 영역을 유효화할 수도 있다. 만약 이 호출의 결과로 무효 영역 전체가 유효화된다면, 메시지 큐에 현재 저장되어 있는 WM_PAINT 메시지는 제거된다.
- WM_KEYDOWN, WM_CHAR 메시지 ( 제프리 : 990p 메시지Q 꺼내오기)
2. POST 메시지 큐에 메시지가 있으면 인자로 전달된 MSG 구조체에 해당 메시지 정보를 복사한 후 리턴한다.
3. QS_QUIT 플래그가 설정되어 있으면 WM_QUIT 메시지를 리턴하고, QS_QUIT 플래그를 제거한다.
4. 하드웨어 입력 큐에 메시지(WM_KEYDOWN, WM_LBUTTONDOWN, …)가 있다면 MSG 구조체에 해당 메시지 정보를 복사한 후 리턴한다.
5. QS_PAINT 플래그가 설정되어 있으면 WM_PAINT 메시지를 리턴한다.
6. QS_TIMER 플래그가 설정되어 있으면 WM_TIMER 를 리턴한다.
WM_KEYDOWN, WM_CHAR, WM_KEYUP 의 처리 순서도 위의 알고리즘을 통하면 쉽게 이해할 수 있다. 기본적으로 사용자가 키보드를 눌렀다 떼게 되면 WM_KEYDOWN, WM_KEYUP 이 하드웨어 입력 메시지 큐에 추가된다.
WM_KEYDOWN 이 TranslateMessage 를 통하면 적절한 형태의 WM_CHAR 메시지가 PostMesage 로 큐에 추가된다. 물론 이 과정에서 WM_CHAR 가 WM_KEYUP 보다 늦게 추가되었지만 위의 알고리즘의 우선순위 판정에 의해서 다음 번 처리되는 메시지는 WM_CHAR 가 되는 것이다.
- SendMessage : 동기 메시지 전달(Non-Queue) GetMessage에서 리턴하지 않는다??
- PostMessage : 비동기 메시지 전달( 리턴값을 받을수 없다. ) 메시지를 보낸후에 대기하지 않는다.
- 0x000~0x03FF : 미리 정의 메시지
- 0x0400~0x7FFF : Control Message WM_USER+a
- 0x8000~0xBFFF : Application Message WM_APP+a
- 0xC000~0xFFFF : Register Window Message()
- 0x10000~ : Reserved 보호되는 영역
- WPARAM => UINT : 16비트 시절 2바이트로 word였다. 지금은 4바이트
- LPARAM => ULONG
1. Sent messages
2. Posted messages
3. Input (hardware) messages and system internal events
4. Sent messages (again)
- WM_PAINT는 중복되었을때 무효영역의 합해진 크기를 지니게 된다. PAINTSTRUCT에 저장 넘어감.
1. SendMessage( 우선적으로 처리하게 한다.)
2. SendMesageNotify( 리턴을 기달리지 않는다. 그러면서 우선적으로 처리하게 된다. )
// 계산기의 Hwnd를 얻어 일반용으로 만든다.
{
HWND hwnd = FindWindow( 0, "계산기" );
if( hwnd == 0 )
{
printf( "계산기를먼저 실행\n");
return 0;
}
SendMessage( hwnd, WM_COMMAND, 305, 0 );
}
// 계산기의 Hwnd를 얻어 윈도우를 닫는다.
int main()
{
HWND hwnd = FindWindow( 0, "계산기" );
if ( hwnd == 0 )
{
printf( " B를 먼저 실행.\n" );
return 0;
}
//---------------------------
int result = 0;
result = SendMessage( hwnd, WM_CLOSE, 10, 20 );
printf( "결과 : %d\n", result );
}
// 2. WM_CLOSE 와 WM_NCHITTEST
{
switch( msg )
{
case WM_CLOSE:
{
UINT ret = MessageBox( hwnd, "정말 종료?", "확인", MB_YESNO );
if( ret == IDYES )
{
// 죽인다.. 또는 DefWindowProc()로 보내도 된다.
DestroyWindow( hwnd );
}
}
return 0;
// WM_NCHITTEST 를 처리하는 방법
case WM_NCHITTEST:
{
// 1. 먼저 DefWindowProc()으로 전달해서 hit test code를 얻는다.
int code = DefWindowProc( hwnd, msg, wParam, lParam );
// 2. code를 조작한다. Ctrl 누르고 끌면 창이 이동.
if( code == HTCLIENT && GetKeyState( VK_CONTROL ) < 0 )
code = HTCAPTION;
// 3. code를 리턴한다. - 다음 메세지는 이 리턴값에 의해 결정된다.
return code;
}
case WM_DESTROY:
PostQuitMessage(0);
return 0;
}
return DefWindowProc( hwnd, msg, wParam, lParam);
}
- CreateWindow : OS가 윈도우를 관리할 때 가지고 있는 구조체를 가지고 윈도우를 표현한다.
- ShowWindow는 이 구조체의 위치를 Handle로 받아 표시한다.
- Handle Table
- 이 구조체에 접근할 수 있는 함수는
- SetWindowLong, SetWindowLongPtr( 32 or 64bit 호환 ), GetWindowLong, GetWindowLongPtr
- 윈도우는 눈에 보이는 구조체 일뿐 이라고 할 수 있다. ( WND32 구조체 )
- 등록된 윈도우 클래스로 부터 여러개의 윈도우를 만들수 있는데 윈도우클래스 관리구조체에 여분의 메모리를 추가 하여 사용자가 사용할 수 있다.
- Console App : 콘솔창을 생성한다. main, 콘솔(/SUBSYSTEM:CONSOLE)
- GUI App : 윈도우창을 생성. WinMain,Windows (/SUBSYSTEM:WINDOWS)
- Net App : CLR을 동작한다.
- Linker 옵션의 Subsytem차이 일 뿐이다.
- #pragma comment( linker, "[옵션]" )
- WhatExe ( EXE의 종류를 파악한다. )
more..
P
Pointer를 나타낸다. ( ex : PPOINT -> POINT* )
LP
32비트 Code에서는 P와 완전히 같다. WIN32에서 PSTR과 LPSTR은 같은 코드.
T,_t
T는 DBCS와 UNICODE Type을 동시에 지원하기 위한 매크로이다.(TCHAR->char,wchar_t)
W
UNICODE Type이다.
C
const를 의미한다.
STR
문자열을 나타낸다.( ex : PSTR->char*)
H
H로 시작하는 대부분의 Type은 핸들형 Type을 나타낸다.( ex : HWND, HPEN, HBRUSH )
- 윈도우 구조체의 크기를 얻는 이유( Window Version이 많아서-_-.. )
- 구조체의 크기는 조건에 따라 달라지므로 구조체에서 크기를 저장하는 변수를 가지고 있다.
- OS는 HANDLE 번호를 만들어서 리턴해준다.
- HWND : 윈도우 번호
- HPEN : 펜 번호
- HBRUSH : 브러시 번호
4. 윈도우 만들기 GUI 윈도우 생성!!
- 모든 윈도우는 윈도우 클래스로 부터 만들어진다.
- 배경색, 아이콘, 이름, 윈도우틀(클래스).. 이 필요하다.
- 윈도우 클래스를 시스템에 등록한다.
- 등록된 클래스로 윈도우를 생성한다.
- 윈도우를 보여준다.
- 미리 가져다 쓸수 있는 등록된 클래스가 40~50개가 있다.(button, edit...)
- OS는 윈도우클래스모음 List를 가지고 이를 관리한다. 65536개를 관리 가능하다.
- API함수의 대부분은 핸들만 알고 있다면 윈도우를 조작가능 하다. ex) MoveWindow, Setmenu...
- 핸들을 얻을 수 있는 함수들. ex) FindWindow, WindowFromPoint
- 예제 코드 ( 계산기 자식 윈도우 )
more..
- 예제코드 ( 계산기 자식윈도우 숨기기 )
more..
-
UNICODE
- 초기 한 글자를 1Byte(256) 라서 문자를 표현하는데에 한계가 있었다.
- So, 한 글자를 2Byte(65536) 으로 문자를 표현 했지만 메모리를 너무 잡아먹는다.
- So, 영어권 : 1Byte, 다른나라 : 2Byte 로 정해 해결하였다.
- UNICODE : 모든 글자에 고유한 번호를 지정하였다.
-
매크로기반 코드가 나오게 되는 이유 : (tchar.h에 정의된 것)
- Strlen은 null 하나만 있어도 길이를 구해주기 때문에 제대로 문자열의 길이를 구하지 못한다. null 두개 일때의 길이를 구하는 함수는 wcslen이 있다.
- WBCS는 네트워크에 용량이 많아지는 단점이 있다.
- DBCS 는 모든 나라 말을 표현 하지 못한다. 영어 + 그 지역의 언어만 표현한다.
- 이를 해결하기 위해 UTF=> 한글+영어+일본어.. 호환을 해주게 된다.
- UTF는 각 언어에 1byte,2byte,3byte,4byte 를 할당하여 표현한다.
-
정적 라이브러리
- 잘 만들어 놓은 함수 -> obj -> lib 으로 만들기 위해선 링크만 해주면 된다.
- static Library(정적 라이브러리)는 오브젝트(obj)파일의 집합이라 할 수 있다.
- lib를 만들때의 주의 점은 name mangling으로 호출규약이 c와cpp가 다르므로 extern "C"로 만들 함수를 감싸주는 것이 필요하다.
- lib의 특징은 exe의 기계어 코드가 포함되어 실행파일로 만들어지므로 exe만 배포하여 프로그램을 실행 할 수 있다는데에 있다.
-
단점은 lib가 중복되어 배포될 수 있다는 점이다.(메모리 낭비) 또한, Update 할때 마다 프로그램을 다시 Build 해줘야 하는 단점이 있다.
-
DLL(PE포맷)
- 배포시 lib(type), dll, .h 를 같이 동봉해야 한다.
- .edata : 정보를 누출해야 다른 실행파일에서 접근 할수 있으므로 import 해줘야 한다.
- edata는 제공하는 함수에 대한 정보들이 있다. export, import를 구현해야 한다.
- lib => object lib은 함수의 기계어 코드가 포함되어 있어서 파일의 크기가 크지만,
-
lib => type lib은 링크 정보만을 가지고 있다.
-
라이브러리를 추가하는 2가지 방법
- 소스 : pragma comment( lib, "라이브러리" )
- 설정: 프로젝트설정->Linker->Input->Additional depends(추가 종속성)
5. 다른 윈도우의 HWND에 DestroyWindow( hwnd ); 가 먹히지 않는 이유.
MSDN 에서는 이문제를 한줄로 표현 해놓았는데
"A thread cannot use DestroyWindow to destroy a window created by a different thread"
"스레드간의 WM_DESTROY 메세지 보내는 것을 허용하지 않는다." 란다.-_-..
GetLastError 로 확인 해보면 ERROR_ACCESS_DENIED( 접근 금지 ) 에러코드가 생성된다.
이 문장을 본 한 블로거는 이런 표현을 썻다. 왓 더~ 헬~
"WHAT THE...!?! I have words for that that are inappropriate for this blog. I need a good chunk of lye for cleaning of the mouth."
출처 : http://cubicspot.blogspot.com/2005_03_01_archive.html
아무튼 위대한 MS의 Windows 개발자가 막아놨다. -_-..
typedef basic_string<char> string;
typedef basic_string<wchar_t> wstring; int _tmain()
{
string s;
basic_string<char> s1 = "hello";
basic_string<wchar_t> s2 = L"ABCD";
}
// 2. MBCS- > WBCS
int _tmain()
{
char s[] = "ABCD가나다라";
// 먼저 s를 wide character로 변경할 때 필요한 버퍼의 크기를 계산한다.
size_t sz = MultiByteToWideChar( CP_ACP, 0, s, strlen(s), 0, 0 );
//이제 버퍼를 할당해서 사용한다.
wchar_t* s2 = new wchar_t[sz];
// DBCS(MBCS) -> wideChar 로 변환
MultiByteToWideChar( CP_ACP, // 문자열 종류( CP_ACP : ANSI 표준 문자열 )
0,
s,
strlen(s),
s2, 10 );
setlocale( LC_ALL, "Korean" );
wprintf( s2 ); // wide char를 출력하는 printf, C++의 wcout
cout << endl;
delete[] s2;
}
// 3.UNICODE
int _tmain()
{
TCHAR s[] = _T("ABCD가나다라");
cout << sizeof(s) << endl;
cout << _tcslen(s) << endl;
}
// 4. UNICODE 매크로
// 매크로
#define UNICODE
#ifdef UNICODE
typedef wchar_t TCHAR;
#define _T(x) L##x // ## : Token paste 두 Token을 붙인다.
#define TEXT(x) _T(x)
#define _TEXT(x) _T(x)
#define _tcslen wcslen
#define _tmain wmain
#else
typedef char TCHAR;
#define _T(x) x
#define _tcslen strlen
#define _tmain main
#endif
int _tmain(int argc, TCHAR* argv)
{
TCHAR s[] = "ABCD가나다라";
cout << sizeof( s ) << endl;
cout << _tcslen(s) << endl;
}
// 4. WBCS( 2바이트로 인식 )
int main()
{
wchar_t s[] = L"ABCD가나다라";
cout << sizeof(s) << endl; // 18 (null 문자도 2byte )
cout << strlen((char*)s) << endl;
cout << wcslen(s) << endl;
}
// 5. DBCS
int main()
{
char s[] = "ABCD가나다라";
cout << sizeof(s) << endl; // ?
char* p = s;
while(*p)
{
cout << p << endl;
// p = p + 1; // 단순 포인터 연산을 사용한 경우
p = CharNext(p); // DBCS 관련함수를 통해서 다음 글자 얻기
}
return 0;
}
int main()
{
HWND h = FindWindow( 0, "계산기" ); // 열려 있는 메뉴의 핸들을 얻는다.
PostMessage( h, WM_QUIT, 0, 0 );
//DestroyWindow( h ); // 안된다.이유는 이론편에..
}
int main()
{
while( 1 )
{
Sleep(3000);
HWND h = FindWindow( "#32768", 0 ); // 열려 있는 메뉴의 핸들을 얻는다.
ShowWindow( h, SW_HIDE );
}
}
int main()
{
//HWND hwnd = FindWindow(
// 0, // 윈도우 클래스 이름
// "계산기" ); // 캡션바 내용
// progman 은 바탕화면 이다.
HWND hwnd = FindWindow( "Shell_TrayWnd", 0 ); // taskbar의 핸들을 얻음.
if ( hwnd == 0 )
{
cout << "계산기를 먼저 실행하세요." << endl;
return 0;
}
// -----------------------------
HRGN h = CreateEllipticRgn( 0, 0, 300, 300 );
SetWindowRgn( hwnd, h, TRUE );
//SetMenu( hwnd, 0 );
//if( IsWindowVisible( hwnd ) )
// ShowWindow( hwnd, SW_HIDE );
//else
// ShowWindow( hwnd, SW_SHOW );
}
// 2. DLL 만들어보기
// Dll 소스
//a.c
#include <stdio.h> // 이 안에 wchar_t 가 typedef되어 있다.
#define DLLSOURCE
#include "a.h" // DLL 헤더
void PutStringA( const char* s )
{
printf(s);
}
void PutStringW( const wchar_t* s )
{
wprintf(s);
}
//a.h
// DLL 내의 모든 함수의 선언을 제공한다.
// 이헤더는 DLL의 제작자가 사용할 때는 각 함수가 export 되도록 한다.
// DLL 사용자가 사용할 때는 import 되도록 한다.
// 결국 MFC에서 등장하는 AFX_EXT_CLASS의 개념도 아래 DLLAPI의 개념과 완전 동일하다.
#ifdef DLLSOURCE
#define DLLAPI __declspec(dllexport)
#else
#define DLLAPI __declspec(dllimport)
#endif
#ifdef __cplusplus
extern "C"{
#endif
DLLAPI void PutStringA( const char* s );
DLLAPI void PutStringW( const wchar_t* s );
#ifdef __cplusplus
}
#endif
// UNICODE 매크로에따라서함수를결정할수있는매크로를제공한다.
#ifdef UNICODE
#define PutString PutStringW
#else
#define PutString PutStringA
#endif
// 1. C/C++ 을모두지원하기위해__cplusplus 사용
// 2. export/import 를위한C++ 지시어를알아야한다.
////////////////////////////////////////////////////////////////////////////////// DLL을 사용하는 방법
#include "a.h" // 1. 헤더 include
#pragma comment(lib, "MyDll.lib") // 2. lib를 링커에게 알려준다.
int main()
{
PutStringA("hello");
PutStringW(L"World\n");
}