'2017/03/18'에 해당되는 글 3건

  1. 2017.03.18 메모장 만들기 - 함수들
  2. 2017.03.18 메모장 만들기 - 리소스
  3. 2017.03.18 메모장 만들기 - 알고리즘

안녕하십니까 이번 포스팅에서는 메모장을 작성할 때 쓰인 함수들을 살펴보겠습니다.

쓰인 함수는 다음과 같습니다.

- void SaveFile_as(HWND hWnd) : 공통 대화 상자를 호출하여 파일을 저장할 때 쓰이는 함수입니다.

- void SaveFile(HWND hWnd) :  공통 대화 상자를 호출하지 않고 파일을 저장할 때 쓰이는 함수입니다.

- void FileOpen(HWND hWnd) : 공통 대화 상자를 호출하여 파일을 열 때 쓰이는 함수입니다.

- void SaveDlgBox(HWND hWnd) : 파일을 작성하다가 다른 작업을 수행할 때 작성된 파일의 저장여부를 묻는 작업을 하는 함수입니다.

먼저 SaveFile_as 함수를 살펴봅시다. 매개변수는 메모장의 핸들입니다.(edit 윈도우 핸들이 아닙니다)

하나씩 설명해 드리겠습니다.

- OPENFILENAME Save;

OPENFILENAME 구조체 변수인 Save를 선언합니다. OPENFILENAME 구조체를 살펴봅시다.

이 구조체는 파일 열기 대화상자와 파일 저장 대화상자를 열기 위해 사용하는 구조체입니다. 멤버 변수가 굉장히 많은데 이중 쓰인 멤버 변수만 설명하겠습니다.

* DWORD lStructSize

이 구조체의 크기를 받는 멤버변수 입니다. 필수로 입력해줘야 합니다.

* HWND hwndOwner

호출되는 공통대화상자를 소유하는 윈도우의 핸들을 받는 멤버변수 입니다. 이 값이 없으면 메모장과 공통대화상자가 따로 놀게 됩니다.

* LPCTSTR lpstrFilter

공통대화상자를 사용할 때의 필터 형식을 받는 멤버변수입니다. 즉

이 부분을 의미합니다. 보시다시피 텍스트 문서와 모든 파일, 총 2개의 필터로 되어있는 것을 확인할 수 있습니다. 이 필터 형식은 \0를 이용하여 작성하는데 이는 나중에 추가로 설명하겠습니다.

* LPTSTR lpstrFile

공통 대화 상자에서 사용자가 작성한 파일 이름과 경로를 저장할 배열을 받는 멤버변수입니다. 배열에 값이 들어있으면 처음 공통 대화상자를 호출할 때 그 값이 파일 이름으로 지정되어 있습니다. 메모장에서는 *.txt가 초기값으로 적혀있습니다.

* DWORD nMaxFile

lpstrFile멤버 변수에 저장된 배열의 크기를 받는 멤버변수입니다.

* DWORD Flags

호출하는 대화상자의 속성을 받는 멤버 변수 입니다. 속성은 여러가지가 있지만 여기서 쓰인 속성들만 알아 봅시다.

OFN_PATHMUSTEXIST : 사용자가 유효하지 않은 파일 이름을 입력했을 경우 경고 메세지 박스를 표시합니다.

OFN_OVERWRITEPROMPT : 파일을 저장할 때 동일한 파일이 있을 경우 사용자에게 덮어쓸건지의 여부를 묻는 메세지 박스를 표시합니다.

* LPCTSTR lpstrDefExt

사용자가 파일 이름에 확장자를 입력하지 않았을 때 자동으로 쓰일 확장자를 멤버변수로 받습니다. 메모장에서는 *.txt가 기본 확장자로 지정되어있습니다.

자 이제 설명이 끝났으니 다음 코드를 살펴봅시다.

- TCHAR name[256] = L"*.txt",notepad_title[261];

공통 대화 상자에서 사용자가 입력한 파일 이름을 저장시킬 name변수와 메모장의 타이틀 부분에 표시할 문자열을 저장하기 위한 notepad_title변수를 선언합니다.

- HANDLE file,filename;

새로운 파일을 만들기 위한 핸들형 file변수와 만든 파일에서 경로명을 제외한 파일명을 추출하기 위한 핸들형 filename을 선언합니다. 참고로 이 HANDLE은 HWND하고는다릅니다. HWND는 윈도우의 핸들을 저장하는 용도이고 HANDLE은 파일의 핸들을 저장하는 용도로 쓰입니다.

- LRESULT editstr_len;

메모장의 edit 윈도우에 작성된 내용의 길이를 저장시키기 위한 변수 editstr_len을 선언합니다. 자료형이 LRESULT인 이유는 edit 윈도우에 작성된 내용의 길이를 받기 위해 사용하는 SendMessage함수의 반환값이 LRESULT이기 때문입니다.

- LPTSTR Wide_str;

edit 윈도우에 작성된 문자열(유니코드)을 저장하기 위해서 선언하였습니다.

- char *mult_str;

유니코드 문자열을 멀티바이트로 변환한 후 변환된 멀티바이트 문자열을 저장하기 위해 선언하였습니다.

- WIN32_FIND_DATA WFD;

WIN32_FIND_DATA구조체 변수인 WFD를 선언합니다. MSDN에서 WIN32_FIND_DATA구조체를 한 번 알아봅시다.

정리하면 WIN32_FIND_DATA구조체는 FindFirstFile, FindFirstFileEx, FindNextFile 함수로 찾은 파일의 정보를 저장할 때 쓰이는 구조체라고 되어 있네요. 여기서 이 구조체를 쓴 이유는 저장한 파일의 파일명을 추출하기 위해서입니다. 메모장에서 파일을 저장시키면 메모장의 제목표시줄 부분이 저장된 파일 명으로 바뀌지요? 즉 메모장의 제목표시줄 내용을 바꾸기 위해 사용되었다고 보시면 됩니다.

여기서 멤버변수는 딱 하나 사용하였습니다. 사용한 멤버변수만 설명드리겠습니다.

*TCHAR cFileName

찾은 파일의 파일명을 저장하는 멤버변수입니다.

- int str_len;

유니코드 문자열을 멀티바이트 문자열로 변환시킬 때 필요한 멀티바이트 문자열의 길이를 받기 위해 선언하였습니다. 유니코드 문자열을 멀티바이트 문자열로 변환시키기 위해선 변환될 멀티바이트 문자열의 길이가 필요한데 이 길이를 받기 위한 변수입니다.

- memset(&Save,0,sizeof(Save));

처음에 선언한 OPENFILENAME구조체의 Save변수는 현재 각 멤버값이 쓰레기값으로 설정되어 있으므로 원활한 사용을 위해 Save변수의 각 멤버들을 memset함수를 사용하여 전부 0으로 초기화 시킵니다. memset함수의 정의를 한 번 살펴봅시다.

지정한 버퍼를 지정한 값으로 설정한다고 나와있습니다. 인수는 총 3개가 있습니다만 간단하니 간략하게 설명만 하고 넘어가겠습니다.

dest는 버퍼의 주소, c는 버퍼에 설정할 값, count는 버퍼의 사이즈를 받습니다. 간단하지요? 다음으로 넘어갑시다.

- Save.lStructSize = sizeof(Save);

lStructSize멤버 변수에 Save 구조체의 크기를 넣어줍니다. 이 부분은 필수로 해줘야 합니다.

- Save.hwndOwner = hWnd;

hwndOwner에 메모장의 핸들값을 넣어 메모장이 공통대화상자를 소유하도록 하였습니다.

- Save.lpstrFilter = L"텍스트 문서(*.txt)\0*.txt\0모든 파일 (*.*) \0*.*";

lpstrFilter에 공통대화상자에서 쓰일 필터형식을 넣습니다. 처음의 문자열은 첫번째 필터목록에 적혀있는 글자를 적었구요. 그 다음은 \0으로 구분을 한후 이 필터를 선택할 시에 보여질 확장자를 입력합니다. 텍스트 문서이니 당연히 *.txt 확장자를 입력해야겠지요? 그다음 다시 \0으로 구분을 한 후 두번째 필터에 적혀있는 글자를 적고 모든 파일을 의미하는 확장자인 *.*을 입력하였습니다.

- Save.lpstrFile = name;

파일의 이름과 경로를 저장시킬 배열(name)을 lpstrFile멤버 변수에 집어넣습니다.

- Save.nMaxFile = sizeof(name);

name의 배열크기를 넣어주었습니다.

- Save.Flags = OFN_PATHMUSTEXIST | OFN_OVERWRITEPROMPT;

앞서 설명드린 2개의 옵션을 넣어주었습니다.

- Save.lpstrDefExt = L"*.txt";

메모장에선 대부분 txt파일을 사용하므로 기본 확장자를 txt로 지정해주었습니다.

- if(GetSaveFileName(&Save) != 0)

GetSaveFileName함수의 결과값이 참일 경우 if문을 수행합니다. GetSaveFileName함수를 한 번 알아봅시다.

파일을 저장하는 공용대화상자를 호출하는 함수라고 되어있네요. 인자는 OPENFILENAME 구조체의 주소입니다. 호출한 대화상자에서 사용자가 이름을 입력하고 확인 버튼을 누르면 반환값은 0이 아니구요. 그 외의 오류나 혹은 사용자가 취소를 눌렀을 경우 반환값은 0입니다. 이 함수가 정상적으로 수행이 되면 Save구조체 멤버변수에 각각의 값이 들어가집니다.

즉 이 if문은 사용자가 파일 이름을 입력하고 확인 버튼을 눌러 파일을 저장하려고 할 때 수행합니다.

-  wsprintf(FILEPATH,L"%s",Save.lpstrFile);

wsprintf함수를 사용하여 TCHAR 형 FILEPATH 전역변수에 Save.lpstrFile의 값을 %s형식으로 저장합니다. 이 함수를 수행하고 나면 FILEPATH에는 저장한 파일의 경로가 들어가게 됩니다. 이러한 작업을 거치는 이유는 이 문서가 저장이 되지 않은 새로운 문서인지 아니면 저장된 문서를 불러와 수정하는 문서인지를 구별하기 위해서입니다. 이는 나중에 자세히 알려드리겠습니다.

- file = CreateFile(Save.lpstrFile,GENERIC_READ | GENERIC_WRITE,NULL,NULL,CREATE_ALWAYS,FILE_ATTRIBUTE_NORMAL,NULL);

CreateFile함수를 사용하여 파일을 만들고 그 파일의 핸들을 file변수에 저장시키는 작업을 하는 부분입니다. CreateFile함수를 자세히 살펴봅시다.

설명을 보면 파일 혹은 입출력 장치를 열거나 만드는 함수라고 되어있습니다. 인자들을 하나씩 살펴봅시다.

* LPCTSTR lpFileName

열거나 만들 파일의 경로명입니다. 여기서는 GetSaveFileName함수에 의해 지정된 경로명인 Save.lpstrFile의 값이 들어갔습니다.

* DWORD dwDesiredAccess

파일의 권한을 설정하는 부분입니다. 파일을 읽기전용으로 작업할려면  GENERIC_READ, 쓰기 전용으로 작업할려면 GENERIC_WRITE, 읽고 쓰기로 작업할려면 두 옵션을 전부 넣어주면 됩니다. 여기서는 두 옵션을 전부 넣었습니다.

* DWORD dwShareMode

파일의 공유모드를 설정하는 부분인데 지금은 딱히 신경쓸 필요가 없으므로 NULL값을 줍시다.

* LPSECURITY_ATTRIBUTES lpSecurityAttributes

파일의 보안과 핸들 상속에 관해 설정하는 부분인데 역시 지금은 신경쓸 필요가 없으므로 NULL값을 주었습니다.

* DWORD dwCreationDisposition

파일을 열건지 만들건지에 대해 설정하는 부분입니다. 여기는 GetSaveFileName함수가 성공했을 때 실행하는 부분이기 때문에 파일을 무조건 만들거나 혹은 이미 있는 파일에 덮어쓰기를 해야 하므로 CREATE_ALWAYS옵션을 주었습니다.

* DWORD dwFlagsAndAttributes

파일의 속성을 지정하는 부분입니다. 딱히 다른 속성을 넣을 필요성을 못느낀다면 일반적으로 FILE_ATTRIBUTE_NORMA를 줍니다.

* HANDLE                hTemplateFile

파일의 확장속성을 제공하는 템플릿 파일의 핸들을 받습니다. 솔직히 이부분은 무슨 뜻인지 몰라 그냥 NULL값을 주었습니다.

다음으로 넘어갑시다.

- editstr_len = SendMessage(Edit,WM_GETTEXTLENGTH,0,0);

SendMessage함수를 사용하여 Edit 윈도우에 WM_GETTEXTLENGTH메세지를 보내고 그 결과값을 editstr_len에 저장합니다. WM_GETTEXTLENGTH메세지는 윈도우와 연관된 텍스트의 길이를 반환해달라고 요청하는 메세지이며 반환되는 값은 NULL문자를 포함하지 않은 문자열의 길이입니다.

-  Wide_str = (TCHAR *)malloc(sizeof(TCHAR)*(editstr_len+1));

malloc함수를 이용해 Wide_str배열을 editstr_len의 길이에 맞게 동적할당합니다. 원래 malloc함수의 기본 반환값은 void 포인터 이지만 대부분 특정 포인터를 반환하기 위해 형변환을 많이 사용합니다. Edit윈도우의 문자는 TCHAR이므로 TCHAR의 size * (문자열의 길이 + NULL) 만큼 할당하고 Wide_str의 자료형은 TCHAR이므로 TCHAR 포인터로 반환합니다.

- mult_str = (CHAR *)malloc(sizeof(TCHAR)*(editstr_len+1));

마찬가지로 malloc함수를 이용해 mult_str배열도 editstr_len의 길이에 맞게 동적할당합니다. 다만 mult_str은 멀티바이트 문자열을 저장하기 위한 변수이므로 반환할 떄에는 CHAR 포인터로 반환합니다.

- memset(Wide_str,0,sizeof(TCHAR)*(editstr_len+1));
  memset(mult_str,0,sizeof(TCHAR)*(editstr_len+1));

할당된 두 배열들을 0으로 초기화 시켜 줍니다. 참고로 여기서 주의 하실 점이 있는데 동적할당된 배열을 memset으로 초기화 시킬 때에는 sizeof(배열)이 아닌 동적할당 할때의 그 size를 주어야 합니다.
  
- GetWindowText(Edit,Wide_str,editstr_len+1);

배열의 준비가 다 되었으니 이제 문자열을 긁어올 때입니다. GetWindowText함수를 사용하여 Edit윈도우에 적혀있는 텍스트를 널까지 포함하여 긁어온 후 Wide_str에 저장시킵니다.

- str_len = WideCharToMultiByte(949,NULL,Wide_str,lstrlen(Wide_str),NULL,NULL,NULL,NULL);

str_len 변수에 WideCharToMultiByte함수의 결과값을 넣습니다. 이 코드는 WideCharToMultiByte함수를 사용하여 Wide_str에 저장되어 있는 유니코드 문자열을 멀티바이트 문자열로 바꾸기 위한 첫번째 작업입니다. WideCharToMultiByte함수를 사용하기 위해선 멀티바이트 문자열의 길이가 필요합니다만 아직 멀티바이트로 변환도 되지 않은 상태에서 유니코드의 문자열만으로 멀티바이트 문자열의 길이를 계산하기엔 조금 복잡합니다. 그러므로 WideCharToMultiByte함수의 반환값이 멀티바이트 문자열의 길이라는 것을 이용하여 str_len변수에 길이를 저장시키는 것입니다.

WideCharToMultiByte함수의 정의를 한 번 봅시다.

UTF-16(유니코드) 문자열을 새로운 문자열로 변환시키는 함수라고 되어있군요. 인자들을 하나씩 살펴봅시다.

* UINT CodePage

문자열을 변환할 때 기준이 되는 코드 페이지를 인자로 받습니다. 여기서는 지정된 인자를 쓰지 않았는데 이유는 지정된 인자로 문자열을 변환시켜 저장한 파일을 일반 메모장에서 불러오면 글자가 깨져버리기 때문입니다. 그래서 저는 일반 메모장을 리버싱하여 메모장에서 WideCharToMultiByte함수를 사용할 때 쓰인 인자인 949를 넣어주었습니다. 이 949라는 값은 윈도우즈에서 사용하는 일반적인 한글을 뜻합니다.

* DWORD dwFlags

변환 타입을 지정하는 부분입니다. 이 부분은 잘 몰라서 NULL값을 주었습니다.

* LPCWSTR lpWideCharStr

바꿀 유니코드 문자열입니다.

* int cchWideChar

유니코드 문자열의 길이입니다.

* LPSTR lpMultiByteStr

코드페이지에 의해 변환된 멀티바이트 문자열을 저장할 배열을 인수로 받습니다. 하지만 처음 쓰인 WideCharToMultiByte함수의 이 인자는 NULL값인데 그 이유는 목적이 문자열 변환이 아닌 변환된 문자열의 길이를 얻기 위해서입니다. 즉 쓰일 필요가 없어서 그냥 NULL값을 주었습니다.

* int cbMultiByte

멀티바이트의 문자열의 길이를 인자로 넣습니다. 역시 첫 WideCharToMultiByte함수는 이 값을 알아내기 위해 사용하는 것이므로 이 인자또한 NULL로 주었습니다.

* LPCSTR lpDefaultChar

유니코드 문자열 중 지정한 코드페이지로 변환이 불가능한 문자가 있을 경우 이 인자에 지정된 문자열로 변환합니다. 사용하지 않을 경우에는 NULL을 줍니다.

* LPBOOL lpUsedDefaultChar

유니코드 문자열 중 지정한 코드페이지로 변환이 불가능한 문자가 있어 lpDefaultChar의 값으로 변환이 된 경우는 TRUE, 아니면 FALSE 값을 저장시킬 BOOL형 변수의 포인터를 인자로 받습니다. 역시 사용하지 않을 경우에는 NULL을 줍니다.

마지막으로 이 함수의 반환값은 변환된 멀티바이트 문자열의 길이입니다. 즉 우리는 이 코드를 통해 멀티바이트로 변환된 문자열의 길이를 알아내어 str_len변수에 저장시킵니다.

- WideCharToMultiByte(949,NULL,Wide_str,lstrlen(Wide_str),mult_str,str_len,NULL,NULL);

앞 WideCharToMultiByte함수로 알아낸 멀티바이트로 변환된 문자열의 길이를 이용하여 실제로 유니코드 문자열을 멀티바이트 문자열로 변환시키는 작업을 하는 코드입니다. 인자에 mult_str과 str_len이 들어가신 것을 볼 수 있습니다.

- WriteFile(file,mult_str,strlen(mult_str),(LPDWORD)&str_len,NULL);

CreateFile함수로 생성된 파일에 mult_str에 들은 문자열을 쓰는 부분입니다. WriteFile함수를 살펴봅시다.

지정된 파일 혹은 입출력 장치에 데이터를 기록해주는 함수라고 되어있습니다. 인자들을 살펴봅시다.

* HANDLE hFile

기록할 파일의 핸들입니다.

* LPCVOID lpBuffer

파일에 기록될 데이터가 들어있는 버퍼입니다.

* DWORD nNumberOfBytesToWrite

기록될 데이터의 길이입니다.

* LPDWORD lpNumberOfBytesWritten

작성된 데이터의 바이트 수를 저장시킬 변수의 포인터를 인자로 받습니다. 여기서는 str_len의 주소를 인자로 주었습니다. 다만 자료형이 LPDWORD이므로 형변환을 해주었습니다.

* LPOVERLAPPED lpOverlapped

OVERLAPPED구조체의 포인터를 인자로 받습니다. 이 부분은 잘 모르므로 그냥 NULL값을 주었습니다.

즉 이 WriteFile함수를 사용하여 파일에 멀티바이트로 변환된 데이터를 작성합니다.즉 여기까지 수행하면 저장이 완료된 것입니다. 그럼 이제 마무리가 남았군요. 한 번 봅시다.
 
- filename = FindFirstFile(Save.lpstrFile,&WFD);

FindFirstFile함수를 이용하여 Save.lpstrFile 경로에 있는 파일을 찾아 그 파일의 핸들을 filename에 저장합니다. FindFirstFile함수를 살펴봅시다.

지정한 파일을 찾아 열고 그 파일의 핸들을 반환하는 함수입니다. 인자들을 하나씩 살펴봅시다.

* LPCTSTR lpFileName

찾을 파일의 경로를 인자로 받습니다.

* LPWIN32_FIND_DATA lpFindFileData

WIN32_FIND_DATA 구조체의 주소를 인자로 받습니다. 파일을 찾는데 성공하면 이 구조체에 파일의 정보가 들어가게 됩니다.

즉 정리하면 이 코드는 CreateFile함수로 만든 파일을 찾아서 그 정보를 WIN32_FIND_DATA 구조체인 WFD의 각 멤버에 저장시킵니다. 이러한 작업을 거치는 이유는 파일의 이름을 추출하기 위해서이지요. 파일의 이름을 추출하는 이유는 메모장의 제목표시줄의 내용을 수정하기 위해서인데 Save.lpstrFile을 쓰면 경로까지 다 포함되기 때문에 우리는 파일명만 추출하기 위해 FindFirstFile함수를 사용한 것입니다.

이 코드가 실행하고 나면  WFD의 cFileName멤버에 파일명(확장자 포함)이 들어가게 됩니다.

- memset(notepad_title,0,sizeof(TCHAR)*(lstrlen(WFD.cFileName)+1));

notepad_title 변수를 파일명의 길이+NULL값 만큼만 초기화를 시켜줍니다.

- wsprintf(notepad_title,L"%s - 메모장",WFD.cFileName);

notepad_title변수에 WFD.cFileName의 값을 "%s - 메모장" 형식으로 넣습니다. 이 코드가 수행되면 notepad_title에는 파일이름.txt - 메모장 이런 식으로 값이 들어가게 됩니다.

- SetWindowText(hWnd,notepad_title);

SetWindowText함수를 사용하여 메모장의 제목표시줄을 notepad_title의 값으로 수정합니다. 이 SetWindowText함수는 보통 인자로 받은 핸들의 제목표시줄을 수정할 때 쓰입니다.

- free(Wide_str);
  free(mult_str);

모든 작업이 끝났으므로 동적할당한 배열들을 해제시킵니다.
  
- FindClose(filename);
  CloseHandle(file);

  
모든 작업이 끝났으므로 열려있는 파일의 핸들들을 전부 닫습니다. 

- str_CHANGE = 0;

성공적으로 저장이 끝나면 str_CHANGE 전역변수의 값을 0으로 줍니다. 이 str_CHANGE 변수는 문자열이 변경됬는지 변경 안됬는지의 여부를 저장시키는 변수입니다. 메모장에서는 저장이 되지 않은 파일을 닫기 버튼을 눌렀을 경우 edit윈도우에 적힌 글자수가 0이나 아니냐에 의해 변경된 내용을 저장시키는 박스가 뜹니다. 허나 파일을 한 번 저장한 후 추가 작업을 하거나 불러온 파일을 작업하는 경우에는 문자열의 길이에 상관없이 텍스트가 살짝이라도 변경되면 박스가 뜹니다.

좀더 쉽게 얘기하자면 12345라는 값이 저장된 텍스트를 불러온후 바로 닫으면 박스가 뜨지 않지만 12345라는 값에 6을 적어 123456으로 만든 후 다시 6을 지워 12345로 만들었을 경우에는 원본 파일이랑 값이 똑같지만 저장을 묻는 박스가 뜨게 됩니다.

- else
  CANCEL = 1;

사용자가 공용대화상자에서 저장버튼을 누르지 않고 취소버튼을 눌렀을 때의 처리입니다. 이때는 CANCEL 전역 변수의 값을 1로 설정합니다. 이렇게 하는 이유는 예를 들어 설명드리겠습니다.

메모장에서 내용을 작성하다가 닫기 버튼을 누르면 변경 내용을 저장하겠습니까? 라는 창이 뜨지요? 그 창에는 저장, 저장 안함, 취소 버튼이 있는데 저장 버튼을 누를 경우 경우에 따라 대화상자를 띄워서 저장을 하거나 아니면 대화상자를 띄우지 않고 저장을 한 후 메모장이 종료됩니다. 반대로 저장 안함을 누르면 파일을 저장하지 않고 종료가 됩니다. 그러나 마지막으로 남은 취소 버튼을 누르면 종료가 되지 않습니다. 즉 종료 알고리즘을 제대로 수행할려면 이 취소 버튼을 눌렀느냐 누르지 않았느냐의 여부를 체크해야 하는데 이를 위해 선언한 변수가 CANCEL입니다.

그런데 이 CANCEL변수가 파일을 저장하는 것과 무슨 관계가 있을 까요? 정답은 사용자가 호출된 공용대화상자에서 취소 버튼을 눌러 저장을 하지 않았을 때 메모장이 종료되지 않는다는 것입니다. 즉 저장, 저장 안함, 취소 버튼 중 저장 버튼을 눌렀음에도 불구하고 사용자가 저장을 하지 않으면 메모장이 종료되지 않기 때문에 이를 구현하기 위해 사용하였습니다. 나중에 좀 더 자세히 설명드리겠습니다.

후 길고 긴 SaveFile_as함수 설명이 끝났습니다. 이 함수는 보통 메모장에서 새 파일을 저장할 때나 혹은 다른 이름으로 저장 버튼을 눌렀을 때 쓰입니다.

다음으로 넘어가 SaveFile함수를 설명드리겠습니다. 매개변수는 SaveFile_as랑 동일합니다. 이 함수는 대화상자를 호출한다는 점을 제외하면 SaveFile_as 함수랑 똑같은 기능을 수행합니다. 그러므로 SaveFile_as에서 설명한 코드는 따로 설명하지 않겠습니다.

 - GetWindowText(hWnd,notepad_title,261);

일반적으로 GetWindowText함수는 edit윈도우의 내용을 불러오는 데 쓰이지만 일반 윈도우 핸들을 인자로 받는 GetWindowText함수는 윈도우의 제목표시줄의 값을 읽어옵니다. 여기서는 현재 작성중인 문서가 새로 작성하는 문서인지 혹은 이미 저장된 파일을 읽어와 수정하는 문서인지를 판별하기 위해 사용하였습니다.

새로 작성하는 문서라면 제목표시줄의 값은 제목 없음 - 메모장 일 것이고 수정하는 문서라면 파일이름.txt - 메모장 일 것입니다. 그리고 이 값에 따라 저장한는 방법이 달라지게 됩니다.

정리하면 메모장의 제목표시줄에서 261자만큼 문자열을 들고와 notepad_title에 저장시킵니다.

- if(lstrcmp(notepad_title,L"제목 없음 - 메모장") == 0)
   SaveFile_as(hWnd);

lstrcmp함수를 이용하여 notepad_title의 문자열과 "제목 없음 - 메모장" 문자열을 비교합니다. 만약 두 문자열의 값이 같다면 이 함수는 0을 반환하고 아니면 0이 아닌 값을 반환합니다.

이 함수가 0을 반환하면 메모장의 제목표시줄의 내용은 제목 없음 - 메모장 이라는 뜻이고 이는 곧 이 파일이 새로 작성하는 문서라는 것을 알 수 있습니다. 그러므로 새로 작성하는 문서를 저장할 때에는 공용대화상자를 호출하여 저장하기 위해 SaveFile_as(hWnd) 함수를 호출하여 처리합니다.

- else 

아닌 경우에는 대화상자를 호출하지 않고 파일 저장 작업을 시도합니다. 이 다음 코드는 SaveFile_as함수 코드랑 비슷하니 설명을 생략하겠습니다.

이제 다음 함수인 FileOpen함수를 살펴봅시다. 말 그대로 파일을 여는 작업을 하는 함수를 뜻하며 파일을 열때에는 공용대화상자를 무조건 호출하므로 파일을 저장하는 작업과는 다르게 함수가 하나입니다. 아까랑 마찬가지로 역할이 비슷한 코드는 생략하겠습니다.

- OPENFILENAME Open;

공용대화상자를 호출하기 위해 OPENFILENAME 구조체 변수인 Open을 선언합니다.

- TCHAR name[256] = L"",notepad_title[261];

name과 notepad_title변수를 선언합니다. 다만 파일을 저장할때하고는 다르게 열때에는 초기값이 따로 지정되어 있지 않으므로 그냥 ""로 초기화시켰습니다.

- int filesize,str_len;

저장된 파일의 크기를 읽어올 변수인 filesize와 MultiByteToWideChar함수에서 유니코드 문자열 길이를 저장시킬 변수인 str_len변수를 선언하였습니다. 같은 자료형이므로 변수를 하나로 선언해서 써도 되지만 가독성을 위해 일부러 2개의 변수로 선언하였습니다.

-DWORD size;

ReadFile함수에서 쓰일 DWORD형 size변수를 선언하였습니다.

Open구조체 멤버의 초기화 부분은 SaveFile_as함수와 동일하므로 생략하겠습니다.

- if(GetOpenFileName(&Open) != 0)

GetOpenFileName함수를 이용하여 파일 열기 대화 상자를 호출합니다. 그 외는 GetSaveFileName함수와 동일합니다. 파일 열기 버튼을 누른다면 아래의 코드를 수행하게 됩니다.
 
- wsprintf(FILEPATH,L"%s",Open.lpstrFile);

 FILEPATH전역 변수에 Open.lpstrFile값을 %s형식으로 저장시킵니다. 앞에서도 이야기 했지만 이 FILEPATH 변수는 이 파일이 새로 작업하는 파일인지 아니면 수정된 파일인지를 체크하는 부분입니다. 이 FILEPATH변수는 메모장의 타이틀 부분이 바뀔 때마다 값이 달라집니다.

- file = CreateFile(Open.lpstrFile,GENERIC_READ | GENERIC_WRITE,NULL,NULL,OPEN_ALWAYS,FILE_ATTRIBUTE_NORMAL,NULL);

CreateFile함수를 이용하여 파일을 엽니다. 앞의 함수에서는 파일을 만드는 옵션인 CREATE_ALWAYS옵션을 썻다면 여기서는 파일을 여는 옵션인 OPEN_ALWAYS옵션을 사용합니다.

- filesize = GetFileSize(file,NULL);

CreateFile함수를 이용해 연 파일의 핸들로부터 파일 크기를 받아옵니다. 앞의 SaveFile_as함수처럼 Edit윈도우에서 크기를 받아올 수 없으므로 GetFileSize함수를 사용하여 파일크기를 구합니다. GetFileSize함수를 한 번 알아봅시다.

파일 크기를 바이트 단위로 반환해주는 함수입니다. 인자를 한 번 살펴봅시다.

* HANDLE hFile

대상 파일의 핸들입니다.

* LPDWORD lpFileSizeHigh

파일의 크기가 클 경우 사용하는 인자입니다. 지금은 일단 필요없으니 NULL값을 줍시다.

다음으로 넘어갑시다.

- mult_str = (CHAR *)malloc(sizeof(TCHAR)*(filesize+1));
  Wide_str = (TCHAR *)malloc(sizeof(TCHAR)*(filesize+1));

앞서 구한 filesize의 값을 이용하여 각 배열들을 동적할당합니다.

- memset(mult_str,0,sizeof(TCHAR)*(filesize+1));
  memset(Wide_str,0,sizeof(TCHAR)*(filesize+1));

동적할당 후 초기화 시켜줍시다.
  
- ReadFile(file,mult_str,filesize,&size,NULL);

ReadFile함수를 사용하여 파일을 읽습니다. 이 ReadFile함수를 살펴봅시다.

지정된 파일에서 데이터를 읽는 함수입니다. 인자들을 한 번 살펴봅시다.

* HANDLE hFile,

대상 파일의 핸들입니다.

* LPVOID lpBuffer

읽은 데이터를 저장시킬 버퍼를 인자로 받습니다.

* DWORD nNumberOfBytesToRead

읽어올 최대 바이트 값을 인자로 받습니다.

* LPDWORD lpNumberOfBytesRead

읽은 바이트 수를 저장시킬 DWORD형 변수의 주소를 인자로 받습니다. 여기서는 size변수를 인자로 주었습니다.

*LPOVERLAPPED lpOverlapped

OVERLAPPED구조체 변수의 주소를 인자로 받습니다. 이 부분은 WriteFile함수와 동일하게 NULL값을 주었습니다.

- str_len = MultiByteToWideChar(949,NULL,mult_str,strlen(mult_str),NULL,NULL);

읽어들인 멀티바이트 코드를 유니코드로 변환시키기 위해 MultiByteToWideChar함수를 사용하여 변환될 유니코드 길이를 구한 후 str_len에 저장시킵니다. 이 MultiByteToWideChar함수를 살펴봅시다.

말 그대로 멀티바이트 코드를 UTF-16(유니코드)문자열로 바꿔주는 함수입니다. 인자들을 살펴봅시다.

* UINT CodePage

변환에 수행할 코드페이지입니다. 앞서 WideCharToMultiByte에서 949 값으로 변환을 했으니 여기서도 949값을 이용합니다.

* DWORD dwFlags

변환 타입을 지정하는 부분입니다. 이 부분은 WideCharToMultiByte와 동일하게 NULL값을 줍시다.

* LPCSTR lpMultiByteStr

바꿀려는 멀티바이트 문자열을 인자로 받습니다.

* int cbMultiByte

멀티바이트 문자열의 길이입니다.

* LPWSTR lpWideCharStr

변환된 유니코드 문자열을 저장할 배열을 인자로 받습니다.

*int cchWideChar

유니코드 문자열의 길이입니다.

이번에도 WideCharToMultiByte함수와 똑같이 유니코드 문자열 길이를 구하기 위해 첫 MultiByteToWideChar함수에는 마지막 인자 2개 값을 NULL로 주었습니다. 이렇게 구해진 유니코드 문자열의 길이는 str_len변수에 저장됩니다.

- MultiByteToWideChar(949,NULL,mult_str,strlen(mult_str),Wide_str,str_len);

 str_len의 값을 이용해 멀티바이트 문자열을 유니코드로 변환시켜 Wide_str변수에 저장합니다.
  
- SetWindowText(Edit,Wide_str);

Edit클래스의 내용을 Wide_str문자열로 설정합니다. 여기까지 코드가 수행되면 이제 파일 열기 작업은 다 끝난 것입니다. 그 다음 코드들은 메모장의 제목표시줄을 수정하고 동적할당 된 배열을 해제, 파일 핸들을 닫는 작업인데 이 부분은 SaveFile_as함수와 동일하므로 생략하겠습니다.

마지막으로 SaveDlgBox 함수를 설명하겠습니다. 역시 이 함수의 인자도 메모장의 핸들입니다. 이 함수는 작업 도중 메모장을 종료할 때 나오는 메세지 박스를 구현한 것입니다. 이 메세지 박스는 일반 MessageBox함수를 사용한 것이 아니기 때문에 따로 함수를 사용하여 구현하였습니다.

- TASKDIALOGCONFIG TDBOX;

TASKDIALOGCONFIG 구조체 변수인 TDBOX를 선언합니다. TASKDIALOGCONFIG의 정의를 한 번 봅시다.

이 구조체는 메세지 박스를 띄우기 위해 사용하는 구조체입니다. 다시 한 번 말하지만 이 메세지 박스는 MessageBox 함수가 띄우는 메세지 박스하고는 다릅니다.

여기서 사용된 멤버들만 한 번 알아봅시다.

* UINT cbSize;

구조체의 크기를 받는 멤버변수입니다.

* HWND hwndParent;

이 메세지 박스의 소유자 핸들을 받는 멤버변수입니다.

* HINSTANCE hInstance;

보통 WINAPI함수의 인자를 매개변수로 받습니다.

* TASKDIALOG_COMMON_BUTTON_FLAGS dwCommonButtons

이 멤버변수는 메세지박스에 표시할 버튼을 지정하는 부분입니다. 사용자가 지정할 버튼 외에 따로 추가할 버튼을 인자로 주시면 됩니다.

자료형이 TASKDIALOG_COMMON_BUTTON_FLAGS입니다. 한 번 살펴봅시다.

여러개의 플래그가 포함되어 있는 것을 확인할 수 있습니다. 이 6개의 플래그중 메세지 박스에 표시할 버튼을 골라 값을 주시면 됩니다.

여기서는 사용자 지정 버튼(저장, 저장 안함)을 제외한 취소 버튼을 추가하기 위해 TDCBF_CANCEL_BUTTON을 값으로 주었습니다.

 * PCWSTR pszWindowTitle;

메세지박스의 제목표시줄을 인자로 받는 부분입니다.

* const TASKDIALOG_BUTTON * pButtons;

TASKDIALOG_BUTTON 구조체의 주소를 인자를 받는 부분입니다. 이 부분이 바로 사용자가 정의한 버튼을 받는 부분이라고 볼 수 있습니다. TASKDIALOG_BUTTON 구조체를 한 번 살펴봅시다.

메세지 박스에 표시될 버튼의 정보를 저장하는 구조체입니다. 각 인자들을 살펴봅시다.

** int nButtonID

버튼이 선택되었을 때 반환될 값을 인자로 받습니다. 쉽게 말하면 버튼의 ID라고 보시면 됩니다.

** PCWSTR pszButtonText

버튼에 표시될 문자열을 인자로 받습니다.

만약 여러개의 버튼을 정의하고 싶으시다면 TASKDIALOG_BUTTON 구조체의 변수를 배열로 선언하시면 됩니다.

* UINT cButtons

pButtons의 배열의 항목 수입니다. 직접 입력해줘도 되지만 ARRAYSIZE함수를 사용해도 됩니다.

* PCWSTR pszMainInstruction

메세지 박스에 표시할 문자열을 인자로 받습니다.

자 다음으로 넘어갑시다.

- const TASKDIALOG_BUTTON TDBUTTON[] = { {IDOK, L"저장"},{IDNO, L"저장 안함"} };

pButtons 인자에 넣을 배열인 TASKDIALOG_BUTTON 구조체 변수인 TDBUTTON을 배열로 선언합니다. 그리고 선언된 배열에 값을 주어 IDOK라는 저장 버튼과 IDNO 라는 저장 안함 버튼을 만듭니다. IDOK과 IDNO는 매크로 상수를 의미하며 각각 OK버튼과 NO버튼을 의미합니다.

- LRESULT editstr_len;

SendMessage함수를 이용하여 문자열의 길이를 받아올 변수를 선언하였습니다.

- LPTSTR MessageStr;

메세지박스에 표시할 문자열을 저장할 변수 MessageStr을 선언하였습니다.

- int result = 0,str_len = 0;

메세지 박스에서 버튼을 눌렀을 때 그버튼의 ID값을 저장할 변수인 result와 SendMessage함수를 사용하지 않고 문자열의 길이를 저장할 변수 str_len 변수를 선언하였습니다.

- memset(&TDBOX,0,sizeof(TDBOX));

구조체를 초기화시킵니다.

- TDBOX.cbSize = sizeof(TDBOX);

cbSize멤버 변수에 구조체의 크기를 넣어줍니다.

- TDBOX.hwndParent = hWnd;

부모핸들을 메모장의 핸들로 지정합니다.

- TDBOX.hInstance = Global_hInstance;

hInstance를 WINAPI함수의 인자인 hInstance값을 넣어줍니다. Global_hInstance는 hInstance값을 쓰기 위한 전역변수입니다.

- TDBOX.dwCommonButtons = TDCBF_CANCEL_BUTTON;

사용자가 정의한 버튼 외에 취소 버튼을 추가하기 위해서 TDCBF_CANCEL_BUTTON 값을 넣어주었습니다.

- TDBOX.pszWindowTitle = L"메모장";

메세지박스의 제목표시줄을 메모장으로 설정합니다.

- TDBOX.pButtons = TDBUTTON;

정의한 버튼의 배열인 TDBUTTON을 인자로 집어넣어 줍니다.

- TDBOX.cButtons = ARRAYSIZE(TDBUTTON);

TDBUTTON 배열의 항목개수를 인자로 넣어줍니다. 2라는 값을 넣어도 되고 ARRAYSIZE함수를 이용하여 주어도 됩니다. ARRAYSIZE함수는 매개변수를 배열로 받아 배열의 요소 개수를 반환하는 함수입니다.
 
- if(lstrcmp(FILEPATH,L"제목 없음 - 메모장") == 0)

FILEPATH와 "제목 없음 - 메모장" 문자열을 비교합니다. 값이 같다면 지금 작성하고 있는 파일은 새로 작성하는 파일을 의미하며 값이 다르다면 저장된 파일을 수정하는 파일을 의미합니다. 새로 작성하는 파일일 경우 아래의 코드를 수행합니다.

-  editstr_len = SendMessage(Edit,WM_GETTEXTLENGTH,0,0);

Edit 윈도우로부터 문자열의 길이를 받습니다.

- if(editstr_len > 0)

Edit 윈도우에 적힌 문자가 1개 이상 있을 경우 아래의 코드를 수행합니다.
  
- TDBOX.pszMainInstruction = L"변경 내용을 제목 없음에 저장하시겠습니까?";

메세지박스의 내용을 "변경 내용을 제목 없음에 저장하시겠습니까?"로 지정합니다.

- TaskDialogIndirect(&TDBOX, &result, NULL, NULL);

TaskDialogIndirect함수를 이용하여 실질적으로 메세지박스를 호출합니다. TaskDialogIndirect함수를 살펴봅시다.

메세지 박스를 호출하는 함수입니다. 인자들을 살펴봅시다.

* const TASKDIALOGCONFIG *pTaskConfig

TASKDIALOGCONFIG 구조체 변수의 주소를 인자로 받습니다.

int *pnButton

메세지 박스에서 버튼을 눌렀을 때 그 버튼의 값(ID)을 저장하는 변수의 주소를 인자로 받습니다.

* int *pnRadioButton

메세지 박스에서 라디오 버튼을 눌렀을 때 그 버튼의 값(ID)을 저장하는 변수의 주소를 인자로 받습니다. 사용하지 않을 경우 NULL값을 줍니다.

* BOOL *pfVerificationFlagChecked

검증 체크 박스의 선택 여부를 인자로 받는 부분인데 아직은 우리랑 상관이 없으니 NULL값을 주었습니다.

- switch (result)

메세지박스에서 버튼을 눌렀을 때 그 버튼의 처리를 위한 switch문입니다. 

 - case IDOK:
     SaveFile(hWnd);
     break;
   case IDNO:
     CANCEL = 0;
     break;
  case IDCANCEL:
     CANCEL = 1;
     break;

IDOK(저장)버튼을 눌렀을 경우 SaveFile함수를 호출합니다. IDNO(저장안함) 버튼을 눌렀을 경우  CANCEL 변수를 0으로 설정합니다. IDCANCEL(취소) 버튼을 눌렀을 경우 취소버튼을 눌렀다는 것을 알리기 위해 CANCEL값을 1로 설정합니다.

- else
   CANCEL = 0;

만약 Edit 윈도우에 아무것도 적혀있지 않은 상태라면 메세지 박스를 호출하지 않고 CANCEL 값을 0으로 줍니다.

- else

이번 else는 메모장의 제목표시줄의 값이 "제목 없음 - 메모장"이 아닐 경우의 처리입니다. 즉 FILEPATH 변수의 값이 제목 없음 - 메모장이 아니라는 것이지요. 이럴 때는 아래의 코드를 수행합니다.

-  if(str_CHANGE == 1)

str_CHANGE 의 값이 1일 경우 아래의 코드를 수행합니다. 메모장의 제목표시줄 값이 제목 없음 - 메모장이 아닐 경우에는 지금 작업중인 파일이 한번 저장된 파일 혹은 불러온 파일 이라는 것을 의미합니다. 이런 경우에는 Edit 윈도우의 문자열 길이랑은 상관없이 문자열이 변경되었는지의 여부에 따라 메세지박스 호출 여부가 달라집니다.

정리하면 문자열이 변경되었을 경우 아래의 작업을 수행합니다.  
  
- str_len = lstrlen(FILEPATH);

FILEPATH 변수에 들어있는 문자열의 길이를 구하여 str_len변수에 저장합니다. 지금 FILEPATH변수에는 현재 작업 중인 파일의 경로가 들어있습니다.

- MessageStr = (TCHAR *)malloc(sizeof(TCHAR)*(str_len+19));

MessageStr변수를 str_len에 맞게 동적할당 해줍니다. 여기 보시면 str_len값에 19를 더해주는 부분이 있는데 이는 나중에 설명드리겠습니다.

- memset(MessageStr,0,sizeof(TCHAR)*(str_len+19));

동적할당한 변수를 초기화시킵니다.

wsprintf(MessageStr,L"변경 내용을 %s에 저장하시겠습니까?",FILEPATH);

MessageStr에 FILEPATH값을 "변경 내용을 %s에 저장하시겠습니까?" 형식으로 저장합니다. 여기 보시면 %s외에도 다른 문자열이 추가로 들어간 것을 볼 수 있습니다. %s를 제외한 문자열은 "변경 내용을 에 저장하시겠습니까?" 인데 이 문자열의 길이는 총 18입니다. 즉 앞의 동적할당에서 그냥 str_len의 길이만큼만 동적할당을 해버리면 MessageStr변수는 %s외의 추가적인 문자열을 저장하지 못하게 됩니다. 그러므로 str_len에 추가 문자열(18)과 NULL(1)을 추가로 더해주어 MessageStr이 모든 문자열을 저장할 수 있게 해주었습니다.

- TDBOX.pszMainInstruction = MessageStr;

이렇게 저장된 MessageStr의 값을 메세지박스의 내용으로 지정해줍니다.

- TaskDialogIndirect(&TDBOX, &result, NULL, NULL);

메세지 박스를 호출합니다.

- switch (result)
    case IDOK:
     SaveFile(hWnd);
     break;
    case IDNO:
     CANCEL = 0;
     break;
    case IDCANCEL:
     CANCEL = 1;
     break;

누른 버튼에 따라 처리를 해줍니다.

- free(MessageStr);

모든 작업이 다 끝났으니 동적할당한 배열을 해제합시다.

네 이것으로 메모장에서 쓰인 4개의 함수들의 설명을 마치겠습니다. 다음 포스팅에선 소스코드를 직접 보여드리겠습니다. 

Posted by englishmath
,

안녕하십니까 이번 포스팅에서는 메모장을 만들때 쓰인 리소스를 설명하겠습니다.

먼저 메뉴부분 리소스 입니다.

이 부분의 캡션을 자세히 보시면 파일(&F)라고 되어있는 것을 볼 수 있습니다. 여기서 &는 &다음에 오는 문자를 단축키로 만들겠다는 뜻입니다. 일반 메모장에서 alt+F를 누르면 파일 메뉴가 자동으로 선택되지요? 그것을 구현한 것입니다.

그외의 다른 메뉴도 이와 똑같이 작성하여 단축키를 구현하였습니다. 그리고 각 서브메뉴의 ID는 여러분 편한대로 지정해주시면 됩니다.

이번엔 아이콘 리소스를 봅시다.

이 아이콘 리소스는 메모장에서 쓰인 아이콘 리소스를 그대로 추출한 것입니다. 이 아이콘은 여타 쓰인 아이콘과는 조금 다른 아이콘인데 이 아이콘의 특징은 아이콘이 쓰이는 곳에 따라 크기와 모습이 달라지는 아이콘입니다. 실제로 메모장을 실행하면

작업표시줄에는 이런 모양의 아이콘이 보입니다. 하지만 메모장 프로그램을 살펴보면

위와 같이 작업표시줄에 뜨인 아이콘과는 다른 모습의 아이콘이 등장합니다. 즉 이를 구현하기 위해 메모장에 쓰인 아이콘을 그대로 추출하여 사용하였습니다.

이제 다이로그 리소스를 살펴봅시다.

이 부분은 페이지 설정이란 메뉴를 눌렀을 때 나타나는 페이지 설정 대화상자를 구현한 부분입니다. 용지,방향,여백,미리보기 부분은 GROUPBOX를 사용하였고 크기,공급 등의 글자는 static text를 사용하였으며 세로,가로는 radio button, 미리보기 안의 사진과 그림자 부분은 picture control, 크기와 공급을 설정하는 부분은 combobox, 여백값과 머리글,바닥글을 입력하는 부분은 edit control을 사용하였습니다.

이제 액셀러레이터 리소스를 살펴봅시다.

여기서 액셀러레이터라는 것은 단축키를 설정할 때 사용합니다. 단 아까와 같이 Alt를 눌러 사용하는 단축키가 아닌 독자적인 단축키를 설정할 때 사용합니다. 일반 메모장에서는 저장과 열기 등을 누를 때 Ctrl+S, Ctrl+O를 사용하지요? 이를 구현하기 위해 작성한 리소스입니다.

ID는 단축키를 눌렀을 때 동작될 메뉴의 ID를 의미합니다. Key Modifer은 단축키와 추가적으로 누를 키를 설정합니다. 여기서는 Ctrl로 설정하였습니다. 그리고 Key는 말그대로 단축키를 의미하며 Type은 ascii와 virtual key 둘 중 하나로 지정할 수가 있는데 Key Modifer기능을 사용할려면 virtual key로 지정해야 하기에 .virtual key로 지정하였습니다.

네 이것으로 리소스 설명을 마치겠습니다.

다음 포스팅에서는 메모장을 만들 때 쓰인 함수를 설명하겠습니다.

'Programing > 프로그램 직접 개발하기(C)' 카테고리의 다른 글

메모장 만들기 - 소스  (0) 2017.03.27
메모장 만들기 - 함수들  (0) 2017.03.18
메모장 만들기 - 알고리즘  (0) 2017.03.18
지뢰찾기 다듬기 1  (0) 2017.01.09
지뢰찾기 만들기 - 소스  (3) 2017.01.07
Posted by englishmath
,

안녕하십니까? 우선 포스팅이 늦어진 점 사과드립니다.

이번 포스팅에서는 윈도우즈에서 제공하는 메모장 프로그램을 한번 만들어보겠습니다. 물론 100퍼 똑같이 구현은 하지 못하지만 그래도 최대한 비슷하게 만들어보았습니다.

먼저 메모장의 알고리즘을 설명하자면

1. 메모장은 일반 윈도우와 Edit윈도우 2개로 이루어져 있다

2. Edit윈도우의 크기는 일반 윈도우의 크기에 따라 달라진다.

3. 메모장에 작성한 내용을 저장시킬 때에는 유니코드로 작성된 텍스트를 멀티바이트로 변환시켜 저장한다.

4. 반대로 텍스트 파일을 열 경우에는 텍스트를 멀티바이트로 읽어 유니코드로 변환시킨 후 Edit윈도우에 표시한다.

5. 파일을 열거나 저장할 때는 윈도우즈에서 제공하는 공통대화상자를 사용한다.

등이 있겠네요.

자 그럼 이제 다음 포스팅에서 본격적으로 시작해보겠습니다.

 

Posted by englishmath
,