当前位置:网站首页>微软是如何解决 PC 端程序多开问题的——内部实现
微软是如何解决 PC 端程序多开问题的——内部实现
2022-04-23 03:02:00 【dotNET跨平台】
前言
上次,我们通过《引用 Microsoft.VisualBasic 解决程序多开的问题》。
虽然它非常简单,但是仅适用于 WinForm 应用程序,而且还需要引用不常用的Microsoft.VisualBasic
类库。
因此,我们决定深挖一下,看看具体是如何实现的。
原理
通过查看WindowsFormsApplicationBase
的Run
方法实现(代码有删减):
Public Sub Run(commandLine As String())
If Not IsSingleInstance Then
DoApplicationModel()
Else
' This is a Single-Instance application
Dim pipeServer As NamedPipeServerStream = Nothing
If TryCreatePipeServer(ApplicationInstanceID, pipeServer) Then
' --- This is the first instance of a single-instance application to run.
Using pipeServer
WaitForClientConnectionsAsync(pipeServer, AddressOf OnStartupNextInstanceMarshallingAdaptor, cancellationToken:=tokenSource.Token)
DoApplicationModel()
End Using
Else
Dim awaitable = SendSecondInstanceArgsAsync(ApplicationInstanceID, commandLine, cancellationToken:=tokenSource.Token).ConfigureAwait(False)
awaitable.GetAwaiter().GetResult()
End If
End If 'Single-Instance application
End Sub
可以分析出整个流程如下:
创建一个
NamedPipeServerStream
实例如果创建成功,则用
WaitForClientConnectionsAsync
等待第 2 个应用实例进行连接如果创建失败,则用
SendSecondInstanceArgsAsync
向第 1 个应用实例发送数据
NamedPipeServerStream
使用NamedPipeServerStream
类可以创建命名管道。
命名管道在管道服务器和一个或多个管道客户端之间提供进程间通信。命名管道可以是单向的,也可以是双向的。它们支持基于消息的通信,并允许多个客户端使用相同的管道名称同时连接到服务器进程。
详细使用说明,请参阅官方文档《使用命名管道进行网络进程间通信》[1]
实现
下面我们用控制台程序进行演示:
const string pipeName = "MyIO";
const PipeOptions NamedPipeOptions = PipeOptions.Asynchronous | PipeOptions.CurrentUserOnly;
static async Task Main(string[] args)
{
try
{
using (var pipeServer = new NamedPipeServerStream(
pipeName: pipeName,
direction: PipeDirection.In,
maxNumberOfServerInstances: 1,
transmissionMode: PipeTransmissionMode.Byte,
options: NamedPipeOptions))
{
WaitForClientConnectionsAsync(pipeServer,str => Console.WriteLine(str));
Console.WriteLine($"start server {args[0]}");
Console.ReadKey();
}
}
catch
{
await SendSecondInstanceArgsAsync(()=> $"call from {args[0]}").ConfigureAwait(false);
}
}
需要注意的是,WaitForClientConnectionsAsync
不能加await
,否则后续代码不能执行。
WaitForClientConnectionsAsync
实现代码如下:
private static async Task WaitForClientConnectionsAsync(NamedPipeServerStream pipeServer, Action<string> callback)
{
CancellationTokenSource cancellationTokenSource = new CancellationTokenSource();
while (true)
{
await pipeServer.WaitForConnectionAsync(cancellationTokenSource.Token).ConfigureAwait(false);
try
{
const int bufferLength = 1024;
var buffer = new byte[bufferLength];
using (var stream = new MemoryStream())
{
while (true)
{
var bytesRead = await pipeServer.ReadAsync(buffer.AsMemory(0, bufferLength), cancellationTokenSource.Token).ConfigureAwait(false);
if (bytesRead == 0)
{
break;
}
stream.Write(buffer, 0, bytesRead);
}
stream.Seek(0, SeekOrigin.Begin);
callback(Encoding.UTF8.GetString(stream.ToArray()));
}
}
finally
{
pipeServer.Disconnect();
}
}
}
循环等待客户端连接
读取客户端发送的数据,转换成字符串
调用
callback
处理字符串,这里是str => Console.WriteLine(str)
断开客户端连接
SendSecondInstanceArgsAsync
实现代码如下:
private static async Task SendSecondInstanceArgsAsync(Func<string> func)
{
using (var pipeClient = new NamedPipeClientStream(
serverName: ".",
pipeName: pipeName,
direction: PipeDirection.Out,
options: NamedPipeOptions))
{
CancellationTokenSource cancellationTokenSource2 = new CancellationTokenSource();
cancellationTokenSource2.CancelAfter(2500);
await pipeClient.ConnectAsync(cancellationTokenSource2.Token).ConfigureAwait(false);
await pipeClient.WriteAsync(Encoding.UTF8.GetBytes(func()), cancellationTokenSource2.Token).ConfigureAwait(false);
}
}
创建客户端连接本地管道服务
向服务端发送
func
产生的数据,,这里是()=> $"call from {args[0]}"
Demo
创建多开脚本:
start " " "ConsoleApp1.exe" firstInstance
start " " "ConsoleApp1.exe" secondInstance
start " " "ConsoleApp1.exe" thirdInstance
执行后,我们发现程序只能打开一次。
并且收到了其它多开应用发过来的数据:
结论
使用NamedPipeServerStream
相对互斥锁Mutex
的实现要复杂。
但是由于可以进行通讯,因此可以做到更灵活的控制。
比如,应用定时启动自己的另一个实例去下载更新,下载完成后通知当前应用提示用户是否更新。
想了解更多内容,请关注我的个人公众号”My IO“
参考资料
[1]
《使用命名管道进行网络进程间通信》: https://docs.microsoft.com/zh-cn/dotnet/standard/io/how-to-use-named-pipes-for-network-interprocess-communication?WT.mc_id=DT-MVP-38491
版权声明
本文为[dotNET跨平台]所创,转载请带上原文链接,感谢
https://blog.csdn.net/sd7o95o/article/details/124310722
边栏推荐
- When using art template inheritance, compileerror: invalid or unexpected token generated
- The problem of removing spaces from strings
- Android high-level interview must ask: overall business and project architecture design and reconstruction
- Opencv combines multiple pictures into video
- Get together to watch (detailed version) eat a few cents a day
- c#可变参数params的介绍
- @Usage and difference between mapper and @ repository
- 树莓派开发笔记(十二):入手研华ADVANTECH工控树莓派UNO-220套件(一):介绍和运行系统
- First knowledge of C language ~ branch statements
- Niuke white moon race 5 [problem solving mathematics field]
猜你喜欢
Log cutting - build a remote log collection server
Android 高阶面试必问:全局业务和项目的架构设计与重构
HLS / chisel practice CORDIC high performance computing complex square root
Detailed explanation of distributed things
树莓派开发笔记(十二):入手研华ADVANTECH工控树莓派UNO-220套件(一):介绍和运行系统
Golden nine silver ten interview season, you are welcome to take away the interview questions (with detailed answer analysis)
Q-Learning & Sarsa
Windows MySQL 8 zip installation
Kubernetes - Introduction to actual combat
Binary tree
随机推荐
c#可变参数params的介绍
Cherno_ Game engine series tutorial (5): 101~
Typescript Learning Guide
How can enterprises with major hazard installations ensure the completion of the digital construction task of double prevention mechanism by the end of the year
C# WPF UI框架MahApps切换主题
tf. keras. layers. MaxPooling? D function
Niuke white moon race 6 [solution]
Gavl021, gavl281, AC220V to 5v200ma small volume non isolated chip scheme
Introduction and use of openfeign component
The way to conquer C language
ROP Emporium x86_ 64 7 ~ 8 questions
REINFORCE
【Hcip】OSPF常用的6种LSA详解
Microservices (distributed architecture)
[format] simple output (2)
tf. keras. layers. Density function
How to use C language to realize [guessing numbers game]
Domestic lightweight Kanban scrum agile project management tool
Linux redis - redis ha sentinel cluster construction details & redis master-slave deployment
Opencv fills the rectangle with a transparent color