我使用 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() 未知

本文的测试代码放在githubgitee 欢迎访问

可以通过如下方式获取本文的源代码,先创建一个空文件夹,接着使用命令行 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 方法设置一个停止响应的窗口将卡调用方的更多相关文章

  1. React(0.13) 利用componentDidMount 方法设置一个定时器

    <html> <head> <title>hello world React.js</title> <script src="build ...

  2. [问题解决]《GPU高性能编程CUDA实战》中第4章Julia实例“显示器驱动已停止响应,并且已恢复”问题的解决方法

    以下问题的出现及解决都基于"WIN7+CUDA7.5". 问题描述:当我编译运行<GPU高性能编程CUDA实战>中第4章所给Julia实例代码时,出现了显示器闪动的现象 ...

  3. 显示器驱动程序 NVIDIA Windows Kernel Mode Driver Version 已停止响应 并且己成功恢复 解决方法

    原文:http://news.160.com/?p=1890 在玩游戏中 经常 出现显示器驱动程序 NVIDIA Windows Kernel Mode Driver Version 已停止响应 并且 ...

  4. 设置一个DIV块固定在屏幕中央(两种方法)

    设置一个DIV块固定在屏幕中央(两种方法) 方法一: 对一个div进行以下设置即可实现居中. <style> #a{ position: fixed; top: 0px; left: 0p ...

  5. JS垃圾回收——和其他语言一样,JavaScript 的 GC 策略也无法避免一个问题:GC 时,停止响应其他操作,这是为了安全考虑

    JavaScript 内存管理 & 垃圾回收机制 标记清除 js 中最常用的垃圾回收方式就是标记清除.当变量进入环境时,例如,在函数中声明一个变量,就将这个而变量标记为“进入环境”.从逻辑上讲 ...

  6. 作用域通信对象:session用户在登录时通过`void setAttribute(String name,Object value)`方法设置用户名和密码。点击登录按钮后,跳转到另外一个页面显示用户

    作用域通信对象:session session对象基于会话,不同用户拥有不同的会话.同一个用户共享session对象的所有属性.作用域开始客户连接到应用程序的某个页面,结束与服务器断开连接.sessi ...

  7. mysql 5.5 win7安装最后一步总是停止响应

    今天刚开始安装了64位版本的mysql5.5 ,安装很顺利,后来发现库不兼容的问题,于是卸载,安装mysql-5.5.27-win32的32位版本,奇怪了,怎么安装,怎么卸载都不行,就是到最 后一步停 ...

  8. 在生成一个窗体的时候,点击窗体的右上角关闭按钮激发窗体事件的方法:窗体Frame为事件源,WindowsListener接口调用Windowsclosing()。

    事件模式的实现步骤: 开发事件对象(事件发送者)——接口——接口实现类——设置监听对象 一定要理解透彻Gril.java程序.   重点:学会处理对一个事件源有多个事件的监听器(在发送消息时监听器收到 ...

  9. worker 启动时向 etcd 注册自己的信息,并设置一个带 TTL 的租约,每隔一段时间更新这个 TTL,如果该 worker 挂掉了,这个 TTL 就会 expire 并删除相应的 key。

    1.通过etcd中的选主机制,我们实现了服务的高可用.同时利用systemd对etcd本身进行了保活,只要etcd服务所在的机器没有宕机,进程就具备了容灾性. https://mp.weixin.qq ...

  10. 【ASP.NET Core】设置Web API 响应的数据格式——Produces 特性篇

    开春首文,今天老周就跟各位大伙伴们聊一个很简单的话题:怎么设定API响应的数据格式. 说本质一点,就是设置所返回内容的 MIME 类型(Content-Type 头).当然了,咱们不会使用在HTTP管 ...

随机推荐

  1. Excalidraw:绘制图形的新利器

    摘要: Excalidraw是一款简洁设计.直观易用的绘图应用,用户可以通过它创建流程图.示意图.架构图等各种图形.除了提供手绘效果外,Excalidraw还支持多人实时协作编辑,并提供端到端加密以确 ...

  2. 02.Android之IPC机制问题

    目录介绍 2.0.0.1 什么是Binder?为什么要使用Binder?Binder中是如何进行线程管理的?总结binder讲的是什么? 2.0.0.2 Android中进程和线程的关系?什么是IPC ...

  3. Java 多级文件夹创建

    File类中的mkdir()和mkdirs(): mkdir():只能创建一层目录.  mkdirs():可以创建多层目录 String path = "E:\\lxwtest\\test& ...

  4. KingbaseES集群运维案例之---主备库failover后auto-recovery机制

    KingbaseES集群运维案例之---主备库failover后auto-recovery机制 案例说明: KingbaseES集群,在备库数据库服务down后,可以实现节点数据库服务的自动恢复:在集 ...

  5. Web Audio API 第3章 音量和响度

    此章介绍的科普物理声音知识相当有用,编程的反而涉及的少 音量和响度 Loudness 响度 注:根据<韦氏词典>,响度是"一种声音的属性,它决定了所产生的听觉感觉的大小,主要取决 ...

  6. Linux电脑如何下载QGIS?

      本文介绍在Linux操作系统Ubuntu版本中,通过命令行的方式,配置QGIS软件的方法.   在Ubuntu等Linux系统中,可以对空间信息加以可视化的遥感.GIS软件很少,比如ArcGIS下 ...

  7. MySQL 索引失效场景总结

    查询条件有 or 假设在 customer_name 字段设置了普通索引,执行以下 sql: # type: ref, possible_keys: idx_customer_name, key: i ...

  8. Access文件清理占用内存

    1.用access打开access.accdb文件 2.找到数据库工具的压缩和修复数据库,单击就行 3.数据库文件成功便成500K内存占用

  9. #线性筛,哈希#CF1225D Power Products

    题目 给定一个长度为 \(n\) 的正整数序列 \(a\),问有多少对 \((i,j),i<j\) 使得存在一个整数 \(x\) 满足 \(a_i\times a_j=x^k\) 分析 将 \( ...

  10. winrt新dx截图最小实现

    转自:https://stackoverflow.co/questions/11283015 效果还是很不错的 #include <iostream> #include <Windo ...