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之前的原因,掉换引入头文件的顺序后问题被解决。
相关推荐
### C# 如何确保应用程序只运行一个实例 在C#编程中,经常遇到的一个需求是限制应用程序只能运行一个实例。这种需求通常出现在那些不需要或不允许同时打开多个窗口的应用程序中。下面将详细介绍如何通过C#代码实现...
1. **运行即托盘**:当程序启动时,它会立即在系统托盘显示一个图标,而不是在桌面上打开一个窗口。这通常通过在程序初始化阶段设置窗口属性和消息循环来实现。 2. **最小化到托盘**:当用户最小化程序窗口时,程序...
当用户尝试打开一个已经在后台运行的文件时,程序会检测到这个文件对应的进程已经存在,并将其激活到前台,而不是启动一个新的进程。这样做有以下几个优点: 1. 节省内存和系统资源:新进程的创建会消耗额外的内存...
【标题】"dubbo-admin前台管理程序"是一个用于管理和监控Dubbo服务的Web应用程序,它提供了用户友好的界面,使得开发者和运维人员可以方便地查看、配置和管理Dubbo服务的运行状态。这个程序通常被部署在Apache ...
3. **实例应用**:文中提到的`weapp-demo`是一个简单的实例,可以从GitHub仓库克隆到本地。使用微信Web开发者工具(版本0.9.092300或以上)打开项目,该工具提供了代码编辑和预览功能。在项目目录下,可以使用脚本...
如果`createdNew`为`true`,则表示当前是第一个运行的实例,程序将继续执行正常的初始化流程,如启用视觉样式、设置文本渲染默认值,并运行主窗体。 ```csharp Application.EnableVisualStyles(); Application....
标题中的“asp.net自己做的一个网站,后台+前台(C#)包括数据库”表明这是一个完整的Web应用程序,包含了前后台功能,并且与数据库进行了集成。C#是.NET Framework的主要编程语言,用于编写服务器端代码,处理用户...
标题中的“天气预报_微信小程序模板js代码前台前端H5页面源码.rar”表明这是一个关于天气预报功能的微信小程序模板,包含JavaScript(js)代码,用于构建小程序的前端页面,同时可能也支持H5(HTML5)页面。...
【标题】中的“系统级热键的实现”指的是在计算机软件开发中,如何创建一个能够全局响应的快捷键,即使当应用程序不在前台运行时也能触发特定功能的技术。系统级热键,也称为全局热键,允许用户通过键盘组合快速执行...
【百度智能小程序官方Demo】是百度为开发者提供的一个详尽的参考实例,它包含了构建一个完整小程序所需的各种组件和功能的展示。这个Demo旨在帮助开发者快速理解和上手百度智能小程序的开发过程,以便于创建出符合...
在这个项目中,"js代码"指的是JavaScript部分,它是驱动小程序运行的核心,处理逻辑和数据交互。 其次,提到的"画布"是指HTML5的Canvas元素,这是一个矩形区域,可以通过JavaScript来绘制图形。Canvas API提供了...
首先,我们要理解这个实例的核心目标是创建一个应用程序,能够在特定时间自动执行签到操作,这通常需要利用到系统的闹钟服务、权限控制以及后台运行机制。 1. **Android系统版本要求**:描述中提到需要Android 4.2...
标题和描述中提到的是一个利用PHP和jQuery实现的持续执行任务并实时更新前端数据库内容的示例。这个案例特别适用于那些不熟悉计划任务(如cron job)但希望实现实时数据展示的应用场景。 PHP是一种广泛使用的服务器...
标题中的"spring框架项目+jQueryAjax提交实例"指的是一个整合了Spring框架和jQuery AJAX技术的Web应用程序项目。这个项目提供了一个完整的流程,演示了如何在前端使用jQuery的AJAX功能向后端Spring MVC控制器发送...
首先,文档提到的客户端程序设计包括使用Java Applet来构建用户界面,界面中包含一个文本区用于显示信息和一个文本框用于输入信息。客户端与服务器通过Socket方式连接,并在初始化时获取输入/输出流。客户端程序通过...