^(코딩캣)^ = @"코딩"하는 고양이;
썸네일 이미지
[Windows API] 크리티컬 섹션을 사용하여 스레드간 변수 공유
Windows API 본 시리즈에서는 Windows API 개별 함수에 대해 간략한 사용법 및 응용에 대해 정리한다. 이전 게시글: 스레드의 생존 여부 확인하기 다음 게시글: OutputDebugString을 printf처럼 서식(포맷) 적용하여 사용하기 크리티컬 섹션을 사용하여 스레드간 변수 공유 본 포스팅에서는 크리티컬 섹션(CRITICAL_SECTION) 구조체를 사용하여 스레드간 변수를 공유하는 방법에 대해 정리한다. CRITICAL_SECTION 스레드간 공유하고자 하는 변수와 함께 CRITICAL_SECTION 구조체 변수를 선언한다. /* 스레드간 공유하고자 하는 정수형 변수 */ DWORD g_dwInteger = 0; /* 상기 정수형 변수에 접근하려면 아래 구조체를 통해 권한을 얻어야 ..
API/Windows API
2019. 12. 19. 11:19

[Windows API] 크리티컬 섹션을 사용하여 스레드간 변수 공유

API/Windows API
2019. 12. 19. 11:19

Windows API


본 시리즈에서는 Windows API 개별 함수에 대해 간략한 사용법 및 응용에 대해 정리한다.

이전 게시글: 스레드의 생존 여부 확인하기

다음 게시글: OutputDebugString을 printf처럼 서식(포맷) 적용하여 사용하기

 

크리티컬 섹션을 사용하여 스레드간 변수 공유


본 포스팅에서는 크리티컬 섹션(CRITICAL_SECTION) 구조체를 사용하여 스레드간 변수를 공유하는 방법에 대해 정리한다.

 

CRITICAL_SECTION


스레드간 공유하고자 하는 변수와 함께 CRITICAL_SECTION 구조체 변수를 선언한다.

/* 스레드간 공유하고자 하는 정수형 변수 */
DWORD g_dwInteger = 0;
/* 상기 정수형 변수에 접근하려면 아래 구조체를 통해 권한을 얻어야 한다. */
CRITICAL_SECTION g_stCriticalSection;

 

InitializeCriticalSection


CRITICAL_SECTION 구조체를 사용하기 위해서는 반드시 리셋되어야 한다. 구조체를 리셋하는 함수는 InitializeCriticalSection이다.

VOID InitializeCriticalSection(
    LPCRITICAL_SECTION lpCriticalSection 
);

 

lpCriticalSection 매개변수를 통해 리셋할 CRITICAL_SECTION을 지정한다.

InitializeCriticalSection(&g_stCriticalSection);

 

EnterCriticalSection, LeaveCriticalSection


VOID EnterCriticalSection(
    LPCRITICAL_SECTION lpCriticalSection  
);
BOOL TryEnterCriticalSection(
    LPCRITICAL_SECTION lpCriticalSection
);
VOID LeaveCriticalSection(
    LPCRITICAL_SECTION lpCriticalSection
);

스레드간 공유 변수에 접근할 때 EnterCriticalSection으로 크리티컬 섹션에 진입하고 공유 변수 사용이 끝나면 LeaveCriticalSection으로 크리티컬 섹션을 벗어난다. EnterCriticalSection은 타 스레드에서 크리티컬 섹션을 사용하고 있을 때 대기한다. 대기과정(현재 스레드의 중지) 없이 즉시 사용중 여부를 확인하고자 하면 TryEnterCriticalSection을 사용한다. 임계영역에 진입했다면 TRUE를 반환하고 그렇지 못했다면 FALSE를 반환한다.

EnterCriticalSection(&g_stCriticalSection);
{
    /* ... g_dwInteger에 대한 읽고 쓰기 */
}
LeaveCriticalSection(&g_stCriticalSection);

 

실행 예제


다음은 크리티컬 섹션의 실행 예이다. 창에 대한 스레드, 콘솔창에 대한 스레드 총 2개의 스레드가 실행중이며 콘솔창으로 1부터 30까지 센 후 콘솔창은 닫힌다. 창의 버튼을 클릭하면 콘솔창에서 세고 있는 숫자는 1로 리셋된다. 이 과정에서 크리티컬 섹션이 사용된다.

크리티컬 섹션을 사용하여 공유 변수가 수정될 수 있다.
카테고리 “API/Windows API”
more...
썸네일 이미지
[단막 Windows API] 스레드의 생존 여부 확인하기
단막 Windows API 스레드의 생존 여부 확인하기 본 포스팅에서는 특정 핸들(HANDLE)에 대한 스레드의 생존 여부, 다시 말해서 현재 실행중인지 종료되었는지 여부를 확인하는 방법을 정리한다. GetExitCodeThread 스레드의 생존 여부를 확인할 수 있는 함수로는 GetExitCodeThread가 있다. 이 함수의 원형은 다음과 같다. BOOL GetExitCodeThread( HANDLE hThread, LPDWORD lpExitCode ); hThread는 생존 여부를 확인하고자 하는 스레드의 핸들(HANDLE)이다. lpExitCode를 통해 특정 스레드의 생존 또는 종료 코드를 얻을 수 있다. 생존 여부를 확인할 수 있으면 TRUE를 반환하고 그렇지 않으면(즉, 오류가 발생하면) F..
API/Windows API
2019. 12. 18. 22:57

[단막 Windows API] 스레드의 생존 여부 확인하기

API/Windows API
2019. 12. 18. 22:57

단막 Windows API


스레드의 생존 여부 확인하기


본 포스팅에서는 특정 핸들(HANDLE)에 대한 스레드의 생존 여부, 다시 말해서 현재 실행중인지 종료되었는지 여부를 확인하는 방법을 정리한다.

 

GetExitCodeThread


스레드의 생존 여부를 확인할 수 있는 함수로는 GetExitCodeThread가 있다. 이 함수의 원형은 다음과 같다.

BOOL GetExitCodeThread(
    HANDLE hThread,
    LPDWORD lpExitCode
);

 

hThread는 생존 여부를 확인하고자 하는 스레드의 핸들(HANDLE)이다. lpExitCode를 통해 특정 스레드의 생존 또는 종료 코드를 얻을 수 있다.

생존 여부를 확인할 수 있으면 TRUE를 반환하고 그렇지 않으면(즉, 오류가 발생하면) FALSE를 반환한다. 이 때 오류 내용은 GetLastError 함수로 확인 가능하다.

스레드의 생존 여부를 위해 GetExitCodeThread를 호출하는 예이다.

BOOL fResult = FALSE;
DWORD dwExitCode = 0;
/* ... */
fResult = GetExitCodeThread(/* THREAD */, &dwExitCode);

 

위와 같이 호출하여 dwExitCode의 값이 STILL_ACTIVE이면 해당 스레드는 현재 실행중인 상태임을 의미한다. 해당 스레드가 종료되었다면, 종료 당시 스레드 프로시저가 리턴한 값이 보관된다. 여기서 알 수 있는 사실은, 스레드 프로시저를 작성할 때 종료 코드를 STILL_ACTIVE와 중복시키면 안 된다는 사실이다. STILL_ACTIVE의 실제 값은 정수 259이다.

 

실행 예제


static TCHAR szTextBuffer[4096];
HANDLE hThread = /* CreateThread(...) */;
DWORD dwErrorCode = 0;
/* ... */
GetExitCodeThread(hThread, &dwErrorCode);
if (dwErrorCode == STILL_ACTIVE) {
    _stprintf(szTextBuffer, TEXT("WM_USER: hThread = %08p, dwErrorCode = STILL_ACTIVE\n"), hThread);
} else {
    _stprintf(szTextBuffer, TEXT("WM_USER: hThread = %08p, dwErrorCode = %08lx\n"), hThread, dwErrorCode);
}
OutputDebugString(szTextBuffer);
/* ... */

 

버튼으로 채워진 창을 하나 열고, 이 창이 열릴 때 오랜 시간이 걸리는 작업을 수행하는 콘솔창도 하나 더 띄운다. 이 콘솔창은 스레드가 종료되면 자동으로 닫힌다. 버튼을 클릭하면 GetExitCodeThread을 실행하여 해당 스레드의 생존 여부를 디버그 출력창으로 보일 것이다.

스레드가 실행중일 때는 STILL_ACTIVE 값이 반환된다.

 

스레드가 종료되었을 때는 해당 프로시저의 리턴 값을 알 수 있다.
카테고리 “API/Windows API”
more...
썸네일 이미지
[단막 Windows API] CreateThread로 스레드 생성하기
단막 Windows API CreateThread로 스레드 생성하기 본 포스팅에서는 CreateThread를 사용하여 스레드를 생성하는 간단한 예를 정리한다. 새 스레드에서 실행할 프로시저 형식 새 스레드에서 호출할 프로시저의 형식(시그니처)은 다음과 같이 정의한다. DWORD (LPVOID lpParam); lpParam 매개변수는 CreateThread 함수를 실행할 때 스레드로 전달될 수 있다. 위 형식을 따라 콘솔 창을 열고 1부터 30까지 세는데 1,000 밀리세컨드마다 지연시키는 프로시저를 작성해 보겠다. lpParam에는 이 스레드를 만든 부모 창 HWND를 전달한다고 약속한다. DWORD ThreadProc(LPVOID lpParam) { HWND hWnd = (HWND)lpParam; /..
API/Windows API
2019. 12. 15. 20:13

[단막 Windows API] CreateThread로 스레드 생성하기

API/Windows API
2019. 12. 15. 20:13

단막 Windows API


CreateThread로 스레드 생성하기


본 포스팅에서는 CreateThread를 사용하여 스레드를 생성하는 간단한 예를 정리한다.

 

새 스레드에서 실행할 프로시저 형식


새 스레드에서 호출할 프로시저의 형식(시그니처)은 다음과 같이 정의한다.

DWORD <NAME>(LPVOID lpParam);

 

lpParam 매개변수는 CreateThread 함수를 실행할 때 스레드로 전달될 수 있다.

위 형식을 따라 콘솔 창을 열고 1부터 30까지 세는데 1,000 밀리세컨드마다 지연시키는 프로시저를 작성해 보겠다. lpParam에는 이 스레드를 만든 부모 창 HWND를 전달한다고 약속한다.

DWORD ThreadProc(LPVOID lpParam) {
    HWND hWnd = (HWND)lpParam;
    /* 새 콘솔창을 띄운다. */
    AllocConsole();
    
    /* 새 콘솔창에 맞추어 표준 입출력 핸들을 다시 연다. */
    freopen("CONIN$", "r", stdin);
    freopen("CONOUT$", "w", stdout);
    freopen("CONOUT$", "w", stderr);
    
    printf("Console Created!\n");
    
    /* 1000 밀리세컨드의 간격을 두고 1부터 30까지 출력한다. */
    while (TRUE) {
        static int count = 0;
        
        printf("count = %d\n", ++count);
        Sleep(1000);
        
        if (count >= 30) break;
    }
    
    /* 5000 밀리세컨드의 여유를 두고 콘솔창을 닫는다. */
    Sleep(5000);
    FreeConsole();
    
    /* 이 스레드를 종료한다. */
    return 0;
}

 

새 스레드 생성 및 실행


CreateThread의 원형은 다음과 같다.

HANDLE CreateThread(
    LPSECURITY_ATTRIBUTES   lpThreadAttributes,
    DWORD                   dwStackSize,
    LPTHREAD_START_ROUTINE  lpStartAddress,
    LPVOID                  lpParameter,
    DWORD                   dwCreationFlags,
    LPDWORD                 lpThreadId
);

 

매개변수에 대한 자세한 내용은 MSDN을 참조한다. 여기에서는 간략한 사용법만을 보기 위해 각종 보안 코드들은 생략한다. 다음은 가장 간단한 호출법이다. lpStartAddress(3번째 매개변수)에 앞서 작성한 프로시저를 지정하고, lpThreadId(마지막 매개변수)에 새로 생성된 스레드의 ID값이 전달된다. 반환되는 값은 생성된 스레드의 핸들(HANDLE)이다.

hThread = CreateThread(NULL, 0, [PROCEDURE], (LPVOID)[PARAMETER], 0, &[THREAD ID]);

 

다음은 실제 소스에 작성한 예이다.

LRESULT CALLBACK WndProc(HWND hWnd, UINT uMessage, WPARAM wParam, LPARAM lParam) {
    /* 중략 */
    switch (uMessage) {
    /* 중략 */
    case WM_CREATE:
    {
        /* 창을 생성할 때 스레드도 함께 생성한다. */
        HANDLE hThread = NULL;
        DWORD dwThreadId = 0;
        
        hThread = CreateThread(NULL, 0, ThreadProc, (LPVOID)hWnd, 0, &dwThreadId);
    }
    break;
    /* 중략 */
    }
    /* 중략 */
}

 

실행 결과


프로그램을 실행하면 빈 창과 함께 콘솔창이 출력된다. 콘솔창이 1부터 30까지 세고 닫히는 동안 창에서는 GUI 이벤트를 독립적으로 수행할 수 있다.

1부터 30까지 센 다음 콘솔창을 닫는다. 이 창의 스레드는 창의 스레드와 다르다.
카테고리 “API/Windows API”
more...
썸네일 이미지
[단막 Windows API 활용법] Windows API 폰트 변경하기
단막 Windows API 활용법 Windows API 폰트 변경하기 Windows API로 UI를 만들 경우 폰트가 다음과 같이 투박하게 보여짐을 확인할 수 있다. 기본적으로 투박한 폰트로 보여지는 Windows API 윈도우 본 포스팅에서는 Windows API로 작성한 윈도우에 폰트를 적용하는 방법에 대해 정리하겠다. 시스템 정의 기본 폰트 사용하기 시스템 정의 기본 폰트는 제목 표시줄, 메시지 박스, 메뉴, 상태 표시줄, 캡션 표시줄 등에 사용되기 위해 미리 정의된 폰트이다. 이를 불러오기 위해서는 SystemParametersInfo 함수를 SPI_GETNONCLIENTMETRICS 매개변수를 적용하여 호출한다. WndProc 콜백 프로시저의 WM_CREATE 섹션에 다음과 같이 적는다. /* ..
API/Windows API
2019. 1. 20. 20:51

[단막 Windows API 활용법] Windows API 폰트 변경하기

API/Windows API
2019. 1. 20. 20:51

단막 Windows API 활용법


Windows API 폰트 변경하기


Windows API로 UI를 만들 경우 폰트가 다음과 같이 투박하게 보여짐을 확인할 수 있다.

기본적으로 투박한 폰트로 보여지는 Windows API 윈도우

 

본 포스팅에서는 Windows API로 작성한 윈도우에 폰트를 적용하는 방법에 대해 정리하겠다.

 

시스템 정의 기본 폰트 사용하기


시스템 정의 기본 폰트는 제목 표시줄, 메시지 박스, 메뉴, 상태 표시줄, 캡션 표시줄 등에 사용되기 위해 미리 정의된 폰트이다. 이를 불러오기 위해서는 SystemParametersInfo 함수를 SPI_GETNONCLIENTMETRICS 매개변수를 적용하여 호출한다.

WndProc 콜백 프로시저의 WM_CREATE 섹션에 다음과 같이 적는다.

/* WndProc: HWND, UINT, WPARAM, LPARAM */
// 새로 지정할 폰트 핸들
static HFONT s_hFont = (HFONT)NULL;
// 시스템 정의 폰트는 NONCLIENTMETRICS라는 구조체를 통해 전달된다.
NONCLIENTMETRICS nonClientMetrics;
/* ... */
// 구조체의 내용을 0으로 리셋한다.
ZeroMemory(&nonClientMetrics, sizeof(NONCLIENTMETRICS));
// cbSize 필드만 특별히 구조체의 크기로 지정한다.
nonClientMetrics.cbSize = sizeof(NONCLIENTMETRICS);

// 구조체를 통해 전달되는 폰트는 LOGFONT 형식이다.
// GDI에서 사용하기 위해 LOGFONT에서 기술된 폰트 정보를 바탕으로 HFONT를 생성한다.
s_hFont = CreateFontIndirect(&nonClientMetrics.lfCaptionFont);

// 창 스스로에게 WM_SETFONT 메시지를 전달한다.
SendMessage(hWnd, WM_SETFONT, (WPARAM)s_hFont, (LPARAM)MAKELONG(TRUE, 0));

 

자기 자신에게 WM_SETFONT를 전달했으므로 WndProc에서 WM_SETFONT 섹션을 추가하여 이벤트를 처리해보겠다. 다음은 그 예이다.

/* WndProc: HWND, UINT, WPARAM, LPARAM */
// 새로 지정할 폰트 핸들
static HFONT s_hFont = (HFONT)NULL;
// 시스템 정의 폰트는 NONCLIENTMETRICS라는 구조체를 통해 전달된다.
NONCLIENTMETRICS nonClientMetrics;
/* ... */
lResult = DefWindowProc(hWnd, uMessage, wParam, lParam);
// Static 윈도우에 폰트 변경 메시지를 보낸다.
SendMessage(s_hwndStatic, WM_SETFONT, wParam, lParam);
/* ... */

 

다음과 같이 윈도우의 폰트가 변경되었음을 확인할 수 있다.

시스템 정의 기본 폰트로 변경된 모습

 

LOGFONT 직접 지정하기


시스템 기본 폰트 외에 다른 폰트를 지정해보겠다. LOGFONT 구조체의 각 멤버들을 직접 지정하면 다양한 폰트들을 얻어서 윈도우에 적용 가능하다.

이번에는 굴림, 10 포인트를 LOGFONT를 활용해 적용한다.

/* WndProc: HWND, UINT, WPARAM, LPARAM */
HDC hDC = (HDC)NULL;
LOGFONT logFont;
/* ... */

/* 굴림, 10 포인트로 폰트 설정 */
ZeroMemory(&logFont, sizeof(LOGFONT));
hDC = GetDC(hWnd);
\logFont.lfHeight = -MulDiv(10, GetDeviceCaps(hDC, LOGPIXELSY), 72);
logFont.lfWeight = FW_NORMAL;
_tcscpy(logFont.lfFaceName, TEXT("Gulim"));
s_hFont = CreateFontIndirect(&logFont);
ReleaseDC(hDC);

/* ... */

 

10 포인트의 굴림으로 적용된 모습

 


카테고리 “API/Windows API”
more...
썸네일 이미지
[단막 Windows API 활용법] OpenFolderDialog 열기
단막 Windows API 활용법 OpenFolder Dialog 열기 [폴더 찾아보기...] 종류의 버튼을 클릭할 때 나오는 다이얼로그를 Windows API에서 어떻게 호출하고, 그 선택된 폴더 경로를 가져오는 방법에 대해 설명한다. 이 기능은 쉘shell에 대한 기능이므로 쉘 함수를 사용하기 위하여 #include 를 포함한다. 레이아웃 설정 빈 화면에 다음과 같이 윈도우들을 배치하도록 WndProc을 작성한다. /* WndProc: HWND, UINT, WPARAM, LPARAM */ static HWND s_hwndStatic1 = (HWND)NULL; static HWND s_hwndStatic2 = (HWND)NULL; static HWND s_hwndStatic3 = (HWND)NULL; ..
API/Windows API
2019. 1. 20. 16:15

[단막 Windows API 활용법] OpenFolderDialog 열기

API/Windows API
2019. 1. 20. 16:15

단막 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;
/* ... */

 

WM_CREATE 메시지 처리에서 지정한 레이아웃의 실행 결과

 

폴더 다이얼로그 띄우기


위와 같이 레이아웃을 구성한 다음 [찾아보기(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)을 참조하면 된다.

 

BIF_NEWDIALOGSTYLE를 적용한 다이얼로그
BIF_NEWDIALOGSTYLE를 적용하지 않은 다이얼로그

 

콜백 프로시저


다이얼로그의 콜백 프로시저는 다음과 같이 선언한다.

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멤버로 전달된다.

카테고리 “API/Windows API”
more...

“API/Windows API” (5건)