Single instance app communication

  • Thread starter Peter Morris [CapableObjects]
  • Start date
P

Peter Morris [CapableObjects]

I have an app which uses a mutex to run as a single instance application.
This is because it exclusively locks a specific resource. When I now need
to do is to have a file association with my application so that when a file
of a certain type is double-clicked my application deals with it (processes
it using the locked resource).

If the app is not running then it is simple enough to check the application
parameters when the app starts, but how do I deal with the scenario where my
app is already running? Windows will launch a second instance of my app
which will then immediately terminate, how can I see a message to the
already running application passing the filename so that it can deal with
the operation?

Thanks

Pete
 
M

Marc Gravell

perhaps use any of {sockets, remoting, wcf, some other technology} to
pass the request onto the active app?

Marc
 
P

Peter Morris [CapableObjects]

sockets

I don't want to raise alarms on firewalls.


I presume I can easily attach to + communicate with an existing instance
then? It's been years since I have looked at remoting.



Pete
 
M

Marc Gravell

I don't want to raise alarms on firewalls.
local named pipes, then? You can do this with remoting using the
IpcChannel
I presume I can easily attach to...
"can" -yes
"easily" - subjective

I have implemented a remoting hook to do exactly this, but I never
found it intuitive. As such, I'm loathe to post code, because I simply
can't say that I can answer questions on it any more...
If your environment permits, I'd /highly/ recommend WCF; apart from
being far clearer and cleaner (IMO), it is trivial to use named pipes
over WCF, and unlike remoting the configuration is clearly partitioned
for client and server, allowing the same app to use either (or both -
even talking to itslef).

Marc
 
I

Ignacio Machin \( .NET/ C# MVP \)

Hi,


You would have to detect if another instance is already running (you
apparentely do this now), if so you need to take the filename passed as
parameter and send it to the first instance. You could use a windows message
(the easiest solution IMHO) you need the hWnd of it (IIRC
Process.MainWindowHandle will get it) and send a custom made message with
the path of the file
 
F

Fredo

Another way to do it is to use the WM_COPYDATA message. I use this in one of
my apps. The code is below. Simply replace "MyApplicationName" with the name
of your application's process. In the main(), call
OtherInstancesAreRunning() with the arguments as a string. If
OtherInstancesAreRunning() returns true, simply exit the application. When
OtherInstancesAreRunning() is true, it will send a WM_COPYDATA message to
the window of the first app process.

[DllImport("user32.dll", CharSet = CharSet.Auto)]
public static extern IntPtr SendMessage(IntPtr hWnd, uint msg, int wParam,
IntPtr lParam);

[StructLayout(LayoutKind.Sequential)]
public struct COPYDATA
{
public uint dwData;
public uint cbData;
public IntPtr lpData;
}

private static bool OtherInstancesAreRunning(string argData)
{
Process[] processes = Process.GetProcessesByName("MyApplicationName");
bool othersFound = false;
foreach(Process proc in processes)
{
// We just check for the first that doesn't match.
// If there's more than one, there's not much we can
// do. Something must be hung.
if (proc.Id != Process.GetCurrentProcess().Id)
{
othersFound = true;
if (argData != null)
{
SendWMData(proc.MainWindowHandle, argData);
break;
}
}
}
return othersFound;
}

private static void SendWMData(IntPtr hwnd, string argData)
{
COPYDATA cd = new COPYDATA();
cd.dwData = 0;
cd.cbData = (uint) (argData.Length + 1);
cd.lpData = Marshal.StringToHGlobalAnsi(argData);
IntPtr lpPtr = Marshal.AllocHGlobal(Marshal.SizeOf(cd));

Marshal.StructureToPtr(cd, lpPtr, true);
// Send WM_COPYDATA
SendMessage(hwnd, 0x004A, 0, lpPtr);

Marshal.FreeHGlobal(lpPtr);

// Show window normal.
ShowWindow(hwnd, 1);
}

The code below handles receiving the arguments back from the WM_COPYDATA.
This should be in your application's main window:

protected override void WndProc(ref Message m)
{
if (m.HWnd == Handle && m.Msg == 0x004A)
{
MainApp.COPYDATA cd = (MainApp.COPYDATA)
Marshal.PtrToStructure(m.LParam, typeof(MainApp.COPYDATA));
string argData = Marshal.PtrToStringAnsi(cd.lpData);

HandleArguments(argData)
}
base.WndProc (ref m);
}

The only real problem with this code is that the code for finding the first
instance is kind of weak. If you start several instances at once, it
probably won't work as expected. But if there's a second or two between
launches, it'll work fine. It's never been a problem for me, so I've never
bothered to fix it.
 
P

Peter Morris [CapableObjects]

This looks like a 3.5 thing, is it? I should have mentioned that I am on
2.0!

Pete
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top