将命令行传递给单实例应用程序的第一个实例 [英] Pass Command Line to first instance of a Single Instance App
问题描述
我已经实现了上下文菜单,当用户使用注册表在 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; andStartupNextInstance
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屋!