목차
1. 개요
2. 필요한 도구 및 라이브러리
3. 프로젝트 구조 및 파일 설명
4. 주요 코드 해석
5. 프로그램 실행 및 결과
1. 개요
- Win32 API를 사용하여 연락처 관리 프로그램을 만드는 방법 소개
이 예제에서는 기본적인 연락처 정보를 저장하고 표시하는 간단한 프로그램 입니다.
1. 필요한 헤더 파일 포함 :
프로젝트에 필요한 헤더 파일들을 포함시킵니다.
#include "framework.h"
#include "Project1.h"
#include "Commctrl.h"
#include "Windowsx.h"
#include <wchar.h>
2. 다이얼로그 프로시저 선언:
다이얼로그 이벤트 처리를 담당할 DlgProc 함수를 프로토타입으로 선언합니다.
INT_PTR CALLBACK DlgProc(HWND, UINT, WPARAM, LPARAM);
3. 메인 함수 정의:
프로그램의 진입점인 wWinMain 함수를 정의합니다. 여기서 DialogBox 함수를 사용하여 다이얼로그를 생성하고 DlgProc 함수를 콜백으로 전달합니다.
int APIENTRY wWinMain(_In_ HINSTANCE hInstance,
_In_opt_ HINSTANCE hPrevInstance,
_In_ LPWSTR lpCmdLine,
_In_ int nCmdShow)
{
DialogBox(hInstance, MAKEINTRESOURCE(IDD_DIALOG_MAIN), NULL, DlgProc);
return 0;
}
4. 다이얼로그 이벤트 처리:
다이얼로그 이벤트를 처리하는 DlgProc 함수를 정의합니다. 여기서는 다음과 같은 이벤트를 처리합니다.
WM_INITDIALOG: 다이얼로그 초기화
WM_CLOSE: 다이얼로그 닫기
WM_COMMAND: 다이얼로그에서 발생한 커맨드 처리
5. 리스트뷰 생성 및 초기화:
WM_INITDIALOG 이벤트에서 리스트뷰를 생성하고 초기화합니다. 이를 위해 컬럼을 추가하고 각 컬럼에 제목을 설정합니다.
case WM_INITDIALOG:
hList = GetDlgItem(hDlg, IDC_LIST_MEMBER);
lvColumn.mask = LVCF_FMT | LVCF_WIDTH | LVCF_SUBITEM | LVCF_TEXT;
lvColumn.fmt = LVCFMT_CENTER;
for (int i = 0; i < 3; i++) {
lvColumn.cx = 50;
lvColumn.pszText = strMenu[i];
ListView_InsertColumn(hList, i, &lvColumn);
}
return (INT_PTR)TRUE;
6. 연락처 추가 기능 구현:
WM_COMMAND 이벤트에서 사용자가 "추가" 버튼을 클릭할 때 연락처를 리스트뷰에 추가하는 기능을 구현합니다.
case WM_COMMAND:
if (LOWORD(wParam) == IDC_BUTTON_INSERT) {
// 리스트뷰에 새로운 항목을 추가
lvItem.iItem = ListView_GetItemCount(hList);
lvItem.iSubItem = 0;
lvItem.mask = LVIF_TEXT;
_snwprintf_s(string, 100, L"%d", lvItem.iItem);
lvItem.pszText = string;
ListView_InsertItem(hList, &lvItem);
// 입력한 이름과 전화번호를 리스트뷰에 추가
GetDlgItemText(hDlg, IDC_EDIT_NAME, string, 100);
ListView_SetItemText(hList, lvItem.iItem, 1, string);
GetDlgItemText(hDlg, IDC_EDIT_PHONE, string, 100);
ListView_SetItemText(hList, lvItem.iItem, 2, string);
// 에디트 컨트롤을 초기화 (지우기)
SetDlgItemText(hDlg, IDC_EDIT_NAME, NULL);
SetDlgItemText(hDlg, IDC_EDIT_PHONE, NULL);
return (INT_PTR)TRUE;
}
- 프로그램의 기능과 목적 간단히 설명
위 코드는 사용자가 "추가" 버튼을 클릭하면 다음 동작을 수행합니다.
리스트뷰의 항목 수를 가져와 새로운 항목의 인덱스를 설정합니다.
새로운 항목을 리스트뷰에 추가합니다.
입력한 이름과 전화번호를 해당 항목의 서브아이템으로 설정합니다.
에디트 컨트롤의 내용을 지워 초기화합니다.
이 프로그램은 연락처 관리를 위한 기본적인 기능을 제공합니다. 사용자는 이름과 전화번호를 입력하고 "추가" 버튼을 클릭하여 연락처를 리스트뷰에 추가할 수 있습니다. 추가된 연락처는 리스트뷰에서 확인할 수 있습니다.
2. 필요한 도구 및 라이브러리
- Visual Studio 2019 설치
Visual Studio 2019 설치를 진행할때 ' 데스크톱 및 모바일' 에 '.NET 데스크톱 개발 ' , ' C++를 사용한 데스크톱 개발 ' 을 체크하여 설치한다.
C++을 사용한 데스크톱' 안에 있는 선택사항에서 ' 최신 v142 빌드 도구용 C++ MFC ' , ' v142 빌드 도구용 C++/CU 지원 ', 'V142 빌드 도구용 C++ 모듈 ', ' Windows 10 SDK (10.0.17763.0)' 을 선택하여 설치한다.
- Win32 API에 대한 간단한 소개
Win32 API는 윈도우 컴퓨터에서 돌아가는 프로그램들이 운영체제의 다양한 기능을 사용할 수 있도록 도와주는 도구입니다. 이 도구를 이용해서 프로그램 작성자들은 화면에 그림을 그리거나, 파일을 열고 저장하고, 인터넷에 연결하는 등의 작업을 쉽게 할 수 있습니다.
- 사용된 라이브러리(Commctrl.h, Windowsx.h 등) 설명
이 프로그램에서 사용된 라이브러리는 다음과 같습니다.
framework.h: 프로젝트에 필요한 헤더 파일과 라이브러리를 포함하는 헤더 파일입니다. 이 파일은 프로젝트 템플릿에 의해 자동 생성되며, 필요한 헤더 파일과 라이브러리를 추가하거나 수정할 수 있습니다.
Project1.h: 프로젝트의 리소스 관련 헤더 파일입니다. 리소스 관련 상수와 ID가 정의되어 있으며, 프로젝트에서 사용하는 컨트롤의 ID와 일치시키는 것이 중요합니다.
Commctrl.h: 공용 컨트롤 라이브러리 헤더 파일로, 리스트뷰와 같은 공용 컨트롤을 사용하기 위해 필요합니다. 공용 컨트롤 라이브러리는 사용자 인터페이스를 개선하기 위해 제공되는 다양한 컨트롤을 포함하고 있습니다.
Windowsx.h: Windows 프로그래밍의 편의를 위해 추가된 헤더 파일로, 다양한 매크로 및 함수를 제공합니다. 이 헤더 파일을 사용하면 Win32 API를 보다 효율적으로 사용할 수 있습니다.
wchar.h: C 표준 라이브러리의 일부로, 와이드 문자 관련 함수를 제공하는 헤더 파일입니다. 이 프로그램에서는 _snwprintf_s 함수를 사용하여 문자열을 포맷팅하기 위해 이 헤더 파일이 포함되었습니다.
이 프로그램에서 사용된 라이브러리들은 Win32 API 프로그램 작성을 지원하며, 각각의 라이브러리는 고유한 기능과 컨트롤을 제공합니다. 사용자 인터페이스를 구성하고 이벤트 처리를 수행하기 위해 이러한 라이브러리들을 사용하였습니다.
5. 전체 코드
- 전체 코드 및 주석
// 필요한 헤더 파일들을 포함시킵니다.
#include "framework.h"
#include "Project1.h"
#include "Commctrl.h"
#include "Windowsx.h"
#include <wchar.h>
// DigProc 함수를 프로토타입으로 선언합니다.
INT_PTR CALLBACK DlgProc(HWND, UINT, WPARAM, LPARAM);
// wWinMain 함수는 프로그램의 진입점입니다.
int APIENTRY wWinMain(_In_ HINSTANCE hInstance,
_In_opt_ HINSTANCE hPrevInstance,
_In_ LPWSTR lpCmdLine,
_In_ int nCmdShow)
{
// 다이얼로그 박스를 생성하고 DigProc 함수를 콜백으로 전달합니다.
DialogBox(hInstance, MAKEINTRESOURCE(IDD_DIALOG_MAIN), NULL, DlgProc);
return 0;
}
// DigProc 함수는 다이얼로그 이벤트에 대한 처리를 담당합니다.
INT_PTR CALLBACK DlgProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
UNREFERENCED_PARAMETER(lParam);
// hList : 리스트뷰 핸들을 저장할 변수입니다.
static HWND hList;
// strMenu : 리스트뷰의 컬럼 제목을 저장할 배열입니다.
wchar_t* strMenu[] = { L"순번", L"이름",L"전화번호"};
LVCOLUMN lvColumn;
LVITEM lvItem;
wchar_t string[100];
// 메시지에 따라 적절한 처리를 수행합니다.
switch (message)
{
case WM_INITDIALOG: // 다이얼로그 초기화
// 리스트뷰 핸들을 가져옵니다.
hList = GetDlgItem(hDlg, IDC_LIST_MEMBER);
lvColumn.mask = LVCF_FMT | LVCF_WIDTH | LVCF_SUBITEM | LVCF_TEXT;
lvColumn.fmt = LVCFMT_CENTER;
// 리스트뷰에 컬럼을 추가합니다.
for (int i = 0; i < 3; i++) {
lvColumn.cx = 50;
lvColumn.pszText = strMenu[i];
ListView_InsertColumn(hList, i, &lvColumn);
}
return (INT_PTR)TRUE;
case WM_CLOSE: // 다이얼로그 닫기
// 다이얼로그를 종료하고 프로그램을 종료합니다.
EndDialog(hDlg, 0);
return (INT_PTR)TRUE;
case WM_COMMAND: // 다이얼로그에서 발생한 커맨드를 처리합니다.
if (LOWORD(wParam) == IDC_BUTTON_INSERT) {
// 리스트뷰에 새로운 항목을 추가합니다.
lvItem.iItem = ListView_GetItemCount(hList);
lvItem.iSubItem = 0;
lvItem.mask = LVIF_TEXT;
_snwprintf_s(string, 100, L"%d", lvItem.iItem);
lvItem.pszText = string;
ListView_InsertItem(hList, &lvItem);
GetDlgItemText(hDlg, IDC_EDIT_NAME, string, 100);
ListView_SetItemText(hList, lvItem.iItem, 1, string);
GetDlgItemText(hDlg, IDC_EDIT_PHONE, string, 100);
ListView_SetItemText(hList, lvItem.iItem, 2, string);
//에디트 컨트롤 클리어
SetDlgItemText(hDlg, IDC_EDIT_NAME, NULL);
SetDlgItemText(hDlg, IDC_EDIT_PHONE, NULL);
return (INT_PTR)TRUE;
}
}
return (INT_PTR)FALSE;
}
6. 프로그램 실행 및 결과
- 프로그램을 실행 결과
7. 마무리 및 추가 개선 방향
- 프로그램을 만들며 얻은 경험 및 느낀점
1. 다양한 에러가 발생함
"char" 형식의 값을 "LPWSTR"(Long Pointer Wide String) 형식의 엔터티에 할당 할 수 없습니다. 라는 에러가 발생하였다.
원인은 "LPWSTR" 는 Unicode 문자열을 나타내는 식이며, "char*" 는 ANSI 문자열을 나태내어 두 형식 간의 호환성 문제 떄문에 에러가 발생하였다.
이 문제를 해결하려면 코드에서 문자열 처리를 모두 Unicode 문자열로 변경하여야 합니다.
char* strMenu[] = { "이름", "전화번호" };
- 해당 코드에서 ' char* ' 을 ' wchat_t* ' 로 변경하여야 합니다. 왜냐하면 ' char '는 ASCII 문자열을 저장하기에 적합하기 때문에 ' wcat_t ' 타입을 사용하여 유니코드 문자열을 저장할 수 있게 변경해줘야 하기 떄문입니다.
- ' "이름", "전화번호" ' 를 ' L"이름", L"전화번호" ' 로 변경하여야 합니다. 왜냐하면 문자열 상수 앞에 "L" 접두사를 붙여 컴파일러에게 문자열 상수가 유니코드입을 알려주는 역활을 하기 때문에 유니코드 형식임을 나타내기 위해서 변경하여 합니다.
char string[100];
- 해당 코드에서는 'char' 를 ' wchar_t ' 로 변경하여야 합니다. 이유는 위와 같습니다.
sprintf_s(string, "%d", lvItem.iItem);
- 이 코드에서는 ' sprintsf_s ' 를 ' _snwprintf_s ' 로 변경해줘야 합니다. 왜냐하면 기본 함수는 문자열의 형식을 지정하여 출력하는 함수로, ASCII 문자열을 처리합니다. 하지만 프로그램이 유니코드 문자열을 사용하기로 결정하였으므로 ' _snwprintf_s ' 로 변경해줘야 합니다.
- 또한 코드 상단에 헤더파일을 선언하는 곳에서 ` #include <studio.h> ' 를 ' #include <wchar.h> ' 로 변경해줘야 합니다.
왜냐하면 ' <studio.h> ' 는 표준 입출력 함수와 관련된 매크로, 타입 및 함수를 정의하는 헤더파일로 대부분의 경우 ASCII 문자열을 처리하는 곳에 사용되므로 ' <wchar.h> ' 인 유니코드 문자열 처리에 필요한 함수로 변경해줘야 합니다.
이러한 변경을 통해 프로그램은 일관된 유니코드 문자열 처리를 수행할 수 있습니다.
'C++' 카테고리의 다른 글
[C++] Detours (0) | 2024.06.21 |
---|---|
[C++] Win32 API 메시지 박스 (0) | 2023.05.03 |
[C++] Win32API WebView2 (웹뷰) (0) | 2023.04.20 |
[C++] C++언어 (0) | 2023.04.14 |
[C++] Win32 API를 활용한 간단한 연락처 관리 프로그램 만들기(2일차) (0) | 2023.04.12 |
댓글