0

I have a WPF application that is started with no parameters or flags. In the App.xaml.cs I added an OnStartUp handler that attempts to do some IPC to another instance of the application IF it executed with parameters. For example, my main application might be started by simply executing mainApp, which would load the main windows. Then I might later execute mainApp msg bob some_message which would pass the condition in OnStartUp and send "msg bob some_message" to the up and running application to handle in its WndProc override.

Code in App.xaml.cs:

private void OnStartUp(object sedner, StartupEventArgs e)
{
   Process localProcess = Process.GetCurrentProcess();

   foreach (Process process in Process.GetProcessesByName(localProcess.ProcessName))
   {
      if (process.Id != localProcess.Id)
      {
         NativeMethods.SendMessage(process.MainWindowHandle, NativeMethods.HWND_IPC, IntPtr.Zero, IntPtr.Zero);
         Environment.Exit(0);
      }
   }
}

Code in main window codebehind:

public IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr iParam, ref bool handled)
{
   if (msg == NativeMethods.HWND_IPC)
   {
      MessageBox.Show("Message Received!");
   }

   return IntPtr.Zero;
}

internal class NativeMethods
{
   public const int HWND_BROADCAST = 0xffff;
   public const int HWND_IPC = 0xfffe;
   public static readonly int WM_IPC = RegisterWindowMessage("WM_IPC");
   public static readonly int WM_SHOWME = RegisterWindowMessage("WM_SHOWME");
   [DllImport("user32")]
   public static extern bool PostMessage(IntPtr hwnd, int msg, IntPtr wparam, IntPtr lparam);
   [DllImport("user32.dll")]//, CharSet = CharSet.Auto, SetLastError = true)]
   public static extern IntPtr SendMessage(IntPtr hwnd, uint Msg, IntPtr wParam, IntPtr lParam);
   [DllImport("user32")]
   public static extern int RegisterWindowMessage(string message);
}

I've tried this with many permutation of using PostMessage, SendMessage, just using static int messages, actually invoking the RegisterWindowMessage. Nothing seems to work.

Additionally, I would like to not just be able to specify a specific message, but also additional dynamic details like a username and some message text.

kmarks2
  • 4,755
  • 10
  • 48
  • 77
  • possible duplicate of [How to handle WndProc messages in WPF?](http://stackoverflow.com/questions/624367/how-to-handle-wndproc-messages-in-wpf) – Stefan Nov 13 '13 at 20:50

2 Answers2

3

You are sending message HWND_IPC (window handle?) instead of WM_IPC. HWND_IPC is with value 0xFFFE not in the range for user messages WM_USER+x.

You cannot transfer data in this way. There is a WM_COPYDATA which copies the given buffer from one processes private address space to that of the other...

I think you should use a named pipe to transfer data. SendMessage is so Win31 ;-)

  • I'm hoping I can circumvent the whole issue of writing a pipeserver. I think I should be able to in two or so very short methods accomplish this with native window messaging. – kmarks2 Nov 13 '13 at 20:58
  • 2
    I think the Named pipes solution is the most convenient: You set up a "server" stream and wait for some "client" to connect. Then you transfer the data just as you would through any other stream. Here's NamedPipeServerStream: http://msdn.microsoft.com/en-us/library/system.io.pipes.namedpipeserverstream.aspx And this is NamedPipeClientStream : http://msdn.microsoft.com/en-us/library/system.io.pipes.namedpipeclientstream.aspx The code example there pretty much covers it. – Frank Pfattheicher Nov 13 '13 at 21:08
  • Checking it out as we speak. Thanks for the links! – kmarks2 Nov 13 '13 at 21:11
1

I thought you'll need the override, although I am not sure it exists in a WPF view:

try this:

protected override void WndProc( etc...

[UPDATE] You cannot override WndProc in the Window class of WPF this way.

How to handle WndProc messages in WPF?

Will explain that you'll need a hook:

    protected override void OnSourceInitialized(EventArgs e)
    {
        base.OnSourceInitialized(e);
        HwndSource source = PresentationSource.FromVisual(this) as HwndSource;
        source.AddHook(WndProc);
    }

Putting this together:

In your App.xml.cs you'll initialize your App startup arguments. It's easiest to create some static properties. Keep them simple, no collections to avoid multithreading collapses later on:

/// <summary>
/// Interaction logic for App.xaml
/// </summary>
public partial class App : Application
{
    public static string SomeValue { get; private set; }

    protected override void OnStartup(StartupEventArgs e)
    {
        base.OnStartup(e);
        SomeValue = e.Args[0];
    }
}

Now you are able to access SomeValue throughout your code by just calling App.SomeValue.

Then; a WndProc has to be implemented into a window. A window has a message pump. Without the a message pump you just can't send messages (you could create your own, but that's too much)

So in a window, for example MainWindow, implement the override:

public partial class MainWindow : Window
{
    public MainWindow()
    {  
        InitializeComponent();
    }

    protected override void OnSourceInitialized(EventArgs e)
    {
        base.OnSourceInitialized(e);
        HwndSource source = PresentationSource.FromVisual(this) as HwndSource;
        source.AddHook(WndProc);
    }

    public IntPtr WndProc(IntPtr hwnd, int msg, 
                          IntPtr wParam, IntPtr iParam, ref bool handled)
    {
        if (msg == NativeMethods.HWND_IPC)
            MessageBox.Show("Message Received!");

        return IntPtr.Zero;
    }
}

internal class NativeMethods
{

etc. etc.

The only thing left to implement is the moment to fire the message, but since it is IPC, you basically could send it from anywhere, provided you have a handle to the target window.

Combine this info with Franks remarks about WM_COPYDATA and it is ready to go. For info on WM_COPYDATA:

http://msdn.microsoft.com/en-us/library/windows/desktop/ms649011%28v=vs.85%29.aspx

Hope it helps.

Community
  • 1
  • 1
Stefan
  • 17,448
  • 11
  • 60
  • 79
  • Thanks for the reply. Doesn't that override have parameters like "`ref Message message`"? I am targetting framework 4, so I don't think I can use type `Message`. – kmarks2 Nov 13 '13 at 20:41
  • Thanks, just looked at the update. I tried something very similar to this previously, but the problem was the the WndProc was in my MainWindow class. Which whenever I would try to access through App.xaml.cs I would get "Object unused" exceptions. – kmarks2 Nov 13 '13 at 21:06
  • I cannot get it to compile. I am still using the App.xaml StartUp event handler, though. I see nothing for SourceInitialization. – kmarks2 Nov 13 '13 at 21:38
  • But will that not neuter my ability to handle command line parameters when my application is started in that way? I thought I could only get the necessary e.Args array when I handled the OnStartUp directly in App.xaml at the top level. – kmarks2 Nov 13 '13 at 21:43