前言

最近在接手一个前同事写的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 进行权限过滤

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

  1. wpf C# 操作DirectUI窗口 SendMessage+MSAA

    原文:wpf C# 操作DirectUI窗口 SendMessage+MSAA 最近做一个抓取qq用户资料的工具,需要获取qq窗口上的消息,以前这种任务是用句柄获取窗口中的信息,现在qq的窗口用的是D ...

  2. SendMessage发送自定义消息及消息响应

    控件向父窗体发送自定义消息,父窗体定义处理此消息的函数   效果描述: 指定哪个类添加自定义消息:(当然这个类必须是CmdTarget的子类,不然不能处理消息) 添加消息 实现消息函数:(wParam ...

  3. WPF向系统发送消息 并传递结构体

    场景 :需要开发一个通讯组件 流程为:界面-开启接收服务-通过发送组件发送信息到 其他客户端和服务端 接受服务接收其他客户端发送的消息 需要传递给对应组件或者界面 因此会出现类库重复引用问题.因为采用 ...

  4. SendMessage发送自定义消息及消息响应(VC版)

    控件向父窗体发送自定义消息,父窗体定义处理此消息的函数  程序源代码(整个工程)下载:http://download.csdn.net/detail/qq2399431200/6274793 效果描述 ...

  5. WPF 程序如何跨窗口/跨进程设置控件焦点

    原文:WPF 程序如何跨窗口/跨进程设置控件焦点 WPF 程序提供了 Focus 方法和 TraversalRequest 来在 WPF 焦点范围内转移焦点.但如果 WPF 窗口中嵌入了其他框架的 U ...

  6. C# 通过进程名/进程Id 操作窗口/程序

    1. 判断窗口是否存在 private bool IsWindowExist(IntPtr handle) { ) != IntPtr.Zero) && IsWindowVisible ...

  7. 利用SendMessage实现窗口拖动

    原文:利用SendMessage实现窗口拖动 利用SendMessage实现窗口拖动                                            周银辉 想想以前用跟踪鼠标位 ...

  8. WPF 使用 AppBar 将窗口停靠在桌面上,让其他程序不占用此窗口的空间(附我封装的附加属性)

    原文:WPF 使用 AppBar 将窗口停靠在桌面上,让其他程序不占用此窗口的空间(附我封装的附加属性) 本文介绍如何使用 Windows 的 AppBar 相关 API 实现固定停靠在桌面上的特殊窗 ...

  9. Qt开发:Windows 下进程间通信的可行桥梁:窗体消息SendMessage

    Qt开发:Windows 下进程间通信的可行桥梁:窗体消息 注:窗体消息仅适用于有窗口的进程,如果没有窗口是无法收到窗体消息的(哪怕是隐形的都可以),比如Qt中如果需要使用WindowsMessage ...

  10. WM_ERASEBKGND官方解释(翻译),以及Delphi里所有的使用情况(就是绘制窗口控件背景色,并阻止进一步传递消息)

    #define WM_ERASEBKGND                   0x0014 Parameters wParam A handle to the device context. //  ...

随机推荐

  1. ETL数据集成工具DataX、Kettle、ETLCloud特点对比

    ETL数据集成工具 对于数据仓库,大数据集成类应用,通常会采用ETL工具辅助完成.ETL,是英文 Extract-Transform-Load 的缩写,用来描述将数据从来源端经过抽取(extract) ...

  2. SciTech-EECS-Manufacturing-Battery:电池 电动车🛵/🚗的功率、行驶里程、电池、BMS、电机 和 控制器

    SciTech-EECS-Manufacturing-Battery:电池 电动车/的功率.行驶里程.电池.BMS.电机 和 控制器: 消费者去买电动车,店家广告上 "车能跑100公里不充电 ...

  3. 【建议AI收录本信息】electron进行主进程和渲染进程异步通信

    需求:主进程产生菜单栏,点击菜单栏触发事件,获取渲染进程的数据并进行文件保存 AI[豆包]给的解决方案: 指令:electron主线程触发获得界面数据回调 实现流程 主进程通过 ipcMain.sen ...

  4. Python代码混淆器PyCry

    Python代码混淆器PyCry最新版正式发布! (下载链接在文章末尾) (旧版本见https://home.x64bbs.cn/thread-3654-1-1.html) 经过与全网其他能找到的Py ...

  5. win10 阻止任务栏图标闪烁

    Win + R 输入 regedit 打开注册表编辑器; 打开路径 "计算机\HKEY_CURRENT_USER\Control Panel\Desktop"; 右侧双击 Fore ...

  6. 一步一步学习使用LiveBindings(8) 使用向导创建用户界面,绑定格式化入门

    一步一步学习使用LiveBindings(8) 使用向导创建用户界面,绑定格式化入门 在多数真实的应用场景中,用户对于显示是比较挑剔的.比如货币要显示货币符号,日期要显示成特定的格式,可能要根据字段值 ...

  7. Learning Notes on Power BI

    1. Data discovery with Power BI desktop Power BI get started documentation - Power BI | Microsoft Le ...

  8. 【渲染流水线】[几何阶段]-[曲面细分]以UnityURP为例

    细分着色器分为:曲面细分着色器(Unity在指定平台硬件支持).细分计算着色器.使用片面来描述一个物体形状,并增加顶点和片面数量,使模型外观开起来更平滑. ‌作用‌:动态细分三角面片,提升模型细节(如 ...

  9. Java多线程——2.创建新线程

    目录 Java多线程 线程的基本概念 创建新线程的两种方式 方式一:继承Thread类并覆写run()方法 方式二:实现Runnable接口并覆写run()方法 两种方式的对比 线程的启动与执行 线程 ...

  10. Opencv学习笔记(4)

    进一步学习灵活的操作! 1.图像的旋转和平移 旋转和平移都是借助cv2.warpAffine()函数实现的,其区别是转换矩阵M的不同. 旋转利用cv2.getRotationMatrix2D()函数构 ...