Win32 어셈블리 프로그래밍 - 3. 간단한 윈도우

2022. 4. 4. 16:00프로그래밍

728x90

단순 윈도우 만들기

이 자습서에서 우리는 데스크톱에 완벽하게 작동하는 윈도우를 뿌려주는 Windows 프로그램을 작성 할 것입니다



이론:

Windows 프로그램은 GUI  API 기능들에 크게 의존합니다 접근 방식은 사용자와 프로그래머 모두에게 이득을 줍니다사용자는 새로운 프로그램의 GUI 탐색하는 방법을 배울 필요가 없으며 Windows 프로그램의 GUI 비슷합니다프로그래머에게는 GUI 코드가 이미 테스트되고 사용할  있도록 준비가 되어 있다는 것입니다.

프로그래머에게 단점이   있는 것은 복잡성이 증가한다는 것입니다.

윈도우메뉴 또는 아이콘과 같은 GUI 객체를 생성하거나 조작하려면 프로그래머가 엄격한 레시피를 따라야 합니다.

그러나 이는 모듈(프로그래밍이나 OOP 패러다임에 의해 극복   있을 것입니다.

 

데스크탑 하단 바탕 화면에 윈도우를 만드는  필요한 단계에 대한 개요를 설명합니다.

 

1.   프로그램의 핸들 인스턴스를 얻는다(필수)

2.    커맨드라인을 얻어낸다명령어 라인을 진행해야 하는 프로그램이 아닐 경우에는 필요없음)

3.  윈도우 클래스를 등록한다(MessageBox 나 다이얼로그 박스 같은 미리 정의된 윈도우 타입들을 사용하지 않는다면 필요하다)

4.    윈도우를 생성한다(필수)

5.    데스크탑 위에 윈도우를 보여준다 ( 즉시 윈도우를 보여주고 싶다면

6.    윈도우의 클라이언트 영역을 갱신 해준다.

7.    윈도우들로부터의 메시지들을 확인하면서 무한 루프에 들어간다.

8.   메시지가 도착하면그 윈도우가 관할 하는 특별한 함수에 의해서 작업이 되어진다.

9. 사용자가 윈도우를 닫을 때프로그램을 빠져나간다.

 

보시다시피 Windows 프로그램의 구조는 DOS 프로그램에 비해 다소 복잡합니다. 
그러나 Windows의 세계는 DOS 세계와 완전히 다릅니다. 
Windows 프로그램은 서로 평화롭게 공존 할 수 있어야 합니다. 
그들은 보다 엄격한 규칙을 따라야 합니다. 
프로그래머로서 당신은 프로그래밍 스타일과 습관에 보다 엄격 해야 합니다.

 

내용:

아래는 간단한 윈도우 프로그램의 소스 코드입니다. 
Win32 ASM 프로그래밍에 대한 자세한 내용을 살펴보기 전에 프로그래밍을 쉽게 할 수 있는 
몇 가지 유용한 점에 대해 짚고 넘어 가도록 하겠습니다.

  • .asm 파일의 시작 부분에 모든 Windows 상수구조  함수 프로토타입을 하나의 include 파일에 넣고 포함 해야 합니다이렇게 함으로써 많은 노력과 오타를 덜어줍니다현재, MASM을위한 가장 완벽한 include 파일은 hutch 페이지 또는 현재 페이지에서 다운로드   있는 hutch windows.inc입니다자신만의 상수들과 구조체 정의들을 정의   있지만 그것들을 별도의 include 파일에 넣어서 관리 하도록 해야 합니다
  • includelib 지시문을 사용하여 프로그램에 사용되는 가져 오기 라이브러리를 지정합니다예를 들어프로그램이 MessageBox 호출하는 경우 다음 행을 입력 해야 합니다:
includelib user32.lib

asm 파일의 시작 부분에서  지시문은 프로그램이 해당 import 라이브러리의 함수들을 사용한다고 MASM 알리는 역할을 한다고 보면 됩니다프로그램이  이상의 라이브러리에서 함수를 호출하는 경우 사용하는  라이브러리에 대해 includelib 추가합니다. IncludeLib 지시문을 사용하면 링크   import 라이브러리에 대해 걱정할 필요가 없습니다. / LIBPATH 링커 스위치를 사용하여 Link 모든 lib 어디에 있는지 알려줄  있습니다.

  • API 함수 프로토 타입구조체 또는 상수를 포함 파일에 선언   /소문자 포함 Windows 파일에 사용  원래 이름들을 사용하도록 합니다이렇게 하면 Win32 API 참조에서  가지 항목을 찾을  많은 골칫거리를 줄일  있습니다.
  • makefile 사용하여 어셈블리 프로세스를 자동화하면 많은 타이핑을 줄일  있습니다.
.386
.model flat,stdcall
option casemap:none
include windows.inc
include user32.inc
includelib user32.lib            ; calls to functions in user32.lib and kernel32.lib
include kernel32.inc
includelib kernel32.lib
WinMain proto :DWORD,:DWORD,:DWORD,:DWORD
 
.DATA                     ; initialized data
ClassName db "SimpleWinClass",0        ; the name of our window class
AppName db "Our First Window",0        ; the name of our window
 
.DATA?                ; Uninitialized data
hInstance HINSTANCE ?        ; Instance handle of our program
CommandLine LPSTR ?
.CODE                ; Here begins our code
start:
invoke GetModuleHandle, NULL            ; get the instance handle of our program.
                                         ; Under Win32, hmodule==hinstance mov hInstance,eax
mov hInstance,eax
invoke GetCommandLine                  ; get the command line. You don't have to call this function IF
                                         ; your program doesn't process the command line.
mov CommandLine,eax
invoke WinMain, hInstance,NULL,CommandLine, SW_SHOWDEFAULT        ; call the main function
invoke ExitProcess, eax             ; quit our program. The exit code is returned in eax from WinMain.
 
WinMain proc hInst:HINSTANCE,hPrevInst:HINSTANCE,CmdLine:LPSTR,CmdShow:DWORD
    LOCAL wc:WNDCLASSEX                 ; create local variables on stack
    LOCAL msg:MSG
    LOCAL hwnd:HWND
 
    mov   wc.cbSize,SIZEOF WNDCLASSEX      ; fill values in members of wc
    mov   wc.style, CS_HREDRAW or CS_VREDRAW
    mov   wc.lpfnWndProc, OFFSET WndProc
    mov   wc.cbClsExtra,NULL
    mov   wc.cbWndExtra,NULL
    push  hInstance
    pop   wc.hInstance
    mov   wc.hbrBackground,COLOR_WINDOW+1
    mov   wc.lpszMenuName,NULL
    mov   wc.lpszClassName,OFFSET ClassName
    invoke LoadIcon,NULL,IDI_APPLICATION
    mov   wc.hIcon,eax
    mov   wc.hIconSm,eax
    invoke LoadCursor,NULL,IDC_ARROW
    mov   wc.hCursor,eax
    invoke RegisterClassEx, addr wc                       ; register our window class
    invoke CreateWindowEx,NULL,\
                ADDR ClassName,\
                ADDR AppName,\
                WS_OVERLAPPEDWINDOW,\
                CW_USEDEFAULT,\
                CW_USEDEFAULT,\
                CW_USEDEFAULT,\
                CW_USEDEFAULT,\
                NULL,\
                NULL,\
                hInst,\
                NULL
    mov   hwnd,eax
    invoke ShowWindow, hwnd,CmdShow               ; display our window on desktop
    invoke UpdateWindow, hwnd                     ; refresh the client area
 
    .WHILE TRUE                                          ; Enter message loop
                invoke GetMessage, ADDR msg,NULL,0,0
                .BREAK .IF (!eax)
                invoke TranslateMessage, ADDR msg
                invoke DispatchMessage, ADDR msg
   .ENDW
    mov     eax,msg.wParam                            ; return exit code in eax
    ret
WinMain endp
 
WndProc proc hWnd:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM
    .IF uMsg==WM_DESTROY                     ; if the user closes our window
        invoke PostQuitMessage,NULL             ; quit our application
    .ELSE
        invoke DefWindowProc,hWnd,uMsg,wParam,lParam     ; Default message processing
        ret
    .ENDIF
    xor eax,eax
    ret
WndProc endp
 
end start

 

 

코드 분석

간단한 Windows 프로그램이라도 코딩이 많이 필요하다는 사실을 알고 있었을지도 모릅니다하지만 대부분의 코드는 하나의 소스 코드 파일에서 다른 파일로 복사  수있는 * 템플릿 * 코드입니다.

또는 원하는 경우 이러한 코드  일부를 프롤로그  에필로그 코드로 사용할 라이브러리로 조합   있습니다.

WinMain 함수안에 코드를 작성할 수도 있습니다.

 

실제로  과정을 거치게 만들어 주는 것이 C 컴파일러가 하는 역할  것입니다.

컴파일러들은 다른 관리 작업에 대해 걱정하지 않고 WinMain 코드를 작성할  있게 해줍니다.

유일하게 따라야 하는 것이 있다면 WinMain이라는 함수가 있어야 한다는 것이겠죠이렇게 하지 않으면 C 컴파일러는 코드는 프롤로그  에필로그와 결합   없게 됩니다하지만 어셈블리 언어에는 이러한 제한이 없습니다.

WinMain 대신 함수 이름을 사용할 수도 있고 함수가 아니어도 상관 없습니다.

준비 하셨나요 부분에 대한 설명은 길고  튜토리얼이  것입니다 프로그램을 분석해서 끝장을  보죠!

 

.386 
.model flat,stdcall 
option casemap:none

WinMain proto :DWORD,:DWORD,:DWORD,:DWORD

include \masm32\include\windows.inc 
include \masm32\include\user32.inc 
include \masm32\include\kernel32.inc 
includelib \masm32\lib\user32.lib 
includelib \masm32\lib\kernel32.lib

 

처음  줄은 "필수적인 내용"입니다.

.386
MASM에게  프로그램이 80386 명령어 세트를 사용할 것을 알립니다.

.model flat, stdcall
우리 프로그램이 플랫 메모리 어드레싱 모델을 사용한다는 것을 MASM 알려주고프로그램에서 stdcall 매개 변수 전달 규칙을 기본값으로 사용한다고 알려 줍니다.

 

다음은 WinMain 함수 프로토 타입입니다나중에 WinMain 호출  것이므로 함수 프로토 타입을 먼저 정의해야 호출   있습니다.

 

소스 코드의 시작 부분에 windows.inc 포함해야 합니다여기에는 프로그램에서 사용하는 중요한 구조체들과  상수들이 포함되어 있습니다. include 파일  windows.inc 단순 텍스트 파일 이며텍스트 편집기로   있습니다.

windows.inc에는 전체 구조체들과 상수들이 (아직) 포함되어 있지 않습니다

여전히 허치와 나는 이에 대해서 연구를 하고 있습니다파일 내에 원하는 내용이 없으면 새로운 내용을 추가   있습니다. 

 

우리 프로그램은 user32.dll (CreateWindowEx, RegisterWindowClassEx ) kernel32.dll (ExitProcess) 있는 API 함수를 호출하므로 프로그램을  개의 import 라이브러리에 연결 해야 합니다.

다음 으로 어떤 import 라이브러리를  프로그램에서 import 해야 하는지 어떻게   있을까요?

 

 : 프로그램에 의해 호출  API 함수가 있는 곳을 알아야 합니다예를 들어, gdi32.dll에서 API 함수를 호출하면 gdi32.lib 링크 해야 합니다.


 
이것이 MASM 접근 방식입니다. TASM import 라이브러리 연결 방법은 훨씬 간단합니다.

다음과 같이 import32.lib 파일 하나만 연결 하면 됩니다 : 

.DATA 
    ClassName db "SimpleWinClass",0 
    AppName  db "Our First Window",0

.DATA? 
hInstance HINSTANCE ? 
CommandLine LPSTR ?

 

다음은 “DATA” 섹션들 입니다
 

.DATA에서우리는  개의 0 종결 문자열 (ASCIIZ 문자열) 선언합니다 :

ClassName 윈도우 클래스의 이름이고 AppName 윈도우의 이름입니다

 변수들은 초기화 된다는 것에 주의하십시오. 

.DATA?에서는  개의 변수가 선언되었습니다 : hInstance (프로그램의 인스턴스 핸들) CommandLine (프로그램의 명령 ) 그것 인데요익숙하지 않은 데이터 형식  HINSTANCE  LPSTR DWORD 새로운(다른이름입니다. windows.inc파일에서 에서 선언들을 찾을  있습니다.

.DATA? 섹션의 모든 변수는 초기화되지 않습니다.

시작할  특정 값을 보유  필요는 없지만 나중에 사용할  있도록 공간을 예약 하려고 하는 것입니다.

.CODE 
 start: 
     invoke GetModuleHandle, NULL 
     mov    hInstance,eax 
     invoke GetCommandLine 
     mov    CommandLine,eax 
     invoke WinMain, hInstance,NULL,CommandLine, SW_SHOWDEFAULT 
     invoke ExitProcess,eax 
     ..... 
end start

 

.CODE 에는 모든 인스트럭션들을 포함합니다.

작성되는 모든 코드는  <시작 라벨> : 종료 <시작 라벨사이에 있어야 합니다라벨의 이름은 중요하지 않습니다유일해야 하며, MASM 명명 규칙을 위반하지 않는  원하는 이름을 지정할  있습니다.

 

 번째 인스트럭션은 GetModuleHandle 호출하여 프로그램의 인스턴스 핸들을 검색하는 것입니다.

Win32에서는 인스턴스 핸들과 모듈 핸들이 하나이며 동일합니다.

인스턴스 핸들은 프로그램의 ID 생각할  있습니다.

프로그램에서 호출해야 하는 여러 API 함수에 대한 매개 변수로 사용되므로 일반적으로 프로그램 시작  선언하는 것이 좋습니다.


Note: Actually under win32, instance handle is the linear address of your program in memory.

참고 : 실제로 win32에서 인스턴스 핸들은 메모리에 있는 프로그램의 선형 주소입니다.


 

Win32 함수에서 반환   함수의 반환 값은 eax에서 찾을  있습니다다른 모든 값은 호출에 대해 정의한 함수 매개 변수 목록에 전달  변수를 통해 반환됩니다.

 

호출하는 Win32 함수는 세그먼트 레지스터와 ebx, edi, esi  ebp 레지스터의 값을 거의 항상 보존합니다.

반대로 ecx edx 스크래치 레지스터로 간주되며 Win32 함수에서 반환   항상 정의되지 않습니다.


Note: Don't expect the values of eax, ecx, edx to be preserved across API function calls.

참고 : eax, ecx, edx 값은 API 함수 호출을 통해 유지되지 않습니다.


 

결론은 API 함수를 호출   eax 반환 값을 기대한다는 것입니다. Windows에서 함수를 호출하는 경우 함수를 반환   세그먼트 레지스터 ebx, edi, esi  ebp 값을 보존하고 복원 해야 하는 규칙을 따라야 합니다그렇지 않으면 프로그램이 바로 충돌을 일으킬 것입니다여기에는 윈도우 프로 시저와 윈도우 콜백 함수들이 포함되어 있습니다.


 

프로그램이 명령 행을 처리하지 않으면 GetCommandLine 호출이 필요하지 않습니다 예에서는 프로그램에서 필요할 때를 대비하여 호출하는 방법을 보여줍니다.
 

다음은 WinMain 호출입니다.

여기서 프로그램의 인스턴스 핸들프로그램의 이전 인스턴스의 인스턴스 핸들처음 보이는 명령   윈도우 상태의  가지 매개 변수를 받습니다. Win32에서는 이전 인스턴스가 없습니다.

 프로그램은 자신만의 주소 공간이 있기 때문에 hPrevInst 값은 항상 0입니다.

이는 프로그램의 모든 인스턴스가 동일한 주소 공간에서 실행되고 인스턴스가  번째 인스턴스인지 여부를 알고 자하는 Win16에서 남은 잔재 같은 것으로 남은 것입니다 .

win16에서 hPrevInst NULL이면 인스턴스가  번째 인스턴스입니다.

 

참고 : 함수 이름을 WinMain으로 선언  필요는 없습니다사실이러한 선언에 대해서 어떤 이름을 정할  있는 자유가 있다는 것이죠.  WinMain 같은 함수을 전혀 사용할 필요가 없습니다.  GetCommandLine 옆의 WinMain 함수 안에 코드를 붙여 넣어도 프로그램은  동작합니다.


 

WinMain에서 돌아 오면 eax 종료 코드로 채워집니다. Exit 코드를 응용 프로그램을 종료시키는 ExitProcess 매개 변수로 전달합니다.

WinMain proc Inst:HINSTANCE,hPrevInst:HINSTANCE,CmdLine:LPSTR,CmdShow:DWORD

위의 코드는 WinMain 함수 선언입니다. PROC 지시문 뒤에 오는 parameter : type 쌍을 주목하십시오.   변수는 WinMain 호출자로부터 받는 매개 변수입니다이러한 매개 변수는 스택 조작 대신 이름으로 참조   있습니다또한 MASM 함수에 대한 프롤로그  에필로그 코드를 생성합니다따라서 우리는 함수 입력  종료  스택 프레임에 대해 신경  필요가 없습니다.

    LOCAL wc:WNDCLASSEX 
    LOCAL msg:MSG 
    LOCAL hwnd:HWND

LOCAL 지시문은 함수에서 사용  지역 변수에 대해 스택에서 메모리를 할당합니다.  LOCAL 지시어는 PROC 지시문 바로 아래에 있어야합니다.

LOCAL 지시문 바로 다음에 <로컬 변수 이름> : <변수 유형>이옵니다따라서 LOCAL wc : WNDCLASSEX구문은 MASM에게 wc라는 변수에 대한 WNDCLASSEX 구조체 크기의 메모리를 스택에서 할당하도록 지시합니다.

 

이렇게 함으로써스택 조작에 대한 어려움 없이 코드에서 wc 참조   있습니다이것이 정말 멋진  입니다단점은 로컬 변수가 작성된 함수 외부에서 사용될  없으며 함수가 호출자에게 반환   자동으로 삭제된다는 것입니다 다른 단점은 로컬 변수를 자동으로 초기화   없다는 것입니다왜냐하면 함수가 입력   스택 메모리가 동적으로 할당되기 때문입니다. LOCAL 지시문 뒤에 원하는 값을 수동으로 지정 해야합니다.

   mov   wc.cbSize,SIZEOF WNDCLASSEX 
    mov   wc.style, CS_HREDRAW or CS_VREDRAW 
    mov   wc.lpfnWndProc, OFFSET WndProc 
    mov   wc.cbClsExtra,NULL 
    mov   wc.cbWndExtra,NULL 
    push  hInstance 
    pop   wc.hInstance 
    mov   wc.hbrBackground,COLOR_WINDOW+1 
    mov   wc.lpszMenuName,NULL 
    mov   wc.lpszClassName,OFFSET ClassName 
    invoke LoadIcon,NULL,IDI_APPLICATION 
    mov   wc.hIcon,eax 
    mov   wc.hIconSm,eax 
    invoke LoadCursor,NULL,IDC_ARROW 
    mov   wc.hCursor,eax 
    invoke RegisterClassEx, addr wc

 

위의 라인은 개념이 매우 간단하지만 선언해야만 하는 부분입니다이를 가능하게 하기 위해서는  줄의 인스트럭션이 필요합니다 코드를 조작하는 것에 대한 개념이 바로 윈도우 클래스입니다여기에서 윈도우 클래스는 단지 윈도우에 대한 청사진 혹은 정의에 불과합니다 클래스에서 아이콘커서 기능을 담당하는 함수색상 등등과 같은 윈도우의 중요한 특성을 정의합니다윈도우 클래스에서는 윈도우를 만듭니다이것은 일종의 객체 지향 개념입니다

동일한 특성을 갖는  이상의 윈도우을 작성하려는 경우이러한 모든 특성을  곳에서만 저장하고 필요할  이를 참조 해야 한다는 얘기입니다 개념은 정보의 중복을 피함으로써 많은 메모리를 절약합니다. Windows 메모리 칩이 없고 대부분의 컴퓨터에 1MB 메모리가 있는 과거에 설계되었습니다. Windows 부족한 메모리 리소스를 사용할  매우 효율적 이어야 합니다요점은 : 자신 만의 윈도우을 정의한 경우 WNDCLASS 또는 WNDCLASSEX 구조에서 윈도우의 원하는 특성을 채우고 창을 생성하기 전에 RegisterClass 또는 RegisterClassEx 호출해야 합니다

윈도우을 만들려는  윈도우 유형마다 윈도우 클래스를  번만 등록 하면 됩니다.


 

Windows에는 단추  편집 상자와 같은  가지 미리 정의  Window 클래스가 있습니다이러한 윈도우 (또는 컨트롤) 경우 윈도우 클래스를 등록  필요 없이 미리 정의  클래스 이름으로 CreateWindowEx 호출 하면 됩니다.

 

WNDCLASSEX에서 가장 중요한 단일 구성원은 lpfnWndProc입니다.

lpfn 함수에 대한 포인터를 나타냅니다.

Win32에서는 새로운 FLAT 메모리 모델로 인해 "near"포인터 또는 "far"포인터가 없으며 단지 포인터로 통일 되었습니다하지만 이는 여전히 Win16 시대부터 남은 유물 같은 것이라고 생각하면 됩니다 윈도우 클래스는 window procedure라는 함수와 연결 됩니다 윈도우 프로시저는 연관된 윈도우 클래스에서 생성  모든 윈도우들의 메시지 처리를 담당합니다. Windows 사용자 키보드 또는 마우스 입력과 같은 윈도우와 관련된 중요한 이벤트를 알리기 위해 윈도우 프로시저에 메시지를 보내는 것을 책임집니다수신   윈도우 메시지에 적절히 응답하는 것은  윈도우 프로시저에서 진행됩니다

우리는 윈도우 프로시저에서 이벤트 핸들러를 작성하는  거의 대부분의 시간을 할애하게 됩니다.

아래 WNDCLASSEX  구성원들의 설명입니다.

 

WNDCLASSEX STRUCT DWORD 
  cbSize            DWORD      ? 
  style             DWORD      ? 
  lpfnWndProc       DWORD      ? 
  cbClsExtra        DWORD      ? 
  cbWndExtra        DWORD      ? 
  hInstance         DWORD      ? 
  hIcon             DWORD      ? 
  hCursor           DWORD      ? 
  hbrBackground     DWORD      ? 
  lpszMenuName      DWORD      ? 
  lpszClassName     DWORD      ? 
  hIconSm           DWORD      ? 
WNDCLASSEX ENDS
cbSize: 
바이트 단위의 WNDCLASSEX 구조체의 크기. SIZEOF 연산자를 사용하여 값을 사용   있습니다.


style: 
 클래스에서 만들어진 윈도우 스타일. "or" 연산자를 사용하여 여러 스타일을 함께 결합   있습니다.


lpfnWndProc: 
 클래스에서 만든 윈도우를 담당하는 윈도우 프로시저의 주소


cbClsExtra: 
윈도우 클래스 구조체에 따라 할당  여분의 바이트 수를 지정합니다. 운영 체제는  바이트를 우선 0으로 초기화합니다. 여기에 윈도우 클래스  데이터를 저장할  있습니다.


cbWndExtra:  

윈도우 인스턴스 따라 할당  여분의 바이트 수를 지정합니다. 운영 체제는 바이트를 0으로 초기화합니다. 응용 프로그램에서 리소스 파일에서 CLASS 지시문으로 대화 상자를 생성하는 경우 WNDCLASS 구조체를 사용하여 등록합니다. 이때,  멤버를 DLGWINDOWEXTRA 설정 해야만 합니다.

hInstance: 
모듈의 인스턴스 핸들

hIcon: 
아이콘 핸들, LoadIcon 호출로 얻을  있습니다.

hCursor: 
아이콘 핸들, LoadIcon 호출로 얻을  있습니다

hbrBackground: 
클래스로부터 생성된 윈도우들의 배경 색깔

lpszMenuName: 
클래스로부터 생성된 윈도우들의 기본 메뉴 핸들

lpszClassName: 
윈도우 클래스로의 이름

hIconSm: 
윈도우 클래스와 관련된 작은 아이콘을 처리합니다.  멤버가 NULL이면, 
시스템은 hIcon 멤버가 지정한 아이콘 리소스를 검색하여 작은 아이콘으로 사용할 적절한 크기의 아이콘을 찾습니다.
   invoke CreateWindowEx, NULL,\ 
         		ADDR ClassName,\ 
           		ADDR AppName,\ 
         		WS_OVERLAPPEDWINDOW,\ 
                CW_USEDEFAULT,\ 
                CW_USEDEFAULT,\ 
                CW_USEDEFAULT,\ 
                CW_USEDEFAULT,\ 
                NULL,\ 
                NULL,\ 
                hInst,\ 
                NULL

윈도우 클래스를 등록한 후에는 CreateWindowEx 호출하여 완성   클래스를 기반으로 윈도우을 만들  있습니다 함수에는 12 개의 매개 변수가 있습니다.

CreateWindowExA proto dwExStyle:DWORD,\ 
   lpClassName:DWORD,\ 
   lpWindowName:DWORD,\ 
   dwStyle:DWORD,\ 
   X:DWORD,\ 
   Y:DWORD,\ 
   nWidth:DWORD,\ 
   nHeight:DWORD,\ 
   hWndParent:DWORD ,\ 
   hMenu:DWORD,\ 
   hInstance:DWORD,\ 
   lpParam:DWORD

다음은 각 파라메터에 대한 상세 대해서 알아보도록 하겠습니다:

dwExStyle: 
추가 윈도우 스타일이것은 이전 CreateWindow 추가   매개 변수입니다. Windows 95  NT   윈도우 스타일을 여기에 입력   있습니다. dwStyle에서 일반 윈도우 스타일을 지정할  있지만 최상위 윈도우와 같은 특수 스타일이 필요한 경우 여기에 지정 해야 합니다추가 윈도우 스타일을 원하지 않으면 NULL 사용할  있습니다.


lpClassName: (필수). 
 윈도우에 대한 템플릿으로 사용할 윈도우 클래스의 이름을 포함하는 ASCIIZ 문자열의 주소클래스는 사용자 고유의 등록  클래스 또는 사전 정의  윈도우 클래스가   있습니다위에서 설명한 것처럼 모든 윈도우들은 윈도우 클래스를 기반으로 만들어야 합니다.


lpWindowName: 
윈도우의 이름을 포함하는 ASCIIZ 문자열의 주소윈도우 제목 표시 줄에 표시됩니다 매개 변수가 NULL이면 윈도우의 제목 없는 표시 줄이 됩니다.


dwStyle:  
윈도우의 스타일여기에 윈도우의 모양을 지정할  있습니다. NULL 넘기는 것은 괜찮지만 시스템 메뉴 상자가 없고 최소화/최대화 버튼도 없으며 닫기 버튼도 없는 윈도우가 만들어 집니다별로 쓸모 없는 윈도우가 만들어 진다는 거죠닫으려면 Alt + F4 눌러야 합니다가장 일반적인 윈도우 스타일은 WS_OVERLAPPEDWINDOW입니다윈도우 스타일은 비트 플래그로 조합하여 만들  있습니다따라서 "or"연산자로 여러 윈도우 스타일을 결합하여 원하는 윈도우 모양을 얻을  있습니다. WS_OVERLAPPEDWINDOW 스타일은 실제로  방법으로 가장 일반적인 윈도우 스타일을 조합하여 만들어  것입니다.


X,Y: 
윈도우의 왼쪽  모퉁이의 좌표입니다일반적으로  값은 CW_USEDEFAULT 여야 합니다, Windows에서 바탕 화면에 윈도우의 위치를 결정   있습니다.


nWidth, nHeight: 
윈도우 픽셀단위의 너비와 높이. CW_USEDEFAULT 윈도우가 적당한 너비와 높이를 가지게 설정   있습니다.

 
hWndParent: 
윈도우의 부모 윈도우에 대한 핸들 (있는 경우).  매개 변수는 윈도우가 다른 윈도우의 하위 (종속)인지 여부와 그렇지 않은 윈도우를 부모에게 알려줍니다다중 문서 인터페이스 (MDI) 부모 - 자식 관계의 내용은 아닙니다하위 윈도우는 상위 윈도우의 클라이언트 영역에 바인딩 되지 않습니다 관계는 특히 Windows 내부 용입니다상위 윈도우가 제거되면 모든 하위 윈도우가 자동으로 삭제됩니다정말 간단하고   있습니다 예에서는 하나의 윈도우만 있기 때문에  매개 변수를 NULL 지정합니다.


hMenu: 
윈도우 메뉴의 핸들클래스 메뉴를 사용하려 한다면 NULL 지정합니다. WNDCLASSEX 구조체의 멤버  lpszMenuName 다시 살펴보도록 합시다. lpszMenuName  클래스의 * default * 메뉴를 지정합니다  클래스에서 생성  모든 윈도우는 기본적으로 동일한 메뉴를 갖습니다.  hMenu 매개 변수를 통해 특정 윈도우에 대한 * 재정의 * 메뉴를 지정하지 않는 ,  hMenu 실제로 이중 목적의 매개 변수가 됩니다생성하려는 윈도우가 미리 정의  윈도우 유형 ( : 컨트롤 경우 해당 컨트롤은 메뉴를 소유   없습니다대신 hMenu 해당 컨트롤의 ID 사용됩니다. Windows hMenu 실제로 lpClassName 매개 변수를보고 메뉴 핸들인지 제어 ID인지 여부를 결정할  있습니다미리 정의  윈도우 클래스의 이름  경우 hMenu 컨트롤 ID입니다그렇지 않은 경우 윈도우 메뉴에 대한 핸들입니다.


hInstance: 
윈도우를 생성하는 프로그램 모듈의 핸들 인스턴스


lpParam: 
윈도우에 전달  데이터 구조의 선택적 포인터이것은 MDI 창에서 CLIENTCREATESTRUCT 데이터를 전달하는  사용됩니다일반적으로  값은 NULL 설정됩니다,  CreateWindow () 통해 데이터가 전달되지 않습니다 창은 GetWindowLong 함수를 호출하여  매개 변수의 값을 검색   있습니다.
    mov   hwnd,eax 
    invoke ShowWindow, hwnd,CmdShow 
    invoke UpdateWindow, hwnd

CreateWindowEx에서 성공적으로 반환되면 윈도우 핸들이 eax 반환됩니다우리는 향후 사용을 위해  값을 보존해야 합니다방금 만든 창은 자동으로 표시되지 않습니다. ShowWindow 윈도우 핸들과 창의 * 표시 상태 * 호출하여 화면에 표출되도록 해야합니다다음으로 UpdateWindow 호출하여 윈도우에 클라이언트 영역을 다시 칠하도록 지시   있습니다 기능은 클라이언트 영역의 내용을 업데이트하고자   유용합니다 호출은 생략   있습니다.

 .WHILE TRUE 
 	invoke GetMessage, ADDR msg,NULL,0,0 
    .BREAK .IF (!eax) 
    invoke TranslateMessage, ADDR msg 
    invoke DispatchMessage, ADDR msg 
   .ENDW

 시점에서 우리의 윈도우가 화면에 나타납니다그러나 외부로부터 입력을 받을 수는 없는 형태입니다그래서 우리는 관련 이벤트에 대해 * 통지 *   있는 메커니즘이 필요합니다이것이 메시지 루프를 통해 수행된다고 보면 됩니다 모듈마다 하나의 메시지 루프  존재합니다 메시지 루프는 GetMessage 호출로 Windows에서 오는 메시지를 계속 확인합니다.

GetMessage MSG 구조체에 대한 포인터를 Windows 전달합니다. MSG 구조체는 Windows에서 모듈의 창으로 보내려는 메시지에 대한 정보로 채워집니다. GetMessage 함수는 모듈에 윈도우에 대한 메시지가 나타날 때까지 반환되지 않습니다 시간 동안 Windows 다른 프로그램을 제어   있습니다

이것은 Win16 플랫폼의 협업 멀티 태스킹 스키마를(cooperative multitasking scheme) 만드는 것입니다.

WM_QUIT 메시지가 수신되면 GetMessage 메시지 루프에서 루프를 종료하고 프로그램을 종료하는 경우 FALSE 반환합니다.

 

TranslateMessage 원시 키보드 입력을 받아서 메시지 대기열에 있는  메시지 (WM_CHAR) 생성하는 유틸리티 함수입니다. WM_CHAR 포함  메시지에는 눌려진 키의 ASCII 값이 포함되어 있으며 원시 키보드 스캔 코드보다 처리하기 쉽습니다프로그램이  입력을 처리하지 않으면  호출을 생략   있습니다. DispatchMessage 메시지가 있는 특정 창을 담당하는  프로시저에 메시지 데이터를 보냅니다.

    mov     eax,msg.wParam 
    ret 
WinMain endp

메시지 루프가 종료되면 종료 코드는 MSG 구조체의 wParam 멤버에 저장됩니다 종료 코드를 eax 저장하여 Windows 되돌려   있습니다현재 Windows 반환 값을 사용하지 않지만 안전한 측면에 있고 규칙에 따라는 것이 좋습니다.

 

WndProc proc hWnd:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM

 

위의 코드는 우리 윈도우 프로시져를 나타냅니다. WndProc이라고 이름을 지을 필요는 없습니다 번째 매개 변수  hWnd 메시지가 예정된 윈도우 핸들입니다. uMsg 메시지입니다. uMsg MSG 구조가 아닙니다정말로 숫자 일뿐입니다. Windows 수백 가지의 메시지를 정의합니다대부분의 경우 프로그램에 관심이 없습니다. Windows 해당 메시지와 관련된 메시지가 있는 경우 해당 메시지를 윈도우에 전송합니다윈도우 프로시저는 메시지를 수신하여 적절하게 응답합니다. wParam  lParam 일부 메시지에서 사용하기 위한 추가 매개 변수입니다일부 메시지는 메시지 자체 이외에 데이터도 함께 전송합니다이러한 데이터는 lParam  wParam 통해 윈도우 프로시저에 전달됩니다.

    .IF uMsg==WM_DESTROY 
        invoke PostQuitMessage,NULL 
    .ELSE 
        invoke DefWindowProc,hWnd,uMsg,wParam,lParam 
        ret 
    .ENDIF 
    xor eax,eax 
    ret 
WndProc endp

 

여기에 우리가 관심을 가져야  중요한 부분이 있습니다위의 코드가 프로그밍을 위한 대부분의 로직을 설계하는 곳입니다 Windows 메시지에 응답하는 코드는 윈도우 프로시저에 있습니다코드에서 관심 있는 메시지인지 확인하려면 Windows 메시지를 확인 해야 합니다관심 있는 메시지일 경우 해당 메시지에 대한 응답으로 수행  작업을 처리  다음 eax 0 반환하십시오그렇지 않은 경우 DefWindowProc 호출 하면 됩니다기본 처리를 위해 받은 모든 매개 변수를 다시  함수에 전달해야 합니다 DefWindowProc 프로그램에서 관심이 없는 메시지를 처리하는 API 함수입니다.

 

응답해야 하는 유일한 메시지는 WM_DESTROY입니다 메시지는 창을 닫을 때마다 윈도우 프로시저로 전송됩니다윈도우 프로시저가  메시지를 받으면 윈도우가 이미 화면에서 제거됩니다이것은 윈도우가 파괴되었다는 알림  뿐이므로 Windows 돌아가기 위한 준비작업을 수행 해야 합니다이에 대한 응답으로 Windows 돌아 가기 전에 정리 작업을 수행   있습니다 상태가 되면 시스템을 빠져 나갈  밖에 없습니다사용자가 창을 닫지 못하도록 하려면  WM_CLOSE 메시지를 처리 해야만 합니다이제

하우스 키핑 작업을 수행   WM_DESTROY 돌아가서 WMQQUIT 메시지를 해당 모듈에게 게시   있는 PostQuitMessage 호출 해야 합니다. WM_QUIT GetMessage eax 0 값을 반환 하도록 하며차례로 메시지 루프를 종료하고 Windows 종료합니다.  DestroyWindow 함수를 호출하여 자신의 윈도우 프로시저에 WM_DESTROY 메시지를 보낼  있습니다.

 

이상.

 

728x90