news 2026/8/9 20:21:35

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/8/9 6:53:41

终极FGO助手Chaldea:从材料管理到战斗策略的完整解决方案

终极FGO助手Chaldea&#xff1a;从材料管理到战斗策略的完整解决方案 【免费下载链接】chaldea Chaldea - Yet Another Material Planner and Battle Simulator for Fate/Grand Order aka FGO 项目地址: https://gitcode.com/gh_mirrors/ch/chaldea 还在为FGO复杂的养成…

作者头像 李华
网站建设 2026/8/9 6:53:38

Scoop 全局安装指南

Scoop 全局安装指南 什么是 Scoop 全局安装&#xff1f; Scoop 支持两种安装模式&#xff1a; 本地安装&#xff1a;应用程序安装在用户目录下&#xff08;C:\Users\用户名\scoop&#xff09;&#xff0c;仅当前用户可用全局安装&#xff1a;应用程序安装在系统目录下&#xff…

作者头像 李华
网站建设 2026/8/9 5:02:25

80亿参数仅激活3B:Qwen3-Next架构如何重塑AI效率格局

80亿参数仅激活3B&#xff1a;Qwen3-Next架构如何重塑AI效率格局 【免费下载链接】Qwen3-Next-80B-A3B-Thinking Qwen3-Next-80B-A3B-Thinking 在复杂推理和强化学习任务中超越 30B–32B 同类模型&#xff0c;并在多项基准测试中优于 Gemini-2.5-Flash-Thinking 项目地址: ht…

作者头像 李华
网站建设 2026/8/9 12:39:51

【建议收藏】告别API焦虑!Gemini 3.0与DeepSeek V3同台竞技,开发者如何用一行代码实现“模型自由”?(附压测数据)

2024年&#xff0c;注定是AI模型“神仙打架”的一年。 早上你可能还在惊叹 Gemini 3.0 的多模态理解能力。 中午 DeepSeek V3.2 就发布了更强的代码生成基准。 到了晚上&#xff0c;Banana Pro 又以极高的性价比刷屏了技术圈。 对于我们开发者来说&#xff0c;这既是幸福&a…

作者头像 李华