I made an application that works this way. It uses Windows-messages to communicate. So in the seconde instance, you only need the Handle of the MainForm in the first instance. I saved this handle in a ClickOnce setting named hwnd.
using ProjectApplicationTemplate.Properties;
using System;
using System.Globalization;
using System.IO;
using System.Reflection;
using System.Runtime.Hosting;
using System.Runtime.InteropServices;
using System.Threading;
using System.Windows.Forms;
namespace ProjectApplicationTemplate
{
    static class Program
    {
        static Mutex mutex = new Mutex(true, guid());
        static string guid()
        {
            // http://stackoverflow.com/questions/502303/how-do-i-programmatically-get-the-guid-of-an-application-in-net2-0
            Assembly assembly = Assembly.GetExecutingAssembly();
            var attribute = (GuidAttribute)assembly.GetCustomAttributes(typeof(GuidAttribute), true)[0];
            return attribute.Value;
        }
        static int MainWindowHandle
        {
            get
            {
                Settings.Default.Reload();
                return Settings.Default.hwnd;
            }
            set
            {
                Settings sett = Settings.Default;
                sett.hwnd = value;
                sett.Save();
            }
        }
        public static string GetFileName()
        {
            ActivationArguments a = AppDomain.CurrentDomain.SetupInformation.ActivationArguments;
            // aangeklikt bestand achterhalen
            string[] args = a == null ? null : a.ActivationData;
            return args == null ? "" : args[0];
        }
        /// <summary>
        /// The main entry point for the application.
        /// </summary>
        [STAThread]
        static void Main()
        {
            if (mutex.WaitOne(TimeSpan.Zero, true))
            {
                #region standaard
                Application.EnableVisualStyles();
                Application.SetCompatibleTextRenderingDefault(false);
                #endregion
                MainForm frm = new MainForm();
                MainWindowHandle = (int)frm.Handle;
                Application.Run(frm);
                MainWindowHandle = 0;
                mutex.ReleaseMutex();
            }
            else
            {
                int hwnd = 0;
                while (hwnd == 0)
                {
                    Thread.Sleep(5);
                    hwnd = MainWindowHandle;
                }
                Win32.CopyDataStruct cds = new Win32.CopyDataStruct();
                try
                {
                    string data = GetFileName();
                    cds.cbData = (data.Length + 1) * 2; // number of bytes
                    cds.lpData = Win32.LocalAlloc(0x40, cds.cbData); // known local-pointer in RAM
                    Marshal.Copy(data.ToCharArray(), 0, cds.lpData, data.Length); // Copy data to preserved local-pointer
                    cds.dwData = (IntPtr)1;
                    Win32.SendMessage((IntPtr)hwnd, Win32.WM_COPYDATA, IntPtr.Zero, ref cds);
                }
                finally
                {
                    cds.Dispose();
                }
            }
        }
    }
}
And in your MainForm
using System;
using System.Data;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Security.Permissions;
using System.Windows.Forms;
namespace ProjectApplicationTemplate
{
    public partial class MainForm : Form
    {
        public MainForm()
        {
            InitializeComponent();
            OpenFile(Program.GetFileName());
        }
        [PermissionSet(SecurityAction.Demand, Name = "FullTrust")]
        protected override void WndProc(ref Message m)
        {
            switch (m.Msg)
            {
                case Win32.WM_COPYDATA:
                    Win32.CopyDataStruct st = (Win32.CopyDataStruct)Marshal.PtrToStructure(m.LParam, typeof(Win32.CopyDataStruct));
                    string strData = Marshal.PtrToStringUni(st.lpData);
                    OpenFile(strData);
                    Activate();
                    break;
                default:
                    // let the base class deal with it
                    base.WndProc(ref m);
                    break;
            }
        }
        void OpenFile(string filename)
        {
            if (filename == "") return;
            if (!File.Exists(filename)) return;
            IDocument[] vensters = MdiChildren.Select(T => (IDocument)T).Where(T => T.CurrentFileName == filename).ToArray();
            if (vensters.Length == 0)
            {
                ChildForm frm = new ChildForm();
                frm.OpenFile(filename);
                frm.MdiParent = this;
                frm.Show();
            }
            else
            {
                vensters[0].Activate();
            }
        }
        private void fileMenu_DropDownOpening(object sender, EventArgs e)
        {
            IDocument active = (IDocument)ActiveMdiChild;
            if (active == null)
            {
                saveToolStripMenuItem.Enabled = false;
                saveAsToolStripMenuItem.Enabled = false;
                printToolStripMenuItem.Enabled = false;
                printSetupToolStripMenuItem.Enabled = false;
                printPreviewToolStripMenuItem.Enabled = false;
            }
            else
            {
                saveToolStripMenuItem.Enabled = active.Changed;
                saveAsToolStripMenuItem.Enabled = true;
                printToolStripMenuItem.Enabled = active.CanPrint;
                printSetupToolStripMenuItem.Enabled = active.CanPrint;
                printPreviewToolStripMenuItem.Enabled = active.CanPrint;
            }
            // fill the MRU-list
            tmiOnlangsGeopend.DropDownItems.Clear();
            string RecentFolder = Environment.GetFolderPath(Environment.SpecialFolder.Recent);
            string[] bestanden = Directory.GetFiles(RecentFolder).Where(T => T.EndsWith(".text.lnk")).ToArray();
            if (bestanden.Length == 0)
            {
                tmiOnlangsGeopend.DropDownItems.Add(new ToolStripMenuItem(Properties.Resources.NoRecent) { Enabled = false });
            }
            else
            {
                foreach (string bestand in bestanden.OrderBy(T => File.GetLastWriteTime(T)).Reverse())
                {
                    ToolStripMenuItem tmi = new ToolStripMenuItem(Path.GetFileNameWithoutExtension(bestand.Substring(0, bestand.Length - 4)));
                    tmi.Click += delegate { OpenFile(ResolveShortCut(bestand)); };
                    tmiOnlangsGeopend.DropDownItems.Add(tmi);
                }
            }
        }
        string ResolveShortCut(string shc)
        {
            // Add Reference -> COM -> Windows Script Host Object Model
            if (File.Exists(shc))
            {
                IWshRuntimeLibrary.WshShell shell = new IWshRuntimeLibrary.WshShell();
                IWshRuntimeLibrary.IWshShortcut link = (IWshRuntimeLibrary.IWshShortcut)shell.CreateShortcut(shc);
                return link.TargetPath;
            }
            else
            {
                return "";
            }
        }
    }
}
Win32.cs
using System;
using System.Runtime.InteropServices;
namespace ProjectApplicationTemplate
{
    public partial class Win32
    {
        public const int WM_COPYDATA = 0x004A;
        public struct CopyDataStruct : IDisposable
        {
            public IntPtr dwData;
            public int cbData;
            public IntPtr lpData;
            public void Dispose()
            {
                if (this.lpData != IntPtr.Zero)
                {
                    LocalFree(this.lpData);
                    this.lpData = IntPtr.Zero;
                }
            }
        }
        /// <summary>
        /// The SendMessage API
        /// </summary>
        /// <param name="hWnd">handle to the required window</param>
        /// <param name="Msg">the system/Custom message to send</param>
        /// <param name="wParam">first message parameter</param>
        /// <param name="lParam">second message parameter</param>
        /// <returns></returns>
        [DllImport("user32.dll")]
        public static extern int SendMessage(IntPtr hWnd, int Msg, IntPtr wParam, ref CopyDataStruct lParam);
        [DllImport("kernel32.dll", SetLastError = true)]
        public static extern IntPtr LocalAlloc(int flag, int size);
        [DllImport("kernel32.dll", SetLastError = true)]
        public static extern IntPtr LocalFree(IntPtr p);
    }
}
If anyone wants some more information about this:
http://pieterjan.pro/?a=Projecten_csharp_DrawIt.php
And this one is a c# template featuring loads of stuff:
- single-instance application with file-associations
- Localization (at runtime as well)
- MDI and interface for traversing the user commands
- Checking for updates
- Most-recently used list
http://pieterjan.pro/Projecten/csharp/ProjectApplicationTemplate.zip