news 2026/4/18 7:27:29

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

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
WPF 调用 ChangeWindowMessageFilterEx 修改指定窗口 (UIPI) 消息筛选器的用户界面特权隔离

说到了 发送端是普通权限的窗体 给 接收端是 管理员权限的窗体,通过 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

};

// 调用 API

bool 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 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 CopyData

public 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

};

// 调用 API

bool 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”。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/18 5:24:06

联想拯救者BIOS高级设置解锁指南:3步恢复隐藏选项

联想拯救者BIOS高级设置解锁指南&#xff1a;3步恢复隐藏选项 【免费下载链接】LEGION_Y7000Series_Insyde_Advanced_Settings_Tools 支持一键修改 Insyde BIOS 隐藏选项的小工具&#xff0c;例如关闭CFG LOCK、修改DVMT等等 项目地址: https://gitcode.com/gh_mirrors/le/LE…

作者头像 李华
网站建设 2026/4/18 5:25:00

DeepLX终极指南:免费翻译API的完整解决方案

还在为DeepL官方API的高昂费用和Token限制而烦恼吗&#xff1f;DeepLX来了&#xff01;这是一个完全开源的DeepL免费API实现&#xff0c;让你无需任何Token就能享受高质量的翻译服务。无论你是个人开发者还是小型团队&#xff0c;DeepLX都能为你提供简单高效的翻译解决方案。 【…

作者头像 李华
网站建设 2026/4/18 7:03:57

ModernWMS开源仓库管理系统:3小时从零搭建企业级仓储平台

ModernWMS开源仓库管理系统&#xff1a;3小时从零搭建企业级仓储平台 【免费下载链接】ModernWMS The open source simple and complete warehouse management system is derived from our many years of experience in implementing erp projects. We stripped the original c…

作者头像 李华
网站建设 2026/4/18 5:41:15

5分钟快速上手gif-h:轻量级C++ GIF动画生成终极指南

5分钟快速上手gif-h&#xff1a;轻量级C GIF动画生成终极指南 【免费下载链接】gif-h Simple C one-header library for the creation of animated GIFs from image data. 项目地址: https://gitcode.com/gh_mirrors/gi/gif-h 想要在C项目中轻松创建动态GIF动画吗&#…

作者头像 李华
网站建设 2026/4/17 2:11:56

C#转java的最好利器easy-query就是efcore4j sqlsugar4j freesql4j

数据库模型image点击查看实体代码案例查询用户信息和最早开户的银行卡信息通过模型我们可以清晰的看到SysUser和BankCard是一对多的关系&#xff0c;eq如何实现这种一对多的数据返回呢&#xff1f;使用临时对象返回var list easyEntityQuery.queryable(SysUser.class).select(…

作者头像 李华
网站建设 2026/4/18 4:37:03

在线服务器的应用场景都有哪些?

在线服务器作为现代网络架构的核心组件&#xff0c;凭借其强大的计算、存储和数据处理能力&#xff0c;广泛应用于众多领域&#xff0c;深刻改变着人们的生活与工作方式。在线服务器用于对重要数据进行定期备份&#xff0c;防止数据丢失&#xff0c;备份数据可以存储在本地服务…

作者头像 李华