Click-Once Single Instance File Association Already Open Application

lordfeff

Member
Joined
Oct 13, 2015
Messages
6
Programming Experience
1-3
Is there an easy way to get the file location for an associated file that is used to open an already open Click-Once application?
Using Visual Studio 2012

In reference to ApplicationEvents.vb:

This code works perfectly for the file association when the application is closed and is opened with the associated file:
VB.NET:
Private Sub MyApplication_Startup(sender As Object, e As ApplicationServices.StartupEventArgs) Handles Me.Startup
            Try
                For Each s As String In AppDomain.CurrentDomain.SetupInformation.ActivationArguments.ActivationData
                    Dim fs As StreamReader = New StreamReader(Replace(s.Substring(8), "%20", " "))
                    _DataA = fs.ReadLine()
                    _DataB = fs.ReadLine()
                    fs.Close()
                Next
            Catch
            End Try
End Sub

However I have tried this with StartupNextInstance and I get the original file that opened this forms location and information.
In addition I have tried as a test the following:
VB.NET:
   Private Sub MyApplication_StartupNextInstance(sender As Object, e As ApplicationServices.StartupNextInstanceEventArgs) Handles Me.StartupNextInstance
            MsgBox(e.CommandLine.Count)
            For Each a As String In e.CommandLine
                MsgBox(a)
            Next
   End Sub

I always receive 0 as a count when opening via the file that is associated with this program for the above code.

So in short:
I would like to know if what I want to do has a simple solution like the first code instance that works?
What is the best way to test file association (Release, Debug, Or After Publish)?

The application is Click-Once Deployed and has the single instance checkbox already checked.

I don't code a lot but I have been doing so off and on in VB and this area I am very new at.

I appreciate your support in this matter.


Best Regards,

Mark
 

jmcilhinney

VB.NET Forum Moderator
Staff member
Joined
Aug 17, 2004
Messages
14,346
Location
Sydney, Australia
Programming Experience
10+
Just to be clear, you're saying that if you double-click a data file to open your application and then double-click a second data file, the StartupNextInstance event handler gives you the path of the first data file instead of the second, right?
 

lordfeff

Member
Joined
Oct 13, 2015
Messages
6
Programming Experience
1-3
Just to be clear, you're saying that if you double-click a data file to open your application and then double-click a second data file, the StartupNextInstance event handler gives you the path of the first data file instead of the second, right?

Yes That is correct.
 
Last edited:

lordfeff

Member
Joined
Oct 13, 2015
Messages
6
Programming Experience
1-3
here is the file path in both cases (first/next instance):
VB.NET:
AppDomain.CurrentDomain.SetupInformation.ActivationArguments.ActivationData(0)
c# - How to get command line from a ClickOnce application? - Stack Overflow

You can check if debugging or deployed with My.Application.IsNetworkDeployed.

After Going over this information and testing this does not seem to work. When testing in Debug and Release in the VB Studio and opening a file that is associated with the application all activation parameters shown in the MSDN example return nothing (null) because the application was not opened with any file of course. This brings the issue full circle and I once again would only receive the arguments for the file association that "OPENED" the application initially.

What I really need is for this to work with an already open, Single Instance, Click-Once Application preferably using the StartupNextInstance to grab this information. I thank you for the attempt to help anyway.

-Mark
 
Last edited:

JohnH

VB.NET Forum Moderator
Staff member
Joined
Dec 17, 2005
Messages
15,438
Location
Norway
Programming Experience
10+
I think I may have misread the information when testing last night, I did a new test today and as you say ActivationData(0) is the initial file also in StartupNextInstance.

The problem is specific to ClickOnce deployment, because if you associate the file type directly with the .exe then standard event information by e.CommandLine will work both for first and next instance.
 

lordfeff

Member
Joined
Oct 13, 2015
Messages
6
Programming Experience
1-3
I think I may have misread the information when testing last night, I did a new test today and as you say ActivationData(0) is the initial file also in StartupNextInstance.

The problem is specific to ClickOnce deployment, because if you associate the file type directly with the .exe then standard event information by e.CommandLine will work both for first and next instance.

That is good to know John. I might want to start using .exe deployment if I can define an update location that would allow the application to update whenever said network drive location has an updated software revision automatically.
Right now I like the Click-Once because of the easy way of having the user update forcefully by setting a minimum revision and simply publishing to the publish folder. I am all about ease of use. I have only used the free install shield in visual studio a few times to create an executable so I have little knowledge about its update structure and I admit fumbling through it to get the exe output for the few applications I made that did not require updating.
This would be good for future applications if I can figure out how to make these update based on a network drive location but my current project has already been deployed using click-once and is currently in use in this way.
I appreciate your time investigating this and agree the problem seems to be the click-once deployment as I have read via googling around for scraps of information. I may have to buckle down and try the mutex examples I have seen but I am not in a rush as this feature is simply to make the application more user friendly with file associations. If there is another way that does not require me to "what I feel" code around the problem I would prefer that method.
 

JohnH

VB.NET Forum Moderator
Staff member
Joined
Dec 17, 2005
Messages
15,438
Location
Norway
Programming Experience
10+
Unless there is an actual way to get the second file path, I would probably turn off the 'single instance' and manage this myself in Startup event, startup can be cancelled conditionally (e.Cancel).
For example use IO.Pipes or other IPC to transmit the second path to the first process, this can also be used to detect if there already is an existing process (client can connect to server or not).
 

lordfeff

Member
Joined
Oct 13, 2015
Messages
6
Programming Experience
1-3
Unless there is an actual way to get the second file path, I would probably turn off the 'single instance' and manage this myself in Startup event, startup can be cancelled conditionally (e.Cancel).
For example use IO.Pipes or other IPC to transmit the second path to the first process, this can also be used to detect if there already is an existing process (client can connect to server or not).

Thanks John. That sounds reasonable. Let me try to apply this method after a little bit of reading at MSDN on those examples. I'll report back once I get it working.
 
Last edited:

lordfeff

Member
Joined
Oct 13, 2015
Messages
6
Programming Experience
1-3
Okay so I got it working with Johns help. What really helps is controlling the single instance myself with the e.cancel = true method. In this way I can easily pass information. John I thank you for your help. I did try looking into the IO.Pipes and testing it I found that it wanted me to transmit byte data which would mean converting information from string to byte which was a little bit beyond my experience. I may not have looked at this correctly but if you want to give additional information on this method please do I will be happy to read up on how to pass strings this way. In the end I decided to create a temporary file to store the data while the main program sniffs for this file every 2 seconds to see if it exists. It was within my comfort zone so I went this way for now. (Hopefully I don't get any flak if it doesn't work on someones computer)

Here are the parts:

VB.NET:
Private Sub MyApplication_Startup(sender As Object, e As ApplicationServices.StartupEventArgs) Handles Me.Startup
            Dim fw As StreamWriter
            Try
                Dim p() As Process
                p = Process.GetProcessesByName("S-FORMS")
                If p.Count > 1 Then
                    Try
                        For Each s As String In AppDomain.CurrentDomain.SetupInformation.ActivationArguments.ActivationData
                            Dim fs As StreamReader = New StreamReader(Replace(s.Substring(8), "%20", " "))
                            _SFORM = fs.ReadLine()
                            _Record = fs.ReadLine()
                            fs.Close()
                        Next
                        If Trim(_SFORM) <> Nothing And Trim(_SFORM) <> "" Then
                            fw = New StreamWriter(Path.GetTempPath & "SFORM.tmpr")
                            fw.WriteLine(_SFORM)
                            fw.WriteLine(_Record)
                            fw.Close()
                        End If
                    Catch
                        fw = Nothing
                    End Try
                    e.Cancel = True
                Else
                    Try
                        For Each s As String In AppDomain.CurrentDomain.SetupInformation.ActivationArguments.ActivationData
                            Dim fs As StreamReader = New StreamReader(Replace(s.Substring(8), "%20", " "))
                            _SFORM = fs.ReadLine()
                            _Record = fs.ReadLine()
                            fs.Close()
                        Next
                    Catch
                    End Try
                End If
            Catch ex As Exception
                MsgBox(ex.Message)
            End Try
        End Sub

VB.NET:
 Private Sub S_Form_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
              CheckLoad.Start() 'A timer that ticks a sub event every 2 seconds
 End Sub

VB.NET:
  Private Sub checkloading() Handles CheckLoad.Tick
        If File.Exists(Path.GetTempPath & "SFORM.tmpr") Then
            Try
                Dim fs As StreamReader = New StreamReader(Path.GetTempPath & "SFORM.tmpr")
                While fs.EndOfStream = False
                    _SFORM = fs.ReadLine
                    _Record = fs.ReadLine
                End While
                fs.Close()
                File.Delete(Path.GetTempPath & "SFORM.tmpr")
            Catch
            End Try
        End If
        If _SFORM = "S101" Then
            _SFORM = Nothing
            Dim aForm As New Form
            aForm = New S101
            aForm.Show()
        ElseIf _SFORM = "S102" Then
            Dim aForm As New Form
            aForm = New S102
            _SFORM = Nothing
            aForm.Show()
        ElseIf _SFORM = "S103" Then
            Dim aForm As New Form
            aForm = New S103
            _SFORM = Nothing
            aForm.Show()
        ElseIf _SFORM = "ERF" Then
            Dim aForm As New Form
            aForm = New ERF
            _SFORM = Nothing
            aForm.Show()
        End If
        _Record = Nothing
        _SFORM = Nothing
    End Sub

Hopefully this will help those curious to get past this issue. Once again I thank you John for the information as it helped me greatly to work through this problem. If I can do something to help your rep I will just tell me how and I will do so.
Also I am aware I did not call out the variables in a great way. This program is old and reflects my very first years starting at VB where I know now I should declare things before the If blocks etc so I don't need to reuse them over and over.

-Mark
 

JohnH

VB.NET Forum Moderator
Staff member
Joined
Dec 17, 2005
Messages
15,438
Location
Norway
Programming Experience
10+
That's great!
converting information from string to byte which was a little bit beyond my experience
Dim bytes = System.Text.Encoding.UTF8.GetBytes("some string")

Although you could just use a StreamWriter/StreamReader to write/read from stream.
 
Top Bottom