我使用 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. 记录--uni-app adb安卓wifi无线调试

    这里给大家分享我在网上总结出来的一些知识,希望对大家有所帮助 方法一 adb connect连接调试 前提条件: 电脑已安装adb工具 手机和电脑连接的同一个WIFI CMD进入到adb工具所在目录, ...

  2. 美团一面:说一说Java中的四种引用类型?

    引言 在JDK1.2之前Java并没有提供软引用.弱引用和虚引用这些高级的引用类型.而是提供了一种基本的引用类型,称为Reference.并且当时Java中的对象只有两种状态:被引用和未被引用.当一个 ...

  3. FFmpeg开发笔记(三)FFmpeg的可执行程序介绍

    ​外界对于FFmpeg主要有两种使用途径,一种是在命令行运行FFmpeg的可执行程序,该方式适合没什么特殊要求的普通场景:另一种是通过代码调用FFmpeg的动态链接库,由于开发者可以在C代码中编排个性 ...

  4. 开发必会系列:J2EE是什么

    为什么Java是跨平台的? 高级语言通过编译器,转为汇编语言,汇编语言通过汇编器转为0和1. 当c转为汇编时,不同厂家cpu,用不同的指令集,所以有不同的汇编语言结果,导致c不能跨平台. java在各 ...

  5. AI金融预测领域综述文章筛选,附论文及代码链接,2021年版

    21年的综述最近读了3篇,总结笔记如下: (2021)Systematic Literature Review: Stock Price Prediction Using Machine Learni ...

  6. RepPointsV2:更多的监督任务,更强的性能 | NIPS 2020

    RepPointsV2的整体思想类似与Mask R-CNN,加入更多的任务来监督目标检测算法的学习.虽然在创新性上可能不够新颖,但论文的通用性还是很不错的,而且将角点任务的输出用于联合推理,从对比实验 ...

  7. Python BeautifulSoup 简单使用方法

  8. Spring Bean 的一生

    Spring Bean 的一生包括其从创建到消亡的整个过程: 实例创建 => 填充 => 初始化 => 使用 => 销毁. 这里需要注意的是,从 bean 实例的创建到可以使用 ...

  9. 2024年:如何根据项目具体情况选择合适的CSS技术栈

    2024年:如何根据项目具体情况选择合适的CSS技术栈 (请注意,这是一篇主观且充满个人技术偏好的文章) 方案一: antd/element ui/类似竞品 适合情形: 项目没有设计师 or 大部分人 ...

  10. OpenHarmony社区运营报告(2023年3月)

      本月快讯 • <OpenHarmony 2022年度运营报告>于3月正式发布,2022年OpenAtom OpenHarmony(以下简称"OpenHarmony" ...