단막 Windows API 활용법
OpenFolder Dialog 열기
[폴더 찾아보기...] 종류의 버튼을 클릭할 때 나오는 다이얼로그를 Windows API에서 어떻게 호출하고, 그 선택된 폴더 경로를 가져오는 방법에 대해 설명한다.
이 기능은 쉘shell에 대한 기능이므로 쉘 함수를 사용하기 위하여
#include <ShlObj.h>
를 포함한다.
레이아웃 설정
빈 화면에 다음과 같이 윈도우들을 배치하도록 WndProc
을 작성한다.
/* WndProc: HWND, UINT, WPARAM, LPARAM */
static HWND s_hwndStatic1 = (HWND)NULL;
static HWND s_hwndStatic2 = (HWND)NULL;
static HWND s_hwndStatic3 = (HWND)NULL;
static HWND s_hwndStatic4 = (HWND)NULL;
static HWND s_hwndEdit = (HWND)NULL;
static HWND s_hwndButton = (HWND)NULL;
/* ... */
case WM_CREATE:
s_hwndStatic1 = CreateWindow
(
TEXT("STATIC"),
TEXT("폴더 경로를 입력하시오."),
WS_CHILD | WS_VISIBLE | SS_CENTERIMAGE,
0, 0, 300, 30,
hWnd,
(HMENU)ID_STATIC1,
g_hInstance,
(LPVOID)NULL
);
s_hwndEdit = CreateWindow
(
TEXT("EDIT"),
TEXT(""),
WS_CHILD | WS_VISIBLE | WS_THICKFRAME,
0, 30, 180, 30,
hWnd,
(HMENU)ID_EDIT,
g_hInstance,
(LPVOID)NULL
);
s_hwndButton = CreateWindow
(
TEXT("BUTTON"),
TEXT("찾아보기(&B)..."),
WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON,
180, 30, 105, 30,
hWnd,
(HMENU)ID_BUTTON,
g_hInstance,
(LPVOID)NULL
);
s_hwndStatic2 = CreateWindow
(
TEXT("STATIC"),
TEXT("폴더 이름은 :"),
WS_CHILD | WS_VISIBLE | SS_CENTERIMAGE,
0, 60, 150, 30,
hWnd,
(HMENU)ID_STATIC2,
g_hInstance,
(LPVOID)NULL
);
s_hwndStatic3 = CreateWindow
(
TEXT("STATIC"),
TEXT(""),
WS_CHILD | WS_VISIBLE | SS_CENTERIMAGE,
150, 60, 150, 30,
hWnd,
(HMENU)ID_STATIC3,
g_hInstance,
(LPVOID)NULL
);
s_hwndStatic4 = CreateWindow
(
TEXT("STATIC"),
TEXT("^(코딩캣)^ = @\"코딩\"하는 고양이."),
WS_CHILD | WS_VISIBLE | SS_CENTERIMAGE,
0, 90, 300, 30,
hWnd,
(HMENU)ID_STATIC3,
g_hInstance,
(LPVOID)NULL
);
break;
/* ... */
폴더 다이얼로그 띄우기
위와 같이 레이아웃을 구성한 다음 [찾아보기(B)...] 버튼(ID_BUTTON
, s_hwndButton
)을 클릭하여 폴더 다이얼로그를 띄우도록 하겠다. WM_COMMAND
에서 LOWORD(wParam)
값이 ID_BUTTON
인 경우에 다음과 같이 이벤트를 처리한다.
/* WndProc: HWND, UINT, WPARAM, LPARAM */
case WM_COMMAND:
switch (LOWORD(wParam))
{
case ID_BUTTON:
{
BROWSEINFO browseInfo;
ITEMIDLIST * pidlBrowse = NULL;
TCHAR szPath[MAX_PATH];
TCHAR szDisplayName[MAX_PATH];
ZeroMemory(&browseInfo, sizeof(BROWSEINFO));
// 본 다이얼로그를 소유한 창의 핸들이다.
browseInfo.hwndOwner = hWnd;
// 여기서 지정한 특정 폴더 이하의 경로만 선택 가능하다.
browseInfo.pidlRoot = (LPCITEMIDLIST)NULL;
// 폴더의 실제 이름과 보여지는 이름이 다를 때 지정한 버퍼로 보여지는 이름을 전달한다.
browseInfo.pszDisplayName = szDisplayName;
// 폴더 다이얼로그를 통해 사용자에게 보여질 메시지를 지정한다.
browseInfo.lpszTitle = TEXT("선택하고 싶은 폴더를 선택합니다...");
// 각종 옵션을 지정한다.
browseInfo.ulFlags =
BIF_NEWDIALOGSTYLE | // 새로운 형식의 다이얼로그([새 폴더 만들기] 버튼 있는 창)
BIF_RETURNONLYFSDIRS | // 로컬 경로의 디렉토리만 선택 가능하도록 함
BIF_EDITBOX; // 폴더 이름을 타자로도 입력할 수 있도록 텍스트 상자 제공함
// 다이얼로그 창에 대한 콜백을 지정한다.
browseInfo.lpfn = BrowseProc;
// 콜백 함수에 전달할 추가적인 데이터가 있다면 이것을 통해 전달한다.
browseInfo.lParam = (LPARAM)NULL;
// 폴더 열기 다이얼로그를 실행한다.
pidlBrowse = SHBrowseForFolder(&browseInfo);
if (pidlBrowse != NULL)
{
// 다이얼로그에서 선택한 폴더 항목을 문자열 경로로 변환한다.
if (SHGetPathFromIDList(pidlBrowse, szPath))
{
// 경로를 Edit로 출력하고
SetWindowText(s_hwndEdit, szPath);
// 선택한 폴더의 DisplayName도 Static으로 출력한다.
SetWindowText(s_hwndStatic3, szDisplayName);
}
else
{
OutputDebugString(TEXT("Invalid Path\n"));
}
}
}
break;
/* ... */
BROWSEINFO
구조체의 ulFlags
에 지정 가능한 옵션은 MSDN(https://docs.microsoft.com/ko-kr/windows/desktop/api/shlobj_core/ns-shlobj_core-_browseinfoa)을 참조하면 된다.
콜백 프로시저
다이얼로그의 콜백 프로시저는 다음과 같이 선언한다.
int CALLBACK BrowseProc(HWND hWnd, UINT uMsg, LPARAM lParam, LPARAM lpData)
다음은 콜백 함수의 구현 예시이다.
/* BrowseProc: HWND, UINT, LPARAM, LPARAM */
int CALLBACK BrowseProc(HWND hWnd, UINT uMsg, LPARAM lParam, LPARAM lpData)
{
static TCHAR s_szPath[MAX_PATH];
int iResult = 0;
switch (uMsg)
{
case BFFM_INITIALIZED:
// 다이얼로그 상자가 이제 막 떴을 때 처리할 내용은 이 곳에 적는다.
// BFFM_SETSELECTION: 기본 값으로 선택하고 있을 폴더가 있을 때 사용한다.
// lParam은 선택하고 있을 폴더이다.
// lParam이 텍스트 형식의 경로라면 wParam = TRUE
// lParam이 PIDL 형식의 객체라면 wParam = FALSE
SendMessage(hWnd, BFFM_SETSELECTION, TRUE, lParam);
// BFFM_SEROKTEXT: [확인] 버튼의 텍스트를 변경한다.
// lParam은 [확인] 버튼에 새로 넣을 텍스트이다.
SendMessage(hWnd, BFFM_SETOKTEXT, 0, (LPARAM)TEXT("이걸로"));
break;
case BFFM_SELCHANGED:
if (SHGetPathFromIDList((LPITEMIDLIST)lParam, s_szPath))
{
if (_tcsstr(s_szPath, TEXT("blocked")))
{
// 예를 들어,
// 경로 중 일부 문자에 "blocked"가 포함되면 확인버튼 비활성화.
// BFFM_ENABLEOK: [확인] 버튼을 활성화/비활성화 한다.
// lParam은 [확인] 버튼의 활성화(TRUE) / 비활성화(FALSE) 여부이다.
SendMessage(hWnd, BFFM_ENABLEOK, 0, FALSE);
}
else
{
SendMessage(hWnd, BFFM_ENABLEOK, 0, TRUE);
// BFFM_SETSTATUSTEXT: 현재 선택한 폴더 경로를 다이얼로그의 Text 속성으로 지정함.
// lParam은 새로 선택된 폴더 경로
SendMessage(hWnd, BFFM_SETSTATUSTEXT, 0, (LPARAM)s_szPath);
}
}
break;
case BFFM_VALIDATEFAILED:
// 다이얼로그에 전달된 문자열이 잘못되었을 때 처리할 내용은 이 곳에 적는다.
// 다이얼로그를 계속 띄우고 있으려면 콜백이 Non-zero를 반환한다.
// 다이얼로그를 닫으려면 콜백이 0을 반환한다.
break;
}
return 0;
}
위 그림은 C:\Users
를 선택한 후 [확인] 버튼을 누른 결과이다. 해당 폴더의 실제 이름은 Users
이지만 한국어 모드에서는 사용자
라는 이름으로 표시된다. 이것은 BROWSERINFO.pszDisplayName
멤버로 전달된다.