【问题标题】:communicate with another process (same source code) via wndProc in WPF通过 WPF 中的 wndProc 与另一个进程(相同的源代码)通信
【发布时间】:2019-12-11 06:15:09
【问题描述】:

我正在通过 WPF 中的HwndSource 实现与另一个进程的通信。

我希望我的程序(我们称之为 A)与 Windows 上另一个进程上的我的程序(也称为 A)进行通信。
下面的图片将帮助我解释我一直在做什么以及我想要实现什么。

两个窗口是完全相同的程序(只是背景颜色不同),并且它们彼此完全知道它们的句柄。如果我注意到两个进程具有相同的标题,那么我调用 sendMessage,它在 user32.dll 中定义,带有 Message -Raw 输入消息常量,可以全局泵送消息 -0x0100WM_KEYDOWN

// if same program - but different process
IntPtr lpData = Marshal.StringToHGlobalAnsi("Hi, Im Sender");
MessageBox.Show(Marshal.PtrToStringAnsi(lpData));
DisplayAnotherProcessHandle.Text = p.MainWindowHandle.ToString();

SendMessage(p.MainWindowHandle, WM_RawInput_key, IntPtr.Zero, lpData);

我成功地将WM_RawInput_KeyIntPtr.Zero传递给对手程序。但我对 lParam 有疑问。我无法获取 lParam 数据。我试图用Marshal.StringToBSTRMarshal.StringToAuto 来编组c# string ...但在下面的方法中我只得到Empty.String

private IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
{
  if (msg != WM_RawInput_key) return IntPtr.Zero;

  if (wParam == IntPtr.Zero)  // <-- opponent process hit this conditional syntax nicely.
  {
    var str = Marshal.PtrToStringAnsi(lParam);  // but I'm failed pass lParam, or Marshalling lParam I don't know ...
    MessageBox.Show(str);
    DisplaySucceedOrNot.Text = str;
  }

  return IntPtr.Zero;
}

不知道IntPtr lParam表示的是哪个内存参数。我什至不知道这种技术是否有效。我需要你的帮助。感谢阅读

以下源代码是我的示例程序的完整源代码。

<Window x:Class="wndProc_IPC_WPF.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:wndProc_IPC_WPF"
        mc:Ignorable="d"
        Title="WndProc-IPC-WPF" Height="450" Width="800">
    <StackPanel>
        <Button Name="AddHandler" Width="Auto" Height="100" Content="Add Handler to This Handle" Click="AddHandler_Click"></Button>
        <Button Name="CreateNewProcess" Width="Auto" Height="100" Content="Create New Process Window" Click="CreateNewProcess_Click"></Button>
        <Button Width="Auto" Height="100" Content="SendMessage To Another Process" Name="SendMessageBtn" Click="SendMessage_Click"></Button>
        <StackPanel Orientation="Horizontal">
            <TextBlock Text="MyHandle : "></TextBlock>
            <TextBlock x:Name="DisplayMyProcessHandle" Margin="50 0 0 0"></TextBlock>
        </StackPanel>
        <StackPanel Orientation="Horizontal">
            <TextBlock Text="Another Handle : "></TextBlock>
            <TextBlock x:Name="DisplayAnotherProcessHandle" Margin="50 0 0 0"></TextBlock>
        </StackPanel>
        <StackPanel Orientation="Horizontal">
            <TextBlock Text="Succed?"></TextBlock>
            <TextBlock x:Name="DisplaySucceedOrNot" Margin="50 0 0 0"></TextBlock>
        </StackPanel>

    </StackPanel>
</Window>
public partial class MainWindow : Window
{
    [DllImport("user32.dll")]
    private static extern bool SendMessage(IntPtr hWnd, UInt32 Msg, IntPtr wParam, IntPtr lParam);

    string AppName;
    public static UInt32 WM_RawInput_key = 0x0100;

    public MainWindow()
    {
        InitializeComponent();
        AppName = this.Title;
    }

    private IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
    {
        if (msg != WM_RawInput_key) return IntPtr.Zero;

        if (wParam == IntPtr.Zero)
        {
            var str = Marshal.PtrToStringAnsi(lParam);
            MessageBox.Show(str);
            DisplaySucceedOrNot.Text = str;
        }

        return IntPtr.Zero;
    }

    private void CreateNewProcess_Click(object sender, RoutedEventArgs e)
    {
        if (Process.GetProcessesByName(AppName).Length > 1)
        {
            MessageBox.Show("Already 2 Process Exist.");
            return;
        }
        var path = (System.Reflection.Assembly.GetExecutingAssembly().GetName().CodeBase);
        Process p = new Process();
        p.StartInfo.FileName = path;
        p.Start();
    }

    private void SendMessage_Click(object sender, RoutedEventArgs e)
    {
        foreach (var p in Process.GetProcessesByName(AppName))
        {
            if (p.MainWindowHandle == Process.GetCurrentProcess().MainWindowHandle) continue; 

            {
                IntPtr lpData = Marshal.StringToHGlobalAnsi("Hi, Im Sender");
                MessageBox.Show(Marshal.PtrToStringAnsi(lpData));
                DisplayAnotherProcessHandle.Text = p.MainWindowHandle.ToString();

                SendMessage(p.MainWindowHandle, WM_RawInput_key, IntPtr.Zero, lpData);

            }
        }
    }

    private void AddHandler_Click(object sender, RoutedEventArgs e)
    {
        HwndSource source = HwndSource.FromHwnd(Process.GetCurrentProcess().MainWindowHandle);
        source.AddHook(new HwndSourceHook(WndProc));
    }

}

【问题讨论】:

    标签: c# wpf windows


    【解决方案1】:

    wparamlparam 只是指向本地进程内存的指针,其他进程无法访问。 wparam 在您的示例中运行良好,因为您只处理指针值而不是它指向的数据。

    出于类似目的,我使用了WM_COPYDATA 消息,您提供了指向COPYDATASTRUCT 的指针。在那里,您还可以封装复杂的数据结构,这些数据结构可以在您的接收过程中以只读方式处理。

    Microsoft 在这里提供了一个有用的示例: https://docs.microsoft.com/en-us/windows/win32/dataxchg/using-data-copy

    【讨论】:

      【解决方案2】:

      谢谢 Mihaeru,实际上我是在发布我的问题后在 Wpf 中寻找 sendMessage/wndProcWM_COPYDATA 的示例来源。像 Raw-Input 消息WM_KEYDOWNWM_COPYDATA 也提供全局消息泵(对吗?,我不知道旧窗口的确切理论...-_-;;)

      我失败了很多次。我发现我应该用字符串长度分配cbData。如果我添加属性 [MarshalAs(UnmanagedType.LpWStr)] 每个字符 2 个字节,那么我需要将 string.length*2 分配给 cbData。

      我成功将消息传递给对方进程。

      以下来源是答案来源

              [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
              public struct COPYDATASTRUCT
              {
                  public IntPtr dwData;
                  public int cbData;
                  [MarshalAs(UnmanagedType.LPStr)]
                  public string lpData;
              }
      
              [DllImport("user32.dll")]
              private static extern bool SendMessage(IntPtr hWnd, UInt32 Msg, IntPtr wParam, ref COPYDATASTRUCT lParam);
      
              string AppName;
              public static UInt32 WM_COPYDATA = 0x004A;
      
              public MainWindow()
              {
                  InitializeComponent();
                  AppName = this.Title;
              }
      
              private IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
              {
                  if (msg != WM_COPYDATA) return IntPtr.Zero;
      
                  if (wParam == IntPtr.Zero)
                  {
      
                      COPYDATASTRUCT cd = new COPYDATASTRUCT();
                      cd = (COPYDATASTRUCT)Marshal.PtrToStructure(lParam, typeof(COPYDATASTRUCT));
                      MessageBox.Show(cd.lpData);
                      DisplaySucceedOrNot.Text = cd.lpData;
                  }
                  return IntPtr.Zero;
              }
      
              private void CreateNewProcess_Click(object sender, RoutedEventArgs e)
              {
                  if (Process.GetProcessesByName(AppName).Length > 1)
                  {
                      MessageBox.Show("Already 2 Process Exist.");
                      return;
                  }
                  var path = (System.Reflection.Assembly.GetExecutingAssembly().GetName().CodeBase);
                  Process p = new Process();
                  p.StartInfo.FileName = path;
                  p.Start();
              }
      
              private void SendMessage_Click(object sender, RoutedEventArgs e)
              {
                  foreach (var p in Process.GetProcessesByName(AppName))
                  {
                      if (p.MainWindowHandle == Process.GetCurrentProcess().MainWindowHandle) continue;
      
                      {
                          IntPtr lpData = Marshal.StringToHGlobalAnsi("Hi, Im Sender");
      
                          COPYDATASTRUCT cd = new COPYDATASTRUCT();
                          cd.lpData = "Hi, Im Sender";
                          cd.dwData = p.MainWindowHandle;
                          cd.cbData = cd.lpData.Length+1;
      
                          MessageBox.Show(cd.lpData);
                          DisplayAnotherProcessHandle.Text = p.MainWindowHandle.ToString();
                          SendMessage(p.MainWindowHandle, WM_COPYDATA, IntPtr.Zero, ref cd);
                      }
                  }
              }
      
              private void AddHandler_Click(object sender, RoutedEventArgs e)
              {
                  HwndSource source = HwndSource.FromHwnd(Process.GetCurrentProcess().MainWindowHandle);
      
                  source.AddHook(new HwndSourceHook(WndProc));
                  DisplayMyProcessHandle.Text = Process.GetCurrentProcess().MainWindowHandle.ToString();
              }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2016-11-09
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-01-08
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多