.h
#pragma once
#define SEND_TASK(...) _message_loop_win.SendTask(__VA_ARGS__)
#define POST_TASK(...) _message_loop_win.PostTask(__VA_ARGS__)
#define POST_DELAY_TASK(...) _message_loop_win.PostDelayTask(__VA_ARGS__)
#define CURRENTLY_ON_TID_UI _message_loop_win.CurrentlyOnTidUI()
#define POST_MAIN_THRAD_TASK(...) POST_TASK(__VA_ARGS__)
#define POST_MAIN_THRAD_SYNC_TASK(...) SEND_TASK(__VA_ARGS__)
#include <functional>
#include <windows.h>
#include <queue>
#include <memory>
#include <mutex>
#include <stdexcept>
#include <future>
#include <tchar.h>
#include <memory>
#include <list>
class CMessageLoopWin
{
using Task = std::function<void()>;
using DelayTask = std::function<DWORD()>;
friend LRESULT CALLBACK MessageWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
public:
CMessageLoopWin();
~CMessageLoopWin();
void Close();
template<class F, class... Args>
auto SendTask(HWND hwnd, F&& f, Args&&... args)->std::future<typename std::result_of<F(Args...)>::type>
{
using return_type = typename std::result_of<F(Args...)>::type;
auto task = std::make_shared<std::packaged_task<return_type()>>(std::bind(std::forward<F>(f), std::forward<Args>(args)...));
std::future<return_type> res = task->get_future();
PostTask(hwnd, [&]{ (*task)(); });
res.wait();
return res;
}
void PostTask(HWND hwnd, const Task& task)
{
std::shared_ptr<Task> safe_task = std::make_shared<Task>([=]
{
if (hwnd == nullptr || IsWindow(hwnd))
task();
});
std::lock_guard<std::recursive_mutex> lck(_mutex);
_task_group.push(safe_task);
::PostMessage(_message_hwnd_, _message_id, 0, 0);
}
void PostDelayTask(HWND hwnd, DWORD dwDelay, const Task& task)
{
{
std::lock_guard<std::mutex> _locker(_delay_mutex);
if (_delay_thread == NULL)
{
_delay_thread = CreateThread(NULL, 0, _delay_thread_func, this, 0, NULL);
}
}
DWORD dwBegin = GetTickCount();
std::shared_ptr<DelayTask> delay_task = std::make_shared<DelayTask>([=]
{
DWORD dwTime = GetTickCount() - dwBegin;
if (dwTime >= dwDelay)
{
PostTask(hwnd, task);
return (DWORD)0;
}
else
{
return dwDelay - dwTime;
}
});
{
std::lock_guard<std::mutex> _locker(_delay_mutex);
_delay_task_group.push_back(delay_task);
}
_delay_cond.notify_all();
}
inline bool CurrentlyOnTidUI()
{
return _thread_id == GetCurrentThreadId();
}
protected:
HWND _message_hwnd_;
UINT _message_id;
DWORD _thread_id;
std::recursive_mutex _mutex;
std::queue<std::shared_ptr<Task>> _task_group;
bool _stop;
std::mutex _delay_mutex;
std::list<std::shared_ptr<DelayTask>> _delay_task_group;
std::condition_variable _delay_cond;
HANDLE _delay_thread;
static DWORD WINAPI _delay_thread_func(LPVOID p);
};
extern CMessageLoopWin _message_loop_win;
class CTaskVirtualWin
{
public:
CTaskVirtualWin()
: m_hNotifyWnd(NULL)
, m_hVirtualWnd(NULL)
{
const TCHAR kVirtualWndClass[] = _T("CVirtualTaskWindow");
WNDCLASSEX wc = { sizeof(wc) };
HINSTANCE hInstance = ::GetModuleHandle(NULL);
wc.lpfnWndProc = MessageWndProc;
wc.hInstance = hInstance;
wc.lpszClassName = kVirtualWndClass;
RegisterClassEx(&wc);
m_hVirtualWnd = CreateWindow(kVirtualWndClass, 0, 0, 0, 0, 0, 0, HWND_MESSAGE, 0, hInstance, 0);
::SetWindowLongPtr(m_hVirtualWnd, GWLP_USERDATA, reinterpret_cast<LONG_PTR>(this));
}
virtual ~CTaskVirtualWin()
{
::SetWindowLongPtr(m_hVirtualWnd, GWLP_USERDATA, reinterpret_cast<LONG_PTR>(nullptr));
SendMessage(m_hVirtualWnd, WM_CLOSE, 0, 0);
}
inline HWND GetVirtualWnd() const
{
return m_hVirtualWnd;
}
void SetNotifyWnd(HWND _hwnd)
{
m_hNotifyWnd = _hwnd;
}
static LRESULT CALLBACK MessageWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
CTaskVirtualWin* self = (CTaskVirtualWin*)::GetWindowLongPtr(hWnd, GWLP_USERDATA);
if (self != NULL && self->m_hNotifyWnd != NULL)
{
return ::PostMessage(self->m_hNotifyWnd, message, wParam, lParam);
}
else
{
return DefWindowProc(hWnd, message, wParam, lParam);
}
}
protected:
HWND m_hVirtualWnd;
HWND m_hNotifyWnd;
};
.cpp
#include "stdafx.h"
#include "main_message_task.h"
#include <tchar.h>
const TCHAR kWndClass[] = _T("CMessageWindow");
const TCHAR kTaskMessageName[] = _T("CCustomTask");
CMessageLoopWin _message_loop_win;
CMessageLoopWin::CMessageLoopWin()
: _message_hwnd_(nullptr)
, _message_id(::RegisterWindowMessage(kTaskMessageName))
, _thread_id(GetCurrentThreadId())
, _stop(false)
, _delay_thread(NULL)
{
WNDCLASSEX wc = { sizeof(wc) };
HINSTANCE hInstance = ::GetModuleHandle(NULL);
wc.lpfnWndProc = MessageWndProc;
wc.hInstance = hInstance;
wc.lpszClassName = kWndClass;
RegisterClassEx(&wc);
_message_hwnd_ = CreateWindow(kWndClass, 0, 0, 0, 0, 0, 0, HWND_MESSAGE, 0, hInstance, 0);
::SetWindowLongPtr(_message_hwnd_, GWLP_USERDATA, reinterpret_cast<LONG_PTR>(this));
}
CMessageLoopWin::~CMessageLoopWin()
{
Close();
}
void CMessageLoopWin::Close()
{
if (!_stop)
{
_stop = true;
_delay_cond.notify_all();
WaitForSingleObject(_delay_thread, INFINITE);
_delay_task_group.clear();
}
}
DWORD WINAPI CMessageLoopWin::_delay_thread_func(LPVOID p)
{
CMessageLoopWin* pWin = (CMessageLoopWin*)p;
while (!pWin->_stop)
{
DWORD dwWaitTime = INFINITE;
{
std::lock_guard<std::mutex> _locker(pWin->_delay_mutex);
for (auto it = pWin->_delay_task_group.begin(); it != pWin->_delay_task_group.end();)
{
DWORD dwRet = (*(*it))();
if (dwRet == 0)
{
pWin->_delay_task_group.erase(it++);
continue;
}
else if (dwRet < dwWaitTime)
{
dwWaitTime = dwRet;
}
++it;
}
}
pWin->_delay_cond.wait_for(std::unique_lock<std::mutex>(pWin->_delay_mutex), std::chrono::milliseconds(dwWaitTime));
}
return 0;
}
LRESULT CALLBACK MessageWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
CMessageLoopWin* self = (CMessageLoopWin*)::GetWindowLongPtr(hWnd, GWLP_USERDATA);
if (self && message == self->_message_id)
{
std::shared_ptr<CMessageLoopWin::Task> task;
{
std::lock_guard<std::recursive_mutex> lck(self->_mutex);
if (!self->_task_group.empty())
{
task = std::move(self->_task_group.front());
self->_task_group.pop();
}
}
if (task != nullptr)
{
(*task)();
}
}
else switch (message) {
case WM_DESTROY:
// Clear the reference to |self|.
LONG_PTR result = ::SetWindowLongPtr(hWnd, GWLP_USERDATA, reinterpret_cast<LONG_PTR>(nullptr));
break;
}
return DefWindowProc(hWnd, message, wParam, lParam);
}