Передача командной строки в первый экземпляр приложения с одним экземпляром

#vb.net #menu #command #line #sendto

#.net #vb.net

Вопрос:

Я уже реализовал контекстное меню, которое появляется, когда пользователь щелкает правой кнопкой мыши файл в проводнике Windows с помощью реестра. Адрес файла будет передан приложению в виде командной строки. Синтаксический анализ не проблема.

Как я могу реализовать это аналогично «Добавить в список воспроизведения проигрывателя Windows Media»? Он не открывает другой экземпляр приложения, но работает в том же открытом окне и добавляет его в список?

Ответ №1:

Есть 2 способа сделать это в зависимости от того, как запускается ваше приложение.

Метод 1: использование VB App Framework и MainForm

Это самый простой способ, потому что в основном вам просто нужно добавить некоторый код для события приложения. Сначала добавьте метод в свою основную форму, чтобы получать новые аргументы от последующих экземпляров вашего приложения:

 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
  

Далее:

  • Откройте свойства проекта
  • Установите флажок «Создать один экземпляр»
  • Внизу нажмите Просмотреть события приложения
  • Это откроет новое окно кода, как и любое другое; Выберите MyApplication Events в левом выпадающем списке; и StartupNextInstance в правом.

Здесь мы находим основную форму и отправляем аргументы командной строки в созданный нами метод:

 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
  

Примечание: Не пытайтесь извлечь основную форму из Application.OpenForms . Несколько раз мне не удавалось найти открытую форму в коллекции, поэтому я перестал полагаться на нее. Application.MainForm также проще.

Вот и все — при запуске нового экземпляра его аргументы командной строки должны быть переданы в форму и отображены в списке (или обработаны, как ваш метод считает нужным).


Способ 2: начиная с Sub Main

Это сложнее, потому что запуск вашего приложения из подосновы означает, что платформа приложений VB не используется, что обеспечивает StartupNextInstance событие. Решение заключается в создании подкласса WindowsFormsApplicationBase для обеспечения необходимой функциональности.

Сначала дайте вашей основной форме значимое имя и добавьте NewArgumentsReceived(args As String()) что-то вроде приведенного выше.

Для тех, кто не в курсе, вот как запустить ваше приложение с Sub Main() :

  • Добавьте модуль с именем «Program» в свое приложение
  • Добавьте Public Sub Main() к нему a .
  • Перейдите в Проект -> Свойства -> Приложение
  • Снимите флажок Enable Application Framework
  • Выберите свой новый «Sub Main» в качестве объекта запуска

Модуль на самом деле может быть назван как угодно, Program это соглашение VS используется для приложений на C #. Код для Sub Main будет позже, после того, как мы создадим класс. Многое из следующего взято из старой статьи или блога MSDN или чего-то в этом роде.

 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
  

Обратите внимание, что все три основные вещи, которые платформа приложений может сделать для нас («Включить стили XP», «Создать один экземпляр» и «Сохранить настройки при выходе»), учтены. Теперь некоторые изменения в 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
  

SingleInstanceApp Класс можно повторно использовать с Sub Main приложением любого стиля, и код в этом методе в основном является шаблонным для копирования-вставки, за исключением, возможно, ссылки на форму и фактического имени NewArgumentsReceived метода.


Тестирование

Скомпилируйте приложение, затем, используя командное окно, отправьте в приложение некоторые аргументы командной строки. Я использовал:

C:Temp > одиночный экземпляр «First Inst» яблочные летучие мыши кошки

При этом приложение запускается как обычно, с показанными аргументами. Затем:

C:Temp >сингл-экземпляр «Next Inst» зигги зои Заки
C:Temp >одиночный экземпляр «Последний Inst» 111 222 3333

Не имеет значения, какой подход вы используете — они оба работают одинаково. Результат:

введите описание изображения здесьвведите описание изображения здесь

Обратите внимание, что в зависимости от настроек безопасности ваш брандмауэр может запрашивать разрешение для приложений, использующих любой метод для подключения к другим компьютерам. Это результат того, как экземпляр отправляет или прослушивает аргументы от других. По крайней мере, с моим я могу запретить разрешение на подключение, и все по-прежнему работает нормально.

Ответ №2:

решение @Plutonix довольно эффективное и элегантное.

Однако, если ваша программа проходит через несколько форм, т.Е. Если Основная форма может измениться во время выполнения программы, например, если у вас есть форма входа, а затем основная форма или последовательность немодальных форм, Application.Основная форма не всегда будет одинаковой и может быть неизвестна заранее (жестко запрограммирована).

Код Plutonix предполагает, что он известен, и жестко кодирует его. В этом случае вы можете захотеть иметь возможность получать новые аргументы в любое время, в любой форме, которая активна в данный момент в вашем приложении.

Существует 2 решения для расширения решения Plutonix:

1) Повторно принудительно применить.MainForm в определенную форму в коде (я не тестировал это, но приложение.MainForm читается / записывается, поэтому он может работать).

2) Наиболее элегантным является реализация интерфейса для всех форм, которые могут стать основной формой:

Создайте базовый интерфейс:

 Public Interface INewArgumentsReceived
    Sub NewArgumentsReceived(args As String())
End Interface
  

Измените код @Plutonix для MyApplication_StartupNextInstance на:

 Private Sub MyApplication_StartupNextInstance(sender As Object, e As ApplicationServices.StartupNextInstanceEventArgs) Handles Me.StartupNextInstance
            Dim f = Application.MainForm

            If f.GetType.GetInterfaces.Contains(GetType(INewArgumentsReceived)) Then
                CType(f, INewArgumentsReceived).NewArgumentsReceived(e.CommandLine.ToArray)
            Else
                MsgBox("The current program state can't receive new requests.",, vbExclamation)
            End If
  

Теперь для всех возможных форм, которые могут стать основной формой, реализуйте интерфейс INewArgumentsReceived:

 Public Class FormA: Implements INewArgumentsReceived

    Public Sub NewArgumentsReceived(args As String()) Implements INewArgumentsReceived.NewArgumentsReceived
         MsgBox("Got new arguments")
    End Sub
  

Другим преимуществом использования интерфейсов является то, что мы можем проверить, работает ли текущее приложение.MainForm реализует его и может его получать.

Если текущее приложение.MainForm не реализует интерфейс, он корректно завершается с информационным сообщением.