C# 向/从两个正在运行的应用程序发送/接收消息

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/11358841/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-09 17:34:03  来源:igfitidea点击:

Send/Receive message To/From two running application

c#message

提问by Hamid

I have two applications called SENDERand RECEIVER.

我有两个名为SENDERRECEIVER 的应用程序。

RECEIVER will be launched by SENDER with the System.Diagnostics.Process.Startmethod

RECEIVER 将由 SENDER 使用以下System.Diagnostics.Process.Start方法启动

RECEIVER will be launched in hidden mode so it does not have a MainWindowHandle.

RECEIVER 将以隐藏模式启动,因此它没有MainWindowHandle.

Then we could use Win32.WM_COPYDATAin order send message to RECEIVER, but it needs MainWindowHandleso we can't.

然后我们可以使用Win32.WM_COPYDATAorder 向 RECEIVER 发送消息,但它需要MainWindowHandle所以我们不能。

What I need is ability to send and receive messages periodically by any method.

我需要的是通过任何方法定期发送和接收消息的能力。

I checked the following link for a manual about MainWindowHandle, but it didn't help:

我检查了以下链接以获取有关 的手册MainWindowHandle,但没有帮助:

Send message to a Windows process (not its main window)

向 Windows 进程(不是其主窗口)发送消息

One solution might be an object from System.Diagnostics.Processwhich might help us to send messages to a process.

一种解决方案可能是一个System.Diagnostics.Process可以帮助我们向进程发送消息的对象。

采纳答案by Oscar Foley

There are different ways to share information between 2 processes.

在两个进程之间共享信息有不同的方式。

First at all you have to think if both processes are going to be always in the same machine or not when your application scales up.

首先,您必须考虑当您的应用程序扩展时两个进程是否总是在同一台机器上。

Different Machines

不同的机器

  • Use TCP/UDP socket connection (Can be the quickest solution)
  • Use MSMQ
  • Use WebServices, WCF or Restful Web Service.
  • Reading from a common entry in a db. (Not recommended)
  • Named Pipes (Check this) (Named pipes can be in same machine or fly over a network)
  • 使用 TCP/UDP 套接字连接(可以是最快的解决方案)
  • 使用 MSMQ
  • 使用 WebServices、WCF 或 Restful Web 服务。
  • 从数据库中的公共条目读取。(不建议)
  • 命名管道(检查这个)(命名管道可以在同一台机器上或在网络上飞行)

Always in same machine.

总是在同一台机器上。

Preferred choice: MSMQ

首选:MSMQ

If I were you I would preserve the ability of having processes in different machines so I would use, as Maarten suggested, two windows services that uses MSMQ to communicate. Why?

如果我是你,我会保留在不同机器上拥有进程的能力,所以我会像 Maarten 建议的那样使用两个使用 MSMQ 进行通信的 Windows 服务。为什么?

  1. MSMQ allows you not to lose messages (in case RECEIVER is down)
  2. MSMQ allows you to have processes in same machine or in different machines
  3. Windows service give you the ability to start/stop the processes easily
  4. Windows service can me monitored my SNMP and in general they integrate easily with windows admin tools.
  1. MSMQ 允许您不会丢失消息(以防 RECEIVER 关闭)
  2. MSMQ 允许您在同一台机器或不同机器上拥有进程
  3. Windows 服务使您能够轻松启动/停止进程
  4. Windows 服务可以监控我的 SNMP,通常它们可以轻松地与 Windows 管理工具集成。

Second preferred choice: Restful Web Service

第二首选:Restful Web Service

If you don't want to use MSMQ I would use two Restful Web Service hosted in IIS to communicate both processes. It can be useful if you have an scenario where RECEIVER is not interested in messages from SENDER if they arrive late.

如果您不想使用 MSMQ,我将使用托管在 IIS 中的两个 Restful Web 服务来通信两个进程。如果您有一个场景,如果来自 SENDER 的消息迟到,则 RECEIVER 对它们不感兴趣,这会很有用。

回答by Maarten

I think MSMQis a good option.

我认为MSMQ是一个不错的选择。

回答by Birger S?rensen

Old question, I know.
Stumbled on it, as I have a somewhat similar task.
One app started from the another - it will end again, but nobody knows when.
1. app can start 2. again, but must not until previous instances of 2. has exited.
Always on same PC (and Windows).

老问题,我知道。
偶然发现它,因为我有一个有点类似的任务。
一个应用程序从另一个应用程序开始 - 它会再次结束,但没有人知道什么时候。
1. 应用程序可以再次启动 2. ,但必须直到 2. 的前一个实例退出。
始终在同一台 PC(和 Windows)上。

The simple thing is off hand to use the registry to set a value, when 2. program is running, and remove/reset it again when it exits.
1. app can check the registry to see if it is OK to start another instance of the 2. app.

简单的事情是在 2. 程序运行时使用注册表设置一个值,并在它退出时再次删除/重置它。
1.app可以检查注册表看是否可以启动另一个app的实例2.app。

You can also use the registry to pass values between the apps. Drawback is, that apps must poll the registry, instead of sending messages. Simpler but less effective.

您还可以使用注册表在应用程序之间传递值。缺点是,应用程序必须轮询注册表,而不是发送消息。更简单但效率较低。

So probably dependent on what it is needed for.

所以可能取决于它的用途。

回答by Martin

The CreateFromFile methods create a memory-mapped file from an existing file on disk. The following example creates a memory-mapped view of a part of an extremely large file and manipulates a portion of it.

CreateFromFile 方法从磁盘上的现有文件创建内存映射文件。下面的示例创建一个极大文件的一部分的内存映射视图并操作它的一部分。

using System;
using System.IO;
using System.IO.MemoryMappedFiles;
using System.Runtime.InteropServices;

class Program
{
    static void Main(string[] args)
    {
        long offset = 0x10000000; // 256 megabytes
        long length = 0x20000000; // 512 megabytes

        // Create the memory-mapped file.
        using (var mmf = MemoryMappedFile.CreateFromFile(@"c:\ExtremelyLargeImage.data", FileMode.Open,"ImgA"))
        {
            // Create a random access view, from the 256th megabyte (the offset)
            // to the 768th megabyte (the offset plus length).
            using (var accessor = mmf.CreateViewAccessor(offset, length))
            {
                int colorSize = Marshal.SizeOf(typeof(MyColor));
                MyColor color;

                // Make changes to the view.
                for (long i = 0; i < length; i += colorSize)
                {
                    accessor.Read(i, out color);
                    color.Brighten(10);
                    accessor.Write(i, ref color);
                }
            }
        }
    }
}

public struct MyColor
{
    public short Red;
    public short Green;
    public short Blue;
    public short Alpha;

    // Make the view brighter.
    public void Brighten(short value)
    {
        Red = (short)Math.Min(short.MaxValue, (int)Red + value);
        Green = (short)Math.Min(short.MaxValue, (int)Green + value);
        Blue = (short)Math.Min(short.MaxValue, (int)Blue + value);
        Alpha = (short)Math.Min(short.MaxValue, (int)Alpha + value);
    }
}

The following example opens the same memory-mapped file for another process.

以下示例为另一个进程打开相同的内存映射文件。

using System;
using System.IO.MemoryMappedFiles;
using System.Runtime.InteropServices;


class Program
{
    static void Main(string[] args)
    {
        // Assumes another process has created the memory-mapped file.
        using (var mmf = MemoryMappedFile.OpenExisting("ImgA"))
        {
            using (var accessor = mmf.CreateViewAccessor(4000000, 2000000))
            {
                int colorSize = Marshal.SizeOf(typeof(MyColor));
                MyColor color;

                // Make changes to the view.
                for (long i = 0; i < 1500000; i += colorSize)
                {
                    accessor.Read(i, out color);
                    color.Brighten(20);
                    accessor.Write(i, ref color);
                }
            }
        }
    }
}

public struct MyColor
{
    public short Red;
    public short Green;
    public short Blue;
    public short Alpha;

    // Make the view brigher.
    public void Brighten(short value)
    {
        Red = (short)Math.Min(short.MaxValue, (int)Red + value);
        Green = (short)Math.Min(short.MaxValue, (int)Green + value);
        Blue = (short)Math.Min(short.MaxValue, (int)Blue + value);
        Alpha = (short)Math.Min(short.MaxValue, (int)Alpha + value);
    }
}

回答by Paul Williams

For a process running on the same machine, probably the lightest weight solution is to use PostThreadMessage(). I'm really surprised no one gave this answer, it's old school Windows programming. The OP was very close. Observe:

对于在同一台机器上运行的进程,最轻量级的解决方案可能是使用 PostThreadMessage()。我真的很惊讶没有人给出这个答案,这是老派的 Windows 编程。OP非常接近。观察:

  • Every process has a main thread (nativethread).
  • The main thread has a message queue.
  • The main thread has a thread ID which is global to the system.
  • 每个进程都有一个主线程(本地线程)。
  • 主线程有一个消息队列。
  • 主线程有一个线程 ID,它对系统来说是全局的。

All of the ingredients are there, it's a matter of putting them together. Conceptually it's straightforward, the tricky part is communicating the RECEIVER's main thread ID to the SENDER. You have a few options:

所有的成分都在那里,这是把它们放在一起的问题。从概念上讲,它很简单,但棘手的部分是将 RECEIVER 的主线程 ID 传达给 SENDER。您有几个选择:

  1. From the SENDER, in Win32 you could dig out the thread ID from the RECEIVER's Thread Information Block. https://stackoverflow.com/a/8058710/420400
  2. When RECEIVER starts, you could save off the thread ID in its Process.StartInfo.Environment. It's really there, and it will be visible in SysInternals' Process Explorer - but you'll have difficulty getting at it. Again there is a Win32 solution for this. https://www.codeproject.com/Articles/25647/Read-Environment-Strings-of-Remote-Process
  3. When RECEIVER starts, you could save off the thread ID in shared memory.
  4. (Or something better...)
  1. 从 SENDER,在 Win32 中,您可以从 RECEIVER 的线程信息块中挖掘出线程 ID。 https://stackoverflow.com/a/8058710/420400
  2. 当 RECEIVER 启动时,您可以在其 Process.StartInfo.Environment 中保存线程 ID。它真的就在那里,并且可以在 SysInternals 的 Process Explorer 中看到——但是你很难理解它。同样有一个 Win32 解决方案。 https://www.codeproject.com/Articles/25647/Read-Environment-Strings-of-Remote-Process
  3. 当 RECEIVER 启动时,您可以将线程 ID 保存在共享内存中。
  4. (或者更好的东西......)

Options 1 & 2 seem like security exploits, so for this example I went with option 3 and shared the thread ID in a tiny memory mapped file.

选项 1 和 2 看起来像是安全漏洞,因此在本示例中,我使用选项 3 并在一个微小的内存映射文件中共享线程 ID。

The RECEIVER looks something like this

接收器看起来像这样

enum WM { USER = 0x400 }

class MyMessageFilter : IMessageFilter
{
    public bool PreFilterMessage(ref Message m)
    {
        if ((WM)m.Msg == WM.USER)
        {
            Console.WriteLine("WM_USER received.");
            return true;
        }

        return false;
    }
}

class RECEIVER : IDisposable
{
    MemoryMappedFile mmf;
    bool disposed = false;

    public void MyMessageLoop()
    {
        uint mainThreadId = GetCurrentThreadId();
        Console.WriteLine(mainThreadId);
        mmf = MemoryMappedFile.CreateNew(Constants.ThreadIDFileName, IntPtr.Size, MemoryMappedFileAccess.ReadWrite);
        using (var accessor = mmf.CreateViewAccessor(0, IntPtr.Size, MemoryMappedFileAccess.ReadWrite))
        {
            accessor.Write(0, mainThreadId);
        }
        Application.AddMessageFilter(new MyMessageFilter());
        Application.Run();
    }

    [DllImport("kernel32.dll")]
    static extern uint GetCurrentThreadId();

    // Implement IDisposable and ~RECEIVER() to delete the semaphore, omitted for brevity
    // https://docs.microsoft.com/en-us/dotnet/api/system.idisposable?view=netframework-4.7.2
    #region
    ...
    #endregion
}

And the SENDER looks something like this

而 SENDER 看起来像这样

enum WM { USER = 0x400 }

class Program
{
    static void Main(string[] args)
    {
        string procName = "RECEIVER";
        Process[] processes = Process.GetProcesses();

        Process process = (from p in processes
                           where p.ProcessName.ToUpper().Contains(procName)
                           select p
                          ).First();

        uint threadId;
        using (var mmf = MemoryMappedFile.OpenExisting(Constants.ThreadIDFileName, MemoryMappedFileRights.Read))
        using (var accessor = mmf.CreateViewAccessor(0, IntPtr.Size, MemoryMappedFileAccess.Read))
        {
            accessor.Read(0, out serviceThreadId);
        }

        PostThreadMessage(threadId, (uint)WM.USER, UIntPtr.Zero, IntPtr.Zero);
    }

    [return: MarshalAs(UnmanagedType.Bool)]
    [DllImport("user32.dll", SetLastError = true)]
    public static extern bool PostThreadMessage(uint threadId, uint msg, IntPtr wParam, IntPtr lParam);
}