如何告知Windows我正在写入二进制文件?
摘要
本文解释了Windows在操作系统层面并没有内置二进制与文本模式的概念;这种区别是由运行时库(如C运行时)处理的。它澄清了Windows将所有文件视为字节,内容转换必须手动执行或通过库来完成。
<p>一位客户想知道如何告知Windows他们正在以文本模式打开文件,而不是二进制模式。这样Windows可以执行必要的文本转换,比如在换行前添加回车符,或者将ASCII转换为Unicode。</p>
<p>Windows不知道你的文件是二进制还是文本。对Windows而言,它只是一堆字节,如何解释取决于你。因此从某种意义上说,所有文件都是二进制文件。如果你想在换行前插入回车符,你必须自己动手。</p>
<p>很多时候,你使用的是高级库,比如C运行时,这种情况下你可以让库代劳,比如以<tt>"w"</tt>模式打开文件表示运行时应该将文件视为文本文件,或者以<tt>"wb"</tt>模式打开为二进制文件。但这个工作发生在运行时库中,而不是Windows本身。运行时库执行必要的转换,然后将二进制数据传递给Windows。一旦数据到达<code>WriteFile</code>,就不会再进行任何转换。</p>
<p>“但是等等,有一个旧的MS-DOS ioctl <a href="https://www.ctyme.com/intr/rb-2821.htm">AH=4401h (设置设备信息)</a>,你在DX中传递标志,并且<a href="https://www.ctyme.com/intr/rb-2820.htm">第5位是原始(二进制)模式位</a>。那么Windows版本的这个ioctl是什么?”</p>
<p>如果你更仔细地看,那个MS-DOS ioctl只适用于字符设备。<a href="https://github.com/microsoft/MS-DOS/blob/2d04cacc5322951f187bb17e017c12920ac8ebe2/v2.0/source/XENIX2.ASM#L470">如果你尝试在磁盘文件上使用它,你会得到<code>ERROR_<wbr />INVALID_<wbr />FUNCTION</code></a>。</p>
<pre>ioctl_check_permissions:
CMP AL,2
JAE ioctl_control_string
CMP AL,0
MOV AL,BYTE PTR ES:[DI+sf_fcb+fcb_devid]
JZ ioctl_read ; read the byte
OR DH,DH
JZ ioctl_check_device ; can I set with this data?
error error_invalid_data ; no DH <> 0
ioctl_check_device:
TEST AL,devid_ISDEV ; can I set this handle?
<span style="border: solid 1px currentcolor;">JZ ioctl_bad_fun ; no, it is a file.</span>
...
ioctl_bad_fun:
error error_invalid_function
</pre>
<p>这个IOCTL可以用来告诉控制台是否对输入进行行缓冲之类的设置。Win32中对应的函数是<code>SetConsoleMode</code>,大致相当于Unix中的<tt>stty</tt>。</p>
<p>如果你想要对文件执行内容转换,你必须自己动手,或者请别人(比如运行时库)为你代劳。</p>
<p>本文《<a href="https://devblogs.microsoft.com/oldnewthing/20260504-00/?p=112296">如何告知Windows我正在写入二进制文件?</a>》最初发表在<a href="https://devblogs.microsoft.com/oldnewthing">The Old New Thing</a>上。</p>
查看缓存全文
缓存时间: 2026/05/16 03:32
# 如何通知 Windows 我正在写入二进制文件? - The Old New Thing
来源:https://devblogs.microsoft.com/oldnewthing/20260504-00?p=112296
有一位客户想知道如何通知 Windows 他们正在以文本模式(而非二进制模式)打开文件。这样一来,Windows 就可以在必要时执行文本转换,比如在换行符前添加回车符,或者将 ASCII 转换为 Unicode。
Windows 并不知道你的文件是二进制还是文本。就 Windows 而言,它只是一堆字节,具体如何解读完全取决于你。所以从某种意义上说,所有文件都是二进制文件。如果你想在换行符前插入回车符,你得自己动手。
现在,通常你使用的是更高级的库,比如 C 运行时,这时你可以让库替你完成这些操作,例如以 `"w"` 模式打开文件表示运行时应将文件视为文本文件,或以 `"wb"` 模式打开为二进制文件。但这些工作是在运行时库中完成的,而不是在 Windows 本身中。运行时库执行必要的转换,然后将二进制数据传递给 Windows。一旦数据到达 `WriteFile`,就不会再有进一步的转换。
“但等等,有一个旧的 MS-DOS ioctl `AH=4401h`(设置设备信息)(https://www.ctyme.com/intr/rb-2821.htm),你可以在 DX 中传递标志,而**位 5 是原始(二进制)模式位**(https://www.ctyme.com/intr/rb-2820.htm)。那么 Windows 对应这个 ioctl 的版本是什么?”
如果再仔细看,那个 MS-DOS ioctl 仅适用于字符设备。如果你尝试在磁盘文件上使用它,会得到 `ERROR_INVALID_FUNCTION` (https://github.com/microsoft/MS-DOS/blob/2d04cacc5322951f187bb17e017c12920ac8ebe2/v2.0/source/XENIX2.ASM#L470)。
``
ioctl_check_permissions:
CMP AL,2
JAE ioctl_control_string
CMP AL,0
MOV AL,BYTE PTR ES:[DI+sf_fcb+fcb_devid]
JZ ioctl_read ; 读取字节
OR DH,DH
JZ ioctl_check_device ; 能否用此数据设置?
error error_invalid_data ; 不行,DH <> 0
ioctl_check_device:
TEST AL,devid_ISDEV ; 能否设置此句柄?
JZ ioctl_bad_fun ; 不行,它是一个文件。
...
ioctl_bad_fun:
error error_invalid_function
``
这个 IOCTL 可以用于告诉控制台诸如是否在输入时执行行缓冲之类的设置。Win32 中的等价物是 `SetConsoleMode`,大致相当于 Unix 的 `stty`。
如果你希望对文件执行内容转换,你必须自己处理,或者请其他人(比如运行时库)替你处理。
### 分类
### 主题
## 作者
Raymond Chen
Raymond 参与 Windows 的演进已超过 30 年。2003 年,他创办了一个名为“The Old New Thing”的网站,其受欢迎程度远超他最疯狂的想象——这一发展至今仍让他感到毛骨悚然。该网站还衍生了一本书,巧合的是书名也是《The Old New Thing》(Addison Wesley 2007)。他偶尔会出现在 Windows Dev Docs 的 Twitter 账户上,讲述一些毫无实用信息的故事。
相似文章
二进制文件可视化
本文描述了在 'bine' 十六进制编辑器中增强的二进制文件可视化功能的开发,包括使用 Unicode 字符的彩色输出和一种将二进制文件解释为图像以进行大规模查看的方法。
二进制文件的视觉分析
本文探讨用于视觉分析二进制文件的方法,这有助于逆向工程和网络安全等任务。
关于滥用Windows窗口类额外字节的兼容性说明
Raymond Chen 讨论了一个历史性的 Windows 兼容性问题,其中一些 16 位程序滥用窗口类额外字节来存储私有数据,以及微软如何在保持向后兼容性的同时,对 32 位和 64 位程序堵住了这个漏洞。
Windows中窗口和类额外字节的演变
本文追溯了Windows中窗口和类额外字节从16位到32位再到64位的演变,详细描述了随着句柄和指针的扩展,函数名称和数据大小的变化。
如何在Windows运行时中使用Win32结构?
本文解释了如何通过声明具有相同布局的影子结构在Windows运行时中使用Win32结构,包括具体示例和常见结构的替代方案。