C# VB.NET多进程-管道通信,命名管道(Named Pipes)

要向已运行的进程 发送特定命令(如/exit),而不是启动新进程,需要使用进程间通信(IPC)机制。以下是几种常见的实现方法:

一、使用命名管道(Named Pipes)

如果ABC.EXE支持通过命名管道接收命令,可以这样实现:

1. 在 ABC.EXE 中添加管道服务器代码
vbnet 复制代码
' ABC.EXE 中添加以下代码(需在启动时运行)
Imports System.IO.Pipes

Public Sub StartPipeServer()
    Dim thread As New Threading.Thread(AddressOf PipeServerThread)
    thread.IsBackground = True
    thread.Start()
End Sub

Private Sub PipeServerThread()
    Try
        While True
            Using server As New NamedPipeServerStream("ABCControlPipe")
                Console.WriteLine("等待客户端连接...")
                server.WaitForConnection()
                
                Using reader As New StreamReader(server)
                    Dim command As String = reader.ReadLine()
                    Console.WriteLine($"收到命令: {command}")
                    
                    ' 处理命令
                    If command = "/exit" Then
                        ' 执行退出逻辑
                        Application.Exit()
                    End If
                End Using
            End Using
        End While
    Catch ex As Exception
        Console.WriteLine($"管道服务器错误: {ex.Message}")
    End Try
End Sub
2. 在监控程序中发送命令

vb

vbnet 复制代码
Imports System.IO.Pipes

Public Sub SendCommandToRunningProcess(command As String)
    Try
        Using client As New NamedPipeClientStream(".", "ABCControlPipe", PipeDirection.Out)
            client.Connect(2000)  ' 等待2秒连接
            
            Using writer As New StreamWriter(client)
                writer.WriteLine(command)
                writer.Flush()
                Console.WriteLine($"命令已发送: {command}")
            End Using
        End Using
    Catch ex As Exception
        Console.WriteLine($"发送命令失败: {ex.Message}")
    End Try
End Sub

' 使用示例
SendCommandToRunningProcess("/exit")

二、使用命令行参数 + 进程查找

如果ABC.EXE支持通过创建特定文件或命令行参数触发行为,可以这样实现:

1. 修改 ABC.EXE 定期检查命令文件

vb

vbnet 复制代码
' 在ABC.EXE中添加定时检查逻辑
Private Sub StartCommandChecker()
    Dim timer As New Timer(1000)  ' 每秒检查一次
    AddHandler timer.Elapsed, AddressOf CheckForCommands
    timer.Start()
End Sub

Private Sub CheckForCommands(sender As Object, e As ElapsedEventArgs)
    Dim commandFile As String = Path.Combine(Application.StartupPath, "command.txt")
    
    If File.Exists(commandFile) Then
        Try
            Dim command As String = File.ReadAllText(commandFile)
            File.Delete(commandFile)  ' 处理后删除
            
            If command = "/exit" Then
                Application.Exit()
            End If
        Catch ex As Exception
            Console.WriteLine($"读取命令文件失败: {ex.Message}")
        End Try
    End If
End Sub
2. 在监控程序中创建命令文件

vb

复制代码
Public Sub SendCommandToRunningProcess(command As String)
    Dim commandFile As String = Path.Combine(
        Path.GetDirectoryName(Process.GetProcessesByName("ABC").FirstOrDefault()?.MainModule.FileName),
        "command.txt"
    )
    
    Try
        File.WriteAllText(commandFile, command)
        Console.WriteLine($"命令已写入文件: {command}")
    Catch ex As Exception
        Console.WriteLine($"发送命令失败: {ex.Message}")
    End Try
End Sub

三、使用 Windows 消息(适用于有窗口的进程)

如果ABC.EXE有隐藏窗口,可以发送自定义 Windows 消息:

1. 在 ABC.EXE 中注册并处理自定义消息

vb

vbnet 复制代码
Imports System.Runtime.InteropServices

Public Class MainForm
    ' 注册自定义消息
    Private Const WM_USER As Integer = &H400
    Private Const WM_EXIT_COMMAND As Integer = WM_USER + 100
    
    <DllImport("user32.dll", CharSet:=CharSet.Auto)>
    Private Shared Function RegisterWindowMessage(ByVal lpString As String) As Integer
    End Function
    
    Private exitMessageId As Integer = RegisterWindowMessage("ABC_EXIT_COMMAND")
    
    Protected Overrides Sub WndProc(ByRef m As Message)
        If m.Msg = exitMessageId OrElse m.Msg = WM_EXIT_COMMAND Then
            ' 处理退出命令
            Application.Exit()
            Return
        End If
        
        MyBase.WndProc(m)
    End Sub
End Class
2. 在监控程序中发送消息

vb

vbnet 复制代码
Imports System.Runtime.InteropServices

Public Sub SendExitMessageToProcess()
    Dim processes() As Process = Process.GetProcessesByName("ABC")
    
    If processes.Length > 0 Then
        Dim mainWindowHandle As IntPtr = processes(0).MainWindowHandle
        
        If mainWindowHandle <> IntPtr.Zero Then
            ' 注册相同的消息ID
            Dim exitMessageId As Integer = RegisterWindowMessage("ABC_EXIT_COMMAND")
            
            ' 发送消息
            PostMessage(mainWindowHandle, exitMessageId, IntPtr.Zero, IntPtr.Zero)
            Console.WriteLine("退出消息已发送")
        Else
            Console.WriteLine("未找到主窗口句柄")
        End If
    End If
End Sub

<DllImport("user32.dll", CharSet:=CharSet.Auto)>
Private Shared Function RegisterWindowMessage(ByVal lpString As String) As Integer
End Function

<DllImport("user32.dll", CharSet:=CharSet.Auto)>
Private Shared Function PostMessage(ByVal hWnd As IntPtr, ByVal Msg As Integer, ByVal wParam As IntPtr, ByVal lParam As IntPtr) As Boolean
End Function

四、总结

选择哪种方法取决于ABC.EXE的实现:

  1. 优先使用命名管道 :灵活且安全,需修改ABC.EXE代码。
  2. 命令文件方式:简单但有延迟(取决于检查频率)。
  3. Windows 消息:适用于有窗口的进程。

如果无法修改ABC.EXE,可考虑使用外部工具(如 AutoHotkey 发送模拟按键),但可靠性较低。

分享

如果多个进程,管道名称是否要不一样?

是的,如果需要同时管理多个进程实例,每个进程的管道名称必须唯一。否则,不同进程会监听同一个管道,导致命令混淆或无法正确识别目标进程。以下是几种实现多进程管道通信的方案:

一、基于进程 ID(PID)的唯一管道名称

最简单的方法是将进程 ID(PID)作为管道名称的一部分:

1. 在 ABC.EXE 中创建唯一管道

vb

复制代码
Imports System.IO.Pipes

Public Class ABCApplication
    Private pipeServer As NamedPipeServerStream
    Private pipeName As String
    
    Public Sub StartPipeServer()
        ' 使用进程ID生成唯一管道名称
        pipeName = $"ABCControlPipe_{Process.GetCurrentProcess().Id}"
        
        ' 创建并启动管道服务器
        pipeServer = New NamedPipeServerStream(pipeName, PipeDirection.InOut)
        Dim thread As New Threading.Thread(AddressOf HandleIncomingConnections)
        thread.IsBackground = True
        thread.Start()
    End Sub
    
    Private Sub HandleIncomingConnections()
        Try
            While True
                pipeServer.WaitForConnection()
                
                ' 处理命令...
                Using reader As New StreamReader(pipeServer)
                    Dim command As String = reader.ReadLine()
                    If command = "/exit" Then
                        ' 优雅退出逻辑
                        Application.Exit()
                    End If
                End Using
                
                ' 重置管道以接收下一个连接
                pipeServer.Disconnect()
            End While
        Catch ex As Exception
            ' 错误处理
        End Try
    End Sub
End Class
2. 在监控程序中发送命令到特定进程
复制代码
Imports System.IO.Pipes

Public Sub SendCommandToProcess(pid As Integer, command As String)
    Dim pipeName As String = $"ABCControlPipe_{pid}"
    
    Try
        Using client As New NamedPipeClientStream(".", pipeName, PipeDirection.Out)
            client.Connect(2000)  ' 2秒超时
            
            Using writer As New StreamWriter(client)
                writer.WriteLine(command)
                writer.Flush()
            End Using
        End Using
    Catch ex As Exception
        Console.WriteLine($"发送命令到进程 {pid} 失败: {ex.Message}")
    End Try
End Sub

' 示例:向所有ABC进程发送退出命令
Public Sub SendExitToAllProcesses()
    Dim processes() As Process = Process.GetProcessesByName("ABC")
    
    For Each proc In processes
        SendCommandToProcess(proc.Id, "/exit")
    Next
End Sub

二、使用中央管道管理器(更高级的方案)

如果进程数量较多或需要更复杂的管理,可以实现一个中央管道管理器:

1. 中央管理器设计
vbnet 复制代码
Imports System.Collections.Concurrent

Public Class PipeManager
    Private Shared pipeMap As New ConcurrentDictionary(Of Integer, NamedPipeServerStream)
    
    ' 注册新进程的管道
    Public Shared Function RegisterProcess(pid As Integer) As String
        Dim pipeName As String = $"ABCControlPipe_{Guid.NewGuid()}"  ' 使用GUID确保唯一性
        
        Dim server As New NamedPipeServerStream(pipeName, PipeDirection.InOut)
        pipeMap.TryAdd(pid, server)
        
        ' 启动监听线程
        Dim thread As New Threading.Thread(Sub() HandleConnections(pid, server))
        thread.IsBackground = True
        thread.Start()
        
        Return pipeName
    End Function
    
    ' 处理管道连接
    Private Shared Sub HandleConnections(pid As Integer, server As NamedPipeServerStream)
        Try
            While pipeMap.ContainsKey(pid)
                server.WaitForConnection()
                
                ' 处理命令...
                Using reader As New StreamReader(server)
                    Dim command As String = reader.ReadLine()
                    ' 根据PID转发命令到对应进程
                End Using
                
                server.Disconnect()
            End While
        Catch ex As Exception
            ' 错误处理
        End Try
    End Sub
    
    ' 向特定进程发送命令
    Public Shared Sub SendCommand(pid As Integer, command As String)
        If pipeMap.TryGetValue(pid, out Dim server) Then
            ' 通过管道发送命令...
        End If
    End Sub
End Class
2. 进程端注册

vb

vbnet 复制代码
' 在ABC.EXE启动时
Public Sub Main()
    ' 向管理器注册自己并获取唯一管道名称
    Dim pipeName As String = PipeManager.RegisterProcess(Process.GetCurrentProcess().Id)
    
    ' 保存管道名称用于后续通信
    My.Settings.PipeName = pipeName
    My.Settings.Save()
    
    ' 其他初始化逻辑...
End Sub

三、关键注意事项

  1. 管道名称唯一性

    • 确保每个进程的管道名称包含唯一标识符(如 PID、GUID)。
    • 避免使用硬编码的固定名称。
  2. 资源清理

    • 进程退出时,确保关闭并释放管道资源。
    • 可以通过Process.Exited事件监听进程退出,清理对应的管道。
  3. 安全性考虑

    • 对管道名称进行权限控制,防止未授权访问。
    • 考虑使用加密或身份验证增强安全性(如使用PipeSecurity)。
  4. 错误处理

    • 处理管道连接超时、断开等异常情况。
    • 实现重试机制,确保命令可靠发送。

四、总结

通过为每个进程分配唯一的管道名称,可以有效管理多个进程实例。选择哪种方案取决于具体需求:

  • PID 方案:简单直接,适用于大多数场景。
  • 中央管理器:适合复杂系统,提供更灵活的控制和监控能力。

无论采用哪种方式,核心原则是确保管道名称的唯一性和通信的可靠性。

相关推荐
每天吃饭的羊几秒前
箭头函数(Arrow Functions)和普通函数(Regular Functions)
开发语言·javascript·ecmascript
寻觅~流光7 分钟前
封装---统一封装处理页面标题
开发语言·前端·javascript·vue.js·typescript·前端框架·vue
geovindu9 分钟前
Java: OracleHelper
java·开发语言·oracle
Q_Q196328847538 分钟前
python的平安驾校管理系统
开发语言·spring boot·python·django·flask·node.js·php
遇见尚硅谷2 小时前
C语言:游戏代码分享
c语言·开发语言·算法·游戏
小刘|2 小时前
单例模式详解
java·开发语言·单例模式
超浪的晨2 小时前
Java 内部类详解:从基础到实战,掌握嵌套类、匿名类与局部类的使用技巧
java·开发语言·后端·学习·个人开发
晓13132 小时前
JavaScript加强篇——第八章 高效渲染与正则表达式
开发语言·前端·javascript
阳光开朗_大男孩儿3 小时前
nfs 锁机制demo
开发语言·多线程·多进程·文件锁
Hello-Mr.Wang3 小时前
使用Spring Boot和PageHelper实现数据分页
java·开发语言·spring boot