远程控制软件中窗口管理主要有两种模式,一种是以灰鸽子远控为代表的单窗口单任务管理模式,一种是以黑洞远控为代表的多窗口多任务管理模式。前一种比较简单,本文讲第二种方案的实现。这里着重讲窗口多开,多任务另起篇幅讲述。
假如我们需要同时对3台远程主机进行文件管理,那么需要打开3个文件管理的对话框,这3个对话框相同的是什么?不同的又是什么?相同的是对话框界面和处理过程,不同的是通信对象。好了,下面让我们思考下如何实现同与不同。要实现在不同的窗口自由切换,非模式对话框的效果自然是最理想。要实现不同的通信对象,只要绑定不同的SOCKET就行。下面是笔者根据开发和应用的经验,总结得三种方案,希望能对其他朋友有所帮助。
方案一:窗口数组
窗口数组比较好理解,你要3个对话框,正好我那有3个一样的对话框,只要拿过来绑上不同的通信对象就行了。窗口数组流程就是启动的时候建好对话框数组,需要的时候显示窗口并绑定一个通信对象,不需要的时候隐藏窗口并移除通信对象。
首先主窗口类中添加对话框数组CTestDlg1 m_TestDlg[10],在主对话框的OnInitDialog()中创建对话框数组,注意这里创建的是非模式对话框。
for (int i=0;i<10;i++)
{
m_TestDlg[i].Create(IDD_DIALOG2, GetDesktopWindow());
m_TestDlg[i].m_bUseing = FALSE;
m_TestDlg[i].ShowWindow(SW_HIDE);
}
上面要注意到需要有个标识变量来表示对话框是否在使用中。另外由于是非模式对话框,所以我们需要在对话框的DestroyWindow()中销毁它们,不然会造成资源泄露。
for (int i=0;i<10;i++)
{
m_TestDlg[i].DestroyWindow();
}
下面看看如何利用这些对话框,主要是对话框的显示和隐藏。在显示的时候绑定通信对象,在关闭对话框的时候隐藏窗口和移除通信对象
//查找一个没有在使用的对话框
for (int i=0;i<10;i++)
{
if(m_TestDlg[i].m_bUseing == FALSE)
{
m_TestDlg[i].ShowWindow(SW_SHOW);
m_TestDlg[i].m_bUseing = TRUE;
//绑定通信对象代码略
break;
}
}
//重载m_TestDlg对话框的OnCancel()函数,同时设置使用标识为未使用
void CTestDlg1::OnCancel()
{
// TODO: Add extra cleanup here
AfxMessageBox("这里只是隐藏,而没有销毁对话框");
ShowWindow(SW_HIDE);
m_bUseing = FALSE;
//移除通信对象
//CDialog::OnCancel();
}
总体来说这个方案比较容易理解,过程也比较清晰,也是我最初想出来的方案。
方案二:多线程加消息循环
很多人问为什么不直接创建非模式对话框?为了更好处理连接,我们使用了多线程。工作线程中缺乏对消息的响应机制,在其中创建模式对话框会造成窗口阻塞,创建非模式对话框会立即消失。方案二其实是构造一个UI线程,在线程里创建非模式对话框。因为该方案是多线程所以支持无限创建窗口,另外线程自带消息循环可以不同窗口前切换而不消息阻塞,并且在没有收到WM_QUIT消息前不会退出。
//具体线程实现,具体需要转换pParam并绑定通信对象
UINT WorkerThread(LPVOID pParam)
{
CAboutDlg dlg;
dlg.Create(IDD_ABOUTBOX);
dlg.ShowWindow(SW_SHOW);
MSG msg;
while(GetMessage(&msg,NULL,0,0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
dlg.DestoyWindow();
return 0;
}
要使用的时候,直接创建线程,传入通信对象就行了。值得注意的创建UI线程要使用AfxBeginThread而不是CreateThread。
方案三:消息通知主窗口创建
主窗口是个UI线程,可以创建非模式对话框而且参与到主窗口的消息泵中不会销毁。方案三就是利用了这个特性,从而可以在工作线程中发送消息给主窗口并让其创建对话框。
添加自定义消息处理函数LRESULT OnDlgTest(WPARAM wParam, LPARAM lParam),响应消息WM_DLGTEST。
//消息处理函数实现
LRESULT CTestModalDlg::OnDlgTest(WPARAM wParam, LPARAM lParam)
{
CTestDlg* pDlg = new CTestDlg;
pDlg->Create(IDD_DIALOG1, GetDesktopWindow());
pDlg->ShowWindow(SW_SHOW);
return 0;
}
当我们需要创建窗口的时候只要发送消息WM_DLGTEST给主窗口就行了。同前面的方案一样,需要非模式对话框都需要调用DestoryWindow来销毁窗口,另外方案三中要注意的是,pDlg由new分配的,需要delete指针,这个工作是重载CTestDlg的PostNcDestroy函数,在其中释放自己。
三种方案中,笔者比较推荐方案三,支持窗口无限创建,代码量也相对较少,处理起来简单。笔者才疏学浅,如有错误之处,欢迎读者斧正。