wxWidgets提供了一个用来检测是否只有一个实例(instance)在运行的wxSingleInstanceChecker类。为了检测程序只运行一个实例,你可以在程序运行之初使用该类创建一个m_check对象,这个对象将存在于程序的整个生命周期。然后就可以在OnInit函数中调用其IsAnotherRunning函数检测是否已经有别的实例在运行。代码如下所示:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
bool MainApp::OnInit()
{ wxString name = wxString::Format(wxT( "MainApp-%s" ), wxGetUserId().GetData());
m_checker = new wxSingleInstanceChecker(name);
if (m_checker->IsAnotherRunning())
{
wxLogError(wxT( "Another program instance is already running, aborting." ));
delete m_checker;
return false ;
}
... more initializations ...
return true ;
} int MainApp::OnExit()
{ delete m_checker;
return 0;
} |
注意:上面使用了wxGetUserId()来构建实例名,这表示允许不同的用户能同时运行程序的一个实例。如果不这样,那么程序就只能被一个用户运行一次。
但是,如果你想把旧的实例提到前台,或者想使旧的实例打开传递给新的实例的作为命令行参数的文件,该怎么办呢?一般来说,这需要在这两个实例间进行通讯。我们可以使用wxWidgets提供的进程间通讯类来实现。
在下面的实例中,我们将实现程序多个实例间的通讯,以便允许第二个实例请求第一个实例将自己带到前台以提醒用户它已经在运行。下面的代码实现了一个连接类,这个类将被两个实例使用。一个服务器类被旧的实例使用,以便监听新的实例的连接请求。一个客户端类被新的实例使用,以便和旧的实例进行通讯。
1
2
3
4
5
|
class AppServer : public wxServer
{ public :
virtual wxConnectionBase* OnAcceptConnection( const wxString& topic);
}; |
1
2
3
4
5
|
class AppClient : public wxClient
{ public :
virtual wxConnectionBase* OnMakeConnection();
}; |
1
2
3
4
5
6
|
class AppConnection : public wxConnection
{ public :
virtual bool OnExecute( const wxString& WXUNUSED(topic), wxChar* WXUNUSED(data),
int WXUNUSED(size), wxIPCFormat WXUNUSED(format));
}; |
当有新的实例(Client)进行连接请求时,旧的实例(Server)中的OnAcceptConnection函数首先检查旧的实例中没有任何模式对话框。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
wxConnectionBase* AppServer::OnAcceptConnection( const wxString& topic)
{ if (topic.Lower() == wxT( "only-one" ))
{
wxWindowList::Node* node = wxTopLevelWindows.GetFirst();
while (node)
{
wxDialog* dialog = wxDynamicCast(node->GetData(), wxDialog);
if (dialog && dialog->IsModal())
{
return false ;
}
node = node->GetNext();
}
return new AppConnection();
}
else
{
return NULL;
}
} |
OnExecute函数是一个回调函数,在新的实例对其连接对象(由AppConnection创建的对象)调用Execute函数时被调用。OnExecute函数可以有一个空的参数,这表示它只要将自己提到前台就可以了。
1
2
3
4
5
6
7
8
9
10
11
|
bool AppConnection::OnExecute( const wxString& WXUNUSED(topic), wxChar* WXUNUSED(data),
int WXUNUSED(size), wxIPCFormat WXUNUSED(format))
{ wxFrame* frame = wxDynamicCast(wxGetApp().GetTopWindow(), wxFrame);
if (frame)
{
frame->Restore(); // 必须要有这句,不然当主窗口最小化时,就不能被提到前台
frame->Raise();
}
return true ;
} |
接下来我们还需要修改OnInit()函数。当没有别的实例在运行时,这个实例需要将自己设置为Server,等待别的实例的连接请求,如果已经有实例在运行,那么就创建一个和那个实例的连接,请求那个实例将程序的主窗口提到前台。下面的修改后的代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
|
bool MainApp::OnInit()
{ wxString name = wxString::Format(wxT( "MainApp-%s" ), wxGetUserId().GetData());
m_checker = new wxSingleInstanceChecker(name);
if (!m_checker->IsAnotherRunning())
{
m_server = new AppServer();
if (!m_server->Create(wxT( "wxMainApp" )))
{
wxLogDebug(wxT( "Failed to create an IPC service." ));
return false ;
}
}
else
{
AppClient* client = new AppClient();
wxString hostName = wxT( "localhost" );
wxConnectionBase* conn = client->MakeConnection(hostName, wxT( "wxMainApp" ), wxT( "only-one" ));
if (conn)
{
conn->Execute(wxEmptyString);
conn->Disconnect();
delete conn;
}
else
{
wxString msg = wxT( "Sorry, the existing instance may be too busy to respond.n" )
wxT( "Please close any open dialogs and retry." );
wxMessageBox(msg, wxT( "wxMainApp" ), wxICON_INFORMATION | wxOK);
}
delete client; // 如果没有这句,在运行Debug版本时就会显示如下图的警告
return false ;
}
... more initializations ...
return true ;
} |
1
2
3
4
5
6
|
int MainApp::OnExit()
{ delete m_checker;
delete m_server;
return 0;
} |
问题:
1. 调用wxGetApp问题
在调用wxGetApp()时可能会有编译错误,提示说”identifier not found”。这可以通过在App类后加上一行DECLARE_APP(XXXApp)来解决。
2. 引入ipc.h和wx.h时的顺序问题
运行第二个实例的时候,发现它总是会挂起在MakeConnection处,查看进程可以看到有两个实例在运行。在网上找了n久,只在wxWidgets Forum上发现有提到这个问题(Windows service using wxWidgets ipc),可是也没有提到如何解决。只能靠自己啦,经过对程序的一步步排除,终于发现是因为引入头文件时将ipc.h放在wx.h之前的原因,掉换引入头文件的顺序后问题被解决。
相关推荐
虚拟控制台最有用的是,当一个程序出错造成系统死锁时,可以切换到其它虚拟控制台工作,关闭这个程序。 shutdown 1.作用 shutdown命令的作用是关闭计算机,它的使用权限是超级用户。 2.格式 shutdown [-h][-i...
您也可以禁止某些程序运行,记录所有进程运行日志,当进程终止时或达到资源的消耗极限时自动重启,限制了运行实例数量,等等。您甚至可以指示进程,促使其进入高性能电源使用方案和/或防止PC睡眠。游戏模式可以很...
当你在客户端调用Blash.toString(reply)时,后台将采用java.util.date的默认构造方法创建一个实例.然后调用实例的tostring方法.客户端的javascript将返回给reply对象(此时reply是java.util.date的字符串形式) 3.5 The...
这意味着对于每个 SQL Server 实例,都存在一个专用的 MSFTESQL 实例,其中包括专用的组件(例如断字符和筛选器)、资源(例如内存)和配置(例如服务级设置,实例级的 resource_usage 是一个更具体的例子)。...
-Button控件将不再自动拥有display:inline属性,如果希望两个按钮在一行显示,请为第一个按钮设置CssStyle="float:left;"属性。 -修正了弹出菜单的位置在Firefox下不正确的BUG(feedback:eroach)。 -为TriggerBox...
-修正DatePicker中的一个bug(31/01/2010将会返回NULL)使用DateFormatString来生成SelectedDate属性(feedback:OktaEndy)。 -修正extjs最新版本(v3.2.2)中的一个bug,如果下拉列表中存在两个相同的Text,则...