LOGO OA教程 ERP教程 模切知识交流 PMS教程 CRM教程 开发文档 其他文档  
 
网站管理员

记录一下 WPF进程 SendMessage 发送窗口消息进行进程间通信,存在进程权限无法接受消息的问题

freeflydom
2025年10月13日 8:8 本文热度 169

前言

最近在接手一个同事写的WPF项目,是使用.NetFramwork 开发的,使用的进程间通信没有使用我们之前封装的基于WebSocket的封装组件的,而是使用Win32的方式:发送端用的Windows Api:SendMessage ,接受端使用的是 钩子监听windows 的消息回传。

相信很多做桌面应用的,这种通信应该都是很常用,并且见怪不怪的。可是可能很多没有注意到进程权限的情况,这种通信存在有坑,并且这个坑还埋的挺深的。

遇到的问题

由于该WPF的项目的启动方式存在很多方式,如果桌面点击的方式(普通权限的),右键管理员启动的方式(管理员权限的),开机自启的方式(System权限降权的方式,普通权限),OTA之后启动(管理员权限),这样就会出现该进程窗口可能启动后的权限是不可预见的,并且用户是可以随意的变更用户权限去启动。然而,在一次测试中,做了升级后,启用了该应用,其他跟它通信的进程就无法跟该进程通信的。很诡异,只要是OTA之后,其他进程就无法通信,开机之后(普通权限)就可以通信。观察了日志,又没有报什么异常。

复现问题

一、创建一个WPF消息 发送端

<Window x:Class="FramworkSender.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:FramworkSender"
        mc:Ignorable="d"
        Title="FramworkSender" Height="450" Width="800">
    <Grid>
        <Button Width="100" Height="100" Content="发送" Click="ButtonBase_OnClick"></Button>
    </Grid>
namespace FramworkSender
{
    /// <summary>
    /// MainWindow.xaml 的交互逻辑
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
            Loaded += MainWindow_Loaded;
        }
        private void MainWindow_Loaded(object sender, RoutedEventArgs e)
        {
        }
        private void ButtonBase_OnClick(object sender, RoutedEventArgs e)
        {
            // 获取接收窗口的句柄

            IntPtr hwnd = FindWindow(null, "FramworkReceieve");
            if (hwnd == IntPtr.Zero)
            {
                MessageBox.Show("找不到窗口");
            }
            else
            {
                SendMessageString(hwnd, "123");
            }
        }
        #region RegisterWindow
        [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        private static extern uint RegisterWindowMessage(string lpString);
        private uint _customMessageId;
        #endregion
        #region CopyData
        [DllImport("user32.dll")]
        public static extern IntPtr SendMessage(IntPtr hWnd, int Msg, IntPtr wParam, ref COPYDATASTRUCT lParam);
        [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
        public static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
        public const int WM_COPYDATA = 0x004A;
        // 定义 COPYDATASTRUCT 结构
        [StructLayout(LayoutKind.Sequential)]
        public struct COPYDATASTRUCT
        {
            public IntPtr dwData;
            public int cbData;
            public IntPtr lpData;
        }
        public static void SendMessageString(IntPtr hWnd, string message)
        {
            if (string.IsNullOrEmpty(message)) return;
            byte[] messageBytes = Encoding.Unicode.GetBytes(message + '\0');
            COPYDATASTRUCT cds = new COPYDATASTRUCT();
            cds.dwData = IntPtr.Zero;
            cds.cbData = messageBytes.Length;
            cds.lpData = Marshal.AllocHGlobal(cds.cbData);
            Marshal.Copy(messageBytes, 0, cds.lpData, cds.cbData);
            try
            {
                var result = SendMessage(hWnd, WM_COPYDATA, IntPtr.Zero, ref cds);
            }
            finally
            {
                //释放分配的内存,即使发生异常也不会泄漏资源
                Marshal.FreeHGlobal(cds.lpData);
            }
        }
        #endregion
    }
}

 

二、创建一个WPF 消息 的接收端

<Window x:Class="FramworkReceieve.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:FramworkReceieve"
        mc:Ignorable="d"
        Title="FramworkReceieve" Height="450" Width="800">
    <Grid>
        <StackPanel Orientation="Horizontal">
            <TextBlock Text="接收到的数据:"/>
            <TextBlock Text="" x:Name="txtMessage"/>
        </StackPanel>
        <Button Height="100" Width="100" Content="清空" Click="ButtonBase_OnClick"></Button>
    </Grid>
</Window>
    /// <summary>
    /// MainWindow.xaml 的交互逻辑
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
            Loaded += MainWindow_Loaded;
        }
        private uint _customMessageId;
        private HwndSource _hwndSource;
        private void MainWindow_Loaded(object sender, RoutedEventArgs e)
        {
            _customMessageId = RegisterWindowMessage("MyApp");
            // 获取窗口句柄并添加消息钩子
            _hwndSource = PresentationSource.FromVisual(this) as HwndSource;
            _hwndSource?.AddHook(WndProc);
        }
        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 16)]
        public string content;
        private IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
        {
            #region CopyData
            if (msg == WM_COPYDATA)
            {
                COPYDATASTRUCT cds = (COPYDATASTRUCT)Marshal.PtrToStructure(lParam, typeof(COPYDATASTRUCT));
                string receivedMessage = Marshal.PtrToStringUni(cds.lpData);
                this.Dispatcher.Invoke(() =>
                {
                    txtMessage.Text = receivedMessage;
                });
                handled = true;
            }
            #endregion
            return IntPtr.Zero;
        }
        #region  RegisterWindows
        [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        private static extern uint RegisterWindowMessage(string lpString);
        #endregion
        #region CopyData
        public const int WM_COPYDATA = 0x004A;
        // 定义 COPYDATASTRUCT 结构
        [StructLayout(LayoutKind.Sequential)]
        public struct COPYDATASTRUCT
        {
            public IntPtr dwData;
            public int cbData;
            public IntPtr lpData;
        }
        #endregion
        protected override void OnClosed(EventArgs e)
        {
            _hwndSource?.RemoveHook(WndProc);
            base.OnClosed(e);
        }
        private void ButtonBase_OnClick(object sender, RoutedEventArgs e)
        {
            txtMessage.Text = "";
        }
    }

 

三、结果

1、俩个都是管理员权限的,是可以接受到数据的

 

2、俩个进程都是普通权限的,是可以接受到数据

 3、发送端是管理员权限,接收端是 普通权限,是可以接受到数据

 

4、发送端是普通权限,接收端是 管理员权限,是接受不到数据

 

 总结:

1、进程间通信,最好使用无权限限制的方案

2、使用ChangeWindowMessageFilterEx 进行权限过滤

转自https://www.cnblogs.com/wuty/p/19137419


该文章在 2025/10/13 8:08:09 编辑过
关键字查询
相关文章
正在查询...
点晴ERP是一款针对中小制造业的专业生产管理软件系统,系统成熟度和易用性得到了国内大量中小企业的青睐。
点晴PMS码头管理系统主要针对港口码头集装箱与散货日常运作、调度、堆场、车队、财务费用、相关报表等业务管理,结合码头的业务特点,围绕调度、堆场作业而开发的。集技术的先进性、管理的有效性于一体,是物流码头及其他港口类企业的高效ERP管理信息系统。
点晴WMS仓储管理系统提供了货物产品管理,销售管理,采购管理,仓储管理,仓库管理,保质期管理,货位管理,库位管理,生产管理,WMS管理系统,标签打印,条形码,二维码管理,批号管理软件。
点晴免费OA是一款软件和通用服务都免费,不限功能、不限时间、不限用户的免费OA协同办公管理系统。
Copyright 2010-2025 ClickSun All Rights Reserved