방법: Windows 메시지 루프와 함께 작업 큐 사용
예시
작업 큐를 Windows 메시지 루프와 함께 통합할 수 있습니다. 일반적으로 완료 포트에 대기 중인 콜백이 메시지 루프와 동일한 스레드에서 실행되도록 할 수 있습니다.
다음 예제에서는 작업에 스레드 풀을 사용하지만 완료 포트 콜백을 Win32 창 프로세서에 통합합니다. 이 예제에서는 다른 스레딩 모델과 통합하는 경우 작업 큐의 올바른 종료 방법도 보여 줍니다.
// Example of using a task queue and Window proc together.
XTaskQueueHandle g_queue;
XTaskQueueRegistrationToken g_monitorToken;
// Posted when there's a completion callback.
#define WM_QUEUE_COMPLETION (WM_USER + 1)
struct WorkData
{
HWND hwnd;
WCHAR text[80];
};
void CALLBACK WorkCompletion(void* context, bool cancel)
{
WorkData* data = (WorkData*)context;
if (!cancel)
{
SetWindowText(data->hwnd, data->text);
}
delete data;
}
void CALLBACK BackgroundWork(void* context, bool cancel)
{
if (!cancel)
{
WorkData* data = new WorkData;
data->hwnd = (HWND)context;
if (GetTimeFormatEx(
LOCALE_NAME_USER_DEFAULT, 0, nullptr,
nullptr, data->text, 80) == 0)
{
swprintf_s(data->text, L"Error : %d", GetLastError());
}
// Now, take our formatted string and submit it as a completion callback.
XTaskQueueSubmitCallback(
g_queue,
XTaskQueuePort::Completion,
data,
WorkCompletion);
}
}
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
HRESULT hr;
switch (msg)
{
case WM_CREATE:
// We'll do work on the thread pool, but completion
// callbacks should be manual so that we can integrate them with
// the message loop.
hr = XTaskQueueCreate(
XTaskQueueDispatchMode::ThreadPool,
XTaskQueueDispatchMode::Manual,
&g_queue);
if (SUCCEEDED(hr))
{
hr = XTaskQueueRegisterMonitor(g_queue, hwnd,
[](void* context, XTaskQueueHandle, XTaskQueuePort port)
{
// If a new callback was submitted to the completion
// port, post a message so that we dispatch it in our message loop.
if (port == XTaskQueuePort::Completion)
{
HWND hwnd = static_cast<HWND>(context);
PostMessage(hwnd, WM_QUEUE_COMPLETION, 0, 0);
}
}, &g_monitorToken);
}
if (FAILED(hr))
{
PostQuitMessage(1);
return 0;
}
break;
case WM_LBUTTONDOWN:
hr = XTaskQueueSubmitCallback(
g_queue,
XTaskQueuePort::Completion,
hwnd,
BackgroundWork);
if (FAILED(hr))
{
MessageBox(hwnd, L"Failed to submit callback.", L"Error", MB_OK);
}
break;
case WM_QUEUE_COMPLETION:
XTaskQueueDispatch(g_queue, XTaskQueuePort::Completion, 0);
break;
case WM_CLOSE:
// Terminate the task queue. When this is done, destroy our window.
// The termination callback is queued to the completion
// port, so it will already be on the UI thread.
hr = XTaskQueueTerminate(g_queue, false, hwnd, [](void* context)
{
HWND hwnd = static_cast<HWND>(context);
DestroyWindow(hwnd);
XTaskQueueUnregisterMonitor(g_queue, g_monitorToken);
XTaskQueueCloseHandle(g_queue);
});
if (SUCCEEDED(hr))
{
// Prevent DefWndProc from destroying our window because
// the termination callback will do it.
return 0;
}
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
}
return DefWindowProc(hwnd, msg, wParam, lParam);
}
void TestWndProc()
{
WNDCLASS wndClass;
ZeroMemory(&wndClass, sizeof(wndClass));
wndClass.lpfnWndProc = WndProc;
wndClass.lpszClassName = L"TestClass";
wndClass.hInstance = GetModuleHandle(nullptr);
wndClass.hbrBackground = GetSysColorBrush(COLOR_WINDOW);
ATOM c = RegisterClass(&wndClass);
HWND h = CreateWindow(L"TestClass", L"Window",
WS_OVERLAPPEDWINDOW | WS_VISIBLE,
10, 10, 300, 100, nullptr, nullptr,
GetModuleHandle(nullptr), 0);
if (!h)
{
return;
}
MSG m;
while (GetMessage(&m, nullptr, 0, 0))
{
TranslateMessage(&m);
DispatchMessage(&m);
}
}