`
yaasshole
  • 浏览: 662619 次
文章分类
社区版块
存档分类
最新评论

WTL7.5在VC8中的使用简单剖析

 
阅读更多
WTL7.5VC8中的使用简单剖析
目录
我看了很多关于MFC/ATL/COM方面的书,我发现国外的技术作者大多喜好挖掘代码内部最晦涩难懂的精华部分,试图解释给读者,当然我非常感激,我也因此受益匪浅。但是常常发现当我在钻研技术底层的时候,容易迷失了方向,搞不清这些东西做什么用。我常常想,写书的人,在每每挖掘内核的时候,总是能在一开始提纲携领的描述一下我们要做什么,可能效果好得多。但是也许那些人实在站在比我高得太多的山巅上,那些在他们已经非常熟悉以至于不需要多言的事情,对我而言很多却是陌生的领域。
所以我写文章还是按照我的能够接受的思路,首先从应用角度看待WTL,了解WTL和ATL提供的诸多窗口类的功能,如何搭配使用,实现各种不同风格的窗口或者控件。至于内部消息流动机制是如何的优秀,还是放在另外的专门文章里面讨论巴。
WTL7.5安装
从Microsoft网站上下载并解压到WTL7.5目录下面,执行WTL75/AppWiz目录下面的setup80.js脚本程序,这样我们就可以在VC8中使用WTL Wizard了。
使用WTL Wizard创建一个WTL项目,不管是什么,创建后要在工程属性中添加WTL75/include文件所在的绝对路径到C/C++和ReSources的Additional Include Directories属性中。
WTL 对话框
使用WTL Wizard向导,我们很容易产生一个对话框程序。具体操作不用多说,我们现在来看向导生成的模式对话框的代码:
class CMainDlg : public CAxDialogImpl<CMainDlg>
{
public:
enum { IDD = IDD_MAINDLG };
BEGIN_MSG_MAP(CMainDlg)
MESSAGE_HANDLER(WM_INITDIALOG, OnInitDialog)
COMMAND_ID_HANDLER(ID_APP_ABOUT, OnAppAbout)
COMMAND_ID_HANDLER(IDOK, OnOK)
COMMAND_ID_HANDLER(IDCANCEL, OnCancel)
END_MSG_MAP()
LRESULT OnInitDialog(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/);
LRESULT OnAppAbout(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/);
LRESULT OnOK(WORD /*wNotifyCode*/, WORD wID, HWND /*hWndCtl*/, BOOL& /*bHandled*/);
LRESULT OnCancel(WORD /*wNotifyCode*/, WORD wID, HWND /*hWndCtl*/, BOOL& /*bHandled*/);
public:
};
从上面,我们可以看到很多ATL窗口类的痕迹,CAxDialogImpl类是ATL的一个对话框类,使用该类可以创建模式或者非模式对话框,该类的第一个模板参数我们应该传递我们的派生类,这里是CMainDlg类。第二个参数则是整个派生层次中的父类,默认是CWindow,这同样是一个ATL窗口类,CWindow类提供了很多成员函数,大多是使用窗口句柄的sdk函数的封装。
如果我们要对话框能够容纳ActiveX控件,就应该使用CAxDialogImpl类,否则可以使用
ATL提供的另一个类CDialogImpl类。
BEGIN_MSG_MAP/END_MSG_MAP宏以及它们之间使用的HANDLER宏都属于ATL的窗口消息映射机制。看上去模板对话框类在WTL中很简单。
让我们来看看非模板对话框类。
class CMainDlg : public CAxDialogImpl<CMainDlg>, public CUpdateUI<CMainDlg>,
public CMessageFilter, public CIdleHandler
{
public:
enum { IDD = IDD_MAINDLG };
virtual BOOL PreTranslateMessage(MSG* pMsg);
virtual BOOL OnIdle();
BEGIN_UPDATE_UI_MAP(CMainDlg)
END_UPDATE_UI_MAP()
BEGIN_MSG_MAP(CMainDlg)
MESSAGE_HANDLER(WM_INITDIALOG, OnInitDialog)
COMMAND_ID_HANDLER(ID_APP_ABOUT, OnAppAbout)
COMMAND_ID_HANDLER(IDOK, OnOK)
COMMAND_ID_HANDLER(IDCANCEL, OnCancel)
END_MSG_MAP()
LRESULT OnInitDialog(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/);
LRESULT OnAppAbout(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/);
LRESULT OnOK(WORD /*wNotifyCode*/, WORD wID, HWND /*hWndCtl*/, BOOL& /*bHandled*/);
LRESULT OnCancel(WORD /*wNotifyCode*/, WORD wID, HWND /*hWndCtl*/, BOOL& /*bHandled*/);
void CloseDialog(int nVal);
};
这里CMainDlg多了三个父类:CUpdateUI,CMessageFilter,CIdleHandler类。CUpdateUI类用来动态刷新各种UI元素,因为非模式对话框常常不被关闭,而是经常被其它窗口遮挡,因此这个特性很有用。常常需要配合下列宏使用:
BEGIN_UPDATE_UI_MAP(CMainFrame)
UPDATE_ELEMENT(ID_VIEW_TOOLBAR, UPDUI_MENUPOPUP)
UPDATE_ELEMENT(ID_VIEW_STATUS_BAR, UPDUI_MENUPOPUP)
END_UPDATE_UI_MAP()
在CUpdateUI的父类中定义了下面的一些需要刷新的子元素的类型:
enum
{
// UI element type
UPDUI_MENUPOPUP = 0x0001,
UPDUI_MENUBAR = 0x0002,
UPDUI_CHILDWINDOW= 0x0004,
UPDUI_TOOLBAR = 0x0008,
UPDUI_STATUSBAR = 0x0010,
// state
UPDUI_ENABLED = 0x0000,
UPDUI_DISABLED = 0x0100,
UPDUI_CHECKED = 0x0200,
UPDUI_CHECKED2 = 0x0400,
UPDUI_RADIO = 0x0800,
UPDUI_DEFAULT = 0x1000,
UPDUI_TEXT = 0x2000,
// internal state
UPDUI_CLEARDEFAULT = 0x4000,
};
class CMessageFilter
{
public:
virtual BOOL PreTranslateMessage(MSG* pMsg) = 0;
};
CMessageFilter类只是一个Abstract Class,继承她的目的是实现PreTranslateMessage,这样CMainDlg类可以拦截一些特定的消息。如下代码过滤掉非对话框消息。
BOOL CMainDlg::PreTranslateMessage(MSG* pMsg)
{
return CWindow::IsDialogMessage(pMsg);
}
这个特性也适合于模式对话框,我们可以手动添加。
class CIdleHandler
{
public:
virtual BOOL OnIdle() = 0;
};
CIdleHandler同样也是提供了一个接口而已,CMainDlg类实现OnIdle函数,该函数将在空闲时(即消息队列为空)被调用。但是返回的BOOL值代表什么呢? 通过跟踪Call Stack我发现,实际上是CMessageLoop类的OnIdle函数内部调用了该Idle函数:
virtual BOOL OnIdle(int /*nIdleCount*/)
{
for(int i = 0; i < m_aIdleHandler.GetSize(); i++)
{
CIdleHandler* pIdleHandler = m_aIdleHandler[i];
if(pIdleHandler != NULL)
pIdleHandler->OnIdle();
}
return FALSE; // don't continue
}
由于并不检查返回值,所以我暂时认为返回值没有什么用。
通过上面的分析,我们已经清楚了向导是如何生成模式和非模式对话框类的,现在我们看看这些类如何使用。
模式对话框的创建方式:
CMainDlg dlgMain;
nRet = dlgMain.DoModal();
非模式对话框的创建方式:
CMainDlg dlgMain;
if(dlgMain.Create(NULL) == NULL)
{
ATLTRACE(_T("Main dialog creation failed!/n"));
return 0;
}
dlgMain.ShowWindow(nCmdShow);
如果我们没有向导的帮助,通过上面的分析,依样画葫芦,我们仍然可以随时随地创建我们想要的对话框。
不如MFC方便的是没有向导帮助我们将对话框上的控件变成对应的变量,因此我们在WTL中总是要这样:(假设IDC_EDIT1是我们的一个控件id)
CEdit m_edit1
m_Edit1.Attach(GetDlgItem(IDC_EDIT1));
m_edit1.SetWindowsText(_T("Hello"));
WTL窗口
MFC的Document/View结构非常的复杂,相比较之下,WTL的窗口程序就简化很多,舍弃了Document的概念,又大大利用了模板的灵活性。
SDI窗口
传统的SDI窗口总是有一个Frame Window类和一个View类构成。WTL也是如此。我们先来看看向导生成的View类:
class CSDIView : public CWindowImpl<CSDIView>
{
public:
DECLARE_WND_CLASS(NULL)
BOOL PreTranslateMessage(MSG* pMsg);
BEGIN_MSG_MAP(CSDIView)
MESSAGE_HANDLER(WM_PAINT, OnPaint)
END_MSG_MAP()
LRESULT OnPaint(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/);
};
CWindowImpl类是ATL的窗口类,它接受三个模板参数,前两个和类CAxDialogImpl类相同,第三个类代表了窗口特征,默认是CControlWinTraits类,我们通过传递不同的窗口特征类可以改变窗口的特征,比如是View还是Frame风格,比如CFrameWinTraits类。
现在我们看看Frame Window类:
class CMainFrame : public CFrameWindowImpl<CMainFrame>, public CUpdateUI<CMainFrame>,
public CMessageFilter, public CIdleHandler
{
public:
DECLARE_FRAME_WND_CLASS(NULL, IDR_MAINFRAME)
CSDIView m_view;
CCommandBarCtrl m_CmdBar;
virtual BOOL PreTranslateMessage(MSG* pMsg);
virtual BOOL OnIdle();
BEGIN_UPDATE_UI_MAP(CMainFrame)
UPDATE_ELEMENT(ID_VIEW_TOOLBAR, UPDUI_MENUPOPUP)
UPDATE_ELEMENT(ID_VIEW_STATUS_BAR, UPDUI_MENUPOPUP)
END_UPDATE_UI_MAP()
BEGIN_MSG_MAP(CMainFrame)
MESSAGE_HANDLER(WM_CREATE, OnCreate)
COMMAND_ID_HANDLER(ID_APP_EXIT, OnFileExit)
COMMAND_ID_HANDLER(ID_FILE_NEW, OnFileNew)
COMMAND_ID_HANDLER(ID_VIEW_TOOLBAR, OnViewToolBar)
COMMAND_ID_HANDLER(ID_VIEW_STATUS_BAR, OnViewStatusBar)
COMMAND_ID_HANDLER(ID_APP_ABOUT, OnAppAbout)
CHAIN_MSG_MAP(CUpdateUI<CMainFrame>)
CHAIN_MSG_MAP(CFrameWindowImpl<CMainFrame>)
END_MSG_MAP()
LRESULT OnCreate(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/);
LRESULT OnFileExit(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/);
LRESULT OnFileNew(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/);
LRESULT OnViewToolBar(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/);
LRESULT OnViewStatusBar(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/);
LRESULT OnAppAbout(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/);
};
就像前面所述,如果我们创建自己的框架窗口,可以使用CFrameWinTraits类作为模板参数传递给CWindowImpl类,这是ATL的实现方法,WTL利用此机理提供了一个更加方便的类CFrameWindowImpl。
DECLARE_FRAME_WND_CLASS(NULL, IDR_MAINFRAME),该宏制定了框架窗口所使用的菜单资源。
同时,框架窗口类包含了一个我们的CSDIView对象作为成员变量。一切是如此的简洁灵活,令我不愿意再回到MFC的编程模型中。:)
MDI窗口
传统上MDI窗口包含Frame Window、ChildFrame Window和View。让我们来看看向导如何实现一个ChldFram类。
class CChildFrame : public CMDIChildWindowImpl<CChildFrame>
{
public:
DECLARE_FRAME_WND_CLASS(NULL, IDR_MDICHILD)
CMDIView m_view;
virtual void OnFinalMessage(HWND /*hWnd*/);
BEGIN_MSG_MAP(CChildFrame)
MESSAGE_HANDLER(WM_CREATE, OnCreate)
MESSAGE_HANDLER(WM_FORWARDMSG, OnForwardMsg)
CHAIN_MSG_MAP(CMDIChildWindowImpl<CChildFrame>)
END_MSG_MAP()
LRESULT OnCreate(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& bHandled);
LRESULT OnForwardMsg(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM lParam, BOOL& /*bHandled*/);
};
WTL提供了CMDIChildWindowImpl类,除此之外,我们无须再做什么。
class CMainFrame : public CMDIFrameWindowImpl<CMainFrame>, public CUpdateUI<CMainFrame>,
public CMessageFilter, public CIdleHandler
{
public:
DECLARE_FRAME_WND_CLASS(NULL, IDR_MAINFRAME)
CMDICommandBarCtrl m_CmdBar;
virtual BOOL PreTranslateMessage(MSG* pMsg);
virtual BOOL OnIdle();
BEGIN_UPDATE_UI_MAP(CMainFrame)
UPDATE_ELEMENT(ID_VIEW_TOOLBAR, UPDUI_MENUPOPUP)
UPDATE_ELEMENT(ID_VIEW_STATUS_BAR, UPDUI_MENUPOPUP)
END_UPDATE_UI_MAP()
BEGIN_MSG_MAP(CMainFrame)
MESSAGE_HANDLER(WM_CREATE, OnCreate)
COMMAND_ID_HANDLER(ID_APP_EXIT, OnFileExit)
COMMAND_ID_HANDLER(ID_FILE_NEW, OnFileNew)
COMMAND_ID_HANDLER(ID_VIEW_TOOLBAR, OnViewToolBar)
COMMAND_ID_HANDLER(ID_VIEW_STATUS_BAR, OnViewStatusBar)
COMMAND_ID_HANDLER(ID_APP_ABOUT, OnAppAbout)
COMMAND_ID_HANDLER(ID_WINDOW_CASCADE, OnWindowCascade)
COMMAND_ID_HANDLER(ID_WINDOW_TILE_HORZ, OnWindowTile)
COMMAND_ID_HANDLER(ID_WINDOW_ARRANGE, OnWindowArrangeIcons)
CHAIN_MSG_MAP(CUpdateUI<CMainFrame>)
CHAIN_MSG_MAP(CMDIFrameWindowImpl<CMainFrame>)
END_MSG_MAP()
LRESULT OnCreate(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/);
LRESULT OnFileExit(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/);
LRESULT OnFileNew(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/);
LRESULT OnViewToolBar(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/);
LRESULT OnViewStatusBar(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/);
LRESULT OnAppAbout(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/);
LRESULT OnWindowCascade(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/);
LRESULT OnWindowTile(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/);
LRESULT OnWindowArrangeIcons(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/);
};
我们的主框架窗口继承自WTL提供的类CMDIFrameWindowImpl。除此之外,我们也不需要再做什么。
几乎所有向导生成的WTL工程都使用了该全局变量:
CAppModule _Module;
CAppModule派生自CComModule类,这是一个ATL提供的旧式类,说它旧式,是因为ATL8已经提供了新的类。CComModule类内部创建了一个静态表,表中保存了com服务器中提供的各个类的对象以及相关信息,比如提供了创建com实现类的类工厂,进行生命周期管理等。
CAppModule类派生自CComModule类,显然也就获得了同样的能力,这就意味着我们可以在我们的WTL工程里面创建com对象,并提供给客户端调用。如果我们建立一个WTL窗口程序,并在某些时候需要暴露一个com类,以供外部客户以DCOM的方式远程调用,就需要这样的功能。
CAppModule自身还增加了一些功能,以适应普通窗口应用程序的要求。比如对CMessageLoop对象的管理,该类主要提供消息检测功能,负责处理消息。
总之,我认为CAppModule主要代表了一个应用程序模块,并且提供了管理com服务器的功能。
现在让我们来看看在模式对话框应用程序,向导为我们生成的_tWinMain函数。
CAppModule _Module;
int WINAPI _tWinMain(HINSTANCE hInstance, HINSTANCE /*hPrevInstance*/, LPTSTR lpstrCmdLine, int nCmdShow)
{
HRESULT hRes = ::CoInitialize(NULL);
// If you are running on NT 4.0 or higher you can use the following call instead to
// make the EXE free threaded. This means that calls come in on a random RPC thread.
// HRESULT hRes = ::CoInitializeEx(NULL, COINIT_MULTITHREADED);
ATLASSERT(SUCCEEDED(hRes));
// this resolves ATL window thunking problem when Microsoft Layer for Unicode (MSLU) is used
::DefWindowProc(NULL, 0, 0, 0L);
AtlInitCommonControls(ICC_BAR_CLASSES); // add flags to support other controls
hRes = _Module.Init(NULL, hInstance);
ATLASSERT(SUCCEEDED(hRes));
AtlAxWinInit();
int nRet = 0;
// BLOCK: Run application
{
CMainDlg dlgMain;
nRet = dlgMain.DoModal();
}
_Module.Term();
::CoUninitialize();
return nRet;
}
看到这里,我忍不住指出向导生成的代码不够完美,考虑到函数运行的过程中,可能会出现异常,CoInitialize/CoUninitialize函数应该包装到一个类里面。同样的问题出现在CAppModule类的Init/Term函数。由于这是模式对话框应用程序,所有的消息循环都在对话框类内部处理,所以_tWinMain函数就显得非常的简单。
现在我们再来看看复杂一点的非模式对话框应用程序:
CAppModule _Module;
int Run(LPTSTR /*lpstrCmdLine*/ = NULL, int nCmdShow = SW_SHOWDEFAULT)
{
CMessageLoop theLoop;
_Module.AddMessageLoop(&theLoop);
CMainDlg dlgMain;
if(dlgMain.Create(NULL) == NULL)
{
ATLTRACE(_T("Main dialog creation failed!/n"));
return 0;
}
dlgMain.ShowWindow(nCmdShow);
int nRet = theLoop.Run();
_Module.RemoveMessageLoop();
return nRet;
}
int WINAPI _tWinMain(HINSTANCE hInstance, HINSTANCE /*hPrevInstance*/, LPTSTR lpstrCmdLine, int nCmdShow)
{
HRESULT hRes = ::CoInitialize(NULL);
// If you are running on NT 4.0 or higher you can use the following call instead to
// make the EXE free threaded. This means that calls come in on a random RPC thread.
// HRESULT hRes = ::CoInitializeEx(NULL, COINIT_MULTITHREADED);
ATLASSERT(SUCCEEDED(hRes));
// this resolves ATL window thunking problem when Microsoft Layer for Unicode (MSLU) is used
::DefWindowProc(NULL, 0, 0, 0L);
AtlInitCommonControls(ICC_BAR_CLASSES); // add flags to support other controls
hRes = _Module.Init(NULL, hInstance);
ATLASSERT(SUCCEEDED(hRes));
AtlAxWinInit();
int nRet = Run(lpstrCmdLine, nCmdShow);
_Module.Term();
::CoUninitialize();
return nRet;
}
由于这里使用了非模式对话框,也就是没有模式对话框的DoModal,该函数直到用户关闭了对话框才能够返回。没有这个函数的帮助,为了防止_tWinMain函数过早结束,这里提供了Run函数。
Run函数内部创建了CMessageLoop对象,该对象代表一个消息循环,CMessageLoop::Run函数内部调用了我们非常熟悉的GetMessage/DispatchMessageAPI处理消息。
SDI/MDI应用程序的_tWinMain也都使用了大致相同的Run函数,不同之处在于创建窗口的方式不同。
WTL向导创建工程时,我么可以选择创建一个多线程版的SDI应用程序。和前面我介绍的SDI应用程序想比,在窗口类代码上完全相同,不同的就是_tWinMain函数。
int WINAPI _tWinMain(HINSTANCE hInstance, HINSTANCE /*hPrevInstance*/, LPTSTR lpstrCmdLine, int nCmdShow)
{
HRESULT hRes = ::CoInitialize(NULL);
// If you are running on NT 4.0 or higher you can use the following call instead to
// make the EXE free threaded. This means that calls come in on a random RPC thread.
// HRESULT hRes = ::CoInitializeEx(NULL, COINIT_MULTITHREADED);
ATLASSERT(SUCCEEDED(hRes));
// this resolves ATL window thunking problem when Microsoft Layer for Unicode (MSLU) is used
::DefWindowProc(NULL, 0, 0, 0L);
AtlInitCommonControls(ICC_COOL_CLASSES | ICC_BAR_CLASSES); // add flags to support other controls
hRes = _Module.Init(NULL, hInstance);
ATLASSERT(SUCCEEDED(hRes));
AtlAxWinInit();
int nRet = 0;
// BLOCK: Run application
{
CSDIMulThreadThreadManager mgr;
nRet = mgr.Run(lpstrCmdLine, nCmdShow);
}
_Module.Term();
::CoUninitialize();
return nRet;
}
该函数内部使用了CXXThreadManage类(XX代表我的工程名)。让我们来看看这个多线程管理类的内部实现:
class CSDIMulThreadThreadManager
{
public:
// thread init param
struct _RunData
{
LPTSTR lpstrCmdLine;
int nCmdShow;
};
// thread proc
static DWORD WINAPI RunThread(LPVOID lpData)
{
CMessageLoop theLoop;
_Module.AddMessageLoop(&theLoop);
_RunData* pData = (_RunData*)lpData;
CMainFrame wndFrame;
if(wndFrame.CreateEx() == NULL)
{
ATLTRACE(_T("Frame window creation failed!/n"));
return 0;
}
wndFrame.ShowWindow(pData->nCmdShow);
::SetForegroundWindow(wndFrame); // Win95 needs this
delete pData;
int nRet = theLoop.Run();
_Module.RemoveMessageLoop();
return nRet;
}
DWORD m_dwCount;
HANDLE m_arrThreadHandles[MAXIMUM_WAIT_OBJECTS - 1];
CSDIMulThreadThreadManager() : m_dwCount(0)
{ }
// Operations
DWORD AddThread(LPTSTR lpstrCmdLine, int nCmdShow)
{
if(m_dwCount == (MAXIMUM_WAIT_OBJECTS - 1))
{
::MessageBox(NULL, _T("ERROR: Cannot create ANY MORE threads!!!"), _T("SDIMulThread"), MB_OK);
return 0;
}
_RunData* pData = new _RunData;
pData->lpstrCmdLine = lpstrCmdLine;
pData->nCmdShow = nCmdShow;
DWORD dwThreadID;
HANDLE hThread = ::CreateThread(NULL, 0, RunThread, pData, 0, &dwThreadID);
if(hThread == NULL)
{
::MessageBox(NULL, _T("ERROR: Cannot create thread!!!"), _T("SDIMulThread"), MB_OK);
return 0;
}
m_arrThreadHandles[m_dwCount] = hThread;
m_dwCount++;
return dwThreadID;
}
void RemoveThread(DWORD dwIndex)
{
::CloseHandle(m_arrThreadHandles[dwIndex]);
if(dwIndex != (m_dwCount - 1))
m_arrThreadHandles[dwIndex] = m_arrThreadHandles[m_dwCount - 1];
m_dwCount--;
}
int Run(LPTSTR lpstrCmdLine, int nCmdShow)
{
MSG msg;
// force message queue to be created
::PeekMessage(&msg, NULL, WM_USER, WM_USER, PM_NOREMOVE);
AddThread(lpstrCmdLine, nCmdShow);
int nRet = m_dwCount;
DWORD dwRet;
while(m_dwCount > 0)
{
dwRet = ::MsgWaitForMultipleObjects(m_dwCount, m_arrThreadHandles, FALSE, INFINITE, QS_ALLINPUT);
if(dwRet == 0xFFFFFFFF)
{
::MessageBox(NULL, _T("ERROR: Wait for multiple objects failed!!!"), _T("SDIMulThread"), MB_OK);
}
else if(dwRet >= WAIT_OBJECT_0 && dwRet <= (WAIT_OBJECT_0 + m_dwCount - 1))
{
RemoveThread(dwRet - WAIT_OBJECT_0);
}
else if(dwRet == (WAIT_OBJECT_0 + m_dwCount))
{
if(::PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
{
if(msg.message == WM_USER)
AddThread(_T(""), SW_SHOWNORMAL);
}
}
else
{
::MessageBeep((UINT)-1);
}
}
return nRet;
}
};
实际上,这里做的就是创建一个辅线程,在辅线程内创建并显示一个窗口,如果接收到一个用户自定义消息WM_USER,就立刻再创建一个辅线程,并再次创建显示一个窗口,直到所有窗口被关闭,程序才结束。
陈抒 1/6/2007
ALL RIGHTS RESERVED.
分享到:
评论

相关推荐

    WTL 7.5

    WTL 7.5 其向導必須與VC7以上版本結合使用(但是在6.0裏面還是可以用它的功能,只是沒向導了)

    WTL合集(7.1和7.5).rar

    WTL7.1和WTL7.5的合集,支持vc6.0,内含114页的WTL指

    WTL 7.0,7.1,7.5,8.0

    WTL 7.0,7.1,7.5,8.0

    WTL 9.1 5270 ReadMe 中文 汉化 中英文对照版 01d

    WTL类库可以在下列开发环境中使用,VC++ 6.0,VC++ .NET 2002、2003,VC++ 2005、2008、2010、2012、2013、2015,以及EVC++ 3.0、4.0。WTL类库为Visual Studio提供应用程序向导组件。 The WTL classes are provided...

    WTL7.0 (支持VC6)

    适用于VC6和Visual Studio .NET的WTL

    WTL 入门学习资料

    所有源码都能在 VC6,WTL7.0 下编译通过。 具体内容如下: 1、WTL个性设置demo 文档和源码。 2、WTL for MFC Programmers 系列翻译文章,以及源码。 3、WTL 体系.doc 4、WTL源码剖析 -ATLAPP.H.doc 5、深入剖析WTL...

    wtl使用指南,深入剖析wtl

    详细的wtl使用说明,深入剖析wtl,让你写出更简单的windows界面

    VC WTL8.0软件包

    VC WTL8.0软件包 开发WTL的必须软件

    在VS2013中配置WTL

    微软最新的编程工具Visual Studio 2013 已经发布,想在该平台下使用WTL的话,目前还没有对应的安装文件(一般学者),本人将自己改写的文件上传到CSDN,供朋友使用。改写的版本为 WTL81_12085 第一步:下载文件包...

    WTL 9.1 5270 Beta 中文 汉化 中英文对照版

    WTL类库可以在下列开发环境中使用,VC++ 6.0,VC++ .NET 2002、2003,VC++ 2005、2008、2010、2012、2013、2015,以及EVC++ 3.0、4.0。WTL类库为Visual Studio提供应用程序向导组件。 The WTL classes are provided...

    C++中ATL与WTL学习

    • 使用 WTL AppWizard 可以得到什么 o 通历向导(VC 6) o 通历向导(VC 7) o 检查生成的代码 • CMessageLoop 内幕 • CFrameWindowImpl 内幕 • 回到时钟程序 • UI 更新 o 控制时钟的新菜单项 o 调用 UIEnable...

    WTL70源码,支持VC6和VC7

    无需安装,工程中使用时包含头文件及lib即可。支持VC6和VC7

    彻底深入剖析WTL精髓

    深入剖析WTL 一.Win32模型 二.如何封装Windows界面程序 三.WTL框架窗口分析 深入剖析WTL

    visual c++ vc基于WTL框架,制作图形菜单.zip

    vc制作图形菜单.zip

    VC6使用的WTL资源

    本资源中包含vc++开发使用的WTL资源,使用者可下载后进行安装,便可使用

    WTL开发者指南+深入剖析WTL

    里面包含两份关于WTL的文档,WTL开发者指南以及深入剖析WTL,有开发桌面应用需要的朋友拿去吧~里面包含两份关于WTL的文档,WTL开发者指南以及深入剖析WTL,有开发桌面应用需要的朋友拿去吧~

    Wtl vc 二级联动控件

    分享一个自己写的选项空间 暂时只支持二级联动 wtl。最近在学wtl就写写了。

    深入剖析WTL

    深入剖析WTL WTL学习教程 WTL WTL编程 界面开发

    WTL在对话框工程中更改字体

    这个代码是我自己写的,通过使用API和WTL的相关函数可以实现。 一次编程中想更改对话框的字体,于是想想WTL可不可以做到呢?我当时尝试着使用标准的win32函数CreateFont来创建字体,并且返回字体的句柄。再在CStatic...

    VC下的 WTL 图形界面入门

    WTL入门必备,详细介绍了WTL的使用,解注意事项。

Global site tag (gtag.js) - Google Analytics