`
yangyou230
  • 浏览: 1662583 次
文章分类
社区版块
存档分类

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

    在实际开发中,**WTL75.exe** 可能是一个安装程序,用于在用户的系统上安装WTL7.5库,而**WTL7.1.zip** 可能是WTL7.1源码的压缩文件,用户可以解压后在VC6.0环境下直接引用和编译。这为那些需要在老版本开发环境中...

    WTL 7.0,7.1,7.5,8.0

    这个库在C++编程中特别受欢迎,因为它允许开发者直接访问Windows API,从而创建高效且小巧的应用程序。WTL 7.0, 7.1, 7.5, 和 8.0 是该库的不同版本,每个版本都包含一系列的改进和新特性。 WTL 7.0是WTL的一个早期...

    WTL7.0 (支持VC6)

    8. **项目设置**:在VC6中使用WTL7.0可能需要进行一些额外的配置,如添加库引用、包含路径等。 9. **文档和示例**:WTL7.0 SDK中通常会包含详细的文档和示例代码,帮助开发者快速上手。 总的来说,WTL7.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...

    WTL70源码,支持VC6和VC7

    - 处理Windows消息:WTL使用 ATL(Active Template Library)的宏和模板来处理Windows消息,你需要理解如何在你的类中声明和实现消息映射。 - 创建窗口和控件:WTL提供了一系列的类来创建和管理窗口及控件,例如...

    VC6使用的WTL资源

    **VC6使用的WTL资源详解** Windows Template Library(WTL)是Microsoft开发的一个开源库,专为Visual C++ 6.0(简称VC6)设计,用于构建轻量级、高性能的Windows应用程序。WTL是MFC(Microsoft Foundation Classes...

    VC WTL8.0软件包

    在本文中,我们将深入探讨WTL8.0的关键特性和使用场景,以及如何利用它来提升你的Windows应用开发效率。 1. **WTL8.0的核心概念** - **模板类**:WTL的核心在于它的模板类,这些类为Windows API提供了一层封装,...

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

    在本文中,我们将深入探讨如何使用Visual C++(VC++)和Windows Template Library(WTL)框架来创建图形用户界面(GUI),特别是图形菜单。WTL是一个轻量级的库,它提供了对Windows API的直接访问,使得开发高效且...

    Wtl vc 二级联动控件

    标题中的“Wtl vc 二级联动控件”指的是在Visual C++(vc)环境中使用Windows Template Library(WTL)框架实现的一种特殊的用户界面控件,它具有两级选择的交互功能,通常用于地区选择、分类筛选等场景。...

    wtl库 8.0版本

    在8.0版本中,WTL提供了丰富的控件和窗口类,使得开发者能够更加方便地进行Windows编程。 在描述中提到的`atlgdix.h`文件是WTL中的一个头文件,它包含了与GDI+相关的类和功能。GDI+是Windows图形设备接口的增强版,...

    wtl使用指南,深入剖析wtl

    深入剖析 WTL,让我们一起探索 Windows Template Library (WTL) 的世界,这是一个轻量级的框架,用于构建高效且小巧的Windows应用程序。WTL源于ATL(Active Template Library),是微软ATL团队开发的一个示例项目,...

    WTL 入门学习资料

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

    MFC程序员的WTL指南PDF版(中文) 【WTL指南】

    WTL 编程指南 WTL(Windows Template Library)是一种基于C++模板...8. WTL 的应用场景 WTL 的应用场景包括桌面应用程序、服务器应用程序、嵌入式系统等。WTL 广泛应用于各个领域,提供了一种高效、灵活的解决方案。

    WTL10_VS2017

    "Samples"目录中的示例代码对于理解如何在实际项目中使用WTL10非常有价值。通过分析和运行这些示例,开发者可以学习WTL10的关键组件和最佳实践。 总的来说,集成WTL10到VS2017是一个涉及项目配置、代码适配和可能的...

    在VS2013中配置WTL

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

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

    总之,WTL是一个强大且高效的Windows桌面应用开发工具,通过深入学习这两份文档,开发者可以全面掌握其精髓,提升在C++环境中构建Windows应用的能力。无论是初学者还是经验丰富的开发者,都能从中受益匪浅。

    WTL 例程,VC编写,供参考

    在VC++环境中,WTL被广泛用于开发桌面应用,因为它提供了直接访问Windows API的途径,无需重负载的MFC(Microsoft Foundation Classes)框架。这使得开发者能够更接近底层,实现更精细的控制,同时避免了MFC带来的...

    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...

    WTL 支持最新版 vs2010

    2. **WTL与VS2010的项目配置**:在VS2010中创建WTL项目,需要确保选择适当的项目类型,如Win32 Console Application或Win32 Project,并在项目属性中配置为使用ATL支持。同时,可能需要手动添加WTL库的头文件和库...

Global site tag (gtag.js) - Google Analytics