#ifndef __WindowThreadBase_h__
#define __WindowThreadBase_h__
#if _MSC_VER > 1000
#pragma once
#endif #pragma warning(disable: 4786)
#include <map>
#include <utility>
#include <malloc.h>
#include <..\MMACRtl\AutoHandle.h>
class TCWindowThreadBase
{
public:
struct XArgs
{
HANDLE hevt;
void* pvParam;
HACCEL hAccel;
bool bXlateDlgMsgs;
HWND hwnd;
HWND hwndParent;
LPCTSTR pszClassName, pszWindowName;
int x, y, cx, cy;
DWORD dwStyle, dwExStyle;
UINT nIdOrMenu;
HINSTANCE hInstance;
DWORD dwLastError;
};
protected:
typedef TCObjectLock<TCWindowThreadBase> CLock;
struct XThreadArgs
{
void* pvThis;
HANDLE hevt;
};
struct XItem
{
HACCEL hAccel;
bool bXlateDlgMsgs;
};
typedef std::map<HWND, XItem> XItems;
public:
TCWindowThreadBase(DWORD dwCoInit);
virtual ~TCWindowThreadBase();
private:
TCWindowThreadBase(const TCWindowThreadBase&);
public:
TCThread* GetThread();
virtual TCThread* GetOrCreateThread() = 0;
static int GetTitleWidth(LPCTSTR psz);
public:
HWND CreateWindowOnThread(LPCTSTR pszWindowName, int* pnError = NULL);
HWND CreateWindowOnThread(LPCTSTR pszClassName, LPCTSTR pszWindowName,
DWORD dwStyle, int x, int y, int nWidth, int nHeight, HWND hwndParent,
UINT nIdOrMenu, HINSTANCE hInstance, void* pvParam,
int* pnError = NULL, bool bSizeIsClient = false,
bool bXlateDlgMsgs = false, HACCEL hAccel = NULL);
HWND CreateWindowExOnThread(DWORD dwExStyle, LPCTSTR pszClassName,
LPCTSTR pszWindowName, DWORD dwStyle, int x, int y,
int nWidth, int nHeight, HWND hwndParent, UINT nIdOrMenu,
HINSTANCE hInstance, void* pvParam,
int* pnError = NULL, bool bSizeIsClient = false,
bool bXlateDlgMsgs = false, HACCEL hAccel = NULL);
bool DestroyWindowOnThread(HWND hwnd);
DWORD ShutdownWindowThread(DWORD dwMilliseconds = INFINITE);
public:
HWND DoCreateWindowOnThread(XArgs& args);
bool DoDestroyWindowOnThread(HWND hwnd);
bool DoInitThread();
void DoExitThread();
public:
void Lock();
void Unlock();
protected:
long GetRefsDlgXlate();
long GetRefsAccel();
virtual void InternalCreateWindow(XArgs& args) = 0;
virtual void InternalDestroyWindow(XArgs& args) = 0;
void AddWindow(XArgs& args);
void RemoveWindow(HWND hwnd);
static ATOM& GetWndProcAtom();
static void SubclassWindow(HWND hwnd);
static void UnsubclassWindow(HWND hwnd);
static LRESULT CALLBACK SubclassWindowProc(HWND, UINT, WPARAM, LPARAM);
void MessageLoop();
bool ProcessThreadMessage(MSG& msg);
bool TranslateAccelerators(MSG& msg);
bool TranslateDialogMessages(MSG& msg);
public:
enum EErrorCodes
{
e_Ok,
e_EventCreationFailed,
e_ThreadCreationFailed,
e_WindowCreationFailed,
e_WindowDestructionFailed,
};
protected:
enum
{
wm_CreateWindowOnThread = WM_APP,
wm_DestroyWindowOnThread,
wm_WindowDestroyed,
};
protected:
DWORD m_dwCoInit;
long m_nRefsDlgXlate, m_nRefsAccel;
TCThread* m_pth;
TCAutoCriticalSection m_cs;
XItems m_windows;
};
inline TCWindowThreadBase::TCWindowThreadBase(DWORD dwCoInit) :
m_dwCoInit(dwCoInit),
m_nRefsDlgXlate(0),
m_nRefsAccel(0),
m_pth(NULL)
{
GetWndProcAtom() = ::GlobalAddAtom(_T("TCWindowThreadBase::PrevWndProc"));
}
inline TCWindowThreadBase::~TCWindowThreadBase()
{
GlobalDeleteAtom(GetWndProcAtom());
}
inline TCThread* TCWindowThreadBase::GetThread()
{
CLock lock(this);
return m_pth;
}
inline int TCWindowThreadBase::GetTitleWidth(LPCTSTR psz)
{
SIZE size = {0, 0};
if (psz)
GetTextExtentPoint32(TCWindowDC(NULL), psz, _tcslen(psz), &size);
return size.cx;
}
inline HWND TCWindowThreadBase::CreateWindowOnThread(LPCTSTR pszWindowName,
int* pnError)
{
int cxScreen = ::GetSystemMetrics(SM_CXSCREEN);
int cyScreen = ::GetSystemMetrics(SM_CYSCREEN);
int cx = GetTitleWidth(pszWindowName);
int cy = cx * cyScreen / cxScreen;
const DWORD dwStyle = WS_OVERLAPPED | WS_THICKFRAME | WS_CAPTION;
#if defined(_ATL)
HINSTANCE hinst = _Module.GetModuleInstance();
#elif defined(_AFX)
HINSTANCE hinst = AfxGetInstanceHandle();
#else
HINSTANCE hinst = GetModuleHandle(NULL);
#endif
HWND hwnd = CreateWindowOnThread(WC_DIALOG, pszWindowName, dwStyle,
0, 0, cx, cy, NULL, 0, hinst, NULL, pnError, true, true, NULL);
if (hwnd)
{
RECT rc;
::GetWindowRect(hwnd, &rc);
cx = rc.right - rc.left;
cy = rc.bottom - rc.top;
const int x = (cxScreen - cx) / 2;
const int y = (cyScreen - cy) / 2;
const UINT uFlags = SWP_NOSIZE | SWP_NOZORDER;
::SetWindowPos(hwnd, NULL, x, y, 0, 0, uFlags);
}
return hwnd;
}
inline HWND TCWindowThreadBase::CreateWindowOnThread(LPCTSTR pszClassName,
LPCTSTR pszWindowName, DWORD dwStyle, int x, int y,
int nWidth, int nHeight, HWND hwndParent, UINT nIdOrMenu,
HINSTANCE hInstance, void* pvParam, int* pnError, bool bSizeIsClient,
bool bXlateDlgMsgs, HACCEL hAccel)
{
return CreateWindowExOnThread(0, pszClassName, pszWindowName, dwStyle,
x, y, nWidth, nHeight, hwndParent, nIdOrMenu, hInstance, pvParam,
pnError, bSizeIsClient, bXlateDlgMsgs, hAccel);
}
inline HWND TCWindowThreadBase::CreateWindowExOnThread(DWORD dwExStyle,
LPCTSTR pszClassName, LPCTSTR pszWindowName, DWORD dwStyle, int x, int y,
int nWidth, int nHeight, HWND hwndParent, UINT nIdOrMenu,
HINSTANCE hInstance, void* pvParam, int* pnError, bool bSizeIsClient,
bool bXlateDlgMsgs, HACCEL hAccel)
{
HWND hwnd = NULL;
int nError = e_Ok;
if (!GetOrCreateThread())
nError = e_ThreadCreationFailed;
else
{
if (bSizeIsClient)
{
RECT rc = {0, 0, nWidth, nHeight};
AdjustWindowRectEx(&rc, dwStyle, !hwndParent && nIdOrMenu, dwExStyle);
nWidth = rc.right - rc.left;
nHeight = rc.bottom - rc.top;
}
XArgs args =
{
NULL, pvParam, hAccel, bXlateDlgMsgs, NULL, hwndParent,
pszClassName, pszWindowName, x, y, nWidth, nHeight, dwStyle, dwExStyle,
nIdOrMenu, hInstance, 0
};
if (GetCurrentThreadId() != GetThread()->m_nThreadID)
{
TCHandle hevt = CreateEvent(NULL, FALSE, FALSE, NULL);
if (!hevt.GetHandle())
nError = e_EventCreationFailed;
else
{
args.hevt = hevt;
if (!GetThread()->PostThreadMessage(wm_CreateWindowOnThread,
WPARAM(&args), 0))
nError = e_WindowCreationFailed;
else
WaitForSingleObject(hevt, INFINITE);
}
}
else
{
InternalCreateWindow(args);
}
if (!nError && !(hwnd = args.hwnd))
{
::SetLastError(args.dwLastError);
nError = e_WindowCreationFailed;
}
}
if (pnError)
*pnError = nError;
return hwnd;
}
inline bool TCWindowThreadBase::DestroyWindowOnThread(HWND hwnd)
{
if (!GetThread())
return false;
XArgs args = {NULL, NULL, false, NULL, hwnd};
if (GetCurrentThreadId() != GetThread()->m_nThreadID)
{
TCHandle hevt = CreateEvent(NULL, FALSE, FALSE, NULL);
if (!hevt.GetHandle())
return false;
args.hevt = hevt;
if (!GetThread()->PostThreadMessage(wm_DestroyWindowOnThread,
WPARAM(&args), 0))
return false;
WaitForSingleObject(hevt, INFINITE);
}
else
{
InternalDestroyWindow(args);
}
if (!args.hwnd)
{
::SetLastError(args.dwLastError);
return false;
}
return true;
}
inline DWORD TCWindowThreadBase::ShutdownWindowThread(DWORD dwMilliseconds)
{
HANDLE hth;
DWORD idth;
HWND* phwnds;
HWND* phwndNext;
{
CLock lock(this);
if (!GetThread())
return WAIT_OBJECT_0;
hth = GetThread()->m_hThread;
idth = GetThread()->m_nThreadID;
phwndNext = phwnds = (HWND*)_alloca((m_windows.size()+1) * sizeof(HWND));
for (XItems::iterator it = m_windows.begin(); it != m_windows.end(); ++it)
*phwndNext++ = it->first;
*phwndNext = NULL;
}
for (phwndNext = phwnds; *phwndNext; ++phwndNext)
DestroyWindowOnThread(*phwndNext);
::PostThreadMessage(idth, WM_QUIT, 0, 0);
return WaitForSingleObject(hth, dwMilliseconds);
}
inline HWND TCWindowThreadBase::DoCreateWindowOnThread(
TCWindowThreadBase::XArgs& args)
{
HWND hwnd = ::CreateWindowEx(args.dwExStyle, args.pszClassName,
args.pszWindowName, args.dwStyle, args.x, args.y, args.cx, args.cy,
args.hwndParent, (HMENU)args.nIdOrMenu, args.hInstance, args.pvParam);
return hwnd;
}
inline bool TCWindowThreadBase::DoDestroyWindowOnThread(HWND hwnd)
{
::SendMessage(hwnd, WM_CLOSE, 0, 0);
return ::IsWindow(hwnd) ? 0 != ::DestroyWindow(hwnd) : true;
}
inline bool TCWindowThreadBase::DoInitThread()
{
if (DWORD(-1) != m_dwCoInit)
if (FAILED(CoInitializeEx(NULL, m_dwCoInit)))
return false;
#ifdef _ATL
_Module.Lock();
#endif return true;
}
inline void TCWindowThreadBase::DoExitThread()
{
#ifdef _ATL
_Module.Unlock();
#endif if (DWORD(-1) != m_dwCoInit)
CoUninitialize();
}
inline void TCWindowThreadBase::Lock()
{
m_cs.Lock();
}
inline void TCWindowThreadBase::Unlock()
{
m_cs.Unlock();
}
inline long TCWindowThreadBase::GetRefsDlgXlate()
{
CLock lock(this);
return m_nRefsDlgXlate;
}
inline long TCWindowThreadBase::GetRefsAccel()
{
CLock lock(this);
return m_nRefsAccel;
}
inline void TCWindowThreadBase::AddWindow(TCWindowThreadBase::XArgs& args)
{
if (!args.hwnd)
return;
CLock lock(this);
XItem item = {args.hAccel, args.bXlateDlgMsgs};
m_windows.insert(XItems::value_type(args.hwnd, item));
if (args.hAccel)
++m_nRefsAccel;
if (args.bXlateDlgMsgs)
++m_nRefsDlgXlate;
}
inline void TCWindowThreadBase::RemoveWindow(HWND hwnd)
{
assert(hwnd);
CLock lock(this);
XItems::iterator it = m_windows.find(hwnd);
assert(it != m_windows.end());
if (it->second.hAccel)
--m_nRefsAccel;
if (it->second.bXlateDlgMsgs)
--m_nRefsDlgXlate;
m_windows.erase(it);
}
inline ATOM& TCWindowThreadBase::GetWndProcAtom()
{
static ATOM atom = NULL;
return atom;
}
inline void TCWindowThreadBase::SubclassWindow(HWND hwnd)
{
if (!hwnd)
return;
::SetProp(hwnd, MAKEINTATOM(GetWndProcAtom()),
(HANDLE)::SetWindowLong(hwnd, GWL_WNDPROC, (LONG)SubclassWindowProc));
}
inline void TCWindowThreadBase::UnsubclassWindow(HWND hwnd)
{
if (!hwnd)
return;
WNDPROC pfnPrev = (WNDPROC)::GetProp(hwnd, MAKEINTATOM(GetWndProcAtom()));
assert(pfnPrev);
WNDPROC pfn = (WNDPROC)::SetWindowLong(hwnd, GWL_WNDPROC, (LONG)pfnPrev);
assert(SubclassWindowProc == pfn);
::RemoveProp(hwnd, MAKEINTATOM(GetWndProcAtom()));
}
inline LRESULT CALLBACK TCWindowThreadBase::SubclassWindowProc(HWND hwnd,
UINT uMsg, WPARAM wp, LPARAM lp)
{
WNDPROC pfnPrev = (WNDPROC)::GetProp(hwnd, MAKEINTATOM(GetWndProcAtom()));
assert(pfnPrev);
if (WM_NCDESTROY == uMsg)
{
UnsubclassWindow(hwnd);
::PostThreadMessage(GetCurrentThreadId(), wm_WindowDestroyed,
WPARAM(hwnd), 0);
}
return CallWindowProc(pfnPrev, hwnd, uMsg, wp, lp);
}
inline void TCWindowThreadBase::MessageLoop()
{
MSG msg;
while (GetMessage(&msg, NULL, 0, 0))
{
if (msg.hwnd || !ProcessThreadMessage(msg))
{
if (!GetRefsAccel() || !TranslateAccelerators(msg))
{
if (!GetRefsDlgXlate() || !TranslateDialogMessages(msg))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
}
}
assert(!m_windows.size());
}
inline bool TCWindowThreadBase::ProcessThreadMessage(MSG& msg)
{
assert(!msg.hwnd);
switch (msg.message)
{
case wm_CreateWindowOnThread:
{
XArgs* pArgs = reinterpret_cast<XArgs*>(msg.wParam);
assert(pArgs);
InternalCreateWindow(*pArgs);
SetEvent(pArgs->hevt);
return true;
}
case wm_DestroyWindowOnThread:
{
XArgs* pArgs = reinterpret_cast<XArgs*>(msg.wParam);
assert(pArgs);
InternalDestroyWindow(*pArgs);
SetEvent(pArgs->hevt);
return true;
}
case wm_WindowDestroyed:
{
HWND hwnd = reinterpret_cast<HWND>(msg.wParam);
_ASSERT(hwnd);
RemoveWindow(hwnd);
return true;
}
}
return false;
}
inline bool TCWindowThreadBase::TranslateAccelerators(MSG& msg)
{
assert(GetRefsAccel());
CLock lock(this);
XItems::const_iterator it = m_windows.find(msg.hwnd);
if (it != m_windows.end() && it->second.hAccel
&& ::TranslateAccelerator(it->first, it->second.hAccel, &msg))
return true;
for (it = m_windows.begin(); it != m_windows.end(); ++it)
if (it->second.hAccel
&& ::TranslateAccelerator(it->first, it->second.hAccel, &msg))
return true;
return false;
}
inline bool TCWindowThreadBase::TranslateDialogMessages(MSG& msg)
{
assert(GetRefsDlgXlate());
CLock lock(this);
XItems::const_iterator it = m_windows.find(msg.hwnd);
if (it != m_windows.end() && it->second.bXlateDlgMsgs
&& ::IsDialogMessage(it->first, &msg))
return true;
for (it = m_windows.begin(); it != m_windows.end(); ++it)
if (it->second.bXlateDlgMsgs
&& ::IsDialogMessage(it->first, &msg))
return true;
return false;
}
#endif