I don't ask too many questions on here these days, but I figured I would ask about this and try to see if anyone maybe has some insight.
What I am looking to do, is prevent multiple instances of my application from running. Yes I know you are probably saying "Just click the single instance application" checkbox, but unfortunatly, it is not that simple.
There is a little known, but nasty bug in VBs application framework (which is what single instance application is part of). It is detailed here by yours truly, and is still waiting for resolution by Microsoft.
If you don't feel like reading the bug submission, basically the problem is this:
the Single Instance Application feature uses .NET remoting to setup a channel to listen for, detect, and handle additional instances of a given application. .NET remoting relies on TCP/IP, and certain firewalls break this functionality (mcafee and comodo to name 2, but there are more, windows onecare/windows firewall does not cause the bug, and it has all to do with how the given firewall product handles a request before access is granted)
So anyway, since all this code wireup happens prior to the global application exception handler being hooked up, there is absolutely NO WAY to capture the socket exception that gets thrown when one of the offending firewalls blocks the remoting call. This causes a hard crash of the application. (you know the "do you want to send an error report" crash.)
A really great VBer by the name of Bill McCarthy came up with a nice solution after seeing my bug report, however it uses features only available in .NET 3.5, and I need to resolve this for apps in .NET 2.0.
So basically I need to try to find a workaround that simulates the single instance application framework, but does not use remoting to accomplish this. I have a feeling this issue will never be fixed by MS in .NET 2.0, and eventually fixed in a SP to 3.5 or perhaps not until whatever the next framework version is.
The workaround needs to
1) check for an already running instance of the app when a new instance is started up
2) shut down the new instance in the event a first instance exists
3) focus the already running instance (ie bring to front or maximize, etc)
4) send any command line params from the second instance back to the first already running instance.
So if you haven't already guessed, the thing I am stuck on at the moment is #4. All the rest are pretty simple using a few different methods.
Does anyone have a solid way to pass data between the 2 exes without remoting?
My application has file associations, so when a user double clicks a file that is associated with my app, it launches my app. This is why I need to handle this multiple instance thing, because if they double click a file, and my app is already open, I need to send that filename over to the already open instance so it can open that file as well.
If you are still reading this, sorry for the long post, but I wanted to be as descriptive as possible to try to come up with a solution.
Last edited by kleinma; Jan 22nd, 2008 at 12:11 PM.
I'm pretty sure Windows doesn't require message queueing, so I guess that would be a problem. Maybe you could create a custom event log for your applications to communicate.
I recently had to do something very similar to this. I was able to accomplish it by using the StartupNextInstance event under Application Events. This allows you to read incoming parameters in an already running instance. I'm not sure if this will help you or not, I just thought I'd throw it out there...
nbrege, that only works when you checkoff the "Single Instance Application" checkbox in the project, which is exactly what I can not do because of the mentioned bug. StartupNextInstance is a product of TCP remoting and the single instance application framework.
How about this? In your Sub Main, get the current list of processes running on that machine, if your application is in there then bring that app to the forefront and call the close on the current instance.
Untested, but my random thought for the day.....and you help me so much I figure I should at least throw my 2 cents at ya!!!
Good Luck!
D
Platforms of choice: Visual Studio 2005/2008 Professional : Visual Studio 2010 Enterprise : PHP - Notepad++/WAMP
Please Rate If I helped you.
Please remember to mark threads as closed if your issue has been resolved.
I think you could use SendMessage Api method to send a message to the app instance main window.
To send message to the window you need its handle so you could use Process.MainWindowHandle. It is possible that there might be more than one process with the same name since there might be more than one app instance therefore you might want to loop through the all processes and send the same message to the windows that corresponds to your app.
The message can be custom message and let say Lparam or W param contains the message you want to pass.
This is not tested but worth to try.
Rating is a way of saying thank you. Don't forget to rate always!
How about this? In your Sub Main, get the current list of processes running on that machine, if your application is in there then bring that app to the forefront and call the close on the current instance.
Untested, but my random thought for the day.....and you help me so much I figure I should at least throw my 2 cents at ya!!!
Good Luck!
D
Sure, but if you check my post again, the problem I am stuck on is #4, what you are talking about is #1-3 which I already have resolved.
I think you could use SendMessage Api method to send a message to the app instance main window.
To send message to the window you need its handle so you could use Process.MainWindowHandle. It is possible that there might be more than one process with the same name since there might be more than one app instance therefore you might want to loop through the all processes and send the same message to the windows that corresponds to your app.
The message can be custom message and let say Lparam or W param contains the message you want to pass.
This is not tested but worth to try.
I have thought about using custom messages too. I actually have a few possible solutions on the table that I am currently testing, so I will post back with the proceedure and result of each of these methods once I have tested them all. Hopefully one of them is a perfect resolution to the issue.
Remoting isn't tied to TCP. Perhaps there's no way to change the protocol used by the VB application framework but if you set up your own Remoting then you can use a different protocol. I'm no networking or remoting guru but maybe IPC wouldn't get interfered with by firewalls the way TCP does.
John, I am actually testing an IPC solution right now after someone on another list mentioned it, I am converting one I found from C# to VB and I will post back with the results of that test.
I was actually unaware it was in .NET 2.0, I thought it was added in .NET 3.5 along with the managed named pipe classes.
I also did find an example of using unmanaged win32 apis to accomplish named pipes as well so I wouldn't need .NET 3.5 for that.
So I am sure one of these will come to resolution. Also if I find IPC works, I am going to start wondering why TCP was used for the application frameworks single instance capabilities versus IPC.
There is a workaround using the clipboard. Here is the code that is tested and works. The only limitation is that since it uses the clipboard it will erase what ever is in there but won’t have other permission or firewall problems.
Code:
Imports System.Runtime.InteropServices
Public Class Form1
<DllImport("user32", CharSet:=CharSet.Auto)> _
Private Shared Function SendMessage( _
ByVal hwnd As IntPtr, _
ByVal wMsg As UInt32, _
ByVal wParam As IntPtr, _
ByVal lParam As IntPtr) As IntPtr
End Function
Private Const WM_CUSTOMMSG As UInt32 = 2000
Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
Dim msg As String = CStr(Date.Now.Ticks)
Me.Label2.Text = "My Tiks: " & msg
Me.Label3.Text = "My Handle: " & Me.Handle.ToString
For Each p As Process In Process.GetProcesses
If p.MainWindowTitle = Me.Text Then
My.Computer.Clipboard.SetText(msg)
SendMessage(p.MainWindowHandle, WM_CUSTOMMSG, IntPtr.Zero, IntPtr.Zero)
End If
Next
End Sub
Protected Overrides Sub WndProc(ByRef m As System.Windows.Forms.Message)
If m.Msg = WM_CUSTOMMSG Then
If My.Computer.Clipboard.ContainsText Then
Me.Label1.Text = "Message:" & Environment.NewLine _
& My.Computer.Clipboard.GetText & Environment.NewLine _
& "Target window handle: " & m.HWnd.ToString
End If
End If
MyBase.WndProc(m)
End Sub
End Class
Last edited by VBDT; Jan 22nd, 2008 at 10:03 PM.
Rating is a way of saying thank you. Don't forget to rate always!
VBDT, it is a clever method of doing things, but this application goes out to hundreds of users, and I couldn't do something like that because that creates non standard behavior in Windows.
If I had an app that kept erasing whatever I might have copied to clipboard, it would drive me mad.
I apologize kleinma for not taking in the part where you only need help with the last one. I was thinking on it though.....could you maybe have an external .ini type file that your program keeps a systemfilewatcher on for changes - then when your app tries to start again with variances from the currently running on you can pick them up and incorporate them via the .ini file. Maybe this is a bit far-fetched and definitely outside the box.....just a thought though
If/When you figure it out can you post the solution, I for one would be most interested in how this is handled.
Thanks and good luck my friend!!
D
Platforms of choice: Visual Studio 2005/2008 Professional : Visual Studio 2010 Enterprise : PHP - Notepad++/WAMP
Please Rate If I helped you.
Please remember to mark threads as closed if your issue has been resolved.
Someone else (not here) made that same suggestion about the filesystemwatcher, and while it would likely work, it does somewhat feel like a cheap hack. That's not to say its a bad idea, but if there is a better fool proof way to handle things, then that is what I am after.
So anyways, I spent pretty much ALL DAY working on this stupid issue, which annoys me because it is all because of a bug that should not exist in the first place. However, I was able to make a successful working demo that does infact do exactly what I need, and still uses remoting, just ICP remoting instead of TCP remoting. This means a firewall won't get in the way.
You can download the source code here
Please note the following things:
1) It is a VS 2008 project, however it targets .NET 2.0 framework. If you don't have VS 2008, sorry. (Or go download it. its way better)
2) I have todo comments in the exception handlers for setting up the remoting because I am not quite sure what the best course of action is in the event remoting fails for any reason and I am not sure how I want it to act yet.
3) The ZIP file doesn't contain the BIN or OBJ folders, so you need to build the project after opening it
4) After you build the project, test it OUTSIDE the IDE. This is because when you run inside the IDE, it tacks ".vshost.exe" to the end of your assemblies name, which causes the name matching not to work so it doesn't see the second instance as the same program.
There may be some revisions over the next few days as I continute to test, so I will update this thread if that happens.
VBDT, it is a clever method of doing things, but this application goes out to hundreds of users, and I couldn't do something like that because that creates non standard behavior in Windows.
If I had an app that kept erasing whatever I might have copied to clipboard, it would drive me mad.
I agree, I would not use it also but I though it might help you with some new ideas.
Rating is a way of saying thank you. Don't forget to rate always!