13

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

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

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

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

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

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

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

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

4

5 回答 5

19

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

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

不同的机器

  • 使用 TCP/UDP 套接字连接(可以是最快的解决方案)
  • 使用 MSMQ
  • 使用 WebServices、WCF 或 Restful Web 服务。
  • 从数据库中的公共条目中读取。(不建议)
  • 命名管道(检查这个)(命名管道可以在同一台机器上或飞过网络)

总是在同一台机器上。

首选:MSMQ

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

  1. MSMQ 允许您不丢失消息(以防 RECEIVER 关闭)
  2. MSMQ 允许您在同一台机器或不同机器中拥有进程
  3. Windows 服务使您能够轻松启动/停止进程
  4. Windows 服务可以让我监控我的 SNMP,并且通常它们很容易与 Windows 管理工具集成。

第二首选:Restful Web Service

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

于 2012-07-06T09:15:20.037 回答
3

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

  • 每个进程都有一个主线程(本机线程)。
  • 主线程有一个消息队列。
  • 主线程具有系统全局的线程 ID。

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

  1. 在 SENDER 中,在 Win32 中,您可以从 RECEIVER 的线程信息块中挖掘出线程 ID。 https://stackoverflow.com/a/8058710/420400
  2. 当 RECEIVER 启动时,您可以将线程 ID 保存在其 Process.StartInfo.Environment 中。它确实存在,并且可以在 SysInternals 的 Process Explorer 中看到——但你很难理解它。同样有一个 Win32 解决方案。 https://www.codeproject.com/Articles/25647/Read-Environment-Strings-of-Remote-Process
  3. 当 RECEIVER 启动时,您可以将线程 ID 保存在共享内存中。
  4. (或者更好的东西......)

选项 1 和 2 看起来像是安全漏洞,所以对于这个例子,我选择了选项 3,并在一个很小的内存映射文件中共享线程 ID。

接收器看起来像这样

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
}

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);
}
于 2020-05-05T00:35:23.060 回答
1

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);
    }
}

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

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);
    }
}
于 2018-12-03T09:06:13.920 回答
1

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

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

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

所以可能取决于它需要什么。

于 2018-07-31T14:27:51.383 回答
-2

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

于 2012-07-06T08:58:37.523 回答