TL;DR
在 Windows
中,通过 RegisterWindowMessage() 注册的消息,其消息 ID
在 0xC000 ~ 0xFFFF
之间。可以使用 GetClipboardFormatName() 根据消息 ID
反向查找已注册消息的名称。
在 Windows
中,通过 RegisterWindowMessage() 注册的消息,其消息 ID
在 0xC000 ~ 0xFFFF
之间。可以使用 GetClipboardFormatName() 根据消息 ID
反向查找已注册消息的名称。
最近,在加班的过程中遇到一个链接错误 —— fatal error LNK1120: 1 unresolved externals
。这种错误是老朋友了,对我这种常年写 bug
的老手来说,完全不是事儿,轻松+愉快。
根据以下的排查思路基本上能解决大多数链接错误:
既然报了链接错误,说明编译已经通过了,问题基本出现在库文件上。
有可能是找不到库文件(缺少库,或者库文件搜索路径不对),可以先确认工程配置是否正确或者使用 /verbose:lib
查看链接过程。
也可能是库文件不对(没包含对应的导出符号),可以通过 dumpbin /exports error.lib > error.txt
查看 lib
库中的导出符号。按照以上步骤排查基本上可以解决绝大多数链接错误。
好的,让我们一起来实战一下吧。
我在《调试实战 | 记一次有教益的内存碎片转储文件分析》中分析了一个由于内存碎片导致的崩溃转储。发现一个很“奇怪”的现象——程序是 32
位的,但是在查看堆空间大小的时候,居然有将近 4GB
。
相信各位小伙伴儿应该听过下面这种说法:32
位进程有 4GB
的虚拟内存,其中低 2GB
是用户空间,应用程序可以访问,高 2GB
是内核空间,应用程序不能访问,但是内核可以访问。在系统开启 /3GB
的情况下,用户空间可以提升至 3GB
,内核空间被压缩到 1GB
。
这也是我学到的关于 32
位进程虚拟内存相关的知识,但是上述描述不够准确。比如,开启了 /3GB
,进程用户空间就一定可以提升至 3GB
吗?在 64
位系统上,上述说法还成立吗?
带着上述疑问,我翻看了微软官方文档,本文尽可能全面的总结进程的虚拟内存空间划分。因为能力有限,本文只涉及 x86/x64
平台,不涉及 ARM
平台。
经常做调试的朋友可能会遇到在 windbg
里通过 k
系列命令得到的调用栈没有太大参考意义。一般是由于线程上下文不对导致的。这时候可以通过 !analyze -v
让 windbg
自动帮我们分析出正确的调用栈及异常发生时的线程上下文。有了上下文信息,就可以执行 .cxr address_to_context
命令切换上下文,这时候再通过 k
命令查看调用栈,一般可以得到一个有意义的调用栈。
但有时候 !analyze -v
分析出来的上下文信息也是不对的。这时候就需要我们自己手动查找异常上下文了。
这不,最近我就遇到了一个需要手动查找异常上下文的情况。经过调查发现了一个非常重要的规律 —— 64
位程序中,KiUserExceptionDispatcher
函数对应栈帧的 Child-SP
的值保存了异常发生时的线程上下文。
本文完整记录了整个查找验证的过程。
吐槽:
64
位程序的参数传递方式与32
位程序大不相同,不能根据ebp
定位参数了。而是需要结合反汇编代码来推断某个函数的参数是否保存到栈上。如果没保存到栈上,基本上很难找到相关参数了。
今天查资料的时候,偶然发现了一位国外网友镜像了 TheOleNewThing
从 2003
年到 2019
年的博文,竟然有 5000
多篇(真是高产)。值得注意的是,微软官方博客中许多链接都已经失效了。为了防止这位网友的镜像链接也失效,我决定赶紧将这些内容下载保存下来。手动保存显然不现实,毕竟有 5000
多篇文章呢!所以,我决定写脚本来自动下载,这才是明智之举!如果是几年前,我肯定要亲自动手写脚本,不过如今,AI
这么强大,我无需再费力。幸运的是,我利用 Cursor 自动生成下载脚本的全过程,并对其进行了简单的修改,成功地将全部 5000
多篇文章下载并压缩存档,并按年月分类打包好了!不得不赞叹一句,AI
真是太强大了。