블로그 이미지
대갈장군

calendar

1 2
3 4 5 6 7 8 9
10 11 12 13 14 15 16
17 18 19 20 21 22 23
24 25 26 27 28 29 30
31

Notice

2012. 12. 18. 03:08 프로그래밍/Windows API

나중에 Copy & Paste 신공을 위해서 코드를 적어둠...


DWORD err;                    // Error Code

TCHAR errMes[1024];      // Error Message Buffer


// Do something here that can fail


err = GetLastError();


FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, err, 0, errMes, 1024, NULL);


wsprintf(str, "Error Code = %d, Message = %s", err, errMes);


MessageBox(hWnd, str, "ERROR", MB_OK");


posted by 대갈장군
2011. 3. 12. 04:33 프로그래밍/Windows API
드디어 실제로 메모리 주소 공간을 들여다 볼 차례...

http://technet.microsoft.com/en-us/sysinternals/dd535533 에서 VMMap 프로그램을 다운 받아서 실행시키면 현재 내 컴퓨터에서 돌아가고 있는 프로세스의 주소 공간을 들여다 볼 수가 있다.


김상형님의 책에 있는 샘플 프로그램을 돌리고 있는 샘플 프로세스의 메모리 주소 공간을 보여준다. 알다시피 프로세스는 총 4GB의 주소 공간을 가지지만 실질적으로 사용자가 사용하는 주소 공간은 0x00010000 부터 0x7FFEFFFF 이다. 대략 2GB 이다. 0x00000000 부터 0x0000FFFF 까지는 준비된 영역으로 접근시 Access Violation이 일어난다. 그리고 2GB 이후의 공간은 운영체제가 필요로 하는 주소 공간이다. 뭔 2GB 씩이나 쓰냐고 하겠지만 2GB도 작아서 3GB로 확장 하게 만들어 놨을 정도다.

아무튼, 위 그림에서 보면 아래쪽에 좌악 주소 공간의 크기 순으로 정렬되어 있는데 타입이 여러가지가 있다. 우선 Free는 할당 되지 않은 공간을 말하고 Private Data의 경우는 시스템의 페이징 파일에 맵핑 되어 있는 것을 말한다. 그리고 Image의 경우는 exe나 DLL 같은 이미지 파일을 맵핑 하고 있는 것을 말하고 Mapped File은 당연히 메모리 맵에 맵핑된 데이터를 말한다. 그리고 Heap도 보이는데 이것도 사실은 Private Data로 분류되고 있다.  

이렇게 펼쳐놓고 보니 메모리 별거 아니다... ㅡ.ㅡ 라고 믿고 싶다. 일단 메모리의 구조에 대한 이해가 끝났다면 다음으로 고고고~
posted by 대갈장군
2011. 3. 12. 04:15 프로그래밍/Windows API
마지막으로 힙과 메모리 맵이 남았다. 힙은 프로세스의 주소 공간 상의 예약 영역으로 가상 메모리 (VirtualAlloc 함수) 방식에 비해서 훠어얼씨인 더 효율적이다. 다만 '예약' 상태가 없기 때문에 편리성은 쬐에끔 떨어진다.

간단하게 결론만 말해서 작은 데이터의 연속적인 생성과 삭제 그리고 접근이 필요하다면 당연히 힙을 이용해야 한다. 만약 거대 메모리를 할당해야 한다면 VirtualAlloc 함수를 이용하여 적극적인 '예약'을 통해 보다 효율적으로 주어진 주소 공간을 사용하면 된다. 그리고 마지막으로 무지막지하게 큰 데이터를 불러와야 하거나 프로세스 간에 서로 메모리를 공유해야 한다면 최선의 방식은 메모리 맵이다.

힙의 할당과 해제는 다음과 같은 명령으로 수행 가능하다.
ptrHeap = HeapAlloc(GetProcessHeap(), 0, sizeof(int));
HeapFree(GetProcessHealp, 0, ptrHeap);

사실 힙은 사용하는 방법은 간단한 편인데 왜 힙이 좋은 녀석인가에 대한 논의가 좀 필요하다. 우선 힙이 어떤 원리를 가지는가 알아보면, 힙은 프로세스 실행시 기본으로 1MB의 영역을 예약하게 된다. 중요한 것은 이 주소 공간은 프로세스가 가지는 다수의 스레드에서 접근 가능하므로 운영체제는 힙에 대해 순차적 접근 및 처리를 기본적으로 하게 된다. 다수의 스레드에 의한 경쟁 상태를 방지 하기 위해서 이다.

고로 바로 이 순차적 처리 부분에서 성능의 저하가 약간 있을 수 있다. 하지만 힙은 여러개 생성할 수 있으며 생성시에 HEAP_NO_SERIALIZE 플래그를 주면 동기화 문제를 고려하지 않고 즉각적인 접근과 변경을 허용하게 된다.

만일 다수의 스레드가 접근해서 읽기와 쓰기를 하는 경우라면 반드시 순차적 접근을 하도록 내버려 두어야 하지만 만약 사용자가 단일 스레드를 위한 하나의 힙을 생성하고 그 스레드만 접근한다는 보장이 된다면 순차적 접근을 무시하도록 하여 보다 빠른 성능을 내게 할 수 있다.

또한 하나의 통일된 힙에 모든 자료와 데이터가 섞이게 되는 경우 자료 A의 오류로 인한 자료 B로의 침범이 일어났을때 에러가 발생하면 운영체제는 자료 B에 의한 에러로 오판 할 수 있다. 

적절한 새로운 힙의 생성은 메모리 공간을 보다 효율적으로 사용하게 해주는 방법이 되기도 한다. 다양한 크기의 자료가 뒤섞여있는 힙에서는 조각화 현상이 나타나게 되므로 실제로 비어있는 여유 공간이 추가적인 데이터를 삽입하기에 충분하더라고 데이터의 배열 문제로 공간이 없다고 생각하게 된다. 이런 문제는 같은 사이즈를 가지는 데이터 별로 힙을 생성해 관리하면 쉽게 해결된다. 

그런 식으로 함으로써 추가적으로 유사한 데이터들이 한 주소 영역에 뭉치는 결과를 가져오며 이것은 시스템 램과 페이징 파일 사이의 스와핑을 줄여준다. 사실 이 스와핑이야 말로 성능 저하의 가장 큰 주범인데 이것을 예방 할 수 있다면 아주 효과적이라고 할 수 있겠다.

그리고 앞서 말했듯이 단일 스레드가 단일 힙을 엑세스 한다는 조건만 만족시켜주면 순차적 접근을 할 필요가 없으므로 동기화 비용을 줄일 수 있다. 

마지막으로 빠른 해제도 가능하다. 해제해야 할 데이터가 한군데 모여 있기 때문에 해제가 매우 빠르다. 마구 마구 섞여 있는 경우에는 해제도 번거롭다.

이런 여러가지 이유 때문에 힙은 아주 유용하게 쓰일 수 있는 놈이다. 

이런 장점을 이용해 클래스의 오브젝트 생성시 힙을 따로 할당하는 방법을 제프리 리처의 WIndows via C/C++ 책에서는 제시하고 있다. 작은 데이터 구조를 여러개 관리하는 프로그램에서는 매우 효과적인 클래스 관리법이 될 수 있을 것 같다.

메모리 맵은 하드디스크 공간에 존재하는 파일을 메모리로 맵핑 시키는 기법이다. 이로써 서로 다른 프로세스가 하나의 맵 파일을 공유할 수 있고 프로세스 간에 자료 교환이 가능하다. 


posted by 대갈장군
2011. 3. 11. 23:54 프로그래밍/Windows API
윈도우에서 메모리를 할당하는 다음 방법은 VirtualAlloc() 함수다. 이 함수는 malloc 함수에서 보다 발전된 형태로 사용자에게 여러 가지 추가 기능을 제공한다.

가장 큰 차이점은 '예약'과 '확정' 상태가 존재한다는 것인데 malloc의 경우 '확정' 상태만 있었다. 여기에 예약이 추가 되었는데 이로써 보다 효율적인 메모리 사용이 가능해 졌다. 예약만 한 경우 물리적 메모리는 전혀 소모되지 않는다. 오직 확정에만 가상 메모리가 실제로 할당된다.

함수의 원형은 다음과 같다.
LPVOID WINAPI VirtualAlloc( __in_opt  LPVOID lpAddress, __in      SIZE_T dwSize, __in      DWORD flAllocationType, __in      DWORD flProtect );

첫번째 인자가 할당하고자 하는 주소의 위치 (4GB의 프로세스 주소 공간에서), 두번째 인자는 할당할 크기, 세번째 인자는 할당 타입인데 바로 여기서 확정, 예약, 높은 번지 할당등을 설정할 수 있다. 자세한 내용은 다음 표 참조. 마지막 인자는 할당할 페이지의 엑세스 타입 설정을 하는데 이것도 malloc과의 다른점 중에 하나다. 읽기 쓰기 접근 제한 설정이 가능하다.

ValueMeaning
MEM_COMMIT
0x1000

Allocates physical storage in memory or in the paging file on disk for the specified reserved memory pages. The function initializes the memory to zero.

To reserve and commit pages in one step, call VirtualAlloc with MEM_COMMIT | MEM_RESERVE.

The function fails if you attempt to commit a page that has not been reserved. The resulting error code is ERROR_INVALID_ADDRESS.

An attempt to commit a page that is already committed does not cause the function to fail. This means that you can commit pages without first determining the current commitment state of each page.

MEM_RESERVE
0x2000

Reserves a range of the process's virtual address space without allocating any actual physical storage in memory or in the paging file on disk.

You can commit reserved pages in subsequent calls to the VirtualAlloc function. To reserve and commit pages in one step, call VirtualAlloc with MEM_COMMIT | MEM_RESERVE.

Other memory allocation functions, such as malloc and LocalAlloc, cannot use a reserved range of memory until it is released.

MEM_RESET
0x80000

Indicates that data in the memory range specified by lpAddress and dwSize is no longer of interest. The pages should not be read from or written to the paging file. However, the memory block will be used again later, so it should not be decommitted. This value cannot be used with any other value.

Using this value does not guarantee that the range operated on with MEM_RESET will contain zeroes. If you want the range to contain zeroes, decommit the memory and then recommit it.

When you specify MEM_RESET, the VirtualAlloc function ignores the value offlProtect. However, you must still set flProtect to a valid protection value, such as PAGE_NOACCESS.

VirtualAlloc returns an error if you use MEM_RESET and the range of memory is mapped to a file. A shared view is only acceptable if it is mapped to a paging file.

간단한 예를 보면, 


ptr = (int *)VirtualAlloc(NULL, sizeof(int)*10, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
VirtualFree(ptr, sizeof(int)*10,MEM_DECOMMIT);
VirtualFree(ptr, 0, MEM_RELEASE);
VirtualFree에 대한 설명이 빠졌는데 메모리 해제 함수다. MEM_DECOMMIT은 확정 해제, MEM_RELEASE는 예약 해제다. 메모리 할당도 MEM_RESERVE와 MEM_COMMIT를 같이 불러 호출하거나 따로 각각 두번 호출해야 한다. malloc보다 좀 번거롭긴 하다.

예약을 할 수 있다는 차이점 이외에도 할당 단위의 차이가 있다. 일단 일반적인 컴퓨터를 기준으로 봤을때 4K 사이즈로 할당이 이루어 진다. 즉, 내가 10K 할당을 요구하면 4의 배수인 12K로 할당한다는 말. 그리고 할당을 시작하는 할당 단위가 있다. 보통의 경우 64K 단위로써 매우 큰 편이다. 즉, 작은 사이즈의 메모리를 여러번 반복적으로 할당하게 되면 할당을 시작하는 단위를 64K로 끊기 때문에 메모리의 조각화가 심해진다. 

그래서 작은 메모리의 할당에는 malloc이 훨씬 더 경제적이다. 그런 경우에는 다음에 알아볼 힙을 사용하는 것도 좋은 방법이다. 

VirtualAlloc의 다른 재미있는 점은 접근 권한 설정이 가능하다는 점인데 너무 보안에 신경을 쓴게 아닌가 싶다.. ㅡ.ㅡ 또 다른 한가지 흥미로운 점은 메모리 잠금 기능인데 할당된 메모리를 물리적 RAM에 상주 하도록 잠그는 함수다. 이로써 보다 빠른 접근 속도를 제공하는데 잘못 사용하면 사용가능한 물리적 RAM을 제한함으로써 운영체제 전체에 속도저하를 유발할 수 있다. 

내가 봤을때 마이크로 소프트에서 야심차게 내놓은 메모리 할당 / 해제 함수인 것 같은데 소규모 프로그램 개발자에게는 그닥 매력적이지 않다. 물론 가능한 기능들은 malloc에 비해서 많고 예약과 확정이라는 효율적인 메커니즘을 제공하기는 하지만 작은 용량을 가지는 다수의 메모리를 반복적으로 할당해야 하는 경우 VirtualAlloc은 메모리 낭비가 너무 심히다.



'프로그래밍 > Windows API' 카테고리의 다른 글

윈도우 메모리 파이널!  (0) 2011.03.12
윈도우 메모리 - 4 (힙과 메모리 맵)  (0) 2011.03.12
윈도우 메모리 - 2 (malloc 함수)  (2) 2011.03.11
윈도우 메모리 - 1  (0) 2011.03.10
윈도우 - 차일드, 팝업  (0) 2009.11.17
posted by 대갈장군
2011. 3. 11. 03:20 프로그래밍/Windows API
앞서 언급했던 윈도우 메모리에 관한 이야기 2탄으로 바로 malloc() 함수에 대한 이야기다.

malloc은 뭐 워낙 유명한 함수라서 굳이 이야기 안해도 사용하는 방법쯤은 다 알거라 생각한다. 바로 코드로 들어가 보자.

   
   1:  #include <stdio.h>
   2:  #include <stdlib.h>
   3:  #include <string.h>
   4:   
   5:  #ifdef _DEBUG
   6:  #include <vld.h>
   7:  #include <vldapi.h>
   8:  #endif
   9:   
  10:  void main()
  11:  {
  12:      // Test int
  13:      int *ptr;
  14:      ptr = (int*)malloc(sizeof(int)*10);
  15:   
  16:      for(int i = 0; i < 10; i++)
  17:      {
  18:          ptr[i] = i * 10;
  19:          printf("ptr[%d] = %d\n", i, ptr[i]);
  20:      }
  21:   
  22:      free(ptr);
  23:   
  24:      // Test char
  25:      char *ptr_char;
  26:      ptr_char = (char *)malloc(sizeof(char)*20);
  27:   
  28:      char *str1 = "MEMORY LEAK";
  29:      strcpy(ptr_char, str1);
  30:   
  31:      printf("ptr_char = %s\n", ptr_char);
  32:   
  33:      free(ptr_char);
  34:   
  35:      // Test array of pointers
  36:      int **double_ptr;
  37:   
  38:      double_ptr = (int **)malloc(sizeof(int *)*10);
  39:   
  40:      for(int i = 0; i < 10; i++)
  41:      {
  42:          double_ptr[i] = (int *)malloc(sizeof(int)*10);
  43:          for(int j = 0; j < 10; j++)
  44:          {
  45:              double_ptr[i][j] = (i * 10) + j;
  46:              printf("double_ptr[%d][%d] = %d\n", i, j, double_ptr[i][j]);
  47:          }
  48:      }
  49:   
  50:      for(int i = 0; i < 10; i++)
  51:      {
  52:          free(double_ptr[i]);
  53:      }
  54:   
  55:      free(double_ptr);
  56:   
  57:  }

위의 코드의 제일 윗부분에 라인 6번과 7번을 보면 vld.h 라는 녀석이 있는데 이 녀석은 이른바 visual leak detector라고 불리는 오픈 소스로서 메모리 누수가 발생하면 콘솔 출력창에 어디서 뭐가 왜 어떻게 유출되었는지 상세 정보를 보여주는 아주 착한 놈이다.

이 오픈 소스는 http://www.codeproject.com/KB/applications/visualleakdetector.aspx 에 가면 다운 받을 수 있다.

코드는 세 파트로 나뉘어져 있는데 첫번째 파트에서는 간단하게 10개의 int 메모리를 할당하고 0 에서 9까지 넣어 출력했고 두번째 파트는 char 메모리 공간을 생성하여 MEMORY LEAK라는 섬뜩한 문구를 출력해 봤고 마지막은 이중 포인터를 이용해서 2차원 int 배열을 생성하여 0부터 99까지 각 배열에 대입 후 출력해 봤다.

암튼, 22번 라인, 33번 라인 그리고 50번에서 55번까지 free()로 할당된 메모리를 해제하는 명령들이 나와있는데 메모리는 할당보다 해제가 더 중요하다. 화장은 하는 것보다 지우는 것이 더 중요한 것 처럼... 

예를 들어 두번째 테스트는 Test char 파트에서 free()를 호출 안하면 (라인 33번 지우면), 다음과 같은 에러가 프로그램 종료 후 출력창에 뜬다.


너무나도 친절하게 얼마만큼의 메모리가 누수되었는지 그리고 들어있던 내용은 무엇인지 그리고 게다가 코드의 어느 부분에서 누수가 있었는지 알려준다... 너무 친절해서 눈물이 날 지경이다..

라인 33번에서 55번까지는 이중 포인터의 개념인데 integer 값을 10개 저장하는 배열을 10개 만들었다. 즉, 10 x 10 = 100 개의 int 배열을 만든 셈이다. 중요한 것은 해제인데 해제 할때는 10개의 개별 배열을 모두 하나씩 해제한 후 마지막에 이중 포인터를 해제 한다. 만약 개별 배열들을 해제하지 않고 이중 포인터만 해제해 버리면 10개의 개별 배열은 부모 잃어버린 자식들이 되므로 메모리 누수가 일어난다. 

개별 배열(자식)을 해제 하기 전에 이중 포인터 (부모)를 날려 버리면 자식들을 찾을 수가 없다. 왜냐면 부모 연락처가 없기 때문에... 

malloc을 이용한 할당은 가장 경제적인 메모리 할당이다. 왜냐면 바이트 단위로 사용자가 원하는 만큼 할당하기 때문이다. 좀 있다 살펴볼 다른 메모리 할당 함수는 malloc 처럼 바이트 단위로 할당을 하지 않는다. 경제적인 것은 그만큼 손이 많이 간다. 고로 해제하는 과정을 사용자가 반드시 꼼꼼히 해야 한다는 불편함이 있다. 


posted by 대갈장군
2011. 3. 10. 05:56 프로그래밍/Windows API
보다 나은 개발을 위해서는 윈도우 메모리에 대해서 전반적인 이해가 필요로 된다. 내가 김상형님의 책을 좋아하는 이유는 김상형님은 책을 쓰실때 항상 '왜' 라는 질문에 대답을 해주시기 때문이다.

과거 16비트 시절의 메모리 구조는 한 마디로 요약하자면 '위험천만'이었다. 가장 큰 문제는 사용자가 잘못 작성한 프로그램이 건드려서는 안되는 중요한 메모리 영역을 건드릴 수 있었다는 점이다. 메모리가 '전역 변수' 처럼 모든 프로그램 및 운영체제에게 통일된 표기 방식으로 공개 되어 있었기 때문이다.

그래서 실수로 운영체제의 영역을 지우거나 바꿔버리면 심지어 윈도우를 다시 깔아야 하는 말도 안되는 불상사가 종종 있었다고 한다. 안타깝게도 나는 그런 일을 겪어볼 행운(?)이 없었다. 

그러면서 32비트 운영체제가 도입되고 윈도우 95/98로 넘어오면서 드디어 새로운 메모리 체계가 완성된다. 바로 '가상 메모리' 시스템이다. 

가상 메모리는 간단히 말해서 물리적 메모리 + 페이징 파일이다. 여기서 물리적 메모리는 우리가 너무나 너무나 잘 알고 있는 바로 RAM이고 페이징 파일은 RAM을 흉내내고 있는 하드 디스크의 일부를 말한다.

RAM은 속도가 빠르지만 용량이 충분하지 않고 페이징 파일은 하드디스크를 이용하므로 용량은 방대하나 RAM에 비해 속도가 떨어진다.

하지만 이것이 전부가 아니다. 가상 메모리를 이용하는 운영체제는 각 프로그램이 운영체제가 제공하는 최대 주소 공간을 각 프로그램에게 할당해 준다. 즉, 32비트 운영체제라면 4GB의 주소 공간을 각 프로그램 다시 말하자면 하나의 프로세스에 할당해 준다.

이 4GB의 주소 공간은 사실 껍데기에 불과하며 물리적 메모리가 아니다. 예를 들어 malloc 함수를 통해 메모리 할당을 요청하면 프로그램이 가지고 있는 4GB 주소 공간의 어딘가에 요청한 메모리가 할당되었다고 알려주지만 실제로는 가상 메모리에 실질적인 메모리가 할당된 후 할당된 주소가 '페이지 테이블'이라고 불리는 맵에 저장되고 4GB에 할당 된 것 처럼 보이는 메모리는 실제로는 가상 메모리로 자동 연결되게 되어 있다.

이런 식의 이중 연결을 구현하는 이유는 이렇게 함으로써 이 프로그램이 다른 프로그램의 주소 영역, 혹은 운영체제의 고유 메모리 영역을 아예 터치 할 수 없도록 차단해 주기 때문이다. 고로 프로세스 A가 망해도 A만 망하지 B,C,D 기타 운영 체제에게는 눈꼽만큼의 영향도 안준다.

이는 16비트 운영체제의 단점을 한방에 커버 시켜주는 그야 말로 최대 강점이다. 최대 강점이면서 동시에 최대 단점이다. 왜냐면 프로세스간에 통신이 겁나 복잡해 졌기 때문이다. 보안성의 강화는 접근성을 약화 시킬수 밖에 없다.

다음 글타래에서는 가상 메모리 시스템에서 메모리를 할당하는 다양한 방법에 대해서 알아보자. 


posted by 대갈장군
2009. 11. 17. 05:01 프로그래밍/Windows API
차일드 윈도우는 자식 윈도우다. 차일드 윈도우를 생성할때는 CreatWindow() 함수에서 3번째 인수값으로 WS_CHILD 플래그를 넣고 8번째 인자에 부모 윈도우의 핸들을 주면 된다. 

고로 상식적으로 부모 윈도우에 딸린 것이 차일드 윈도우이므로 부모 윈도우 내부 영역에서만 차일드 윈도우는 유효하다. 차일드 윈도우도 윈도우 이므로 임의의 WndProc 함수를 가질수 있다. 고로 독립적인 행동이 가능하다. 

차일드 윈도우는 부모가 파괴될 때 자동으로 같이 파괴되므로 WM_DESTROY에 대한 처리가 필요없다. 행여나 처리를 해서도 안된다. 

팝업 윈도우는 차일드와는 달리 부모 윈도우의 작업 영역을 벗어날 수 있고 부모 윈도우 이동시에도 자신의 위치를 고수한다. (차일드의 경우는 부모 윈도우와 같이 움직인다.) 

'프로그래밍 > Windows API' 카테고리의 다른 글

윈도우 메모리 - 2 (malloc 함수)  (2) 2011.03.11
윈도우 메모리 - 1  (0) 2011.03.10
윈도우 - SetClassLongPtr(), SetWindowLongPtr()  (0) 2009.11.17
윈도우 - WNDCLASS 구조체  (0) 2009.11.17
윈도우 - CreateWindow()  (0) 2009.11.17
posted by 대갈장군
2009. 11. 17. 04:50 프로그래밍/Windows API
윈도우 클래스 와 윈도우는 실행 중에도 그 속성을 변경할 수 있다. 다시 말해서 윈도우 클래스에서 설정했던 각종 속성을 실행중 변경가능 하다는 말이다. 이것이 대단한 이유는 서브클래싱이라는 기법을 가능하게 해주기 때문이다.

서브 클래싱이란 윈도우마다 설정되어 있는 동작 함수인 WndProc를 실행중에 교체함으로써 전혀 다른 동작을 할수 있게 만들어주는 기법이다. 

아무튼, 총 8가지의 윈도우 클래스 / 윈도우 속성 설정 함수가 있으나 64비트 운영체제가 도입된 이후로는 기왕이면 Ptr이 붙은 64 비트용 함수를 사용하는 것이 범용성 면에서 유리하다.

윈도우 클래스 속성 설정 함수는 (WNDCLASS 변경) GetClassLongPtr()과 SetClassLongPtr() 이고, 윈도우 속성 설정 함수는 GetWindowLongPtr()과 SetWindowLongPtr() 함수다. 여기서 윈도우 프로시져 함수의 주소를 바꾸고 싶다면 GWLP_WNDPROC를 인수로 넣으면 된다. 

주의 할 것은 스타일이 플래그 형식으로 비트의 On/Off에 의해서 판단하므로 대입연산이 아니라 & 또는 | 연산으로 플래그를 설정해야 한다는 점이다.


'프로그래밍 > Windows API' 카테고리의 다른 글

윈도우 메모리 - 1  (0) 2011.03.10
윈도우 - 차일드, 팝업  (0) 2009.11.17
윈도우 - WNDCLASS 구조체  (0) 2009.11.17
윈도우 - CreateWindow()  (0) 2009.11.17
메모리, Win32 vs. Win16  (2) 2009.05.22
posted by 대갈장군
2009. 11. 17. 03:34 프로그래밍/Windows API
CreateWindow() 함수를 사용하여 응용 프로그램 로컬 윈도우 클래스를 생성하고자 한다면 WNDCLASS 구조체를 채워넣어야 한다.

typedef struct {
    UINT style;
    WNDPROC lpfnWndProc;
    int cbClsExtra;
    int cbWndExtra;
    HINSTANCE hInstance;
    HICON hIcon;
    HCURSOR hCursor;
    HBRUSH hbrBackground;
    LPCTSTR lpszMenuName;
    LPCTSTR lpszClassName;
} WNDCLASS, *PWNDCLASS;

이 구조체 중에서 중요한 3가지는 lpszClassName, lpfnWndProc, 그리고 hInstance 요소로서 이 세놈은 기본값이 없으므로 생략할 수 없다. 

lpszClassName이 바로 CreatWindow() 함수의 첫번째 인자가 될 것이므로 되도록이면 만들고자 하는 윈도우를 잘 나타내는 이름을 지어주자. 

hInstance는 이 윈도우 클래스를 등록한 응용 프로그램의 인스턴스 핸들이다. 여기서 지정된 응용프로그램이 종료되면 윈도우 클래스도 같이 파괴된다. 주로 WinMain 으로 전달된 hInstance 인수를 그대로 사용하면 된다.

마지막으로 lpfnWndProc는 메시지 처리 함수다. 뭐, 이놈이 사실 내가 만드는 윈도우의 모든 동작을 정의한다. 

이 외에 style 인자가 재미 있는 녀석인데 윈도우의 스타일을 지정하는 인수다. 지정할 수 있는 것들로는 
StyleAction
CS_BYTEALIGNCLIENT Aligns the window's client area on a byte boundary (in the x direction). This style affects the width of the window and its horizontal placement on the display.
CS_BYTEALIGNWINDOW Aligns the window on a byte boundary (in the x direction). This style affects the width of the window and its horizontal placement on the display.
CS_CLASSDC Allocates one device context to be shared by all windows in the class. Because window classes are process specific, it is possible for multiple threads of an application to create a window of the same class. It is also possible for the threads to attempt to use the device context simultaneously. When this happens, the system allows only one thread to successfully finish its drawing operation.
CS_DBLCLKS Sends a double-click message to the window procedure when the user double-clicks the mouse while the cursor is within a window belonging to the class.
CS_DROPSHADOW Windows XP: Enables the drop shadow effect on a window. The effect is turned on and off through SPI_SETDROPSHADOW. Typically, this is enabled for small, short-lived windows such as menus to emphasize their Z order relationship to other windows.
CS_GLOBALCLASS Specifies that the window class is an application global class. For more information, see Application Global Classes.
CS_HREDRAW Redraws the entire window if a movement or size adjustment changes the width of the client area.
CS_NOCLOSE Disables Close on the window menu.
CS_OWNDC Allocates a unique device context for each window in the class.
CS_PARENTDC Sets the clipping rectangle of the child window to that of the parent window so that the child can draw on the parent. A window with the CS_PARENTDC style bit receives a regular device context from the system's cache of device contexts. It does not give the child the parent's device context or device context settings. Specifying CS_PARENTDC enhances an application's performance.
CS_SAVEBITS Saves, as a bitmap, the portion of the screen image obscured by a window of this class. When the window is removed, the system uses the saved bitmap to restore the screen image, including other windows that were obscured. Therefore, the system does not send WM_PAINT messages to windows that were obscured if the memory used by the bitmap has not been discarded and if other screen actions have not invalidated the stored image.

This style is useful for small windows (for example, menus or dialog boxes) that are displayed briefly and then removed before other screen activity takes place. This style increases the time required to display the window, because the system must first allocate memory to store the bitmap.

CS_VREDRAW Redraws the entire window if a movement or size adjustment changes the height of the client area.

여기서 주의해서 볼 필요가 있는 속성 두가지는 CS_DBLCLKS와 CS_NOCLOSE인데 첫번째 놈을 설정하지 않으면 마우스 더블클릭은 무시된다. (WndProc 함수로 전달되지 않는다는 말) 그리고 두번째 놈은 종료 버튼이 없도록 만든다. 심지어 Alt+F4도 안된다. 이 플래그가 설정된 경우 윈도우를 닫기 위해서는 반드시 다른 형태의 종료 방법이 제공되야 한다.


posted by 대갈장군
2009. 11. 17. 02:44 프로그래밍/Windows API
윈도우는 CreateWindow() 함수에 의해 만들어진다. 
HWND CreateWindow(      
    LPCTSTR lpClassName,     LPCTSTR lpWindowName,     DWORD dwStyle,     int x,     int y,     int nWidth,     int nHeight,     HWND hWndParent,     HMENU hMenu,     HINSTANCE hInstance,     LPVOID lpParam );
윈도우 역시 클래스로 부터 만들어지는데 이른바 '윈도우 클래스' 중 하나로 부터 만들어진다. 첫번째 인자 lpClassName은 바로 그 '윈도우 클래스'를 지칭하는 인자이다.

재미 있는 것은 MSDN에도 나와 있듯이, 
lpClassName
[in] Pointer to a null-terminated string or a class atom created by a previous call to the RegisterClass or RegisterClassEx function. The atom must be in the low-order word of lpClassName; the high-order word must be zero. IflpClassName is a string, it specifies the window class name. The class name can be any name registered with RegisterClass or RegisterClassEx, provided that the module that registers the class is also the module that creates the window. The class name can also be any of the predefined system class names. For a list of system class names, see the Remarks section.
이 첫번째 인자에 올수 있는 놈은 Null-Terminated String 혹은 A Class Atom (클래스 부류) 라고 했는데 이 놈들은 RegisterClass 함수나 RegisterClassEx 함수에 의해서 미리 (이전에) 등록되어진 놈이라고 한다.

윈도우 클래스를 크게 두가지 종류로 나누자면 시스템 (운영체제)에서 제공하는 기본 윈도우 클래스와 사용자가 정의해서 사용하는 사용자 윈도우 클래스가 있다.

운영체제가 제공하는 윈도우는 우리가 자주 사용하는 "button" "edit" 과 같은 놈들이고 사용자가 정의하는 윈도우는 WNDCLASS 구조체를 사용자가 원하는 윈도우 형태로 채워넣은 다음 RegisterClass 함수를 이용해서 등록한 후 사용하는 것을 말한다. 

그래서 내가 원하는 형태의 윈도우를 그리고 싶다면,
1. 우선 WNDCLASS 구조체의 각 요소를 내가 원하는 옵션으로 설정한다.
2. RegisterClass 함수를 이용해서 생성한 WNDCLASS 구조체를 등록한다.
3. CreateWindow 함수의 첫번째 인자에 내가 작성한 WNDCLASS 의 이름을 넣어준다. 

조금 복잡하더라도 이렇게 함으로써 사용자는 같은 형태의 윈도우를 쉽게 재생성 할 수 있다. (그것이 WNDCLASS를 생성하고 등록하는 이유)

윈도우 클래스는 범위로 분류하자면 시스템 전역 클래스, 응용 프로그램 전역 클래스 그리고 응용 프로그램 로컬 클래스로 나뉜다.

시스템 전역 클래스는 운영체제가 제공하는 윈도우 클래스들로 부팅하면 자동으로 등록되는 놈이다. 이것들이 바로 기본으로 제공되는 놈들로 button, edit, scrollbar, listbox 등이다.

응용 프로그램 전역 클래스는 주로 DLL에 의해 등록되며 이 DLL을 불러들인 프로세스에서 자유롭게 사용된다. 이것들의 예로는 프로그래스, 트리뷰, 리스트뷰 등이 있다. 의외로 이런 것들이 운영체제에서 제공되는 시스템 전역 클래스가 아니라는 점이다. 고로 윈도우의 버전과 종류에 따라 저런 부분의 구현이나 기능, 색상등이 달라지는 것이다. (DLL의 버전이 다르니까)

마지막으로 응용 프로그램 로컬 클래스는 사용자가 WNDCLASS를 정의하고 RegisterClass 함수로 등록하여 사용하는 윈도우 클래스를 말한다. 중요한 점은 클래스 이름이 중복될 경우 가장 먼저 로컬 클래스 부터 선택하고 그다음이 응용 프로그램 전역 클래스 그리고 마지막이 시스템 전역 클래스이다. 뭐, DLL과 검색 순위가 동일하다만. :)


posted by 대갈장군
2009. 5. 22. 03:08 프로그래밍/Windows API
프로그램을 짜려면 메모리가 참 중요한데... 차근 차근 책을 보며 다시 한번 정리해 보았다.

Win32 는 32비트로 메모리 주소공간을 표현한다는 의미로 Windows 95 부터 사용된 것으로 알고 있다. Win16은 Windows 95가 나오기 전에 사용된 것으로 주소공간을 표현하는 비트의 수가 최대 16 비트로 최대 표현 가능한 주소공간이 2의 16승 밖에 되지 않는다. 2의 16승은 64K 군. 

고로 상위 컴퓨터들이 등장하면서 확장된 메모리 공간 표현을 위해서는 Offset을 추가하는등 복잡한 계산법이 필요했단다... 그러다 보니 자연스레 Win32로의 확장이 이루어 진것인데, 엄밀히 말해서 Win16의 문제점은 두가지였다.

1. 증가하는 메모리 용량을 16비트의 레지스터 공간으로는 쉽게 표현이 불가능했다. 
2. 모든 프로그램이 같은 주소 공간에 존재하였으므로 포인터만 가지고 있으면 수퍼맨 처럼 어디든 액세스 가능.

바로 2번은 심각한 안정성 문제를 초래한다. Win16은 주소 공간과 실제 물리적 메모리가 서로 1대 1 대응 관계를 가지고 있었다. 즉, A라는 응용 프로그램의 포인터 값은 실제 물리적 메모리의 주소이며 이는 B라는 응용 프로그램에서도 마찬가지로 접근 가능한 고유한 값이었다.

하지만 이 개념이 Win32에 들어오면서 확 바뀌는데... 어떻게 바뀌냐면...

1. 가상 메모리의 등장. 가상 메모리 = 물리적 RAM + 페이징 파일 
2. 독립적인 4G 바이트의 가상 주소 공간이 각 응용 프로그램에 할당. 
3. 각각의 4G 바이트 공간은 페이지 테이블을 통해 가상 메모리에 연결. 

일단 가상 메모리가 등장하는데 이놈은 실제 컴퓨터가 가지고 있는 RAM 공간 + 페이징 파일이다. 페이징 파일은 물리적인 하드 디스크 공간을 메모리로 사용하는 것을 말하는데 일반적인 RAM보다 속도가 떨어질다는 것 외에는 RAM과 같은 놈이다.

그리고 각 응용 프로그램 당 4G 바이트라는 어마어마한 가상 주소 공간이 할당 되는데 이 가상 주소 공간은 실제로 할당된 메모리가 아니라 최대 저만큼 쓸수 있다는 말이다. 응용 프로그램이 실제로 무엇을 할당하거나 운영 체제의 모듈을 불러들일 경우 중간자 역활을 하는 페이지 테이블을 통해서 가상 메모리에 메모리 공간을 실제적으로 할당한다.

고로 각 응용 프로그램은 각자가 독립적인 4G 바이트의 주소 공간을 가지므로 서로가 서로에게 간섭을 할수가 없다. 즉, 안정성이 확보된것이다. 

응용 프로그램은 운영 체제가 메모리를 관리 (페이지 테이블에 의한) 하여 줌으로써 그 댓가로 안정성을 획득한 셈이다. 이런 구조의 한가지 단점이라면 프로세스간에 (응용 프로그램 간에) 데이터 교환이 용이 하지 않다는 점이다.

왜냐면 서로가 가지고 있는 주소 공간의 독립적이므로 A라는 응용 프로그램이 가지고 있는 주소 값은 B에서는 엉뚱한 값을 가지고 있을 수 있기 때문이다. 이것을 해결하기 위해 IPC 혹은 메모리 맵 파일을 사용해야 한다.


posted by 대갈장군
2009. 3. 11. 04:45 프로그래밍/Windows API
OpenGL 프로그램을 주로 작성하면 사실 Windows API에 대해서 많은 지식이 필요하지 않지만 늘 듣는 말에 대해서는 알아둘 필요가 있는데 아무래도 넘버 1이 Device Context가 아닌가 싶다.

알고 있으면 쉽지만 모르면 알쏭달쏭한 것이 Device Context이다.

기본적으로 Windows는 세가지 동적 연결 라이브러리 (DLL)로 구성되는데 메모리와 프로그램을 관리하는 KERNEL, 유저 인터페이스와 윈도우를 관리하는 USER, 그리고 화면 처리와 그래픽을 담당하는 GDI이다.

GDI는 풀어서 쓰면 Graphic Device Interface인데 한글로 다시 풀어 설명하면 '그래픽 장치 접속 (접근) 함수'이다.

한글로 풀어쓰니 우끼다.. ㅋㅋㅋ

GDI를 통해서 우리는 화면에 글도 쓰고 그림도 넣는 것이다. 그러므로 간단히 생각해도 GDI와 Device Context (DC)가 관계되어 있다는 것을 알수 있다.

일단 답부터 말하면 DC는 걍 구조체이다. 그 구조체 안에는 각종 멤버 함수들이 정보를 담고 있는데 이 정보는 특정한 '하나의' 윈도우에 대한 그래픽 정보이다. 예를 들자면 폰트, 선의 색상, 굵기, 채움 무늬, 그리기 모드, 시작 위치 등이다.

여기서 중요한 점은 '하나의' 윈도우에 대한 것이라는 점이다. Windows API 함수를 이용해 프로그램을 작성하다 보면 항상 임의의 윈도우에 뭔가를 그리기 위해서는 'GetDC()' 함수를 이용해서 '해당 윈도우'의 Device Context를 작성해야 한다.

이 점을 잊어버리게 되면 Device Context를 변경하지 않거나 해제 하지 않고 다른 윈도우에 이전 윈도우의 DC를 사용하는 오류를 저지를 수 있지 않을까 하는 생각이 든다...

아무튼 Device Context는 한글로 해석하면 '장치 내용' 정도가 되겠는데 푸훗.. 우끼다... 이 Device라는 단어를 한국어로 해석하면 '장치'이지만 사실 그 의미는 컴퓨터에 삽입된 하드웨어 장치만 의미하는 것이 아니라 특정한 어떤것이 그 하나로써 독립적이면 전부 Device라고 하는 듯 하다.

즉, 윈도우 한개, 동영상 파일 한개, 그림 이미지 한개 등 그 하나로써 독립성을 완벽하게 가지는 것을 Device라고 한다면 Device Context의 의미가 좀더 쉽게 다가온다.

Device에 대한 각종 정보를 담고 있는 구조체가 바로 Device Context이고 Windows API에서 Device는 바로 '하나의 윈도우'를 의미한다.

이런 DC를 작성하는 것이 바로 GetDC(HWND hWnd) 함수이다. 이 함수는 입력으로 들어오는 HWND hWnd 윈도우에 적당한 DC를 만들어서 (각종 그리기 정보들) 그 값을 리턴하여 준다.

즉, hdc = GetDC(hWnd); 하게 되면 hWnd 윈도우에 알맞는 DC를 만들어서 hdc에 대입하고 이 hdc를 이용해서 그 윈도우에다가 마구 마구 그리면 된다. 참 쉽죠잉~

GetDC() 함수 대신 BeginPaint() 함수도 있는데 이 함수의 특징은 '오직' WM_PAINT 루틴에서만 사용가능하다는 점이다. 흠. 흥미롭죠~

주의할 건 GetDC로 특정 윈도우의 DC를 얻어왔다면 반드시 ReleaseDC로 해제해주는 것을 잊지 말자는 것.




'프로그래밍 > Windows API' 카테고리의 다른 글

윈도우 - 차일드, 팝업  (0) 2009.11.17
윈도우 - SetClassLongPtr(), SetWindowLongPtr()  (0) 2009.11.17
윈도우 - WNDCLASS 구조체  (0) 2009.11.17
윈도우 - CreateWindow()  (0) 2009.11.17
메모리, Win32 vs. Win16  (2) 2009.05.22
posted by 대갈장군
prev 1 next