본문으로 바로가기



-윈도우 전용 함수-

저수준의 read()와 같다. - ReadFile 

저수준의 open()과 같다. - CreateFile 

저수준의 close()와 같다. - CloseHandle 


윈도우에서 비트맵 처리하는 것을 배운다.

이제 저장은 안하고 (도스에서 다 해봤기에) 비트맵 뷰어를 만들 것이다.



자 대충 기본형을 준비한 뒤 메시지 매핑 기본형도 같이 준비하고 간단히 bmp 파일 불러오는 함수 사용하여

읽어들여보자.


#include <windows.h>

LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
HINSTANCE g_hInst;
LPSTR lpszClass = L"BmpViewer";

LRESULT On_Destroy(HWND, WPARAM, LPARAM);
LRESULT On_Create(HWND hWnd, WPARAM wParam, LPARAM lParam);
LRESULT On_Paint(HWND hWnd, WPARAM wParam, LPARAM lParam);

typedef  struct _stMsgMap
{
  UINT uiMsg;
  LRESULT(*fp)(HWND, WPARAM, LPARAM);
}StMsgMap;

StMsgMap msgMap[] =
{
  { WM_DESTROY, On_Destroy },
  { WM_CREATE, On_Create },
  { WM_PAINT, On_Paint },
  { WM_NULL, 0 }
};

int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance
  , LPSTR lpszCmdParam, int nCmdShow)
{
  HWND hWnd;
  MSG Message;
  WNDCLASS WndClass;
  g_hInst = hInstance;

  WndClass.cbClsExtra = 0;
  WndClass.cbWndExtra = 0;
  WndClass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
  WndClass.hCursor = LoadCursor(NULL, IDC_ARROW);
  WndClass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
  WndClass.hInstance = hInstance;
  WndClass.lpfnWndProc = (WNDPROC)WndProc;
  WndClass.lpszClassName = lpszClass;
  WndClass.lpszMenuName = NULL;
  WndClass.style = CS_HREDRAW | CS_VREDRAW;
  RegisterClass(&WndClass);

  hWnd = CreateWindow(lpszClass, lpszClass, WS_OVERLAPPEDWINDOW,
    CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
    NULL, (HMENU)NULL, hInstance, NULL);
  ShowWindow(hWnd, nCmdShow);

  while (GetMessage(&Message, 000)) 
  {
    TranslateMessage(&Message);
    DispatchMessage(&Message);
  }

  return Message.wParam;
}

LRESULT CALLBACK WndProc(HWND hWnd, UINT iMessage, WPARAM wParam, LPARAM lParam)
{
  StMsgMap *stpMap = msgMap;

  while ((*stpMap).uiMsg != WM_NULL)
  {
    if (iMessage == (*stpMap).uiMsg)
    {
      ((*stpMap).fp)(hWnd, wParam, lParam);

      return 0;
    }

    ++stpMap;
  }

  return(DefWindowProc(hWnd, iMessage, wParam, lParam));
}

LRESULT On_Create(HWND hWnd, WPARAM wParam, LPARAM lParam)
{
  HDC hdc;
  HANDLE hFile;  // 한번 읽고 닫기 때문에 static x

  hFile = CreateFile(L"C:\\1.bmp", GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
  if (hFile == INVALID_HANDLE_VALUE)
  {
    MessageBox(hWnd, L"On_Create()의 CreateFile() 에러.", L"Button", MB_OK);
    return 0;
  }
  else
  {
    MessageBox(hWnd, L"On_Create()의 CreateFile()가 성공적으로 열렸습니다.", L"Button", MB_OK);
    CloseHandle(hFile);
  }

  return 0;
}

LRESULT On_Destroy(HWND hWnd, WPARAM wParam, LPARAM lParam)
{
  PostQuitMessage(0);

  return 0;
}

LRESULT On_Paint(HWND hWnd, WPARAM wParam, LPARAM lParam)
{
  HDC hdc;

  return 0;
}


자, 새로운 함수가 보인다. CreateFile().


CreateFile()는

원형

HANDLE CreateFile(LPCTSTR lpFileName, DWORD dwDesiredAccess, DWORD dwShareMode, LPSECURITY_ATTRIBUTES pSecurityAttributes, DWORD dwCreationDisposition, DWORD dwFlagsAndAttributes, HANDLE hTemplateFile);

MFC 원형

해당하는 함수 없음

인수

▶lpFileName : 생성하고자 하는 파일(또는 오브젝트)의 이름을 지정한다. 오브젝트의 이름은 최대 MAX_PATH의 길이로 지정할 수 있으나 NT/2000에서 유니코드로 컴파일할 경우는 32000자까지의 길이를 사용할 수 있다. 이때 파일명은 "\\?\"로 시작되어야 한다. 파일의 경우 완전 경로를 줄 수도 있고 현재 디렉토리를 기준으로 한 상대 경로로 줄 수도 있다.

▶dwDesiredAccess : 파일에 대한 액세스 권한을 지정한다. 생성하거나 연 파일로 어떤 작업을 할 것인가에 따라 적절한 액세스 권한을 지정해야 한다. 다음 플래그 중 하나 또는 조합을 지정할 수 있는데 이 플래그들은 모든 오브젝트에 공통적으로 적용되는 일반형 권한이다.

플래그

설명

0

장치 오브젝트에 대한 쿼리 액세스만 요청한다. 이 권한으로 장치를 열 경우 실제로 장치를 액세스하지 않고도 장치의 특성을 조사할 수 있다. 예를 들어 디스켓이 없는 상태에서도 플로피 드라이브의 타입이나 용량등을 조사하는 것이 가능하다.

GENERIC_READ

읽기 위한 용도로 파일을 연다. 이 액세스 권한으로 연 파일은 읽을 수만 있으며 쓸 수는 없다.

GENERIC_WRITE

쓰기 위한 용도로 파일을 연다. 읽기와 쓰기를 동시에 하려면 GENERIC_READ | GENERIC_WRITE 플래그를 지정하면 된다.

액세스 권한은 가급적이면 최소한으로 요청하는 것이 좋다. 예를 들어 읽기만 할 용도로 파일을 연다면 GENERIC_READ 플래그만 주어야 한다. 불필요하게 읽기와 쓰기 액세스 권한을 동시에 요청할 경우 읽기 전용 파일이나 CD-ROM의 파일을 열지 못하게 된다.

이런 일반형 권한 외에 오브젝트 고유의 권한을 같이 지정하거나 아니면 일반형 권한없이 표준 권한과 고유형 권한으로 액세스 마스크를 구성할 수도 있다. 각 오브젝트의 고유한 권한에 대해서는 해당 오브젝트를 참조하기 바란다.

▶dwShareMode : 파일의 공유 모드를 지정한다. 공유 모드란 파일이 열려져 있는 상태에서 다른 프로세스가 또 이 파일을 오픈할 때 이를 허가할 것인가 아닌가를 지정한다. 만약 현재 프로세스가 파일을 쓰고 있는 상태에서 다른 프로세스가 파일을 읽을 수 없도록 하고 싶다면 공유 모드를 지정하지 않아야 한다. 다음 플래그들의 조합으로 공유 모드를 지정한다.

플래그

설명

FILE_SHARE_READ

다른 프로세스가 읽기 액세스 권한을 요청했을 때 이를 허가한다. 즉, 이 프로세스가 파일을 사용하는 동안에도 다른 프로세스가 파일을 읽을 수 있다.

FILE_SHARE_WRITE

다른 프로세스가 쓰기 액세스 권한을 요청했을 때 이를 허가한다. 즉, 이 프로세스가 파일을 사용하는 동안에도 다른 프로세스가 파일에 데이터를 쓸 수 있다.

FILE_SHARE_DELETE

NT/2000. 삭제 액세스 권한을 요청했을 때만 이를 허가한다.

▶pSecurityAttributes : 파일의 보안 속성을 지정하는 SECURITY_ATTRIBUTES 구조체의 포인터이다. 이 보안 속성에 따라 생성되는 파일의 보안 설명자가 달라지며 차일드 프로세스로 핸들을 상속할 수 있는가의 여부가 결정된다. NULL이면 핸들은 상속될 수 없으며 디폴드 보안 설명자가 할당된다. 단, 파일의 보안 설명자가 할당되기 위해서는 파일이 저장되는 디스크의 파일 시스템이 반드시 NTFS로 포맷되어 있어야 한다.

▶dwCreationDisposition : 파일을 생성할 것인지 열 것인지를 지정한다. 또한 생성하고자 하는 파일이 이미 존재하거나 또는 열고자 하는 파일이 없을 경우의 동작을 지정한다. 적절한 에러를 리턴받기 위해서는 이 플래그를 신중하게 잘 지정해 주어야 한다. 그렇지 않으면 없는 파일이 열리거나 기존 파일이 깨지는 등 프로그램이 오동작을 할 위험이 있다.

플래그

설명

CREATE_NEW

파일을 새로 만든다. 만약 이미 파일이 존재한다면 에러를 리턴한다.

CREATE_ALWAYS

항상 파일을 새로 만든다. 만약 이미 파일이 존재한다면 해당 파일을 덮어쓴다. 이는 기존 파일을 삭제하고 다시 만드는 것과 같다. 파일의 존재 여부에 상관없이 무조건 파일을 생성한다.

OPEN_EXISTING

이미 존재하는 파일을 연다. 만약 열고자 하는 파일이 없다면 이 함수는 에러를 리턴한다. 파일이 아닌 장치를 열고자 할 때는 반드시 이 플래그를 사용해야 한다.

OPEN_ALWAYS

무조건 파일을 연다. 열고자 하는 파일이 없을 경우는 직접 만든 후 이 파일을 연다. 파일이 없어도 에러를 리턴하지 않으므로 기존 파일을 열 때는 이 플래그를 사용하지 말아야 한다.

TRUNCATE_EXISTING

파일을 연 후 크기를 0으로 만든다. 즉, 기존 파일을 다시 작성하고자 할 때 이 플래그를 사용한다. 이 플래그를 사용하는 프로세스는 쓰기 액세스 권한으로 파일을 열어야 하며 파일이 없을 경우는 에러를 리턴한다.

▶dwFlagsAndAttributes : 생성할 파일의 속성 또는 기타 옵젝트이 속성을 지정한다. 파일의 속성은 다음 플래그들의 조합을 사용할 수 있다.

플래그

설명

FILE_ATTRIBUTE_ARCHIVE

기록 속성을 설정한다. 파일의 기록 속성은 백업, 리스토어 프로그램에 의해 사용되며 이 파일이 백업되어야 함을 알리는 플래그이다.

FILE_ATTRIBUTE_ENCRYPTED

파일을 암호화한다. 파일의 경우 파일의 데이터를 암호화하며 디렉토리의 경우 이후부터 생성되는 파일과 서브 디렉토리를 암호화하도록 한다. 시스템 파일에는 적용되지 않는다.

FILE_ATTRIBUTE_HIDDEN

숨김 파일로 생성한다. 숨김 파일은 통상적인 방법으로는 보이지 않으므로 목록에 나타나지 않는다.

FILE_ATTRIBUTE_NORMAL

아무런 속성도 가지지 않는 파일을 만든다. 이 이 플래그는 단독으로 사용될 때만 유효하며 다른 플래그와 함께 사용하면 해당 플래그의 속성이 설정되다.

FILE_ATTRIBUTE_NOT_CONTENT_INDEXED

컨텐트 인덱싱 서비스에 대해 인덱스되지 않도록 한다.

FILE_ATTRIBUTE_OFFLINE

데이터가 오프라인 상태이며 즉시 사용할 수 있는 상태가 아니다. 이 속성은 윈도우즈 2000의 계층적 저장 관리자의 원격 저장소에 의해 사용되므로 응용 프로그램이 이 플래그를 직접 사용해서는 안된다.

FILE_ATTRIBUTE_READONLY

읽기 전용의 파일로 생성한다. 응용 프로그램은 이 파일의 내용을 읽을 수는 있지만 변경하거나 삭제할 수는 없다.

FILE_ATTRIBUTE_SYSTEM

시스템 파일로 생성한다. 시스템 파일은 운영체제에 의해 배타적으로 사용되는 파일이다.

FILE_ATTRIBUTE_TEMPORARY

임시 파일로 생성한다. 임시 파일은 디스크로 곧바로 입출력을 행하지 않고 가급적이면 메모리상에서 읽기와 쓰기를 수행하기 때문에 일반 파일보다 입출력 속도가 빠르다는 장점이 있다. 응용 프로그램은 임시파일을 다 사용한 후 반드시 삭제해 주어야 한다.

파일 속성과 함께 다음 플래그들도 같이 지정할 수 있다.

플래그

설명

FILE_FLAG_WRITE_THROUGH

가급적이면 캐시를 사용하지 않고 곧바로 디스크로 입출력을 행하도록 한다. 그러나 이 플래그를 지정해도 시스템이 쓰기 캐시를 사용할 수는 있되 다만 너무 늦게 버퍼를 비우지 않도록 해 준다. 기록 후 곧바로 사용해야 하는 데이터는 이 플래그를 주는 것이 좋다.

FILE_FLAG_OVERLAPPED

파일 입출력이 완전히 끝날 때까지 대기하지 않고 곧바로 리턴하는 비동기 입출력 모드로 파일을 연다. 이 모드를 사용하면 입출력 시간이 오래 걸릴 때 백그라운드로 파일을 액세스할 수 있으며 하나의 파일 핸들로 동시에 액세스가 가능하다. 이 모드로 열려진 파일을 액세스하는 함수는 OVERLAPPED 구조체를 초기화한 후 제공해여 한다.

비동기 입출력에 관한 상세한 내용은 34-1-사절을 참조하기 바란다.

FILE_FLAG_NO_BUFFERING

버퍼링이나 캐시를 하지 않으므로써 비동기 효율을 극대화한다. 이 플래그를 사용하기 위해서는 몇가지 요구 사항을 충족시켜야 한다.

FILE_FLAG_RANDOM_ACCESS

파일을 랜덤으로 액세스한다는 것을 시스템에게 알려준다. 시스템은 캐시를 최적화할 때 이 정보를 사용한다. 이 플래그는 어디까지나 시스템에 대한 힌트일 뿐이다.

FILE_FLAG_SEQUENTIAL_SCAN

파일을 순차 액세스한다는 것을 시스템에 알려준다. 시스템은 캐시 최적화에 이 정보를 사용하여 순차 액세스의 효율을 높일 수 있는 방식으로 캐시를 사용한다. 그러나 이 플래그를 지정했다고 해서 랜덤 액세스를 하지 못하는 것은 아니다. 큰 파일을 대부분 순차적으로 액세스하고 드물게 랜덤 액세스를 할 경우 이 플래그를 지정하면 효율을 높일 수 있다.

FILE_FLAG_DELETE_ON_CLOSE

이 파일에 대한 모든 핸들이 닫히면 파일을 삭제하도록 한다.

FILE_FLAG_BACKUP_SEMANTICS

NT/2000 이후. 백업, 리스토어를 위해 파일을 연다. 이 경우 시스템은 보안 체크를 무시한다.

FILE_FLAG_POSIX_SEMANTICS

파일을 POSIX 규칙대로 액세스한다. 파일명은 대소문자를 구분하며 대소문자만 다른 같은 파일명을 액세스할 수 있다. 이렇게 생성된 파일은 16비트 프로그램에서 액세스할 수 없다.

FILE_FLAG_OPEN_REPARSE_POINT

Specifying this flag inhibits the reparse behavior of NTFS reparse points. When the file is opened, a file handle is returned, whether the filter that controls the reparse point is operational or not. This flag cannot be used with the CREATE_ALWAYS flag.

FILE_FLAG_OPEN_NO_RECALL

Indicates that the file data is requested, but it should continue to reside in remote storage. It should not be transported back to local storage. This flag is intended for use by remote storage systems or the Hierarchical Storage Management system.

▶hTemplateFile : 생성될 파일의 속성을 제공할 템플릿 파일이다. 95/98은 템플릿 파일을 지원하지 않으므로 반드시 NULL이어야 한다.

리턴

생성 또는 연 파일의 핸들을 리턴한다. 실패할 경우 INVALID_HANDLE_VALUE를 리턴하는데 이 값은 NULL과는 다르므로 NULL과 비교해서는 안된다. 즉 다음과 같은 에러 처리는 잘못된 것이므로 주의하기 바란다.

hFile=CreateFile(...
if (hFile == NULL) {
에러처리 
}

이 함수를 출하기 전에 파일이 이미 존재하고 있었으면 GetLastError는 ERROR_ALREADY_EXISTS를 리턴한다.

설명

이 함수는 파일을 생성하는 가장 기본적인 함수이다. 그러나 이름과는 달리 파일을 생성하는 것뿐만 아니라 기존의 파일을 열 수도 있으며 파일 외에 다음과 같은 오브젝트를 생성하거나 열 수도 있다.

파이프
메일슬롯
COM 포트 등의 통신 장치
디스크 장치
테입 드라이브
콘솔
디렉토리

이 함수로 생성한 핸들은 반드시 CloseHandle로 닫아 주어야 한다.

예제 1 

다음 예제는 파일을 생성하고 파일에 텍스트를 기록한다.

LRESULT CALLBACK WndProc(HWND hWnd,UINT iMessage,WPARAM wParam,LPARAM lParam)
{
	HDC hdc;
	PAINTSTRUCT ps;
	HANDLE hFile;
	DWORD dwWritten;
	LPCTSTR str=TEXT("테스트 파일입니다");

	switch(iMessage) {
	case WM_LBUTTONDOWN:
		hFile=CreateFile("c:\\TestFile.txt",GENERIC_WRITE,0,NULL,
			CREATE_ALWAYS,FILE_ATTRIBUTE_NORMAL,NULL);
		WriteFile(hFile,str,lstrlen(str),&dwWritten,NULL);
		CloseHandle(hFile);
		return 0;
	case WM_PAINT:
		hdc=BeginPaint(hWnd, &ps);
		EndPaint(hWnd, &ps);
		return 0;
	case WM_DESTROY:
		PostQuitMessage(0);
		return 0;
	}
	return(DefWindowProc(hWnd,iMessage,wParam,lParam));
}

C드라이브의 루트 디렉토리에 TestFile.txt 파일을 쓰기 액세스 권한으로 생성하였다. CREATE_ALWAYS 플래그를 주어 파일이 있을 경우 새로 만든다. 예제를 실행한 후 TestFile.txt 를 확인해 보면 문자열이 기록되어 있을 것이다.

참고함수

ReadFile : 파일로부터 데이터를 읽는다.
WriteFile : 파일에 데이터를 기록한다.
CloseHandle : 핸들을 닫는다.

플랫폼

95이상

참조

액세스 권한에 대한 상세한 내용은 39-2-사절을 참고하기 바란다.











원형 부분을 보니

HANDLE CreateFile(LPCTSTR lpFileName, DWORD dwDesiredAccess, DWORD dwShareMode, LPSECURITY_ATTRIBUTES pSecurityAttributes, DWORD dwCreationDisposition, DWORD dwFlagsAndAttributes, HANDLE hTemplateFile);


이와 같이 핸들러를 반환한다고 하니 핸들러를 받는 변수를 선언해받은 뒤 그 핸들러 값을 받아서 우리가 원하는 작업을 하면 될 듯하다.

우선 에러 체킹을 조건문으로 하여 검사하도록 하자.


 hFile = CreateFile(L"C:\\1.bmp", GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
  if (hFile == INVALID_HANDLE_VALUE)
  {
    MessageBox(hWnd, L"On_Create()의 CreateFile() 에러.", L"Button", MB_OK);
    return 0;
  }
  else
  {
    MessageBox(hWnd, L"On_Create()의 CreateFile()가 성공적으로 열렸습니다.", L"Button", MB_OK);
    CloseHandle(hFile);
  }


경로가 제대로 설정되었더니 완벽히 파일이 열리며




위와 같은 메세지를 실행을 하자마자 띄운다.






현재 크리에이트 함수 생성중


#include <windows.h>

LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
HINSTANCE g_hInst;
LPSTR lpszClass = L"BmpViewer";

LRESULT On_Destroy(HWND, WPARAM, LPARAM);
LRESULT On_Create(HWND hWnd, WPARAM wParam, LPARAM lParam);
LRESULT On_Paint(HWND hWnd, WPARAM wParam, LPARAM lParam);

typedef  struct _stMsgMap
{
  UINT uiMsg;
  LRESULT(*fp)(HWND, WPARAM, LPARAM);
}StMsgMap;

StMsgMap msgMap[] =
{
  { WM_DESTROY, On_Destroy },
  { WM_CREATE, On_Create },
  { WM_PAINT, On_Paint },
  { WM_NULL, 0 }
};

//ReadFile()로 읽어들이기 위한 동적할당 받을 buf 변수 생성.
unsigned char *ucpData;      //동적할당용
BITMAPFILEHEADER stBFHead;    //비트맵 헤더용
BITMAPINFOHEADER stBINFOHead;  

int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance
  , LPSTR lpszCmdParam, int nCmdShow)
{
  HWND hWnd;
  MSG Message;
  WNDCLASS WndClass;
  g_hInst = hInstance;

  WndClass.cbClsExtra = 0;
  WndClass.cbWndExtra = 0;
  WndClass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
  WndClass.hCursor = LoadCursor(NULL, IDC_ARROW);
  WndClass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
  WndClass.hInstance = hInstance;
  WndClass.lpfnWndProc = (WNDPROC)WndProc;
  WndClass.lpszClassName = lpszClass;
  WndClass.lpszMenuName = NULL;
  WndClass.style = CS_HREDRAW | CS_VREDRAW;
  RegisterClass(&WndClass);

  hWnd = CreateWindow(lpszClass, lpszClass, WS_OVERLAPPEDWINDOW,
    CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
    NULL, (HMENU)NULL, hInstance, NULL);
  ShowWindow(hWnd, nCmdShow);

  while (GetMessage(&Message, 000)) 
  {
    TranslateMessage(&Message);
    DispatchMessage(&Message);
  }

  return Message.wParam;
}

LRESULT CALLBACK WndProc(HWND hWnd, UINT iMessage, WPARAM wParam, LPARAM lParam)
{
  StMsgMap *stpMap = msgMap;

  while ((*stpMap).uiMsg != WM_NULL)
  {
    if (iMessage == (*stpMap).uiMsg)
    {
      ((*stpMap).fp)(hWnd, wParam, lParam);

      return 0;
    }

    ++stpMap;
  }

  return(DefWindowProc(hWnd, iMessage, wParam, lParam));
}

LRESULT On_Create(HWND hWnd, WPARAM wParam, LPARAM lParam)
{
  HDC hdc;
  HANDLE hFile;  // 한번 읽고 닫기 때문에 static x

  hFile = CreateFile(L"C:\\456.bmp", GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
  if (hFile == INVALID_HANDLE_VALUE)
  {
    MessageBox(hWnd, L"On_Create()의 CreateFile() 에러.", L"Button", MB_OK);
    PostQuitMessage(0);
    //SendMessage(hWnd, WM_DESTROY, 0, 0);  // 못 쓰겠끔 프로그램 종료.
    return 0;
  }
  
  //ReadFile(hFile, ,);

  MessageBox(hWnd, L"On_Create()의 CreateFile()가 성공적으로 열렸습니다.", L"Button", MB_OK);
  CloseHandle(hFile);

  return 0;
}

LRESULT On_Destroy(HWND hWnd, WPARAM wParam, LPARAM lParam)
{
  //MessageBox(hWnd, L"디스트로이", L"Button", MB_OK);  //디스트로이가 호출안되고 바로 종료
  //파일 열때 디스트로이 함수를 생성해버리면 동적할당이 실패했는데도 불구하고 동적할당을 해제하려 들 것이다. 이것을 
  PostQuitMessage(0);

  return 0;
}

LRESULT On_Paint(HWND hWnd, WPARAM wParam, LPARAM lParam)
{
  HDC hdc;

  return 0;
}



버튼 또한 윈도우다.

7-3-가. 에디트

에디트는 문자열을 직접 입력받고자 할 때 사용하는데 버튼과 마찬가지로 윈도우즈에서 가장 흔하게 볼 수 있는 컨트롤이다. 가로로 길쭉하게 생겼으며 여기에 문자열을 입력할 수 있다. 다음 대화상자에서 문자열이 입력된 흰색 컨트롤이 모두 에디트이다. 워낙 흔해 빠진 컨트롤이라 사용방법에 대해서는 이미 익숙할 것이다.

"edit" 윈도우 클래스를 사용하며 생성시에 다음과 같은 스타일을 사용할 수 있다. 물론 이 스타일들은 CreateWindow 함수의 세번째 인수로 지정한다.

스타일설명
ES_AUTOHSCROLL수평 스크롤을 지원한다.
ES_AUTOVSCROLL여러줄 편집시 수직 스크롤을 지원한다.
ES_LEFT왼쪽 정렬한다.
ES_CENTER중앙 정렬한다.
ES_RIGHT오른쪽 정렬한다.
ES_LOWERCASE소문자로 변환하여 표시한다.
ES_UPPERCASE대문자로 변환하여 표시한다.
ES_MULTILINE여러줄을 편집할 수 있도록 한다.
ES_NOHIDESEL포커스를 잃더라도 선택된 영역을 표시한다.
ES_READONLY읽기전용으로 만들어 편집을 금지한다.

지정할 수 있는 스타일이 많은데 각 스타일의 의미에 대해서는 14장에서 자세히 알아볼 것이므로 당장 다 이해할 필요없이 대충 봐 두기만 하면 된다.

자신의 변화에 대해 다음과 같은 통지 메시지를 부모 윈도우로 보내준다. 부모 윈도우는 이 메시지를 받았을 때 적절한 처리를 해 주면 된다.

메시지설명
EN_CHANGE문자열이 변경되었다.
EN_ERRSPACE메모리가 부족하다.
EN_HSCROLL사용자가 수평 스크롤 바를 클릭하였다.
EN_VSCROLL사용자가 수직 스크롤 바를 클릭하였다.
EN_KILLFOCUS포커스를 잃었다.
EN_SETFOCUS포커스를 얻었다.
EN_MAXTEXT지정한 문자열 길이를 초과하였다.
EN_UPDATE문자열이 변경되기 직전이다.

EN_CHANGE와 EN_UPDATE가 비슷한 것처럼 보이지만 약간 다르다. EN_UPDATE는 문자열이 변경된 후 화면에 출력하기 전에 보내주는 메시지이며 이 메시지가 발생했을 때 사용자는 문자열 길이에 따라 에디트의 폭을 늘리거나 별도의 조치를 취할 수 있다. EN_CHANGE는 문자열이 화면으로 출력되고 난 후 보내지는 메시지이다. 즉 에디트는 문자열이 변경된 후 EN_UPDATE 메시지를 보내고 화면에 그린 후 다시 EN_CHANGE 메시지를 보낸다. 상황과 필요에 따라 둘 중 하나를 선택하여 사용하되 대개의 경우 어떤 메시지를 쓰나 별 차이가 없으며 보통 EN_CHANGE 메시지를 많이 사용한다. 메인 윈도우에서 에디트 컨트롤에게 보내는 메시지도 있지만 이 예제에서는 사용되지 않으므로 다음 기회에 알아보도록 하자.

에디트 하나를 배치하고 에디트에 입력된 문자열을 메인 윈도우의 타이틀바에 출력하도록 하는 예제를 작성해 보았다.

#define ID_EDIT 100
HWND hEdit;
char str[128];
LRESULT CALLBACK WndProc(HWND hWnd,UINT iMessage,WPARAM wParam,LPARAM lParam)
{
	switch(iMessage) {
	case WM_CREATE:
		hEdit=CreateWindow("edit",NULL,WS_CHILD | WS_VISIBLE | WS_BORDER | 
		ES_AUTOHSCROLL,10,10,200,25,hWnd,(HMENU)ID_EDIT,g_hInst,NULL);
		return 0;
	case WM_COMMAND:
		switch (LOWORD(wParam)) {
		case ID_EDIT:
			switch (HIWORD(wParam)) {
			case EN_CHANGE:
				GetWindowText(hEdit,str,128);
				SetWindowText(hWnd,str);
			}
		}
		return 0;
	case WM_DESTROY:
		PostQuitMessage(0);
		return 0;
	}
	return(DefWindowProc(hWnd,iMessage,wParam,lParam));
}

WM_CREATE에서 에디트를 생성하였으며 ES_AUTOHSCROLL 스타일을 주어 수평 스크롤이 가능하도록 하였다. 이 스타일을 주지 않으면 에디트의 오른쪽 끝까지만 문자열을 입력할 수 있으나 이 스타일을 주면 에디트 끝부분에 닿았을때 자동으로 수평 스크롤되어 긴 문자열을 입력할 수 있다. 생성된 에디트 컨트롤의 윈도우 핸들값을 hEdit에 저장해 두었다. WM_COMMAND에서 EN_CHANGE 통지 메시지가 전달될 때 에디트의 텍스트를 읽어 메인 윈도우의 타이틀바로 출력하였다. 실행중의 모습은 다음과 같다.

에디트는 사용자가 자신을 편집할 때마다 부모 윈도우로 EN_CHANGE 메시지를 보내며 메인 윈도우는 이 메시지를 받을 때마다 에디트의 텍스트를 읽어 자신의 타이틀 바를 갱신하고 있다. EN_CHANGE 통지 메시지 처리 코드에서 에디트에 입력된 텍스트를 읽을 때 GetWindowText 함수를 사용하였는데 보다시피 컨트롤도 윈도우의 일종이기 때문에 윈도우 관리 함수를 모두 사용할 수 있다.

에디트는 최대 32k까지의 문자열을 편집할 수 있으며 여러줄 편집, 블럭선택, 클립보드 지원 기능까지 다양한 기능을 가지고 있는데 비해 사용하기는 비교적 쉬운편에 속한다. 14장에서 에디트에 대한 상세한 프로그래밍 방법을 익혀 보도록 하자.


WM_PAINT가 호출되는데 내부에 페인트가 가지고 있다. 다른  윈도우 애가 잠시 나왔다가 자기의 페인트로 버튼을 그려준다라는 것이다.

즉, 우리것만 잘짜면 나머지는 전부 다른 윈도우 알아서 잘 해주기때문에 ,

또한, 마우스로 긁을 수도 있다.




예제 치기 전에,


7-1-가. 컨트롤의 정의

컨트롤(Control)이란 사용자와의 인터페이스를 이루는 도구이다. 인터페이스를 이룬다는 말은 사용자로부터 명령을 받아들이고 출력 결과를 보여준다는 뜻이다. 프로그램은 실행중에 끊임없이 사용자와 통신을 하는데 컨트롤을 통해 명령과 정보를 받아들이고 또한 컨트롤을 통해 실행 결과를 사용자에게 보고한다. 컨트롤의 가장 대표적인 예로 푸시 버튼과 에디트 컨트롤을 들 수 있다. 다음은 워드 6.0의 파일 열기 대화상자인데 얼마나 많은 종류의 컨트롤이 사용되는가 보아라.

확인, 취소 등의 버튼이 있고 파일 이름을 입력하는 에디터, 디렉토리 구조를 보여주는 리스트 박스, 옵션을 선택하는 체크 박스 등의 컨트롤들이 배치되어 있다. 사용자는 이 대화상자의 컨트롤들을 통해 프로그램과 정보를 주고 받고 명령을 내린다. 에디트에 파일 이름을 입력하며 리스트 박스에서 디렉토리를 선택하고 푸시 버튼을 눌러 명령을 내린다. 만약 컨트롤이 없다면 사용자에게 현재 설정된 옵션을 보여줄 방법도, 옵션을 바꾸도록 하지도 못할 것이다. 컨트롤의 의미를 한마디로 설명하기는 힘들지만 "버튼, 에디트, 리스트, 스크롤 바 등을 뭉떵거려 컨트롤이라고 한다"라고 이해하는 것이 더 빠를 것이다.(어떤 것으로 가리고 다시 그려지는 것은 하나의 윈도우 인 것이다. 즉, 푸쉬 버튼 이나 에디터들 전부가 하나의 윈도우 들인 것이다.)

윈도우즈 3.1부터 지원하는 컨트롤에는 버튼, 에디트, 체크, 라디오, 리스트 박스, 콤보 박스, 스크롤 바, 스태틱 등 여섯가지에 불과하지만 윈도우즈 95/98부터는 사용할 수 있는 컨트롤의 수가 대폭 증가되었으며 현재는 OCX 컨트롤까지 사용할 수 있도록 되어 컨트롤의 수는 거의 무한대에 이른다. 인터넷을 뒤져보면 사용목적에 부합하는 공개된 컨트롤들이 많이 있으며 사용방법은 갈수록 쉬워지고 있다.

그러나 다행스럽게도 컨트롤의 종류가 많다 해서 공부해야 할 내용이 많아지는 것은 아니다. 왜냐하면 사용방법이 대체로 다 비슷비슷하며 컨트롤에 대한 개념만 있으면 상식적인 수준에서 이해할 수 있도록 만들어지기 때문이다. 마치 게임 많이 해 본 사람이 새 게임을 할 때 별도로 매뉴얼을 읽지 않아도 되는 것처럼 말이다. 이 장에서는 우선 고전적인 표준 컨트롤에 대해서만 알아볼 것이되 세부적인 것보다는 컨트롤의 일반적인 특성과 정의에 대해서 다루며 각 컨트롤에 대해서는 13장 이후에 개별적으로 정리할 기회를 가진다. 여기서는 컨트롤 자체의 일반적인 이해에 중점을 두어 컨트롤 프로그래밍 방법을 자신의 상식 범위 안에 포함시키는데 주력하기 바란다.

컨트롤도 하나의 윈도우이다. 화면상의 일정한 영역을 차지하며 자신의 고유 메시지를 처리할 수 있는 능력을 가지고 있다. 그렇다고 해서 진짜 윈도우처럼 타이틀 바나 경계선을 가지고 독립적으로 사용되는 것은 아니며 보통 대화상자의 차일드 윈도우로 존재한다.

윈도우를 만들 때는 WNDCLASS형의 구조체를 정의한 후 RegisterClass 함수를 사용하여 등록한 후 CreateWindow 함수를 호출하여 윈도우를 만들어야 한다. 그러나 컨트롤은 윈도우즈가 운영체제 차원에서 제공해주기 때문에 윈도우 클래스를 만들어 사용할 필요없이 윈도우즈에 미리 정의되어 있는 클래스를 사용하기만 하면 된다. 미리 정의된 윈도우 클래스는 다음과 같다.

윈도우 클래스컨트롤
button버튼, 체크, 라디오
static텍스트
scrollbar스크롤 바
edit에디트
listbox리스트 박스
combobox콤보 박스

윈도우 클래스를 따로 만들 필요없이 CreateWindow 함수의 첫번째 인수로 미리 정의된 윈도우 클래스를 주면 해당 컨트롤을 만들 수 있다. 잠시 후 실습을 해 보기로 하자.




이 UI를 자동적으로 띄워주는 함수가 존재한다.

직접 정의도 해야하지만 오른쪽의 푸쉬버튼의 가로 세로 크기를 계산해서 얼만큼 띄울것인가 거기 좌표를 또 찍고 가로크기 세로크기

즉, 엄청난 노가다를 해야 한다라는 것이다.

다이얼로그박스() 라고 이것을 자동적으로 만들어주는 함수가 존재한다.


(어떤 것으로 가리고 다시 그려지는 것은 하나의 윈도우 인 것이다. 즉, 푸쉬 버튼 이나 에디터들 전부가 하나의 윈도우 들인 것이다.)



여기 컨트롤 되어있는 곳이 에디터다

하지만 제목이라 되있는 곳은 ? 스테틱이라고 부른다.

왼쪽 스테틱은 '사용자'가 수정이 못한다. 왼쪽을 스테틱으로 만들어도 된다라는 소리다.


7-5-나. 스태틱

윈도우즈 3.1부터 지원하던 표준 컨트롤의 종류는 6가지이다. 지금까지 버튼, 에디트, 리스트 박스, 콤보 박스, 스크롤 바 등 5가지를 이미 소개했다. 체크 박스나 라디오 버튼은 스타일이 다른 버튼의 일종일 뿐이며 별도의 컨트롤은 아니다. 나머지 남은 하나의 표준 컨트롤이 지금 소개하고자 하는 스태틱(static)이다.

스태틱은 모든 컨트롤을 통틀어 제일 간단한 컨트롤이다. 사용자로부터 입력을 받아들이는 기능은 없고 오로지 문자열을 보여주는 것이 기능의 전부이기 때문이다. 다음은 비주얼 C++ 개발자 스튜디오의 File/Page Setup 대화상자인데 여기서 Header, Footer, Left, Top 등의 문자열이 바로 스태틱 컨트롤이다.

주로 에디트나 다른 컨트롤 옆에 위치하며 컨트롤의 용도를 설명해 주는 역할을 한다. 스태틱 컨트롤을 만들 때는 윈도우 클래스를 "static"으로 설정해 주면 된다. 다음은 스태틱 컨트롤을 윈도우에 배치하는 간단한 예제이다.

LRESULT CALLBACK WndProc(HWND hWnd,UINT iMessage,WPARAM wParam,LPARAM lParam)
{
	switch(iMessage) {
	case WM_CREATE:
		CreateWindow("static","Only Text",WS_CHILD | WS_VISIBLE,
			20,20,100,25,hWnd,(HMENU)-1,g_hInst,NULL);
		return 0;
	case WM_DESTROY:
		PostQuitMessage(0);
		return 0;
	}
	return(DefWindowProc(hWnd,iMessage,wParam,lParam));
}

WM_CREATE에서 스태틱 컨트롤을 만들기만 할 뿐 그 외의 코드는 작성할 필요가 없다. 스태틱 컨트롤은 실행중에 부모 윈도우로 통지 메시지를 보낼 필요가 없기 때문에 ID를 -1로 지정해도 되며 여러 개의 스태택 컨트롤이 있을 경우에도 모두 -1의 같은 ID를 사용해도 상관없다. 스태틱은 그 자리에 있는 자체가 존재의 의미이며 다른 컨트롤과 구분할 필요조차도 없기 때문이다. 실행중의 모습은 다음과 같다.

문자열 하나만 출력되어 있으며 아무런 기능도 없다. 그럼 스태틱 컨트롤로 출력한 문자열은 TextOut로 출력한 문자열과는 어떻게 다를까하는 의문이 생길것이다. TextOut로 출력한 문자열은 그냥 문자열일 뿐이므로 언제든지 지워질 수가 있고 그래서 WM_PAINT에서 계속 출력해주어야 한다. 반면 스태틱 컨트롤은 스스로 메시지를 처리할 수 있는 윈도우이기 때문에 일단 배치해 놓기만 하면 더 이상 신경쓰지 않아도 된다. 또한 색상, 글꼴 크기 등 운영체제의 세팅이 바뀔 경우 이런 변화에 대해서도 스스로 대처한다는 장점이 있는데 이에 대해서는 다음 기회에 알아 보기로 한다.

스태택 컨트롤은 일반적으로 문자열이지만 스타일에 따라 사각형 모양이나 아이콘이 될 수도 있다. 잘 사용되지는 않지만 스태틱도 다음과 같은 스타일을 가진다. 아무 스타일도 지정하지 않으면 왼쪽으로 정렬되는 단순한 텍스트이다.

스타일설명
SS_LEFT왼쪽으로 정렬되는 텍스트이며 자동으로 개행된다.
SS_LEFTNOWORDWRAP왼쪽으로 정렬되는 텍스트이며 자동으로 개행되지 않는다.
SS_CENTER중앙으로 정렬되는 텍스트이며 자동으로 개행된다.
SS_RIGHT오른쪽으로 정렬는 텍스트이며 자동으로 개행된다.
SS_SIMPLE단순한 문자열이며 자동개행되지 않는다.
SS_WHITEFRAME윈도우의 배경색으로 그려지는 사각형
SS_WHITERECT윈도우의 배경색으로 그려지는 속이 채워진 사각형
SS_BLACKFRAME화면 배경색으로 그려지는 사각형
SS_BLACKRECT화면 배경색으로 그려지는 속이 채워진 사각형
SS_GRAYFRAME윈도우 프레임 색상으로 그려지는 사각형
SS_GRAYRECT윈도우 프레임 색상으로 그려지는 속이 채워진 사각형
SS_ICON대화상자내에서 아이콘 출력
SS_NOPREFIX&문자를 단축키 지정에 사용하지 않고 그대로 출력한다.

이상으로 표준 컨트롤 6가지에 대한 간략한 소개를 마친다. 앞으로 수많은 컨트롤들을 다루게 될텐데 여기서 배운 지식을 바탕으로 하면 나머지 컨트롤들도 쉽게 다룰 수 있을 것이다. 컨트롤을 잘 다루기 위해서는 ①스타일 ②통지 메시지 ③부모 윈도우가 보내는 메시지 ④메시지에 의해 사용되는 구조체, 이 네가지에 대해 잘 알아야 하는데 굳이 외울 필요까지는 없다. 필요할 때마다 레퍼런스를 참고할 수도 있고 스타일은 개발툴에 따라 비주얼 편집이 가능하기도 하므로 대충의 이해만 하고 있으면 된다.







얘라는 소리다.

윈도우지만 도스로 본다.

가로 : (이부분을 스태틱) 


스태틱을 많이 써도되지만 많이 쓸수록 좌표계산해야한다.

가로 :

세로 :

높이 :


이렇게 있다면

가로 : 밑의 세로 : 얘를 얼마나 띄워서 출력할 것인지 다 설정해줘야 한다라는 것이다.




위의 에디터 예제가 잘 실행이 안되어 검색해봤는데


먼저 드리고 싶은 말씀은.. 열심히 하시는것같은데 그럼에도 불구하고 님의 코드를 보면

어느 강좌로 공부하시는지 모르겠지만 좋지 못한 강좌일것같다는 느낌이 계속드네요..

중요하고 핵심적인 내용은 빼고 부분부분 가르치는것같습니다. 혹시 동영상 강좌를 보고 공부중이시라면

그만두고 책으로 공부하시기 바랍니다. Win32 API라는게 사람이 몇십분 남짓의 강의로 가르칠수있는 분량이아닙니다. 

 

 

일단 제일 큰 문제는 님의 WM_COMMAND 부분의 구조가 좀 어색합니다.

WM_COMMAND의 처리기는 다음과같은 구조를 가져야합니다.

 

 case WM_COMMAND:
      switch (LOWORD(wParam))

        {
          case ID_EDIT:                                      //님의코드에서는 999
               switch (HIWORD(wParam))

              {


               case EN_CHANGE:

 

                   // 뭔가 처리를해준다. example)
                   //GetWindowText(hEdit,str,128);
               //SetWindowText(hWnd,str);


               }
         }
return 0;

 

먼저 wParam라는 4바이트 데이터중 하위2바이트를  LOWORD매크로로 뽑아서 어느 컨트롤로부터 전달된 메시지인지

컨트롤의 ID값을 얻어냅니다. 이렇게 WM_COMMAND 에서 각 컨트롤 별로 코드의 1차분기가 이루어지구요.

 

그다음 wParam의 상위2바이트를 HIWORD매크로로 뽑아냅니다. 님의 코드에서는 이부분이 이루어지지 않아서 문제가 생긴겁니다. 여기에는 컨트롤의 통지메시지가 들어있습니다.

예를 들면 사용자가 에디트박스에 뭔가 입력했다거나(EN_CHANGE).. 사용자가 에디트 박스에 문자열을 입력하다가

한계치를 초과했다거나..(EN_MAXTEXT)

 

이렇게 각 컨트롤에 대해서 또한번 어떤 통지메시지를 보내왔느냐에따라 2차분기를 해줘야합니다.

이게 WM_COMMAND를 처리하는 방법이구요 이제 님의 코드에서 벌어진 현상을 분석해봅시다.

 

처음에 CreateWindow함수로 에디트 박스를 만들죠? 근데 바로 다음줄에있는 CREATE 메시지박스가 생성되기전에

COMMAND 메시지박스가 생성된게 의아하실겁니다. 순서대로 벌어진일들을 정리해보면 다음과같습니다.

 

1.CreateWindow함수로 에디트박스 생성시작.

2.CreateWindow함수 내부에서 에디트박스가 생성되고 EN_UPDATE ,EN_CHANGE 통지메시지 발생

( 자세히말하면 mesg = WM_COMMAND,

LOWORD(wParam) = 999,

HIWORD(wParam)= EN_UPDATE

 

mesg = WM_COMMAND,

LOWORD(wParam) = 999,

HIWORD(wParam)= EN_CHANGE

 

이 2개의 통지메시지가 발생됩니다.)

 

나중에 SendMessage라는 메시지발생시키는 함수를 공부하시면 알겠지만. 메시지를 발생시키면 코드가

해당 메시지를 처리하는 부분으로 점프합니다. 따라서 다음과정은

 

3. case WM_COMMAND 2번 호출,

4. COMMAND 메시지박스 2번생성

 

여기까지가 COMMAND메시지박스가 먼저 2번생성되는 과정입니다. EN_UPDATE , EN_CHANGE 통지메시지가 에디트박스에서 발생하고 처리되기 때문이죠 통지메시지가 처리되면 코드는 다시 원래 자리로 돌아옵니다. 그러면 마지막으로 CREATE메시지박스가 3번째로 생성되겠지요?

 

5.CreateWindow함수가 드디어 리턴됩니다.

6. CRATE 메시지박스 생성

 

그리고 마지막으로 WndProc함수의 리턴값에 대해서 질문하셨는데. 100퍼센트는 아니지만 메시지를 잘처리했으면

return 0을 해주면됩니다. 하지만 각 메시지별로 리턴값의 의미가 다르기때문에 일반화해서 설명할수가 없습니다

리턴값은 WndProc함수를 호출한 주체인 운영체제가 가져가서 읽어보고 뒷처리를해줍니다.

 

 

마지막으로 몇자 적어보자면.. 저는 질문자님이 어떤 상황인지 잘모릅니다. 취준생인지.. 취미로 살짝 API를 맛만보는것인지.

아니면 MFC적당히 배우려고 API도 적당히 공부하시고 계신건지도 모르구요

만약 API도 정복하고 MFC도 정복하겠다! 기초부터 잘쌓아서 정상까지 가겠다! 하시면 다음책으로 공부하시면 좋을것같네요

 

- 윈도우즈 API정복 저자 김상형

 



그리고도 안되서.. 

다른 비슷한 코드 찾아보자


#include <windows.h>
#include <windowsx.h>   // 메시지 크래커 사용을 위해 추가

#define ID_EDIT   100                    // 에디트 윈도우의 Menu ID 값
#define MAXSHAREMEMORY 1024 // 최대 메모리 공유 사이즈

// 윈도우 프로시저 프로토 타입
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);

// 메시지 크랙커 정의 함수 프로토 타입
BOOL MemShare1_OnCreate(HWND, LPCREATESTRUCT);
void MemShare1_OnCommand(HWND, int, HWND, UINT);
void MemShare1_OnPaint(HWND);
void MemShare1_OnDestroy(HWND);

UINT UserMessage; // 사용자 정의 메시지
HWND hEdit;           // 에디트 윈도우
HANDLE hFMap;     // 파일 연결 오브젝트 변수
TCHAR *PtrlnFile;   // 메모리에 매핑의 시작번지를 나타내는 포인터 변수

// 윈도우 메인
int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine,
  int nShowCmd)
{
  HWND   hwnd;
  MSG    msg;
  WNDCLASS  wndclass = { 0 };
  static TCHAR WinMainClassName[] = TEXT("MemShare1");

  wndclass.hbrBackground = (HBRUSH)GetSysColorBrush(COLOR_3DFACE);
  wndclass.hInstance = hInstance;
  wndclass.lpfnWndProc = WndProc;
  wndclass.lpszClassName = WinMainClassName;
  wndclass.style = CS_HREDRAW | CS_VREDRAW;
  RegisterClass(&wndclass);

  hwnd = CreateWindow(
    WinMainClassName,
    WinMainClassName,
    WS_OVERLAPPEDWINDOW,
    CW_USEDEFAULT,
    CW_USEDEFAULT,
    CW_USEDEFAULT,
    CW_USEDEFAULT,
    NULL,
    (HMENU)NULL,
    hInstance,
    NULL
    );

  ShowWindow(hwnd, nShowCmd);

  // 메시지 루프
  while (GetMessage(&msg, NULL, 00))
  {
    TranslateMessage(&msg);
    DispatchMessage(&msg);
  }

  return (int)msg.wParam;
}

// 윈도우 프로시저 구현
LRESULT CALLBACK WndProc(HWND hwnd, UINT iMessage, WPARAM wParam, LPARAM lParam)
{
  switch (iMessage) // 메시지 크랙커
  {
    HANDLE_MSG(hwnd, WM_CREATE, MemShare1_OnCreate);
    HANDLE_MSG(hwnd, WM_COMMAND, MemShare1_OnCommand);
    HANDLE_MSG(hwnd, WM_PAINT, MemShare1_OnPaint);
    HANDLE_MSG(hwnd, WM_DESTROY, MemShare1_OnDestroy);
  }

  if (iMessage == UserMessage) // 사용자 정의 메시지
    SetWindowText(hEdit, PtrlnFile);

  return (DefWindowProc(hwnd, iMessage, wParam, lParam));
}

// 메시지 크랙커 정의 함수 구현
BOOL MemShare1_OnCreate(HWND hwnd, LPCREATESTRUCT lpCreateStruct)
{
  MoveWindow(hwnd, 80200400350, TRUE);
  hEdit = CreateWindow(
    TEXT("edit"),
    NULL,
    WS_CHILD | WS_VISIBLE | WS_VSCROLL | ES_MULTILINE,
    1010500200,
    hwnd,
    (HMENU)ID_EDIT,
    GetModuleHandle(0),
    NULL
    );

  // 사용자 정의 메시지 등록!
  UserMessage = RegisterWindowMessage(TEXT("WM_SYNCSHAREMEMORY"));

  // 파일 연결 오브젝트 생성!
  hFMap = CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0,
    MAXSHAREMEMORY, "MEMORYSHAREMAPPING");

  // 파일뷰를 메모리에 매핑!
  PtrlnFile = (TCHAR *)MapViewOfFile(hFMap, FILE_MAP_ALL_ACCESS, 00,
    MAXSHAREMEMORY);

  return TRUE;
}

void MemShare1_OnCommand(HWND hwnd, int id, HWND hwndCtl, UINT codeNotify)
{
  switch (id)     // LOWORD(wParam)
  {
  case ID_EDIT:
    switch (codeNotify)  // HIWORD(wParam)
    {
    case EN_CHANGE:
    {
      HWND hTarget;
      GetWindowText(hEdit, PtrlnFile, MAXSHAREMEMORY);
      hTarget = FindWindow(NULL, TEXT("MemShare2")); // 찾을 윈도우

      if (hTarget)
        SendMessage(hTarget, UserMessage, 00);
    }
    break;
    }
  }
}

void MemShare1_OnPaint(HWND hwnd)
{
  PAINTSTRUCT ps;
  TCHAR  *Message = TEXT("메모리 공유 테스트");
  HDC   hdc = BeginPaint(hwnd, &ps);
  TextOut(hdc, 10220, Message, lstrlen(Message));
  EndPaint(hwnd, &ps);
}

void MemShare1_OnDestroy(HWND hwnd)
{
  // Memory Mapped File 관련 자원 해제
  UnmapViewOfFile(PtrlnFile);
  CloseHandle(hFMap);

  PostQuitMessage(0);
}





아 세상에.. 수업을 안들었던 탓이다.

WCHAR 형으로 유니코드 2바이트 형으로 변수를 선언해줘야 잘 뜬다.

ㅜㅜㅜ..


아무튼 다시 소스 코드를 보면,

#include <windows.h>

LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
HINSTANCE g_hInst;
LPSTR lpszClass = L"BmpViewer";

#define ID_EDIT 100

HWND hEdit;
WCHAR str[128];

int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance
  , LPSTR lpszCmdParam, int nCmdShow)
{
  HWND hWnd;
  MSG Message;
  WNDCLASS WndClass;
  g_hInst = hInstance;

  WndClass.cbClsExtra = 0;
  WndClass.cbWndExtra = 0;
  WndClass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
  WndClass.hCursor = LoadCursor(NULL, IDC_ARROW);
  WndClass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
  WndClass.hInstance = hInstance;
  WndClass.lpfnWndProc = (WNDPROC)WndProc;
  WndClass.lpszClassName = lpszClass;
  WndClass.lpszMenuName = NULL;
  WndClass.style = CS_HREDRAW | CS_VREDRAW;
  RegisterClass(&WndClass);

  hWnd = CreateWindow(lpszClass, lpszClass, WS_OVERLAPPEDWINDOW,
    CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
    NULL, (HMENU)NULL, hInstance, NULL);
  ShowWindow(hWnd, nCmdShow);

  while (GetMessage(&Message, 000))
  {
    TranslateMessage(&Message);
    DispatchMessage(&Message);
  }

  return Message.wParam;
}



LRESULT CALLBACK WndProc(HWND hWnd, UINT iMessage, WPARAM wParam, LPARAM lParam)
{
  switch (iMessage) {
  case WM_CREATE:
    hEdit = CreateWindow(L"edit", NULL, WS_CHILD | WS_VISIBLE | WS_BORDER |
      ES_AUTOHSCROLL, 101020025, hWnd, (HMENU)ID_EDIT, g_hInst, NULL);
    return 0;
  case WM_COMMAND:
    switch (LOWORD(wParam)) {
    case ID_EDIT:
      switch (HIWORD(wParam)) {
      case EN_CHANGE:
        GetWindowText(hEdit, str, 128);
        SetWindowText(hWnd, str);
      }
    }
    return 0;
  case WM_DESTROY:
    PostQuitMessage(0);
    return 0;
  }
  return(DefWindowProc(hWnd, iMessage, wParam, lParam));
}

이렇게 





또다른 윈도우 창인 에디터



현재 구조체 까지 ReadFile() 함수로  읽어 들여서 



#include <windows.h>

LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
HINSTANCE g_hInst;
LPSTR lpszClass = L"BmpViewer";

LRESULT On_Destroy(HWND, WPARAM, LPARAM);
LRESULT On_Create(HWND hWnd, WPARAM wParam, LPARAM lParam);
LRESULT On_Paint(HWND hWnd, WPARAM wParam, LPARAM lParam);

typedef  struct _stMsgMap
{
  UINT uiMsg;
  LRESULT(*fp)(HWND, WPARAM, LPARAM);
}StMsgMap;

StMsgMap msgMap[] =
{
  { WM_DESTROY, On_Destroy },
  { WM_CREATE, On_Create },
  { WM_PAINT, On_Paint },
  { WM_NULL, 0 }
};

//ReadFile()로 읽어들이기 위한 동적할당 받을 buf 변수 생성.
unsigned char *ucpData;      //동적할당용
BITMAPFILEHEADER stBfHead;    //비트맵 헤더용
BITMAPINFOHEADER stBinfoHead;  

int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance
  , LPSTR lpszCmdParam, int nCmdShow)
{
  HWND hWnd;
  MSG Message;
  WNDCLASS WndClass;
  g_hInst = hInstance;

  WndClass.cbClsExtra = 0;
  WndClass.cbWndExtra = 0;
  WndClass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
  WndClass.hCursor = LoadCursor(NULL, IDC_ARROW);
  WndClass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
  WndClass.hInstance = hInstance;
  WndClass.lpfnWndProc = (WNDPROC)WndProc;
  WndClass.lpszClassName = lpszClass;
  WndClass.lpszMenuName = NULL;
  WndClass.style = CS_HREDRAW | CS_VREDRAW;
  RegisterClass(&WndClass);

  hWnd = CreateWindow(lpszClass, lpszClass, WS_OVERLAPPEDWINDOW,
    CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
    NULL, (HMENU)NULL, hInstance, NULL);
  ShowWindow(hWnd, nCmdShow);

  while (GetMessage(&Message, 000)) 
  {
    TranslateMessage(&Message);
    DispatchMessage(&Message);
  }

  return Message.wParam;
}

LRESULT CALLBACK WndProc(HWND hWnd, UINT iMessage, WPARAM wParam, LPARAM lParam)
{
  StMsgMap *stpMap = msgMap;

  while ((*stpMap).uiMsg != WM_NULL)
  {
    if (iMessage == (*stpMap).uiMsg)
    {
      ((*stpMap).fp)(hWnd, wParam, lParam);

      return 0;
    }

    ++stpMap;
  }

  return(DefWindowProc(hWnd, iMessage, wParam, lParam));
}

LRESULT On_Create(HWND hWnd, WPARAM wParam, LPARAM lParam)
{
  HDC hdc;
  HANDLE hFile;  // 한번 읽고 닫기 때문에 static x
  BOOL bRet;
  DWORD dwCount;

  /*bmp 읽어 들이기*/
  hFile = CreateFile(L"C:\\456.bmp", GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
  if (hFile == INVALID_HANDLE_VALUE)
  {
    MessageBox(hWnd, L"On_Create()의 CreateFile() 에러.", L"Button", MB_OK);
    PostQuitMessage(0);
    //SendMessage(hWnd, WM_DESTROY, 0, 0);  // 못 쓰겠끔 프로그램 종료.
    return 0;
  }
  
  /*헤더 읽기*/
  bRet = ReadFile(hFile, &stBfHead, sizeof(BITMAPFILEHEADER), &dwCount, NULL);
  if (bRet == FALSE)
  {
    CloseHandle(hFile);
    MessageBox(hWnd, L"On_Create()의 ReadFile() 에러. - BITMAPFILEHEADER", L"Button", MB_OK);
    PostQuitMessage(0);
    return 0;
  }

  bRet = ReadFile(hFile, &stBinfoHead, sizeof(BITMAPINFOHEADER), &dwCount, NULL);
  if (bRet == FALSE)
  {
    CloseHandle(hFile);
    MessageBox(hWnd, L"On_Create()의 ReadFile() 에러. - BITMAPINFOHEADER", L"Button", MB_OK);
    PostQuitMessage(0);
    return 0;
  }

  MessageBox(hWnd, L"On_Create()의 CreateFile()가 성공적으로 열렸습니다.", L"Button", MB_OK);
  CloseHandle(hFile);

  return 0;
}

LRESULT On_Destroy(HWND hWnd, WPARAM wParam, LPARAM lParam)
{
  //MessageBox(hWnd, L"디스트로이", L"Button", MB_OK);  //디스트로이가 호출안되고 바로 종료
  //파일 열때 디스트로이 함수를 생성해버리면 동적할당이 실패했는데도 불구하고 동적할당을 해제하려 들 것이다. 이것을 
  PostQuitMessage(0);

  return 0;
}

LRESULT On_Paint(HWND hWnd, WPARAM wParam, LPARAM lParam)
{
  HDC hdc;

  return 0;
}




이제 스태틱을 한번 만들어 보자.

윈도우 14개의 줄을 만들어서 


28라인 정도가 필요하므로 그냥 함수로 나눠 불할로 컴파일.



void PrintBmpInfo(HWND hWnd, BITMAPFILEHEADER *stpFH, BITMAPINFOHEADER *stpIH)
{//이제 헤더출력을 위해 윈도우를 만들어 스태틱을 만들 것이다.
  CreateWindow(L"static", 
    L"Magic Number", 
    WS_CHILD | WS_VISIBLE,
    202010025
    hWnd, 
    (HMENU)-1
    g_hInst, 
    NULL);

  return;


디폴트 좌표로 잡을 것이다. 


시작점의 가로 세로 + 위 아래 스태틱의 거리 + 


매직 넘버가 화면에 잘 뜨는지 확인 해보자

#include <windows.h>

//오른쪽으로 띄울 공간
#define  X_POS    20      
#define  Y_POS    20    

//스태틱의 가로, 세로 크기
#define  S_WIDTH    100    
#define  S_HEIGHT  25

//줄과 줄 사이의 공간(세로)
#define  Y_GAP    5  

LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
HINSTANCE g_hInst;
LPSTR lpszClass = L"BmpViewer";

LRESULT On_Destroy(WPARAM, LPARAM);
LRESULT On_Create(WPARAM wParam, LPARAM lParam);
LRESULT On_Paint(WPARAM wParam, LPARAM lParam);
void PrintBmpInfo(BITMAPFILEHEADER *stpFH, BITMAPINFOHEADER *stpIH);

typedef  struct _stMsgMap
{
  UINT uiMsg;
  LRESULT(*fp)(WPARAM, LPARAM);
}StMsgMap;

StMsgMap msgMap[] =
{
  { WM_DESTROY, On_Destroy },
  { WM_CREATE, On_Create },
  { WM_PAINT, On_Paint },
  { WM_NULL, 0 }
};

//ReadFile()로 읽어들이기 위한 동적할당 받을 buf 변수 생성.
unsigned char *ucpData;      //동적할당용

HWND hWnd;

int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance
  , LPSTR lpszCmdParam, int nCmdShow)
{
  MSG Message;
  WNDCLASS WndClass;
  g_hInst = hInstance;

  WndClass.cbClsExtra = 0;
  WndClass.cbWndExtra = 0;
  WndClass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
  WndClass.hCursor = LoadCursor(NULL, IDC_ARROW);
  WndClass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
  WndClass.hInstance = hInstance;
  WndClass.lpfnWndProc = (WNDPROC)WndProc;
  WndClass.lpszClassName = lpszClass;
  WndClass.lpszMenuName = NULL;
  WndClass.style = CS_HREDRAW | CS_VREDRAW;
  RegisterClass(&WndClass);

  hWnd = CreateWindow(lpszClass, lpszClass, WS_OVERLAPPEDWINDOW,
    CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
    NULL, (HMENU)NULL, hInstance, NULL);

  ShowWindow(hWnd, nCmdShow);

  while (GetMessage(&Message, 000)) 
  {
    TranslateMessage(&Message);
    DispatchMessage(&Message);
  }

  return Message.wParam;
}

LRESULT CALLBACK WndProc(HWND hWpWnd, UINT iMessage, WPARAM wParam, LPARAM lParam)
{
  StMsgMap *stpMap = msgMap;

  hWnd = hWpWnd;

  while ((*stpMap).uiMsg != WM_NULL)
  {
    if (iMessage == (*stpMap).uiMsg)
    {
      ((*stpMap).fp)(wParam, lParam);

      return 0;
    }

    ++stpMap;
  }

  return(DefWindowProc(hWnd, iMessage, wParam, lParam));
}

LRESULT On_Create(WPARAM wParam, LPARAM lParam)
{
  HANDLE hFile;  // 한번 읽고 닫기 때문에 static x
  BITMAPFILEHEADER stBfHead;    //비트맵 헤더용
  BITMAPINFOHEADER stBinfoHead;
  BOOL bRet;
  DWORD dwCount;

  /*bmp 읽어 들이기*/
  hFile = CreateFile(L"C:\\1.bmp", GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
  if (hFile == INVALID_HANDLE_VALUE)
  {
    MessageBox(hWnd, L"On_Create()의 CreateFile() 에러.", L"Button", MB_OK);
    PostQuitMessage(0);
    //SendMessage(hWnd, WM_DESTROY, 0, 0);  // 못 쓰겠끔 프로그램 종료.
    return 0;
  }
  
  /*헤더 읽기*/
  bRet = ReadFile(hFile, &stBfHead, sizeof(BITMAPFILEHEADER), &dwCount, NULL);
  if (bRet == FALSE)
  {
    CloseHandle(hFile);
    MessageBox(hWnd, L"On_Create()의 ReadFile() 에러. - BITMAPFILEHEADER", L"Button", MB_OK);
    PostQuitMessage(0);
    return 0;
  }

  bRet = ReadFile(hFile, &stBinfoHead, sizeof(BITMAPINFOHEADER), &dwCount, NULL);
  if (bRet == FALSE)
  {
    CloseHandle(hFile);
    MessageBox(hWnd, L"On_Create()의 ReadFile() 에러. - BITMAPINFOHEADER", L"Button", MB_OK);
    PostQuitMessage(0);
    return 0;
  }

  PrintBmpInfo(&stBfHead, &stBinfoHead);

  MessageBox(hWnd, L"On_Create()의 CreateFile()가 성공적으로 열렸습니다.", L"Button", MB_OK);
  CloseHandle(hFile);

  return 0;
}

LRESULT On_Destroy(WPARAM wParam, LPARAM lParam)
{
  //MessageBox(hWnd, L"디스트로이", L"Button", MB_OK);  //디스트로이가 호출안되고 바로 종료
  //파일 열때 디스트로이 함수를 생성해버리면 동적할당이 실패했는데도 불구하고 동적할당을 해제하려 들 것이다. 이것을 
  PostQuitMessage(0);

  return 0;
}

LRESULT On_Paint(WPARAM wParam, LPARAM lParam)
{
  PAINTSTRUCT ps;

  BeginPaint(hWnd, &ps);
  EndPaint(hWnd, &ps);

  return 0;
}

void PrintBmpInfo(BITMAPFILEHEADER *stpFH, BITMAPINFOHEADER *stpIH)
{//이제 헤더출력을 위해 윈도우를 만들어 스태틱을 만들 것이다.
  CreateWindow(
    L"static", 
    L"Magic Number", 
    WS_CHILD | WS_VISIBLE,
    X_POS, Y_POS, S_WIDTH, S_HEIGHT,
    hWnd, 
    (HMENU)-1
    g_hInst, 
    NULL
    );

  MessageBox(hWnd, L"PrintBmpInfo()의 CreateWindow()가 성공적으로 열렸습니다.", L"Button", MB_OK);

  return;
}


왜 안뜨나 컴페어 돌려보니..


LRESULT CALLBACK WndProc(HWND hWpWnd, UINT iMessage, WPARAM wParam, LPARAM lParam)
{
  StMsgMap *stpMap = msgMap;

  hWnd = hWpWnd;

  while ((*stpMap).uiMsg != WM_NULL)
  {
    if (iMessage == (*stpMap).uiMsg)
    {
      ((*stpMap).fp)(wParam, lParam);

      return 0;
    }

    ++stpMap;
  }

  return(DefWindowProc(hWnd, iMessage, wParam, lParam));
}


이부분의 리턴 값이 원래

  return(DefWindowProc(hWpWnd, iMessage, wParam, lParam));

이거였는데

return(DefWindowProc(hWnd, iMessage, wParam, lParam));

이걸로 바꿔주니 나온다.


아.. 다시 바꾸니 또 된다.

정말 참..




잘 뜨는 것을 확인 했다.


좀 전에 다시 해보다가 발견한 것인데 이미 스테틱은 화면에 뿌려져있는데 안 보이는 것일 뿐이더라, 화면 마구잡이로 흔들고 나오라고 클릭했더니 이제

제대로 뜬다.


이제 옆에 값 출력하는 부분도  배열에 밀어 넣어버리면 된다.