用 SetWindowPos 方法设置一个停止响应的窗口将卡调用方
我使用 User32 的 SetWindowPos 方法去设置一个跨进程的窗口,这个窗口是停止响应的,将让调用的 SetWindowPos 方法卡住,不继续执行逻辑。通过堆栈分析是卡在 NtUserSetWindowPos 方法上,调用 SetWindowPos 方法不返回
原本我以为调用 User32 里面的函数,大部分都是很十分快速返回的。刚好今天遇到了测试告诉我应用没响应,这是一个多进程模型的应用。刚好 lsj 修好了 dnSpy 在 dotnet 6 的调试,于是我就在测试小姐姐那里用 dnSpy 挂上调试
然而我看到了在主应用里面,没有响应的原因是主线程在等待 User32.dll 的 SetWindowPos 方法返回。开始我以为又是某数字杀毒软件干的,虽然没有啥理由,但某数字杀毒软件就是专门用来背锅的
过了几天,在服务器上又有另外一个应用未响应,通过抓 DUMP 回来分析,居然也是主线程在等待 SetWindowPos 方法返回
于是我就开始调查为什么 SetWindowPos 这样的方法能不返回,理论上这个方法不就是设置某个窗口的坐标和宽度高度等信息的?十分简单的一个方法
询问了一圈了解到,其实这个方法不返回的一个可能的原因是,如果设置的窗口没有处理 Windows 消息,那此 SetWindowPos 方法将不返回。也就是说阻塞 SetWindowPos 方法的其中一个原因就是和 SendMessage 一样,如果对应的窗口的 Windows 消息没有被读取,那么调用方将被阻塞
重新等待下一次复现。经过调试发现出现问题的时候,采用 SetWindowPos 设置的窗口句柄确实是属于另一个进程的窗口,而另一个进程刚好也是处于无响应的状态。也就是说本质原因是另一个进程无响应,导致了当前进程通过 SetWindowPos 设置另一个进程的窗口,由于另一个进程无响应,没有处理 Windows 消息,从而让当前进程阻塞也无响应
学到的知识: 如果某个应用调用 SetWindowPos 方法阻塞,那么优先调试调用 SetWindowPos 方法传入的窗口句柄参数,通过窗口句柄寻找对应的进程,调查对应的进程是否无响应或者窗口所在的线程没有继续处理 Windows 消息
那为什么 SetWindowPos 的行为和 SendMessage 如此相同?我请教了 lsj 这个问题,经过 lsj 阅读了 XP 的部分代码,找到了在系统底层里面,在 SetWindowPos 方法的实现里面就调用了 SendMessage 方法。因此 SetWindowPos 卡住的一个原因就如 SendMessage 的原因,要求只有在对方处理了消息才返回
我写了一个简单的 demo 来复现此问题
先创建两个项目,其中一个项目是 WpfApp1 项目,这个项目的功能是在点击按钮时,让主线程卡住,也就是让 UI 线程不处理 Windows 消息,模拟一个未响应进程
在 WpfApp1 项目的 MainWindow.xaml 上放一个按钮,这个按钮就是点击的时候,执行逗比逻辑,卡住主 UI 线程
<Grid>
<Button HorizontalAlignment="Center" VerticalAlignment="Center" Click="Button_Click">卡</Button>
</Grid>
在后台代码编写 Button_Click 方法,我执行的是 Thread.Sleep 的逻辑,让 UI 线程不断执行,而无法处理 Windows 消息
private void Button_Click(object sender, RoutedEventArgs e)
{
while (true)
{
Thread.Sleep(TimeSpan.FromSeconds(1));
}
}
为什么这段逗比代码要用 while (true) 来做?因为我期望可以通过 VisualStudio 断点调试,跳出循环,也就是让 WpfApp1 进程继续处理 Windows 消息
再新建一个叫 NawnayarlallliwurHifowaleeli 的项目,在这个项目尝试去获取 WpfApp1 进程的 MainWindow 且调用 SetWindowPos 方法设置 WpfApp1 进程的 MainWindow 的坐标
为了方便调用 SetWindowPos 方法,我采用了 dotnet 官方开源的 P/Invoke 库,详细请看 https://github.com/dotnet/pinvoke
在 NawnayarlallliwurHifowaleeli 项目,尝试不断设置 WpfApp1 进程的 MainWindow 的坐标。在没有让 WpfApp1 进程卡主线程时,预期是能成功设置且快速返回
using PInvoke;
using System;
using System.Diagnostics;
using System.Linq;
using System.Threading.Tasks;
using System.Windows;
namespace NawnayarlallliwurHifowaleeli;
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
Loaded += MainWindow_Loaded;
}
private async void MainWindow_Loaded(object sender, RoutedEventArgs e)
{
var process = Process.GetProcessesByName("WpfApp1").First();
if (process.MainWindowTitle == "MainWindow")
{
while (_contentLoaded)
{
User32.SetWindowPos
(
hWnd: process.MainWindowHandle,
hWndInsertAfter: IntPtr.Zero,
X: Random.Shared.Next(5),
Y: Random.Shared.Next(5),
cx: 0,
cy: 0,
uFlags: User32.SetWindowPosFlags.SWP_NOSIZE
| User32.SetWindowPosFlags.SWP_NOZORDER
| User32.SetWindowPosFlags.SWP_NOACTIVATE
);
await Task.Delay(TimeSpan.FromMilliseconds(500));
}
}
}
}
为了测试 NawnayarlallliwurHifowaleeli 进程是否进入无响应,也在 NawnayarlallliwurHifowaleeli 的 MainWindow 上放一个按钮,通过鼠标移动到按钮上的效果,即可了解窗口是否无响应
<Grid>
<Button Margin="10,10,10,10"></Button>
</Grid>
跑起来两个项目的进程,可以看到 WpfApp1 的窗口不断被 NawnayarlallliwurHifowaleeli 设置窗口坐标,开始跳 disco 起来
接下来点击 WpfApp1 的按钮让 WpfApp1 进程无响应。可以看到 NawnayarlallliwurHifowaleeli 进程也跟着无响应起来
在 VisualStudio 里面勾选 Native 调试(本机调试,可以调试非托管部分)可以看到 NawnayarlallliwurHifowaleeli 进程是卡在调用 SetWindowPos 方法,如何预期
以下就是 NawnayarlallliwurHifowaleeli 的调用堆栈
win32u.dll!NtUserSetWindowPos() 未知
[托管到本机的转换]
NawnayarlallliwurHifowaleeli.dll!NawnayarlallliwurHifowaleeli.MainWindow.MainWindow_Loaded(object sender, System.Windows.RoutedEventArgs e) 行 31 C#
[正在恢复异步方法]
System.Private.CoreLib.dll!System.Runtime.CompilerServices.AsyncTaskMethodBuilder<System.Threading.Tasks.VoidTaskResult>.AsyncStateMachineBox<System.__Canon>.ExecutionContextCallback(object s) 未知
System.Private.CoreLib.dll!System.Threading.ExecutionContext.RunInternal(System.Threading.ExecutionContext executionContext, System.Threading.ContextCallback callback, object state) 未知
System.Private.CoreLib.dll!System.Runtime.CompilerServices.AsyncTaskMethodBuilder<System.Threading.Tasks.VoidTaskResult>.AsyncStateMachineBox<NawnayarlallliwurHifowaleeli.MainWindow.<MainWindow_Loaded>d__1>.MoveNext(System.Threading.Thread threadPoolThread) 未知
System.Private.CoreLib.dll!System.Runtime.CompilerServices.AsyncTaskMethodBuilder<System.Threading.Tasks.VoidTaskResult>.AsyncStateMachineBox<System.__Canon>.MoveNext() 未知
System.Private.CoreLib.dll!System.Runtime.CompilerServices.TaskAwaiter.OutputWaitEtwEvents.AnonymousMethod__12_0(System.Action innerContinuation, System.Threading.Tasks.Task innerTask) 未知
System.Private.CoreLib.dll!System.Runtime.CompilerServices.AsyncMethodBuilderCore.ContinuationWrapper.Invoke() 未知
System.Private.CoreLib.dll!System.Threading.Tasks.SynchronizationContextAwaitTaskContinuation.GetActionLogDelegate.AnonymousMethod__0() 未知
System.Private.CoreLib.dll!System.Threading.Tasks.SynchronizationContextAwaitTaskContinuation..cctor.AnonymousMethod__8_0(object state) 未知
WindowsBase.dll!System.Windows.Threading.ExceptionWrapper.InternalRealCall(System.Delegate callback, object args, int numArgs) 未知
WindowsBase.dll!System.Windows.Threading.ExceptionWrapper.TryCatchWhen(object source, System.Delegate callback, object args, int numArgs, System.Delegate catchHandler) 未知
WindowsBase.dll!System.Windows.Threading.DispatcherOperation.InvokeImpl() 未知
WindowsBase.dll!MS.Internal.CulturePreservingExecutionContext.CallbackWrapper(object obj) 未知
System.Private.CoreLib.dll!System.Threading.ExecutionContext.RunInternal(System.Threading.ExecutionContext executionContext, System.Threading.ContextCallback callback, object state) 未知
WindowsBase.dll!MS.Internal.CulturePreservingExecutionContext.Run(MS.Internal.CulturePreservingExecutionContext executionContext, System.Threading.ContextCallback callback, object state) 未知
WindowsBase.dll!System.Windows.Threading.DispatcherOperation.Invoke() 未知
WindowsBase.dll!System.Windows.Threading.Dispatcher.ProcessQueue() 未知
WindowsBase.dll!System.Windows.Threading.Dispatcher.WndProcHook(System.IntPtr hwnd, int msg, System.IntPtr wParam, System.IntPtr lParam, ref bool handled) 未知
WindowsBase.dll!MS.Win32.HwndWrapper.WndProc(System.IntPtr hwnd, int msg, System.IntPtr wParam, System.IntPtr lParam, ref bool handled) 未知
WindowsBase.dll!MS.Win32.HwndSubclass.DispatcherCallbackOperation(object o) 未知
WindowsBase.dll!System.Windows.Threading.ExceptionWrapper.InternalRealCall(System.Delegate callback, object args, int numArgs) 未知
WindowsBase.dll!System.Windows.Threading.ExceptionWrapper.TryCatchWhen(object source, System.Delegate callback, object args, int numArgs, System.Delegate catchHandler) 未知
WindowsBase.dll!System.Windows.Threading.Dispatcher.LegacyInvokeImpl(System.Windows.Threading.DispatcherPriority priority, System.TimeSpan timeout, System.Delegate method, object args, int numArgs) 未知
WindowsBase.dll!MS.Win32.HwndSubclass.SubclassWndProc(System.IntPtr hwnd, int msg, System.IntPtr wParam, System.IntPtr lParam) 未知
[本机到托管的转换]
user32.dll!00007ffe839ce858() 未知
user32.dll!00007ffe839ce299() 未知
[托管到本机的转换]
WindowsBase.dll!System.Windows.Threading.Dispatcher.PushFrameImpl(System.Windows.Threading.DispatcherFrame frame) 未知
WindowsBase.dll!System.Windows.Threading.Dispatcher.PushFrame(System.Windows.Threading.DispatcherFrame frame) 未知
WindowsBase.dll!System.Windows.Threading.Dispatcher.Run() 未知
PresentationFramework.dll!System.Windows.Application.RunDispatcher(object ignore) 未知
PresentationFramework.dll!System.Windows.Application.RunInternal(System.Windows.Window window) 未知
PresentationFramework.dll!System.Windows.Application.Run() 未知
NawnayarlallliwurHifowaleeli.dll!NawnayarlallliwurHifowaleeli.App.Main() 未知
[本机到托管的转换]
[内联框架] hostpolicy.dll!coreclr_t::execute_assembly(int) 行 89 C++
hostpolicy.dll!run_app_for_context(const hostpolicy_context_t & context, int argc, const wchar_t * * argv) 行 255 C++
hostpolicy.dll!run_app(const int argc, const wchar_t * * argv) 行 284 C++
hostpolicy.dll!corehost_main(const int argc, const wchar_t * * argv) 行 430 C++
hostfxr.dll!execute_app(const std::wstring & impl_dll_dir, corehost_init_t * init, const int argc, const wchar_t * * argv) 行 146 C++
hostfxr.dll!`anonymous namespace'::read_config_and_execute(const std::wstring & host_command, const host_startup_info_t & host_info, const std::wstring & app_candidate, const std::unordered_map<enum known_options,std::vector<std::wstring,std::allocator<std::wstring>>,known_options_hash,std::equal_to<enum known_options>,std::allocator<std::pair<enum known_options const ,std::vector<std::wstring,std::allocator<std::wstring>>>>> & opts, int new_argc, const wchar_t * * new_argv, host_mode_t mode, const bool is_sdk_command, wchar_t * out_buffer, int buffer_size, int * required_buffer_size) 行 533 C++
hostfxr.dll!fx_muxer_t::handle_exec_host_command(const std::wstring & host_command, const host_startup_info_t & host_info, const std::wstring & app_candidate, const std::unordered_map<enum known_options,std::vector<std::wstring,std::allocator<std::wstring>>,known_options_hash,std::equal_to<enum known_options>,std::allocator<std::pair<enum known_options const ,std::vector<std::wstring,std::allocator<std::wstring>>>>> & opts, int argc, const wchar_t * * argv, int argoff, host_mode_t mode, const bool is_sdk_command, wchar_t * result_buffer, int buffer_size, int * required_buffer_size) 行 1018 C++
hostfxr.dll!fx_muxer_t::execute(const std::wstring host_command, const int argc, const wchar_t * * argv, const host_startup_info_t & host_info, wchar_t * result_buffer, int buffer_size, int * required_buffer_size) 行 579 C++
hostfxr.dll!hostfxr_main_startupinfo(const int argc, const wchar_t * * argv, const wchar_t * host_path, const wchar_t * dotnet_root, const wchar_t * app_path) 行 61 C++
NawnayarlallliwurHifowaleeli.exe!00007ff77e5524b8() 未知
NawnayarlallliwurHifowaleeli.exe!00007ff77e55282b() 未知
NawnayarlallliwurHifowaleeli.exe!00007ff77e553cd8() 未知
kernel32.dll!BaseThreadInitThunk() 未知
ntdll.dll!RtlUserThreadStart() 未知
可以通过如下方式获取本文的源代码,先创建一个空文件夹,接着使用命令行 cd 命令进入此空文件夹,在命令行里面输入以下代码,即可获取到本文的代码
git init
git remote add origin https://gitee.com/lindexi/lindexi_gd.git
git pull origin 72ec5a3dc9c43662d6f7cce7b676ef7bc5488f44
以上使用的是 gitee 的源,如果 gitee 不能访问,请替换为 github 的源
git remote remove origin
git remote add origin https://github.com/lindexi/lindexi_gd.git
获取代码之后,进入 NawnayarlallliwurHifowaleeli 文件夹
用 SetWindowPos 方法设置一个停止响应的窗口将卡调用方的更多相关文章
- React(0.13) 利用componentDidMount 方法设置一个定时器
<html> <head> <title>hello world React.js</title> <script src="build ...
- [问题解决]《GPU高性能编程CUDA实战》中第4章Julia实例“显示器驱动已停止响应,并且已恢复”问题的解决方法
以下问题的出现及解决都基于"WIN7+CUDA7.5". 问题描述:当我编译运行<GPU高性能编程CUDA实战>中第4章所给Julia实例代码时,出现了显示器闪动的现象 ...
- 显示器驱动程序 NVIDIA Windows Kernel Mode Driver Version 已停止响应 并且己成功恢复 解决方法
原文:http://news.160.com/?p=1890 在玩游戏中 经常 出现显示器驱动程序 NVIDIA Windows Kernel Mode Driver Version 已停止响应 并且 ...
- 设置一个DIV块固定在屏幕中央(两种方法)
设置一个DIV块固定在屏幕中央(两种方法) 方法一: 对一个div进行以下设置即可实现居中. <style> #a{ position: fixed; top: 0px; left: 0p ...
- JS垃圾回收——和其他语言一样,JavaScript 的 GC 策略也无法避免一个问题:GC 时,停止响应其他操作,这是为了安全考虑
JavaScript 内存管理 & 垃圾回收机制 标记清除 js 中最常用的垃圾回收方式就是标记清除.当变量进入环境时,例如,在函数中声明一个变量,就将这个而变量标记为“进入环境”.从逻辑上讲 ...
- 作用域通信对象:session用户在登录时通过`void setAttribute(String name,Object value)`方法设置用户名和密码。点击登录按钮后,跳转到另外一个页面显示用户
作用域通信对象:session session对象基于会话,不同用户拥有不同的会话.同一个用户共享session对象的所有属性.作用域开始客户连接到应用程序的某个页面,结束与服务器断开连接.sessi ...
- mysql 5.5 win7安装最后一步总是停止响应
今天刚开始安装了64位版本的mysql5.5 ,安装很顺利,后来发现库不兼容的问题,于是卸载,安装mysql-5.5.27-win32的32位版本,奇怪了,怎么安装,怎么卸载都不行,就是到最 后一步停 ...
- 在生成一个窗体的时候,点击窗体的右上角关闭按钮激发窗体事件的方法:窗体Frame为事件源,WindowsListener接口调用Windowsclosing()。
事件模式的实现步骤: 开发事件对象(事件发送者)——接口——接口实现类——设置监听对象 一定要理解透彻Gril.java程序. 重点:学会处理对一个事件源有多个事件的监听器(在发送消息时监听器收到 ...
- worker 启动时向 etcd 注册自己的信息,并设置一个带 TTL 的租约,每隔一段时间更新这个 TTL,如果该 worker 挂掉了,这个 TTL 就会 expire 并删除相应的 key。
1.通过etcd中的选主机制,我们实现了服务的高可用.同时利用systemd对etcd本身进行了保活,只要etcd服务所在的机器没有宕机,进程就具备了容灾性. https://mp.weixin.qq ...
- 【ASP.NET Core】设置Web API 响应的数据格式——Produces 特性篇
开春首文,今天老周就跟各位大伙伴们聊一个很简单的话题:怎么设定API响应的数据格式. 说本质一点,就是设置所返回内容的 MIME 类型(Content-Type 头).当然了,咱们不会使用在HTTP管 ...
随机推荐
- 记录--使用Canvas绘制一个验证码组件
这里给大家分享我在网上总结出来的一些知识,希望对大家有所帮助 使用Canvas绘制一个验证码组件 前言 验证码,这一日常伴随我们的要素,是我们在线交互的重要安全保障.你的手机短信里是否被它占据半壁江山 ...
- drools执行指定的规则
目录 1.背景 2.方案 2.1 通过AgendaFilter来实现 2.2 通过entry-point来实现 3.实现 3.1 需求 3.2 drl 文件编写 3.3 部分java代码 3.4 运行 ...
- Elasticsearch索引不存在时,查询接口报错怎么办?
1.索引不存在,报错:type=index_not_found_exception, reason=no such index解决办法: DSL: GET /text_index_003/_searc ...
- Involution:空间不共享?可完全替代卷积的高性能算子 | CVPR 2021
其实这篇文章很早就写好了,但作者其它论文涉及到洗稿问题,所以先放着了.目前看这篇文章没被举报有洗稿的嫌疑,所以就发出来了 . 来源:晓飞的算法工程笔记 公众号 论文: Involution: Inve ...
- KingbaseES V8R3 表加密
前言 透明加密是指将数据库page加密后写入磁盘,当需要读取对应page时进行加密读取.此过程对于用户是透明, 用户无需干预. 该文档进行数据库V8R3版本测试透明加密功能,需要说明,该版本发布时间早 ...
- SQL 递归核心思想(递归思维)
目前很缺递归思维,主要是算法代码写得少,本篇记录下以 PostgreSQL 代码举例(主要是非常喜欢这款性能小钢炮数据库). 树状查询不多说,很简单大家基本都会,主要讲 cte 代码递归实现不同需求. ...
- #单调栈,树状数组#CF1635F Closest Pair
题目 设 \(f(x,y)=|a_x-a_y|*(w_x+w_y)\),其中 \(a\) 单调递增 多组询问求 \(\min_{l\leq l'<r'\leq r}\{f(l',r')\}\) ...
- #Pollard-Rho,高精度#洛谷 3499 [POI2010]NAJ-Divine Divisor
题目 给定\(m\)个数\(a_i\),令\(n=\prod_{i=1}^m a_i\), 问有多少个大于1的正整数\(d\)满足\(d^{\max k}|n\) 并输出\(\max k\),\(m\ ...
- css实现带背景颜色的小三角
<div id="first"> <p>带背景颜色的小三角实现是比较简单的</p> <span id="top"> ...
- openGauss/MogDB 学习笔记之 -- PITR恢复
openGauss/MogDB 学习笔记之 -- PITR 恢复 概念描述 背景信息 当数据库崩溃或希望回退到数据库之前的某一状态时,MogDB 的即时恢复功能(Point-In-Time Recov ...