BianChengNan's Blog

Coding is hard, you can make it easy!


  • 首页

  • 关于

  • 标签

  • 分类

  • 归档

  • 搜索

调试实战 | 谁在偷偷占用我的文件?原来是我自己

发表于 2023-12-24 | 更新于: 2025-12-13 | 分类于 调试实战
字数统计: | 阅读时长 ≈ 分钟

缘起

之前基于 .net 官方提供的 FileSystemWatcher 写了一个文件变化监听工具,具体参考这篇文章 。主要解决了以下三个问题:

  1. 事件触发时,文件可能还不能被访问。
  2. 如果监听选项设置的过多,有可能会多次触发文件变化事件。
  3. 监听过滤器不够灵活,我没找到同时监听多种特定文件类型的方法(比如,同时只监听 .docx 和 .bmp 文件)。

为了解决问题1,我在调用用户注册的回调函数前,会先调用 WaitUntilCanAccess() 来确保文件是可访问状态。没想到在测试过程中发现了一个意想不到的问题。本文记录了解决这个问题的过程。

阅读全文 »

开发常识 | 彻底理清 CreateFile 读写权限与共享模式的关系

发表于 2023-12-23 | 更新于: 2025-12-13 | 分类于 开发
字数统计: | 阅读时长 ≈ 分钟

前言

前一阵子,我在编写文件变化监控程序的时候遇到了文件被占用的问题。很早之前写过一篇关于 CreateFile 函数的 dwDesiredAccess 和 dwShareMode 参数的笔记。我发现之前的理解不够全面、准确。为了更好的理解这两个参数的作用,我搜索了大量资料,编写了测试程序及测试脚本,参考了 xp 源码,终于搞清楚这两个参数的作用。简而言之,需要遵循以下两个规则:

规则 1:后续的访问权限与先前的共享模式不能冲突。

规则 2:后续的共享模式与先前的访问权限不能冲突。

如果你对下面的几个问题有明确的答案并且清楚的知道原因,那么可以跳过本文了。

  1. 第一次以读访问权限,写共享模式打开文件,会成功吗?
  2. 如果第一次打开成功了,第二次以写访问权限,读共享模式打开。会成功吗?
  3. 如果第二次打开成功了,第三次以读 / 写 / 读写访问权限,读写共享模式打开,会成功吗?
  4. 第一次以读访问权限,写共享模式打开文件,第二次以写访问权限,读写共享模式打开。第三次以写访问权限,读写共享模式打开,会成功吗?
阅读全文 »

如何确定线程栈的基址?

发表于 2023-10-29 | 更新于: 2025-12-13 | 分类于 调试实战 , 工具
字数统计: | 阅读时长 ≈ 分钟

缘起

很早之前,我遇到过几个与栈相关的问题,当时总结过几篇关于线程栈的文章,分别是 《栈大小可以怎么改?》、《栈局部变量优化探究,意外发现了 vs 的一个 bug ?》、《栈又溢出了》、《有趣的异常》。在这几篇总结中,简单的总结了栈溢出的原因,设置线程栈大小的方法。但是还有一点没弄清楚:操作系统是怎么知道一个线程的栈大小的?一定记录在某个位置了,否则就不能正确的在栈溢出的时候抛出异常了。不能根据 PE 头中的字段判断,因为在创建线程的时候可以指定线程栈大小。TEB 中的 StackLimit 是真正的栈底吗?带着这些疑问一起来刨根问底吧~

友情提示:结论在文章末尾。

阅读全文 »

调试实战 | 记一次有教益的 MFC 程序崩溃分析

发表于 2023-10-22 | 更新于: 2025-12-13 | 分类于 调试
字数统计: | 阅读时长 ≈ 分钟

缘起

实际项目中,resource.h 文件中的控件太多太乱了,合并代码的时候非常痛苦。为了解决这个问题,需要对 resource.h 中的 ID 进行整理。根据之前整理的成果,很快把控件 ID 按对话框分类整理好了。没想到测试的时候遇到了各种崩溃,废了好大劲儿才解决。究其原因,是对 MFC 资源管理机制认识不够深刻。尝试创建某个模块内的对话框的时候,意外地找到了其它模块中的对话框资源。MFC 到底是怎么查找资源模块的呢?应该如何排查这种问题呢?一起来看看吧。

阅读全文 »

如何查找已注册消息的名称?

发表于 2023-10-22 | 更新于: 2025-12-13 | 分类于 调试
字数统计: | 阅读时长 ≈ 分钟

TL;DR

在 Windows 中,通过 RegisterWindowMessage() 注册的消息,其消息 ID 在 0xC000 ~ 0xFFFF 之间。可以使用 GetClipboardFormatName() 根据消息 ID 反向查找已注册消息的名称。

阅读全文 »

调试实战 | 解决另外一个链接错误

发表于 2023-10-22 | 更新于: 2025-12-13 | 分类于 调试
字数统计: | 阅读时长 ≈ 分钟

缘起

最近,在加班的过程中遇到一个链接错误 —— fatal error LNK1120: 1 unresolved externals。这种错误是老朋友了,对我这种常年写 bug 的老手来说,完全不是事儿,轻松+愉快。

根据以下的排查思路基本上能解决大多数链接错误:

既然报了链接错误,说明编译已经通过了,问题基本出现在库文件上。

有可能是找不到库文件(缺少库,或者库文件搜索路径不对),可以先确认工程配置是否正确或者使用 /verbose:lib 查看链接过程。

也可能是库文件不对(没包含对应的导出符号),可以通过 dumpbin /exports error.lib > error.txt 查看 lib 库中的导出符号。按照以上步骤排查基本上可以解决绝大多数链接错误。

好的,让我们一起来实战一下吧。

阅读全文 »

一个 32 位程序的用户空间区域可以有多大?

发表于 2023-10-22 | 更新于: 2025-12-13 | 分类于 调试
字数统计: | 阅读时长 ≈ 分钟

缘起

我在《调试实战 | 记一次有教益的内存碎片转储文件分析》中分析了一个由于内存碎片导致的崩溃转储。发现一个很“奇怪”的现象——程序是 32 位的,但是在查看堆空间大小的时候,居然有将近 4GB。

相信各位小伙伴儿应该听过下面这种说法:32 位进程有 4GB 的虚拟内存,其中低 2GB 是用户空间,应用程序可以访问,高 2GB 是内核空间,应用程序不能访问,但是内核可以访问。在系统开启 /3GB 的情况下,用户空间可以提升至 3GB,内核空间被压缩到 1GB。

这也是我学到的关于 32 位进程虚拟内存相关的知识,但是上述描述不够准确。比如,开启了 /3GB,进程用户空间就一定可以提升至 3GB 吗?在 64 位系统上,上述说法还成立吗?

带着上述疑问,我翻看了微软官方文档,本文尽可能全面的总结进程的虚拟内存空间划分。因为能力有限,本文只涉及 x86/x64 平台,不涉及 ARM 平台。

阅读全文 »

调试实战 | 调试一个由内存分配失败导致的崩溃

发表于 2023-10-17 | 更新于: 2025-12-13 | 分类于 调试
字数统计: | 阅读时长 ≈ 分钟

缘起

前一阵子,朋友开发的 QT 程序遇到了一个崩溃问题,抓了 dump 。我跟着一起分析的。最后发现是由于内存分配失败导致的。

一起来练习一下排查思路吧。

阅读全文 »

调试实战 | 记一次有教益的 vs2022 内存分配失败崩溃分析(续)

发表于 2023-10-16 | 更新于: 2025-12-13 | 分类于 调试
字数统计: | 阅读时长 ≈ 分钟

前言

前一阵子遇到了 vs2022 卡死的问题,在上一篇文章中重点分析了崩溃的原因 —— 当 vs2022 尝试分配 923MB 的内存时,物理内存+页文件大小不足以满足这次分配请求,于是抛出异常。

本篇文章将重点挖掘一下 vs2022 在崩溃之前已经分配的内容。

说明: 本文很早就写了草稿,一直没时间整理发布,Finally~

阅读全文 »

调试实战 | 记一次有教益的 vs2022 内存分配失败崩溃分析

发表于 2023-10-15 | 更新于: 2025-12-13 | 分类于 调试
字数统计: | 阅读时长 ≈ 分钟

前言

之前一直以为 64 位进程很难出现内存分配异常,因为 64 位进程的虚拟内存空间非常大(总共 64 位,目前只用了 48 位,也就是 256TB,用户态可以使用一半,也就是 128TB)。没想到,前一阵子居然遇到了 vs2022( vs 终于有了 64 位的版本)分配内存失败的情况。分析到最后是因为分配 MEM_COMMIT 类型的内存失败导致的异常。一起来看看吧。

说明: 本文很早就写了草稿,一直没时间整理发布,Finally~

阅读全文 »
1234…14
BianChengNan

BianChengNan

140 日志
34 分类
227 标签
RSS
GitHub 知乎 博客园
© 2019 — 2025 BianChengNan | 全博客共 字
0%