Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
294 views
in Technique[技术] by (71.8m points)

.net - Pass Command Line to first instance of a Single Instance App

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?

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

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:

enter image description hereenter image description here

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.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

1.4m articles

1.4m replys

5 comments

57.0k users

...