将命令行传递给单实例应用程序的第一个实例 [英] Pass Command Line to first instance of a Single Instance App

查看:16
本文介绍了将命令行传递给单实例应用程序的第一个实例的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我已经实现了上下文菜单,当用户使用注册表在 Windows 资源管理器中右键单击文件时会出现上下文菜单.文件地址将作为命令行传递给应用程序.解析它没问题.

如何实现类似于添加到 Windows Media Player 播放列表"的功能?它不会打开应用程序的另一个实例,而是在同一个打开的窗口上工作并将其添加到列表中?

解决方案

根据您的应用启动方式,有两种方法可以做到这一点.

方法 1:使用 VB 应用程序框架和 MainForm

这是最简单的,因为您主要只需要为应用程序事件添加一些代码.首先,向您的主表单添加一个方法,以从您的应用程序的后续实例接收新参数:

Public Class MyMainForm ' 注意表单的类名...Public Sub NewArgumentsReceived(args As String())'例如将它们添加到列表框中如果 args.Length >0 那么lbArgs.Items.AddRange(args)万一结束子

下一步:

  • 打开项目属性
  • 选中制作单个实例"选项
  • 在底部,点击查看应用程序事件
  • 这将打开一个新的代码窗口;在左侧下拉列表中选择 MyApplication Events;和 StartupNextInstance 在右边.

在这里,我们找到主窗体并将命令行参数发送到我们创建的方法:

Private Sub MyApplication_StartupNextInstance(sender As Object,e 作为 ApplicationServices.StartupNextInstanceEventArgs) _处理我.StartupNextInstanceDim f = Application.MainForm' 使用您的实际表单类名:如果 f.GetType 是 GetType(MyMainForm) 那么CType(f, MyMainForm).NewArgumentsReceived(e.CommandLine.ToArray)万一结束子

注意:不要试图从 Application.OpenForms 中取出主窗体.有几次我在集合中找不到开放的表单,所以我已经不再依赖它了.Application.MainForm 也更简单.

就是这样 - 当一个新实例运行时,它的命令行参数应该被传递给表单并显示在列表框中(或者按照您的方法认为合适的方式进行处理).


方法二:从Sub Main

开始

这更复杂,因为从 Sub Main 启动您的应用程序意味着不使用 VB 应用程序框架,它提供了 StartupNextInstance 事件.解决方案是将 WindowsFormsApplicationBase 子类化以提供所需的功能.

首先,为您的主表单指定一个有意义的名称,并添加类似上述 NewArgumentsReceived(args As String()) 的内容.

对于那些不知道的人,这里是如何从 Sub Main() 启动您的应用程序:

  • 向您的应用添加名为程序"的模块
  • 向其中添加 Public Sub Main().
  • 转到项目 ->属性 ->申请
  • 取消选中启用应用程序框架
  • 选择新的Sub Main"作为启动对象

模块实际上可以命名为任何名称,Program 是 VS 用于 C# 应用程序的约定.Sub Main 的代码将在我们创建类之后.以下大部分内容源自旧的 MSDN 文章或博客或其他内容.

导入 Microsoft.VisualBasic.ApplicationServices导入 System.Collections.ObjectModel公共类 SingleInstanceApp' 这是 My.Application继承 WindowsFormsApplicationBasePublic Sub New(mode As AuthenticationMode)MyBase.New(模式)初始化应用程序()结束子公共子新建()初始化应用程序()结束子' 我们要实施的标准启动程序受保护的可覆盖子 InitializeApp()Me.IsSingleInstance = 真Me.EnableVisualStyles = True结束子' 即 Application.Run(frm):公共重载子运行(frm As Form)' 设置用作消息泵的主表单Me.MainForm = frm' 传递命令行Me.Run(Me.CommandLineArgs)结束子Private Overloads Sub Run(args As ReadOnlyCollection(Of String))' 将 RO 集合转换为简单数组' 这些将由 Sub Main 为 First 实例处理' 并在其他人的 StartupNextInstance 处理程序中Me.Run(myArgs.ToArray)结束子' 可选:退出时保存设置受保护的覆盖子 OnShutdown()如果 My.Settings.Properties.Count >0 那么My.Settings.Save()万一MyBase.OnShutdown()结束子结束班

请注意,App Framework 可以为我们做的三件事(启用 XP 样式"、制作单个实例"和退出时保存设置")都已考虑在内.现在,对 Sub Main 进行一些修改:

导入 Microsoft.VisualBasic.ApplicationServices导入 System.Collections.ObjectModel模块程序'这个应用程序的主要形式朋友 myForm 作为 MyMainFormPublic Sub Main(args As String())' 创建硬连线到 SingleInstance 的应用程序对象Dim app As New SingleInstanceApp()' 为第二个实例尝试启动时添加处理程序'(魔法发生在那里)AddHandler app.StartupNextInstance, AddressOf StartupNextInstancemyForm = 新的 MyMainForm' 在这里处理命令行参数作为第一个实例' 调用您添加到表单中的方法:myForm.NewArgumentsReceived(args)' 启动应用app.Run(myForm)结束子' 这在后续实例尝试启动时调用.' 抓取并处理他们的命令行Private Sub StartupNextInstance(sender As Object,e 作为 StartupNextInstanceEventArgs)' ToDo:处理 e.CommandLine 中提供的命令行.myForm.NewArgumentsReceived(e.CommandLine.ToArray)结束子终端模块

SingleInstanceApp 类可以与任何 Sub Main 样式的应用程序重用,该方法中的代码主要是复制粘贴样板文件,除了表单引用和 NewArgumentsReceived 方法的实际名称.


测试

编译应用程序,然后使用命令窗口向应用程序发送一些命令行参数.我用过:

<块引用><块引用>

C:Temp> 单个实例第一次实例"苹果蝙蝠猫

这会正常启动应用程序,并显示参数.然后:

<块引用><块引用>

C:Temp> 单个实例下一个实例"ziggy zoey zacky
C:Temp>singleinstanceLast Inst"111 222 3333

您使用哪种方法无关紧要 - 它们的工作方式相同.结果:

请注意,根据安全设置,您的防火墙可能会请求使用任一方法连接其他计算机的应用的权限.这是实例如何发送或侦听来自其他人的参数的结果.至少对于我的,我可以拒绝连接许可,一切仍然正常.

I have already implemented context menu to appear when a user right-clicks a file in windows explorer using Registry. The file address will be passed to the application as command lines. Parsing it is no problem.

How can I implement that is similar to "Add To Windows Media Player Playlist"? It does not open another instance of the app but works on the same open window and adds it to a list?

解决方案

There are 2 ways to do this depending on how your app starts.

Method 1: Using VB App Framework and a MainForm

This is the easiest because you mainly just need to add some code for an Application event. First, add a method to your main form to receive new arguments from subsequent instances of your app:

Public Class MyMainForm       ' note the class name of the form
    ...

    Public Sub NewArgumentsReceived(args As String())
        ' e.g. add them to a list box
        If args.Length > 0 Then
            lbArgs.Items.AddRange(args)
        End If
    End Sub

Next:

  • Open Project Properties
  • Check the "Make Single Instance" option
  • At the bottom, click View Application Events
  • This will open a new code window like any other; Select MyApplication Events in the left drop down; and StartupNextInstance in the right one.

Here, we find the main form and send the command line arguments to the method we created:

Private Sub MyApplication_StartupNextInstance(sender As Object,
                e As ApplicationServices.StartupNextInstanceEventArgs) _
                     Handles Me.StartupNextInstance

    Dim f = Application.MainForm
    '  use YOUR actual form class name:
    If f.GetType Is GetType(MyMainForm) Then
        CType(f, MyMainForm).NewArgumentsReceived(e.CommandLine.ToArray)
    End If

End Sub

Note: Do not try to fish the main form out of Application.OpenForms. A few times I've had it fail to find an open form in the collection, so I have quit relying on it. Application.MainForm is also simpler.

That's it - when a new instance runs, its command line args should be passed to the form and displayed in the listbox (or processed however your method sees fit).


Method 2: Starting From Sub Main

This is more complicated because starting your app from a Sub Main means that the VB Application Framework is not used, which provides the StartupNextInstance event. The solution is to subclass WindowsFormsApplicationBase to provide the functionality needed.

First, give your main form a meaningful name and add something like the NewArgumentsReceived(args As String()) as above.

For those not aware, here is how to start your app from Sub Main():

  • Add a module named 'Program' to your app
  • Add a Public Sub Main() to it.
  • Go to Project -> Properties -> Application
  • Uncheck Enable Application Framework
  • Select your new "Sub Main" as the Startup Object

The module can actually be named anything, Program is the convention VS uses for C# apps. The code for Sub Main will be later after we create the class. Much of the following originated from an old MSDN article or blog or something.

Imports Microsoft.VisualBasic.ApplicationServices
Imports System.Collections.ObjectModel

Public Class SingleInstanceApp
    ' this is My.Application
    Inherits WindowsFormsApplicationBase

    Public Sub New(mode As AuthenticationMode)
        MyBase.New(mode)
        InitializeApp()
    End Sub

    Public Sub New()
        InitializeApp()
    End Sub

    ' standard startup procedures we want to implement
    Protected Overridable Sub InitializeApp()
        Me.IsSingleInstance = True
        Me.EnableVisualStyles = True
    End Sub

    ' ie Application.Run(frm):
    Public Overloads Sub Run(frm As Form)
        ' set mainform to be used as message pump
        Me.MainForm = frm
        ' pass the commandline
        Me.Run(Me.CommandLineArgs)
    End Sub

    Private Overloads Sub Run(args As ReadOnlyCollection(Of String))
        ' convert RO collection to simple array
        ' these will be handled by Sub Main for the First instance
        '    and in the StartupNextInstance handler for the others
        Me.Run(myArgs.ToArray)
    End Sub

    ' optional: save settings on exit
    Protected Overrides Sub OnShutdown()

        If My.Settings.Properties.Count > 0 Then
            My.Settings.Save()
        End If

        MyBase.OnShutdown()
    End Sub
End Class

Note that the three main things the App Framework can do for us ("Enable XP Styles", "Make Single Instance" and "Save Settings on Exit") are all accounted for. Now, some modifications to Sub Main:

Imports Microsoft.VisualBasic.ApplicationServices
Imports System.Collections.ObjectModel

Module Program  
    ' this app's main form
    Friend myForm As MyMainForm

    Public Sub Main(args As String())
        ' create app object hardwired to SingleInstance
        Dim app As New SingleInstanceApp()

        ' add a handler for when a second instance tries to start
        ' (magic happens there)
        AddHandler app.StartupNextInstance, AddressOf StartupNextInstance

        myForm = New MyMainForm

        ' process command line args here for the first instance
        ' calling the method you added to the form:
        myForm.NewArgumentsReceived(args)

        ' start app
        app.Run(myForm)   
    End Sub

    ' This is invoked when subsequent instances try to start.
    '    grab and process their command line 
    Private Sub StartupNextInstance(sender As Object, 
                        e As StartupNextInstanceEventArgs)

        ' ToDo: Process the command line provided in e.CommandLine.
        myForm.NewArgumentsReceived(e.CommandLine.ToArray)

    End Sub   

End Module

The SingleInstanceApp class can be reused with any Sub Main style app, and the code in that method is mainly a copy-paste boilerplate affair except for perhaps the form reference and actual name of the NewArgumentsReceived method.


Testing

Compile the app, then using a command window, send some commandline arguments to the app. I used:

C:Temp>singleinstance "First Inst" apple bats cats

This starts the app as normal, with the arguments shown. Then:

C:Temp>singleinstance "Next Inst" ziggy zoey zacky
C:Temp>singleinstance "Last Inst" 111 222 3333

It doesnt matter which approach you use - they both work the same. The Result:

Note that depending on security settings, your firewall may request permission for apps using either method to connect to other computers. This is a result of how an instance sends or listens for the arguments from others. At least with mine, I can deny permission to connect and everything still works fine.

这篇关于将命令行传递给单实例应用程序的第一个实例的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆