성태의 닷넷 이야기
홈 주인
모아 놓은 자료
프로그래밍
질문/답변
사용자 관리
사용자
메뉴
아티클
외부 아티클
유용한 코드
온라인 기능
MathJax 입력기
최근 덧글
[정성태] VT sequences to "CONOUT$" vs. STD_O...
[정성태] NetCoreDbg is a managed code debugg...
[정성태] Evaluating tail call elimination in...
[정성태] What’s new in System.Text.Json in ....
[정성태] What's new in .NET 9: Cryptography ...
[정성태] 아... 제시해 주신 "https://akrzemi1.wordp...
[정성태] 다시 질문을 정리할 필요가 있을 것 같습니다. 제가 본문에...
[이승준] 완전히 잘못 짚었습니다. 댓글 지우고 싶네요. 검색을 해보...
[정성태] 우선 답글 감사합니다. ^^ 그런데, 사실 저 예제는 (g...
[이승준] 수정이 안되어서... byteArray는 BYTE* 타입입니다...
글쓰기
제목
이름
암호
전자우편
HTML
홈페이지
유형
제니퍼 .NET
닷넷
COM 개체 관련
스크립트
VC++
VS.NET IDE
Windows
Team Foundation Server
디버깅 기술
오류 유형
개발 환경 구성
웹
기타
Linux
Java
DDK
Math
Phone
Graphics
사물인터넷
부모글 보이기/감추기
내용
<div style='display: inline'> <h1 style='font-family: Malgun Gothic, Consolas; font-size: 20pt; color: #006699; text-align: center; font-weight: bold'>Win32 - 윈도우(cbWndExtra) 및 윈도우 클래스(cbClsExtra) 저장소 사용 방법</h1> <p> Window를 하나 생성하려면, 그 윈도우가 속한 "클래스"를 지정해야 하는데요, 일례로 신규 Windows Application 프로젝트를 생성하면 다음과 같은 식으로 기본 코드에 그 과정이 포함됩니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > WCHAR <span style='color: blue; font-weight: bold'>szWindowClass</span>[MAX_LOADSTRING]; // the main window class name int APIENTRY wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ LPWSTR lpCmdLine, _In_ int nCmdShow) { // ...[생략]... LoadStringW(hInstance, <span style='color: blue; font-weight: bold'>IDC_MODALOWNER, szWindowClass</span>, MAX_LOADSTRING); // ...[생략]... } ATOM MyRegisterClass(HINSTANCE hInstance) { WNDCLASSEXW wcex; wcex.<a target='tab' href='https://devblogs.microsoft.com/oldnewthing/20031212-00/?p=41523'>cbSize</a> = sizeof(WNDCLASSEX); wcex.style = CS_HREDRAW | CS_VREDRAW; wcex.lpfnWndProc = WndProc; <span style='color: blue; font-weight: bold'>wcex.cbClsExtra = 0; wcex.cbWndExtra = 0;</span> wcex.hInstance = hInstance; wcex.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_MODALOWNER)); wcex.hCursor = LoadCursor(nullptr, IDC_ARROW); wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW+1); wcex.lpszMenuName = MAKEINTRESOURCEW(IDC_MODALOWNER); <span style='color: blue; font-weight: bold'>wcex.lpszClassName = szWindowClass;</span> wcex.hIconSm = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL)); <span style='color: blue; font-weight: bold'>return RegisterClassExW(&wcex);</span> } BOOL InitInstance(HINSTANCE hInstance, int nCmdShow) { hInst = hInstance; // Store instance handle in our global variable HWND hWnd = CreateWindowW(<span style='color: blue; font-weight: bold'>szWindowClass</span>, szTitle, WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, nullptr, nullptr, hInstance, nullptr); if (!hWnd) { return FALSE; } ShowWindow(hWnd, nCmdShow); UpdateWindow(hWnd); return TRUE; } </pre> <br /> 그리고, 위의 코드에서 cbClsExtra, cbWndExtra 필드가 바로 오늘 다룰 내용의 주제입니다. ^^<br /> <br /> <hr style='width: 50%' /><br /> <br /> 이름에서 의미하듯이 cbClsExtra는 윈도우 클래스 단위로 확보되는 메모리 크기이고, cbWndExtra는 윈도우 단위로 확보되는 메모리 크기입니다. 일반적인 OOP 개념에서 각각 클래스 필드, 인스턴스 필드에 대응하는 것으로 이해해도 무방합니다.<br /> <br /> 따라서, cbClsExtra에 지정하는 메모리 크기는 윈도우 클래스 레벨이므로 그 클래스로 생성되는 모든 윈도우에서 접근하는 공통 저장소 역할을 합니다. 반면, cbWndExtra의 경우라면 개별 윈도우 단위로 유지할 저장소를 제공합니다.<br /> <br /> 간단하게 예를 들어볼까요? ^^ cbClsExtra와 cbWndExtra에 대해 다음과 같이 포인터 크기만큼의 데이터를 확보해 보겠습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > wcex.cbClsExtra = sizeof(LONG_PTR); wcex.cbWndExtra = sizeof(LONG_PTR); </pre> <br /> 이후 클래스 저장소를 접근하고 싶다면 <a target='tab' href='https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getclasslongptrw'>GetClassLongPtr</a> / <a target='tab' href='https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-setclasslongptrw'>SetClassLongPtr</a> API를 사용하면 됩니다. 이름에서 벌써 "Class"가 포함돼 있습니다.<br /> <br /> 반면 윈도우 레벨의 저장소를 접근하고 싶다면 (짐작할 수 있겠지만 "Window" 이름이 들어간) <a target='tab' href='https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getwindowlongptrw'>GetWindowLongPtr</a> / <a target='tab' href='https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-setwindowlongptrw'>SetWindowLongPtr</a> API를 사용하면 됩니다.<br /> <br /> 이것을 이용해 다음과 같이 코딩해 보면,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > // 예를 들기 위해 윈도우 2개 생성 <span style='color: blue; font-weight: bold'>HWND hWnd</span> = CreateWindowW(<span style='color: blue; font-weight: bold'>szWindowClass</span>, szTitle, WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, nullptr, nullptr, hInstance, nullptr); <span style='color: blue; font-weight: bold'>HWND hWnd2</span> = CreateWindowW(<span style='color: blue; font-weight: bold'>szWindowClass</span>, szTitle, WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, nullptr, nullptr, hInstance, nullptr); </pre> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > // 윈도우 저장소 테스트 SetWindowLongPtr(hWnd, 0, 501); // hWnd 윈도우 공간에 501 기록 SetWindowLongPtr(hWnd2, 0, 601); // hWnd2 윈도우 공간에 602 기록 LONG_PTR v1 = GetWindowLongPtr(hWnd, 0); // v1 == 501 LONG_PTR v2 = GetWindowLongPtr(hWnd2, 0); // v2 == 601 </pre> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > // 클래스 저장소 테스트 SetClassLongPtr(hWnd, 0, 502); // 윈도우 클래스 공간에 502 기록 SetClassLongPtr(hWnd2, 0, 602); // 윈도우 클래스 공간에 602 기록 LONG_PTR v3 = ::GetClassLongPtr(hWnd, 0); // v3 == 602 LONG_PTR v4 = ::GetClassLongPtr(hWnd2, 0); // v4 == 602 </pre> <br /> cbClsExtra와 cbWndExtra 공간의 차이점을 확연히 알 수 있습니다. 윈도우가 다른 경우 cbWndExtra 공간도 달라지는 반면, 해당 윈도우들이 같은 클래스를 지정했다면 동일한 cbClsExtra 공간을 접근한 결과입니다.<br /> <br /> <hr style='width: 50%' /><br /> <a name='userdata'></a> <br /> 위에서 cbWndExtra의 경우 포인터 공간을 확보하고 있는데요, 사실 Window Manager는 기본적으로 윈도우 생성 시 포인터 크기만큼의 공간을 사용자를 위해 1개 마련해 두고 있기 때문에 위와 같이 코딩할 필요가 없습니다.<br /> <br /> 해당 영역은 GWLP_USERDATA로 접근할 수 있는데요, 따라서 다음과 같이 코딩할 수 있습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > ATOM MyRegisterClass(HINSTANCE hInstance) { WNDCLASSEXW wcex; wcex.cbSize = sizeof(WNDCLASSEX); // ...[생략]... <span style='color: blue; font-weight: bold'>wcex.cbWndExtra = 0;</span> // ...[생략]... return RegisterClassExW(&wcex); } ...[생략]... { HWND hWnd = CreateWindowW(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, nullptr, nullptr, hInstance, nullptr); HWND hWnd2 = CreateWindowW(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, nullptr, nullptr, hInstance, nullptr); SetWindowLongPtr(hWnd, <span style='color: blue; font-weight: bold'>GWLP_USERDATA</span>, 500); SetWindowLongPtr(hWnd2, <span style='color: blue; font-weight: bold'>GWLP_USERDATA</span>, 600); LONG_PTR v1 = GetWindowLongPtr(hWnd, <span style='color: blue; font-weight: bold'>GWLP_USERDATA</span>); // v1 == 500 LONG_PTR v2 = GetWindowLongPtr(hWnd2, <span style='color: blue; font-weight: bold'>GWLP_USERDATA</span>); // v2 == 600 return TRUE; } </pre> <br /> 참고로, 웹상의 블로그 내용 중에는 cbWndExtra로 추가한 공간을 접근할 때 GWLP_USERDATA를 사용해야 한다고 설명하는 글이 있는데 올바르지 않습니다. GWLP_USERDATA는 기본적으로 생성된 공간이고, cbWndExtra로 추가한 공간은 (바이트 크기의) 0을 기준으로 GetWindowLongPtr, SetWindowLongPtr로 접근해야 합니다.<br /> <br /> 예를 들어, cbWndExtra에 2개 포인터만큼의 크기를 확보했다면,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > wcex.cbWndExtra = sizeof(LONG_PTR) * 2; </pre> <br /> 해당 바이트의 크기만큼 offest을 지정해 다음과 같이 값을 다룰 수 있습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > SetWindowLongPtr(hWnd, <span style='color: blue; font-weight: bold'>0</span>, 501); SetWindowLongPtr(hWnd2, <span style='color: blue; font-weight: bold'>0</span>, 601); SetWindowLongPtr(hWnd, <span style='color: blue; font-weight: bold'>sizeof(LONG_PTR)</span>, 502); SetWindowLongPtr(hWnd2, <span style='color: blue; font-weight: bold'>sizeof(LONG_PTR)</span>, 602); LONG_PTR v1 = GetWindowLongPtr(hWnd, <span style='color: blue; font-weight: bold'>0</span>); // v1 == 501 LONG_PTR v2 = GetWindowLongPtr(hWnd2, <span style='color: blue; font-weight: bold'>0</span>); // v2 == 601 LONG_PTR v3 = GetWindowLongPtr(hWnd, <span style='color: blue; font-weight: bold'>sizeof(LONG_PTR)</span>); // v3 == 502 LONG_PTR v4 = GetWindowLongPtr(hWnd2, <span style='color: blue; font-weight: bold'>sizeof(LONG_PTR)</span>); // v4 == 602 </pre> <br /> 마지막으로, 윈도우 단위로 저장하는 공간에서는 이 외에도 (<a target='tab' href='https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-setwindowlongptrw#parameters'>문서에 나오듯이</a>) GWL_EXSTYLE, GWLP_HINSTANCE, GWLP_ID, GWL_STYLE, GWLP_WNDPROC 값이 있습니다.<br /> <br /> 그중에서, GWLP_HINSTANCE는 Window를 생성할 때 전달했던 HINSTANCE 값인데요,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > HWND hWnd = CreateWindowW(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, nullptr, nullptr, <span style='color: blue; font-weight: bold'>hInstance</span>, nullptr); </pre> <br /> 대개의 경우, hInstance 값을 (편의상) 전역 변수에 저장해 다루곤 하는데 사실 윈도우 핸들(HWND)로부터 이 값을 구할 수 있기 때문에,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > HINSTANCE hInst = (HINSTANCE)GetWindowLongPtr(hWnd, GWLP_HINSTANCE); </pre> <br /> 꼭 필요한 것은 아닙니다.<br /> <br /> <hr style='width: 50%' /><br /> <a name='win_oop'></a> <br /> <strike>우리가 알고 있는 MFC 프레임워크는 GWLP_USERDATA 또는 cbWndExtra를 잘 활용한 하나의 예라고 보시면 됩니다.</strike> 이에 대해서는 마이크로소프트의 공식 문서에서 더 잘 설명하고 있으니 참고하시면 됩니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > Managing Application State ; <a target='tab' href='https://learn.microsoft.com/en-us/windows/win32/learnwin32/managing-application-state-'>https://learn.microsoft.com/en-us/windows/win32/learnwin32/managing-application-state-</a> </pre> <br /> CreateWindowEx를 이용해 상태 정보를 마지막 인자 lpParam에 전달하고, WM_CREATE 메시지에 전달된 CREATESTRUCT로부터,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > typedef struct tagCREATESTRUCTA { <span style='color: blue; font-weight: bold'>LPVOID lpCreateParams;</span> // CreateWindowEx에 전달한 lParam 값 HINSTANCE hInstance; HMENU hMenu; HWND hwndParent; int cy; int cx; int y; int x; LONG style; LPCSTR lpszName; LPCSTR lpszClass; DWORD dwExStyle; } CREATESTRUCTA, *LPCREATESTRUCTA; </pre> <br /> lParam에 전달했던 상태 정보를 lpCreateParams 필드로 구해 그것을 윈도우 저장소에 보관함으로써,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { StateInfo *pState; <span style='color: blue; font-weight: bold'>if (uMsg == WM_CREATE) { CREATESTRUCT *pCreate = reinterpret_cast<CREATESTRUCT*>(lParam); pState = reinterpret_cast<StateInfo*>(pCreate->lpCreateParams); SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)pState); }</span> else { pState = GetAppState(hwnd); } // ...[생략]... } inline StateInfo* GetAppState(HWND hwnd) { LONG_PTR ptr = GetWindowLongPtr(hwnd, GWLP_USERDATA); StateInfo *pState = reinterpret_cast<StateInfo*>(ptr); return pState; } </pre> <br /> <a target='tab' href='https://learn.microsoft.com/en-us/windows/win32/learnwin32/managing-application-state-#an-object-oriented-approach'>이후의 처리를 OOP 방식으로 매끄럽게 연결</a>하는 것을 볼 수 있습니다. ^^<br /> <br /> <hr style='width: 50%' /><br /> <br /> 이번 글은, 아래의 글에 대한 장황한 버전이었습니다. ^^<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > The bonus window bytes at GWLP_USERDATA ; <a target='tab' href='https://devblogs.microsoft.com/oldnewthing/20050303-00/?p=36293'>https://devblogs.microsoft.com/oldnewthing/20050303-00/?p=36293</a> </pre> </p><br /> <br /><hr /><span style='color: Maroon'>[이 글에 대해서 여러분들과 의견을 공유하고 싶습니다. 틀리거나 미흡한 부분 또는 의문 사항이 있으시면 언제든 댓글 남겨주십시오.]</span> </div>
첨부파일
스팸 방지용 인증 번호
8147
(왼쪽의 숫자를 입력해야 합니다.)