WPF 调用 ChangeWindowMessageFilterEx 修改指定窗口 (UIPI) 消息筛选器的用户界面特权隔离

news/2025/10/14 21:32:09/文章来源:https://www.cnblogs.com/wuty/p/19142059

一、回顾

上一篇博客:记录一下 WPF进程 SendMessage 发送窗口消息进行进程间通信,存在进程权限无法接受消息的问题 - wuty007 - 博客园 

说到了 发送端是普通权限的窗体 给 接收端是 管理员权限的窗体,通过 Win32 API的方式调用 SendMessage 发送窗口消息,管理员权限的窗体的钩子消息回传接受不到发送端的数据。

如下图所示:

image

 这是由于 Windows系统在Windows NT6.0 开始,引入了受保护模式,阻止进程将所选窗口消息和其他 USER API 发送到运行完整性较高的进程

详情请看微软的详细说明:

受保护的模式 - Win32 apps | Microsoft Learn

Windows 完整性机制设计 | Microsoft Learn

二、函数说明

针对于以上的问题,微软也提供了相对应的接口来规避: ChangeWindowMessageFilterEx

ChangeWindowMessageFilterEx 函数 (winuser.h)

修改指定窗口 (UIPI) 消息筛选器的用户界面特权隔离。

语法

BOOL ChangeWindowMessageFilterEx([in]                HWND                hwnd,[in]                UINT                message,[in]                DWORD               action,[in, out, optional] PCHANGEFILTERSTRUCT pChangeFilterStruct
);

参数

[in] hwnd

类型:HWND

要修改其 UIPI 消息筛选器的窗口的句柄。

[in] message

类型: UINT

消息筛选器允许通过 或 阻止的消息。

[in] action

类型:DWORD

要执行的操作,可以采用以下值之一:

 
含义
MSGFLT_ALLOW
1
允许消息通过筛选器。 这使 hWnd 能够接收消息,无论消息的来源如何,即使消息来自较低特权进程也是如此。
MSGFLT_DISALLOW
2
阻止消息从较低特权进程传递到 hWnd ,除非使用 ChangeWindowMessageFilter 函数或全局允许该消息在进程范围内传递。
MSGFLT_RESET
0
将 hWnd 的窗口消息筛选器重置为默认值。 允许全局或进程范围内的任何消息都将通过,但任何未包含在这两个类别中以及来自较低特权进程的消息都将被阻止。

[in, out, optional] pChangeFilterStruct

类型: PCHANGEFILTERSTRUCT

指向 CHANGEFILTERSTRUCT 结构的可选指针。

返回值

类型: BOOL

如果函数成功,则返回 TRUE;否则,它将返回 FALSE。 要获得更多的错误信息,请调用 GetLastError。

注解

UIPI 是一项安全功能,可防止从较低完整性级别的发件人接收消息。 可以使用此函数允许将特定消息传递到窗口,即使消息源自较低完整性级别的进程也是如此。 与控制进程消息筛选器的 ChangeWindowMessageFilter 函数不同, ChangeWindowMessageFilterEx 函数控制窗口消息筛选器。

应用程序可以使用 ChangeWindowMessageFilter 函数以进程范围的方式允许或阻止消息。 如果进程消息筛选器或窗口消息筛选器允许该消息,则会将其传递到窗口。

请注意,不允许 SECURITY_MANDATORY_LOW_RID 或以下的进程更改消息筛选器。 如果这些进程调用此函数,它将失败并生成扩展错误代码, ERROR_ACCESS_DENIED。

无论筛选器设置如何,值小于 WM_USER 的某些消息都需要通过筛选器传递。 尝试使用此函数允许或阻止此类消息时,将不起作用。

 

三、如何使用

1、WPF 的接受端窗口增加 对 ChangeWindowMessageFilterEx 函数的定义和封装

 // 定义MessageFilterAction 结构体public enum MessageFilterAction : uint{MSGFLT_RESET = 0,     // 重置过滤器MSGFLT_ALLOW = 1,     // 允许消息MSGFLT_DISALLOW = 2   // 禁止消息
 }// 定义 消息过滤器状态结构体
 [StructLayout(LayoutKind.Sequential)]public struct CHANGEFILTERSTRUCT{public uint cbSize;public uint ExtStatus;}// 导入 user32.dll 中的函数[DllImport("user32.dll", SetLastError = true)]private static extern bool ChangeWindowMessageFilterEx(IntPtr hWnd,uint msg,MessageFilterAction action,ref CHANGEFILTERSTRUCT pChangeFilterStruct);/// <summary>/// 设置消息过滤/// </summary>/// <param name="hWnd"></param>/// <param name="message"></param>/// <param name="action"></param>/// <returns></returns>/// <exception cref="System.ComponentModel.Win32Exception"></exception>public static bool SetMessageFilter(IntPtr hWnd, uint message, MessageFilterAction action){// 初始化结构体CHANGEFILTERSTRUCT changeFilter = new CHANGEFILTERSTRUCT{cbSize = (uint)Marshal.SizeOf(typeof(CHANGEFILTERSTRUCT)),ExtStatus = 0};// 调用 APIbool result = ChangeWindowMessageFilterEx(hWnd, message, action, ref changeFilter);if (!result){// 获取错误信息(可选)int error = Marshal.GetLastWin32Error();throw new System.ComponentModel.Win32Exception(error);}return result;}

2、在接收数据的 FramworkReceieve 窗口 的Loaded时间调用 SetMessageFilter,最主要的是第三个参数需要设置:MessageFilterAction.MSGFLT_ALLOW

 private void MainWindow_Loaded(object sender, RoutedEventArgs e){_customMessageId = RegisterWindowMessage("MyApp");// 获取窗口句柄并添加消息钩子_hwndSource = PresentationSource.FromVisual(this) as HwndSource;if (_hwndSource != null){var handle = _hwndSource.Handle;   SetMessageFilter(handle, WM_COPYDATA, MessageFilterAction.MSGFLT_ALLOW);_hwndSource.AddHook(WndProc);}}

3、完整代码如下:

消息发送端:

<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>
</Window>
namespace FramworkSender
{/// <summary>/// MainWindow.xaml 的交互逻辑/// </summary>public partial class MainWindow : Window{public MainWindow(){InitializeComponent();}private void ButtonBase_OnClick(object sender, RoutedEventArgs e){// 获取接收窗口的句柄
IntPtr hwnd = FindWindow(null, "FramworkReceieve");if (hwnd == IntPtr.Zero){MessageBox.Show("找不到窗口");}else{SendMessageString(hwnd, "123");}}#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}
}

消息接收端:

<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>
namespace FramworkReceieve
{/// <summary>/// MainWindow.xaml 的交互逻辑/// </summary>public partial class MainWindow : Window{public MainWindow(){InitializeComponent();Loaded += MainWindow_Loaded;}private HwndSource _hwndSource;private void MainWindow_Loaded(object sender, RoutedEventArgs e){// 获取窗口句柄并添加消息钩子_hwndSource = PresentationSource.FromVisual(this) as HwndSource;if (_hwndSource != null){var handle = _hwndSource.Handle;SetMessageFilter(handle, WM_COPYDATA, MessageFilterAction.MSGFLT_ALLOW);_hwndSource.AddHook(WndProc);}}private IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled){#region CopyDataif (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;}#endregionreturn IntPtr.Zero;}#region CopyDatapublic const int WM_COPYDATA = 0x004A;// 定义 COPYDATASTRUCT 结构
        [StructLayout(LayoutKind.Sequential)]public struct COPYDATASTRUCT{public IntPtr dwData;public int cbData;public IntPtr lpData;}#endregion// 定义MessageFilterAction 结构体public enum MessageFilterAction : uint{MSGFLT_RESET = 0,     // 重置过滤器MSGFLT_ALLOW = 1,     // 允许消息MSGFLT_DISALLOW = 2   // 禁止消息
        }// 定义 消息过滤器状态结构体
        [StructLayout(LayoutKind.Sequential)]public struct CHANGEFILTERSTRUCT{public uint cbSize;public uint ExtStatus;}// 导入 user32.dll 中的函数[DllImport("user32.dll", SetLastError = true)]private static extern bool ChangeWindowMessageFilterEx(IntPtr hWnd,uint msg,MessageFilterAction action,ref CHANGEFILTERSTRUCT pChangeFilterStruct);/// <summary>/// 设置消息过滤/// </summary>/// <param name="hWnd"></param>/// <param name="message"></param>/// <param name="action"></param>/// <returns></returns>/// <exception cref="System.ComponentModel.Win32Exception"></exception>public static bool SetMessageFilter(IntPtr hWnd, uint message, MessageFilterAction action){// 初始化结构体CHANGEFILTERSTRUCT changeFilter = new CHANGEFILTERSTRUCT{cbSize = (uint)Marshal.SizeOf(typeof(CHANGEFILTERSTRUCT)),ExtStatus = 0};// 调用 APIbool result = ChangeWindowMessageFilterEx(hWnd, message, action, ref changeFilter);if (!result){// 获取错误信息(可选)int error = Marshal.GetLastWin32Error();throw new System.ComponentModel.Win32Exception(error);}return result;}protected override void OnClosed(EventArgs e){_hwndSource?.RemoveHook(WndProc);base.OnClosed(e);}private void ButtonBase_OnClick(object sender, RoutedEventArgs e){txtMessage.Text = "";}}
}

4、运行结果:

接收端是管理员权限,发送端是普通权限,可以收发数据了

image

 

 四、总结

1、调用 ChangeWindowMessageFilterEx 需要设置第三个参数 action 的值为 枚举 :MessageFilterAction.MSGFLT_ALLOW,也就是数值 “1”。

 

参考资料:

1、ChangeWindowMessageFilterEx 函数 (winuser.h) - Win32 apps | Microsoft Learn

2、受保护的模式 - Win32 apps | Microsoft Learn

3、Windows 完整性机制设计 | Microsoft Learn

 

 

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/937107.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

机器学习比赛

机器学习比赛基本上来就用sklearn的随机森林, 和xgboost库. 2个算法先跑一下.基本能拿到一个能看的结果. 速度都很快. 基本30秒内跑完. 然后再考虑其他的特征工程, 深度学习等等..

牙科诊所借力AI营销4个月创收13万

一个叫Jordan的牙科营销老板用AI在4个月里赚了13万。我来说说他具体怎么做的。 Jordan发现一个问题:英国90%的牙科诊所还在用老旧的预约系统,效率很低。他决定用AI来解决这个问题。 📌 他的做法搭建AI预约系统• 以…

10月14日日记

1.今天上午进行工程实训课程,下午学习英语视听说 2.明天学习离散数学 3.只知道把代码用try-catch包起来,但不清楚应该捕获什么异常,什么时候该抛出(throws),什么时候该处理。搞清楚Error和Exception的区别,Runt…

P4653 [CEOI 2017] Sure Bet

题目描述 现在有 n 个A类灯泡和 n 个B类灯泡,每个灯泡都有各自的权值。 我们将这些灯泡分为 n 组,每组包含一个来自A类的灯泡和一个来自B类的灯泡。 你可以从中选取任意个灯泡,每选取一个灯泡需要花费 1 的代价。 在…

PHP虚拟主机测试页面

<?php /*** PHP 虚拟主机测试页面* 说明:用于测试 PHP 是否正常工作及查看服务器基本信息*/// 设置页面编码 header("Content-Type: text/html; charset=utf-8"); ?> <!DOCTYPE html> <ht…

利用脉冲位宽调制技术和一级滤波电路实现正弦波输出

利用PWM和RC滤波电路输出正弦波 1、初始化硬件 (1)时钟使能RCC,选择外部高速晶振将HCLK配置为最大的84MHz(2)PWM使能TIM1,将PWM设为20KHz,将死区设置为25ns注意:为防止开关不同步导致的短路,需要设置“死区”PWM频…

20251014

今天真是充实的一天!线上工程实训的自学过程十分顺畅,让我轻松掌握了不少知识。理发店的体验也很愉快,发型整理后感觉焕然一新。英语课的学习让我的语言能力有所提升,而晚上的离散数学作业完成得也相当顺利。这一天…

歌词本。 - Slayer

10000h、holy、loveyourself10,000 Hours Do you love the rain does it make you dance When youre drunk with your friends at a party Whats your favorite song does it make you smile Do you think of me When …

使用 Docker 快速搭建 MinIO 文件存储服务

在日常开发中,我们经常需要处理文件上传、下载、存储与访问的场景,比如图片、视频、日志文件、模型数据集等。传统的文件系统难以满足高并发、分布式部署以及云原生化的需求。而 MinIO 作为一款高性能、轻量级、兼容…

2025.10.14 正睿二十连测

正睿二十连测 B 赛场上花了 \(40min\) 写了个暴力。赛后看题解 \(20min\) + 写 \(30min\)。 有多少个长度为 \(n\) 排列,使得 \(x(n - x + 1) \le m\) (\(x\) 为 \(n\) 的位置),答案对 \(p\) 取模。 令 \(f_n\) 表…

singleton_pattern

什么是懒汉单例模式(程序在使用的时候才会构造这个类) 什么是饿汉单例模式(程序启动的时候就构造了这个类) 程序内只存在一个这样的类,不需要重复构造,避免浪费,如读取外部文件,只需一次读取,全局使用。 单例…

20251014周二日记

20251014周二日记今日: 1.上午拿麦当劳去实验室,吃早点开会,写财报。 2.中午不饿没吃,下午上课继续改财报,还要算题。顺便帮忙问问下一届考研的教材问题。 3.晚上和研二师哥师姐一起吃了饭,回实验室学下面的那些…

ai出题

江苏海洋大学考研(340 & 808)思维导图说明:此思维导图以两门自命题大纲为中心,分解为主题 → 子主题 → 关键考点,便于记忆与复习。总览340:鱼类增养殖学 / 鱼类育种学 / 水产动物营养与饲料学(重点:生态养…

Python的Numpy、Pandas和Matplotlib(随笔)

Python--Numpy import numpy as np 1、扩展程序库(维度数组与矩阵运算、针对数组运算提供大量的数学函数库) 2、N维数组对象对象:ndarray(别名array,用于存放同类型元素的多维数组 ,以 0 下标为开始进行集合中元…

财务怎样做到业财融合 - 智慧园区

1. 财务端思维转变 ✅跳出传统账本思维模式:财务人员别只盯着记账、算数据,要多琢磨业务核心逻辑,比如产品提价的原因、项目成本的具体构成。 ✅主动贴近业务全流程:主动参加业务部门的项目会、产品讨论会,把采购…

CF2146E

对于数组 \(a\),定义 \(w(a)\) 为 \(a\) 中满足 \(a_i > mex(a)\) 的下标数。现在给定长度为 \(n\) 的数组,对于每个 \(r\), 求出 \(\max\limits_{l = 1}^{r} w(a[l \sim r])\)。 考虑枚举 \(x = mex(a)\),设 \(…

Gradle使用

Gradle Wrapper distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists distributionUrl=https\://services.gradle.org/distributions/gradle-8.6-bin.zip networkTimeout=10000 validateDistributionUr…

Spring Boot项目中集成Spring Security OAuth2和Apache Shiro

1. 引入依赖Spring Boot项目中已经包含了Spring Security依赖、Apache Shiro的依赖。 <!-- Spring Security --><dependency> <groupId>org.springframework.boot</groupId> <artifa…

【博客导航】

文章目录 前言 由于文章内容写的比较杂,其实大部分也是写给自己看的,所以博客的内容也有些杂乱,故在此整理一下,以供想要阅读的朋友方便寻找(还有某些没什么参阅价值的文章就不放上来了)。 博客首页博客首页【ST…

部署向量数据库milvus

系统:CentOS 8.9 部署操作如下 # docker load -i etcd-v3.6.5.tar # docker load -i minio-2023-03-20.tar # docker load -i milvus-v2.3.3.tar # docker load -i attu-v2.3.0.tar # cd /app/ # mkdir milvus_de…