惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

L
LINUX DO - 最新话题
cs.CV updates on arXiv.org
cs.CV updates on arXiv.org
PCI Perspectives
PCI Perspectives
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
K
KPMG report finds enterprise disconnect between AI and its ROI | CIO
H
Heimdal Security Blog
S
Security @ Cisco Blogs
N
News | PayPal Newsroom
J
Java Code Geeks
罗磊的独立博客
Security Archives - TechRepublic
Security Archives - TechRepublic
N
News and Events Feed by Topic
V
V2EX
WordPress大学
WordPress大学
Google Online Security Blog
Google Online Security Blog
N
News and Events Feed by Topic
www.infosecurity-magazine.com
www.infosecurity-magazine.com
月光博客
月光博客
AI
AI
小众软件
小众软件
The GitHub Blog
The GitHub Blog
MongoDB | Blog
MongoDB | Blog
A
Arctic Wolf
CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
美团技术团队
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
Exploit-DB.com RSS Feed
Exploit-DB.com RSS Feed
Hacker News - Newest:
Hacker News - Newest: "LLM"
T
Tailwind CSS Blog
S
Schneier on Security
博客园 - 三生石上(FineUI控件)
F
Full Disclosure
B
Blog RSS Feed
Forbes - Security
Forbes - Security
S
SegmentFault 最新的问题
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
人人都是产品经理
人人都是产品经理
云风的 BLOG
云风的 BLOG
Jina AI
Jina AI
Cisco Talos Blog
Cisco Talos Blog
U
Unit 42
Project Zero
Project Zero
H
Hacker News: Front Page
Y
Y Combinator Blog
Application and Cybersecurity Blog
Application and Cybersecurity Blog
The Cloudflare Blog
大猫的无限游戏
大猫的无限游戏
S
Secure Thoughts
The Hacker News
The Hacker News
Microsoft Azure Blog
Microsoft Azure Blog

博客园 - wuty007

C# 范围运算符 C# 调用WGC 实现桌面屏幕的捕获 完善基于WPF开发的标尺控件(含实例代码) C# 依赖注入 Microsoft.Extensions.DependencyInjection 实现 控制反转(IOC) C# 获取Windows系统的设备名称 记录 Windows系统开启hyper-v ,部分端口被保留,导致端口不能使用而报错的问题 WPF 调用 Win32的SetWindowDisplayAffinity 函数 实现捕获屏幕时,过滤指定的窗口 记录WPF 在清单列表设置了UIACESS为true,没有签名的报错“从服务器返回了一个参照” WPF 的ListBox 去除默认的Item项的 鼠标hover的背景颜色 WPF 调用 ChangeWindowMessageFilterEx 修改指定窗口 (UIPI) 消息筛选器的用户界面特权隔离 记录 使用PsExec启动System权限的WPF 程序 记录 命令行的 findstr 的使用 排查Windows 下的内存使用率过高,但是任务管理器看不到进程 Everything 支持 多实例 运行 指定应用 在 控制面板或者 设置的安装应用 置灰或隐藏卸载按钮 WPF 通过RawInput 获取 系统全局触摸事件 C# 定时任务 Quartz.NET 的使用 记录一下Windows系统下的命令行参数的字符个数限制 WPF 实现支持动态调整高度的文本显示控件
记录一下 WPF进程 SendMessage 发送窗口消息进行进程间通信,存在进程权限无法接受消息的问题
wuty007 · 2025-10-12 · via 博客园 - wuty007

前言

最近在接手一个同事写的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、俩个都是管理员权限的,是可以接受到数据的

image

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

image

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

image

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

image

 总结:

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

2、使用ChangeWindowMessageFilterEx 进行权限过滤:ChangeWindowMessageFilterEx 函数 (winuser.h) - Win32 apps | Microsoft Learn

可以参考我的另外篇博客调用 ChangeWindowMessageFilterEx :WPF 调用 ChangeWindowMessageFilterEx 修改指定窗口 (UIPI) 消息筛选器的用户界面特权隔离 - wuty007 - 博客园