管理対象アプリの1つのインスタンスから別のインスタンスにメッセージを送信する方法は?
-
06-07-2019 - |
質問
WinFormsアプリを持っています。既に実行中のインスタンスがある場合は&ユーザーが別のものを起動しようとすると、Application.Run()を呼び出す前にMutexをチェックして停止します。その部分はうまく機能します。私がやりたいのは、新しいプロセスを強制終了する前に、アプリの新しいインスタンスから(文字列形式のデータと共に)メッセージを既存のインスタンスに渡すことです。
PostMessageを呼び出してみましたが、実行中のアプリでメッセージを受け取りましたが、lparamで渡す文字列が失敗しています(はい、良い文字列を渡すように確認しましたで始まる)。どうすればこれを行うことができますか?
static class Program
{
[DllImport("user32.dll", CharSet = CharSet.Auto)]
public static extern bool PostMessage(int hhwnd, uint msg, IntPtr wparam, IntPtr lParam);
[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
static extern uint RegisterWindowMessage(string lpString);
private const int HWND_BROADCAST = 0xffff;
static uint _wmJLPC = unchecked((uint)-1);
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
{
_wmJLPC = RegisterWindowMessage("JumpListProjectClicked");
if (_wmJLPC == 0)
{
throw new Exception(string.Format("Error registering window message: \"{0}\"", Marshal.GetLastWin32Error().ToString()));
}
bool onlyInstance = false;
Mutex mutex = new Mutex(true, "b73fd756-ac15-49c4-8a9a-45e1c2488599_ProjectTracker", out onlyInstance);
if (!onlyInstance) {
ProcessArguments();
return;
}
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new MainForm());
GC.KeepAlive(mutex);
}
internal static void ProcessArguments()
{
if (Environment.GetCommandLineArgs().Length > 1)
{
IntPtr param = Marshal.StringToHGlobalAuto(Environment.GetCommandLineArgs()[1]);
PostMessage(HWND_BROADCAST, _wmJLPC, IntPtr.Zero, param);
}
}
}
その他、私のフォームで...
protected override void WndProc(ref Message m)
{
try
{
if (m.Msg == _wmJLPC)
{
// always returns an empty string
string param = Marshal.PtrToStringAnsi(m.LParam);
// UI code omitted
}
}
catch (Exception ex)
{
HandleException(ex);
}
base.WndProc(ref m);
}
解決
グレッグ、
StringToHGlobalAutoによって作成されたアンマネージポインターは、それを作成したプロセス空間でのみ有効です。参照するメモリは他のプロセスからアクセスできません。
1つのアプリから別のアプリにデータを渡すには、WM_COPYDATAメッセージでSendMessage()を使用します。
スコット
他のヒント
コードをもう一度確認します。 StringToHGlobalAutoを使用して文字列を作成しています(おそらくUnicodeで終わる可能性があります)。次に、Unicodeを使用していないPtrToStringAnsiを呼び出しています。
このソリューションが機能しない場合、いくつかのオプションがあります。 IPC(プロセス間通信)を探すことでそれらについて読むことができます。
私が使用した1つの方法は、すばやく簡単だったため、必要なコンテンツを含む既知のファイルを作成することです。名前付きイベントでそのファイルの使用をロックし、「所有者」に伝えます。別の名前付きイベントを設定してファイルが変更されたアプリ。 「所有者」イベントを時々チェックするか、ワーカースレッドを起動して監視します。
繰り返しますが、IPCには多くのフレーバーがあります。これらのアイデアがうまくいかない場合は、探し続けてください。
これは簡単な方法です。私はコードを実行していませんが、あなたはアイデアを得ます
class Program
{
static Thread listenThread;
static void Main(string[] args)
{
try
{
using (Mutex mutex = new Mutex(true, "my mutex"))
{
listenThread = new Thread(Listen);
listenThread.IsBackground = true;
listenThread.Start();
}
}
catch (ApplicationException)
{
using (Mutex mutex = Mutex.OpenExisting("my mutex"))
{
mutex.WaitOne();
try
{
using (NamedPipeClientStream client = new NamedPipeClientStream("some pipe"))
{
using (StreamWriter writer = new StreamWriter(client))
{
writer.WriteLine("SomeMessage");
}
}
}
finally
{
mutex.ReleaseMutex();
}
}
}
}
static void Listen()
{
using (NamedPipeServerStream server = new NamedPipeServerStream("some pipe"))
{
using (StreamReader reader = new StreamReader(server))
{
for (; ; )
{
server.WaitForConnection();
string message = reader.ReadLine();
//Dispatch the message, probably onto the thread your form
// was contructed on with Form.BeginInvoke
}
}
}
}
最近では、クロスプロセス通信を行うためのはるかに単純で最新の方法があります。特に、WCFをチェックしてください。
私は学習曲線が少しだけあることを認めますが。一度それを理解すると、それは本当に簡単です。プログラムですべてを行うこともできるため、構成の混乱を心配する必要はありません。
完全な例が欠落しているようです。動作する例を実行するのに少し苦労しました。したがって、これは、複数回実行した場合の単一インスタンスアプリケーションの最小実装であり、メッセージ(最初のコマンドライン引数)を最初のインスタンスに配信します。私のような人が完全な実例を必要とするなら、これは素晴らしいスタートです:
using System;
using System.IO;
using System.IO.Pipes;
using System.Threading;
namespace MutexApp
{
class Program
{
private const string PIPE_NAME = "MY_PIPE"; // Name of pipe
private const string MUTEX_NAME = "MY_MUTEX"; // Mutex name
static void Main(string[] args)
{
string message = "NO MESSAGE";
if (args.Length > 0) // If we have a parameter to the .exe get it
message = args[0];
bool firstinstance = false;
Mutex mutex = new Mutex(true, MUTEX_NAME, out firstinstance);
if (firstinstance) // We are the first instance of this process
{
Console.WriteLine("First instance started");
Console.WriteLine("Message: " + message);
Console.WriteLine("Waiting for messages (ctrl+c to break)...");
while (true) { ProcessNextClient(); } // Unfinite loop that listens for messages by new clients
}
else // This process is already running, parse message to the running instance and exit
{
{
try
{
using (NamedPipeClientStream client = new NamedPipeClientStream(PIPE_NAME)) // Create connection to pipe
{
client.Connect(5000); // Maximum wait 5 seconds
using (StreamWriter writer = new StreamWriter(client))
{
writer.WriteLine(message); // Write command line parameter to the first instance
}
}
} catch (Exception ex)
{
Console.WriteLine("Error: "+ex.Message);
}
}
}
mutex.Dispose();
}
private static void ProcessNextClient()
{
try
{
NamedPipeServerStream pipeStream = new NamedPipeServerStream(PIPE_NAME, PipeDirection.InOut, NamedPipeServerStream.MaxAllowedServerInstances); // Create Server pipe and listen for clients
pipeStream.WaitForConnection(); // Wait for client connection
using (StreamReader reader = new StreamReader(pipeStream)) // Read message from pipe stream
{
string message = reader.ReadLine();
Console.WriteLine("At " + DateTime.Now.ToLongTimeString()+": " + message); // Print message on screen
}
}
catch (Exception e)
{
Console.WriteLine("Error: " + ex.Message);
}
}
}
}