πŸ“˜ Windows - μ°½ (1)

파인·2022λ…„ 2μ›” 1일
0

Windows Programming

λͺ©λ‘ 보기
2/3
post-thumbnail

https://docs.microsoft.com/ko-kr/windows/win32/learnwin32/your-first-windows-program

μœ„ λ‚΄μš©μ„ μ°Έκ³ ν•˜μ—¬ μ •λ¦¬ν•œ λ‚΄μš©μž…λ‹ˆλ‹€.




λͺ¨λ“ˆ1. 첫 번째 Windows ν”„λ‘œκ·Έλž¨

1) μ°½ λ§Œλ“€κΈ°
2) μ°½ 메세지
3) μ°½ ν”„λ‘œμ‹œμ € μž‘μ„±




μ•„λž˜ μ½”λ“œλ“€λ‘œ 창을 λ§Œλ“€κΈ° μœ„ν•΄ Visual Studioλ₯Ό μ΄μš©ν•˜μ˜€λ‹€. Visual Studioμ—μ„œ Windows λ°μŠ€ν¬ν†± μ–΄ν”Œλ¦¬μΌ€μ΄μ…˜μœΌλ‘œ ν”„λ‘œμ νŠΈλ₯Ό μƒμ„±ν•˜λ©΄ λœλ‹€.

πŸ“ Creating a Window

πŸ“Œ Window Classes

μ°½ 클래슀(Window Class)λŠ” μ—¬λŸ¬ μ°½μ—μ„œ κ³΅ν†΅μ μœΌλ‘œ μ‚¬μš©ν•  수 μžˆλŠ” λ™μž‘μ˜ 집합을 λ§ν•œλ‹€. 예λ₯Ό λ“€μ–΄ λ²„νŠΌ κ·Έλ£Ήμ—μ„œ 각 λ²„νŠΌμ€ λ²„νŠΌμ„ 클릭할 λ•Œ μœ μ‚¬ν•œ λ™μž‘μ„ ν¬ν•¨ν•œλ‹€. λ¬Όλ‘  λ²„νŠΌμ΄ μ™„μ „νžˆ λ™μΌν•˜μ§€λŠ” μ•Šκ³ , 각 창에 λŒ€ν•΄ κ³ μœ ν•œ 데이터λ₯Ό μΈμŠ€ν„΄μŠ€ 데이터(instance data)라고 ν•œλ‹€.

μ°½ ν΄λž˜μŠ€λŠ” C++의 "class"κ°€ μ•„λ‹˜μ„ μ΄ν•΄ν•˜λŠ” 것이 μ€‘μš”ν•˜λ‹€.
μ°½ ν΄λž˜μŠ€λŠ” 운영 μ²΄μ œμ—μ„œ λ‚΄λΆ€μ μœΌλ‘œ μ‚¬μš©ν•˜λŠ” 데이터 ꡬ쑰둜 λŸ°νƒ€μž„μ— μ‹œμŠ€ν…œμ— λ“±λ‘λœλ‹€. μƒˆ μ°½ 클래슀둜 등둝 ν•˜λ €λ©΄ λ¨Όμ € ꡬ쑰체λ₯Ό μž…λ ₯ν•˜λ©΄λœλ‹€.

const wchar_t CLASS_NAME[] = L"Sample Window Class";

WNDCLASS wc = { };

wc.lpfnWndProc = WindowProc;
wc.hInstance = hInstance;
wc.lpszClassName = CLASS_NAME;

πŸ”Ž ꡬ쑰체 멀버 μ„€μ •

  • lpfnWndProc : μœˆλ„μš° ν”„λ‘œμ‹œμ €(Window procedure)λΌλŠ” μ‘μš© ν”„λ‘œκ·Έλž¨ μ •μ˜ ν•¨μˆ˜μ— λŒ€ν•œ 포인터이닀. 창의 λ™μž‘ λŒ€λΆ€λΆ„μ„ μ •μ˜ν•œλ‹€.

  • Hinstance : μ‘μš© ν”„λ‘œκ·Έλž¨ μΈμŠ€ν„΄μŠ€μ— λŒ€ν•œ ν•Έλ“€λ‘œ, Wwinmain의 hinstance 맀개 λ³€μˆ˜μ—μ„œ 이 값을 κ°€μ Έμ˜¨λ‹€.

  • lpszClassName : μ°½ 클래슀λ₯Ό μ‹λ³„ν•˜λŠ” λ¬Έμžμ—΄μ΄λ‹€.

그런 λ‹€μŒ ꡬ쑰체의 μ£Όμ†Œλ₯Ό RegisterClass ν•¨μˆ˜μ— μ „λ‹¬ν•œλ‹€. 이 ν•¨μˆ˜λŠ” windows 클래슀λ₯Ό 운영 μ²΄μ œμ— λ“±λ‘ν•œλ‹€.

RegisterClass(&wc);

πŸ“Œ Creating the Window

창의 μƒˆ μΈμŠ€ν„΄μŠ€λ₯Ό λ§Œλ“€λ €λ©΄ CreateWindowEx ν•¨μˆ˜λ₯Ό ν˜ΈμΆœν•˜λ©΄ λœλ‹€.

HWND hwnd = CreateWindowEx(0, // Optional window styles
    CLASS_NAME,  // Window class 
    L"Learn to Program Windows", // Window text
    WS_OVERLAPPEDWINDOW, // Window style

    // Size and Position
    CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,

    NULL, // Parent window
    NULL, // Menu
    hInstance, // Instance handle
    NULL // Additional application data
    );

if (hwnd = NULL)
{
    return 0; 
}
  • λ§ˆμ§€λ§‰ λ§€κ°œλ³€μˆ˜λŠ” void* ν˜•μ‹μ˜ μž„μ˜μ˜ 데이터에 λŒ€ν•œ 포인터이닀. 이 값을 μ‚¬μš©ν•˜μ—¬ 데이터 ꡬ쑰λ₯Ό μœˆλ„μš° ν”„λ‘œμ‹œμ €μ—κ²Œ 전달할 수 μžˆλ‹€.

CreateWindowExλŠ” μƒˆ 창에 λŒ€ν•œ 핸듀을 λ°˜ν™˜ν•˜κ±°λ‚˜, μ‹€νŒ¨ν•  경우 0을 λ°˜ν™˜ν•œλ‹€. 창을 ν‘œμ‹œν•˜λ €λ©΄ μ°½ 핸듀을 ShowWindow ν•¨μˆ˜μ— μ „λ‹¬ν•˜λ©΄ λœλ‹€.

ShowWindow(hwnd, nCmdShow);

hwndλŠ” CreateWindowExμ—μ„œ λ°˜ν™˜λ˜λŠ” μ°½ 핸듀이며 nCmdShowλŠ” 창을 μ΅œμ†Œν™”ν•˜κ±°λ‚˜ μ΅œλŒ€ν™” ν•˜λŠ”λ° μ‚¬μš©ν•  수 μžˆλ‹€. μš΄μ˜μ²΄μ œμ—μ„œλŠ” Wwinmain ν•¨μˆ˜λ₯Ό 톡해 이 값을 ν”„λ‘œκ·Έλž¨μ— μ „λ‹¬ν•œλ‹€.

πŸ”Ž μ°½ λ§Œλ“€κΈ°(전체 μ½”λ“œ)

// Register the window class.
const wchar_t CLASS_NAME[] = L"Sample Window Class";

WNDCLASS wc = { };

wc.lpfnWndPRoc = windowProc;
wc.hInstance = hInstance;
wc.lpszClassName = CLASS_NAME;

RegisterClass(&wc);

// Create the window 

HWND hwnd = CreateWindowEx(0, // Optional window styles
    CLASS_NAME,  // Window class 
    L"Learn to Program Windows", // Window text
    WS_OVERLAPPEDWINDOW, // Window style

    // Size and Position
    CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,

    NULL, // Parent window
    NULL, // Menu
    hInstance, // Instance handle
    NULL // Additional application data
    );

if (hwnd = NULL)
{
    return 0; 
}

μœ„ μ½”λ“œλ₯Ό μ‹€ν–‰ν•˜λ©΄ μ•„λž˜μ™€ 같이
Learn to Program Windows λΌλŠ” μ΄λ¦„μ˜ 창이 λ§Œλ“€μ–΄μ§„λ‹€.




πŸ“ Window Messages (Get Started with Win32 and C++)

GUI μ–΄ν”Œλ¦¬μΌ€μ΄μ…˜μ€ μ‚¬μš©μž 및 운영체제의 μ΄λ²€νŠΈμ— 응닡해야 ν•œλ‹€.

  • μ‚¬μš©μžμ˜ 이벀트 : 마우슀 클릭, ν‚€ 슀트둜크, ν„°μΉ˜ 슀크린 제슀처 λ“±
  • 운영 체제 이벀트 : ν”„λ‘œκ·Έλž¨ λ™μž‘μ— 영ν–₯을 쀄 수 μžˆλŠ” 것듀. ex. μ‚¬μš©μžκ°€ μƒˆ ν•˜λ“œμ›¨μ–΄ λ””λ°”μ΄μŠ€λ₯Ό μ—°κ²°ν•˜λŠ” λ“±

μ΄λŸ¬ν•œ μ΄λ²€νŠΈλ“€μ€ 미리 μ˜ˆμΈ‘ν•  수 없기에 Windows 메세지 전달 λͺ¨λΈ(message-passing model)을 μ΄μš©ν•œλ‹€. 운영 μ²΄μ œλŠ” 메세지λ₯Ό μ „λ‹¬ν•˜μ—¬ μ–΄ν”Œλ¦¬μΌ€μ΄μ…˜ μ°½κ³Ό ν†΅μ‹ ν•œλ‹€.

예λ₯Ό λ“€μ–΄ μ‚¬μš©μžκ°€ 마우슀 μ™Όμͺ½ λ²„νŠΌμ„ λˆ„λ₯΄λ©΄ 창에 λ‹€μŒκ³Ό 같은 메세지가 κ°„λ‹€.

#define WM_LBUTTONDOWN    0x0201

창에 메세지λ₯Ό μ „λ‹¬ν•˜κΈ° μœ„ν•΄ μš΄μ˜μ²΄μ œλŠ” ν•΄λ‹Ή 창에 λ“±λ‘λœ μœˆλ„μš° ν”„λ‘œμ‹œμ €λ₯Ό ν˜ΈμΆœν•œλ‹€.


πŸ“Œ The Message Loop

μ–΄ν”Œλ¦¬μΌ€μ΄μ…˜μ—λŠ” μˆ˜λ§Žμ€ 메세지가 μ˜€κ³ κ°€κΈ° λ•Œλ¬Έμ— 메세지λ₯Ό κ²€μƒ‰ν•˜κ³  μ˜¬λ°”λ₯Έ 창으둜 μ „λ‹¬ν•˜λŠ” 루프가 ν•„μš”ν•˜λ‹€.

μš΄μ˜μ²΄μ œλŠ” μ°½ λ§Œλ“œλŠ” 각 μŠ€λ ˆλ“œμ— λŒ€ν•΄ μ°½ 메세지에 λŒ€ν•œ 큐λ₯Ό λ§Œλ“œλŠ”λ°, 이 νλŠ” ν•΄λ‹Ή μŠ€λ ˆλ“œμ—μ„œ λ§Œλ“  λͺ¨λ“  창에 λŒ€ν•œ 메세지λ₯Ό ν¬ν•¨ν•œλ‹€.

큐λ₯Ό 직접 μ‘°μž‘ν•  μˆ˜λŠ” μ—†μœΌλ‚˜ GetMessage ν•¨μˆ˜λ₯Ό ν˜ΈμΆœν•˜μ—¬ 메세지λ₯Ό λŒμ–΄μ˜¬ 수 μžˆλ‹€.

MSG msg;
GetMessage(&msg, NULL, 0, 0);

이 ν•¨μˆ˜λŠ” 큐의 ν—€λ“œμ—μ„œ 첫번째 메세지λ₯Ό μ œκ±°ν•œλ‹€. 큐가 λΉ„μ–΄ 있으면 λ‹€λ₯Έ 메세지가 큐에 λŒ€κΈ°λ  λ•ŒκΉŒμ§€ ν•¨μˆ˜κ°€ μ°¨λ‹¨λœλ‹€. GetMessageκ°€ λ‹€λ₯Έ 메세지λ₯Ό κΈ°λ‹€λ¦¬λŠ” λ™μ•ˆ 계속 μ‹€ν–‰λ˜λŠ” μΆ”κ°€ μŠ€λ ˆλ“œλ₯Ό λ§Œλ“€ 수 μžˆλ‹€.

GetMessage의 첫번째 λ§€κ°œλ³€μˆ˜λŠ” MSG ꡬ쑰체의 μ£Όμ†Œλ‘œ ν•¨μˆ˜κ°€ μ„±κ³΅ν•˜λ©΄ 메세지에 λŒ€ν•œ μ •λ³΄λ‘œ MSG ꡬ쑰체λ₯Ό μ§€μš΄λ‹€.

MSGλ₯Ό 직접 κ²€μ‚¬ν•˜μ§€λŠ” μ•Šκ³ , λ‹€λ₯Έ 두 ν•¨μˆ˜μ— μ „λ‹¬ν•œλ‹€.

TranslateMessage(&msg);
DispatchMessage(&msg);
  • TranlateMessgae : ν‚€λ³΄λ“œ μž…λ ₯을 문자둜 λ³€ν™˜, DispatchMessage전에 ν˜ΈμΆœν•΄μ•Ό ν•œλ‹€.

  • DispatchMessage : λ©”μ„Έμ§€μ˜ λŒ€μƒμΈ μœˆλ„μš° ν”„λ‘œμ‹œμ €λ₯Ό ν˜ΈμΆœν•˜λ„λ‘ μš΄μ˜μ²΄μ œμ—κ²Œ μ§€μ‹œν•œλ‹€. 즉 μš΄μ˜μ²΄μ œλŠ” μ°½ ν…Œμ΄λΈ”μ—μ„œ μ°½ 핸듀을 μ°Ύκ³  μ°½κ³Ό μ—°κ²°λœ ν•¨μˆ˜ 포인터λ₯Ό μ°Ύμ•„ ν•¨μˆ˜λ₯Ό ν˜ΈμΆœν•œλ‹€.

예λ₯Ό λ“€μ–΄ μ‚¬μš©μžκ°€ 마우슀 μ™Όμͺ½ λ²„νŠΌμ„ λˆ„λ₯Ό 경우 μ•„λž˜μ˜ μˆœμ„œλ‘œ μž‘λ™ν•œλ‹€.

1) μš΄μ˜μ²΄μ œλŠ” WM_LBUTTONDOWN 메세지λ₯Ό 메세지 큐에 λ„£λŠ”λ‹€.
2) ν”„λ‘œκ·Έλž¨μ—μ„œ GetMessage ν•¨μˆ˜λ₯Ό ν˜ΈμΆœν•œλ‹€.
3) GetMessageλŠ” νμ—μ„œ WM_LBUTTONDOWN 메세지λ₯Ό λŒμ–΄μ™€ MSG ꡬ쑰λ₯Ό μ±„μš΄λ‹€.
4) ν”„λ‘œκ·Έλž¨μ—μ„œ TranslateMessage 및 DispatchMessage ν•¨μˆ˜λ₯Ό ν˜ΈμΆœν•œλ‹€.
5) DispatchMessage λ‚΄μ—μ„œ μš΄μ˜μ²΄μ œλŠ” μœˆλ„μš° ν”„λ‘œμ‹œμ €λ₯Ό ν˜ΈμΆœν•œλ‹€.
6) μœˆλ„μš° ν”„λ‘œμ‹œμ €λŠ” 메세지에 μ‘λ‹΅ν•˜κ±°λ‚˜ λ¬΄μ‹œν•  수 μžˆλ‹€.

ν”„λ‘œκ·Έλž¨μ΄ μ‹€ν–‰λ˜λŠ” λ™μ•ˆ λ©”μ„Έμ§€λŠ” 계속 큐에 λ„μ°©ν•˜λ―€λ‘œ μ§€μ†μ μœΌλ‘œ 메세지λ₯Ό λ°›μ•„μ„œ Dispatch ν•˜λŠ” 루프가 μžˆμ–΄μ•Ό ν•œλ‹€.
λ”°λΌμ„œ λ‹€μŒκ³Ό 같은 루프가 ν•„μš”ν•˜λ‹€.

πŸ”Ž μ§€μ†μ μœΌλ‘œ 메세지λ₯Ό λ°›μ•„μ„œ Dispatch ν•˜λŠ” 루프

// Correct.

MSG msg = { };
while (GetMessage(&msg, NULL, 0, 0) > 0)
{
	TranslateMessage(&msg);
    DispatchMessage(&msg);
}

μ–΄ν”Œλ¦¬μΌ€μ΄μ…˜μ„ μ’…λ£Œν•˜κ³  메세지 루프λ₯Ό μ€‘λ‹¨ν•˜λ €λ©΄ PostQuitMessage ν•¨μˆ˜λ₯Ό ν˜ΈμΆœν•œλ‹€.

πŸ”Ž PostQuitMessage ν•¨μˆ˜

	PostQuitMessage(0);

μ—¬κΈ°μ„œ 주의깊게 λ³Ό 점은 μœˆλ„μš° ν”„λ¦¬μ‹œμ €κ°€ WM_QUIT 메세지λ₯Ό 받지 μ•Šμ•„ 이 메세지에 λŒ€ν•œ case 문이 ν•„μš” μ—†λ‹€λŠ” 것이닀.


πŸ“Œ Posted Messages versus Sent Messages

  • 메세지 κ²Œμ‹œ(Post) : 메세지가 메세지 큐둜 μ΄λ™ν•˜κ³  루프λ₯Ό 톡해 Dispatch
  • 메세지 전솑(Send) : 큐λ₯Ό κ±΄λ„ˆλ›°κ³  운영 μ²΄μ œμ—μ„œ μœˆλ„μš° ν”„λ‘œμ‹œμ €λ₯Ό 직접 호좜



πŸ“ Writing the Window Procedure

πŸ”Ž μœˆλ„μš° ν”„λ‘œμ‹œμ €

LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
  • hwnd : 창에 λŒ€ν•œ ν•Έλ“€

  • Umsg : 메세지 μ½”λ“œ (ex. WM_SIZE : 창의 크기가 μ‘°μ ˆλ˜μ—ˆμŒ)

  • wParam & lParam : μΆ”κ°€ 데이터

  • LRESULT : ν”„λ‘œκ·Έλž¨μ—μ„œ Windows μ—κ²Œ λ°˜ν™˜ν•˜λŠ” μ •μˆ˜ κ°’. 이 κ°’μ˜ μ˜λ―ΈλŠ” 메세지 μ½”λ“œμ— 따라 달라진닀.

  • CALLBACK : ν•¨μˆ˜μ— λŒ€ν•œ 호좜 κ·œμΉ™

πŸ”Ž 일반적인 μ½”λ“œ

switch (uMsg)
{
    case WM_SIZE: // Handle window resizing

    // etc
}

일반적인 μœˆλ„μš° ν”„λ‘œμ‹œμ €λŠ” μˆ˜μ‹­ 개의 메세지λ₯Ό μ²˜λ¦¬ν•˜λ―€λ‘œ μ‹œκ°„μ΄ κΈΈμ–΄μ§ˆ 수 μžˆμ–΄, μ½”λ“œλ₯Ό λͺ¨λ“ˆμ‹μœΌλ‘œ λ§Œλ“€κ³ μž κ°œλ³„ ν•¨μˆ˜μ—μ„œ 각 메세지λ₯Ό μ²˜λ¦¬ν•˜λ„λ‘ ν•œλ‹€.
이 λ•Œ μœˆλ„μš° ν”„λ‘œμ‹œμ €μ—μ„œ wParam, lParam을 μΊμŠ€νŒ…ν•˜κ³  ν•¨μˆ˜μ— μ „λ‹¬ν•œλ‹€.

πŸ”Ž WM_SIZE 메세지

LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
	switch (uMsg)
	{
	case WM_SIZE:
	{
		int width = LOWORD(lParam); // Macro to get the low-order word
		int height = HIWORD(lParam);  // Macro to get the high-order word

		// Respond to the message :
		OnSize(hwnd, (UINT)wParam, width, height);
	}
	break;
	}
}

void OnSize(HWND hwnd, UINT flag, int width, int height)
{
	// Handle resizing 
}

πŸ“ Default Message Handling

μœˆλ„μš° ν”„λ‘œμ‹œμ €μ—μ„œ νŠΉμ • 메세지λ₯Ό μ²˜λ¦¬ν•˜μ§€ μ•ŠλŠ” κ²½μš°μ—λŠ” 메세지 맀개 λ³€μˆ˜λ₯Ό DefWindowProc ν•¨μˆ˜μ— 직접 μ „λ‹¬ν•œλ‹€.

πŸ”Ž 맀개 λ³€μˆ˜λ₯Ό DefWindowProc ν•¨μˆ˜μ— 전달

return DefWindowProc(hwnd, uMsg, wParam, lParam);

πŸ“ Avoiding Bottlenecks in Your Window Procedure

μœˆλ„μš° ν”„λ‘œμ‹œμ €λŠ” μ‹€ν–‰λ˜λŠ” λ™μ•ˆ λ™μΌν•œ μŠ€λ ˆλ“œ(thread)에 μƒμ„±λœ windows에 λŒ€ν•œ λ‹€λ₯Έ 메세지λ₯Ό μ°¨λ‹¨ν•œλ‹€. 이런 점으둜 λ¬Έμ œκ°€ λ°œμƒν–ˆμ„ λ•ŒλŠ” Windows에 κΈ°λ³Έ μ œκ³΅λ˜λŠ” λ©€ν‹°νƒœμŠ€ν‚Ή κΈ°λŠ₯ 쀑 ν•˜λ‚˜λ₯Ό μ‚¬μš©ν•˜μ—¬ μž‘μ—…μ„ λ‹€λ₯Έ μŠ€λ ˆλ“œλ‘œ 이동해야 ν•œλ‹€.

  • μƒˆ μŠ€λ ˆλ“œλ₯Ό λ§Œλ“ λ‹€.
  • μŠ€λ ˆλ“œ ν’€(thread pool)을 μ‚¬μš©ν•œλ‹€.
  • 비동기 I/O ν˜ΈμΆœμ„ μ‚¬μš©ν•œλ‹€.
  • 비동기 ν”„λ‘œμ‹œμ € ν˜ΈμΆœμ„ μ‚¬μš©ν•œλ‹€.
profile
κ³΅λΆ€μ •λ¦¬μš©

0개의 λŒ“κΈ€