TONT 37233 当人们要将安全漏洞作为功能的时候:全局可写的文件

原文链接:https://devblogs.microsoft.com/oldnewthing/20041122-00/?p=37233

If I had a nickel each time somebody asked for a feature that was a security hole…

如果每当有人要求加一个实际上是安全漏洞的功能,我就能得到一个镍币的话……

I’d have a lot of nickels.

那我应该早已攒下了很多钱。

For example, “I want a file that all users can write to. My program will use it as a common database of goodies.”

例如,『我想要一个文件,对所有用户可写,我的程序会用它来作为一个存放好东西的公用数据库。』

This is a security hole. For a start, there’s an obvious denial of service attack by having a user open the file in exclusive mode and never letting go. There’s also a data tampering attack, where the user opens the file and write zeros all over it or merely alter the data in subtle ways. Your music index suddenly lost all its Britney Spears songs. (Then again, maybe that’s a good thing. Sneakier would be to edit the index so that when somebody tries to play a Britney Spears song, they get Madonna instead.) [Minor typo fixed. 10am]

这就是一个安全漏洞。首先,这是一个很明显的拒绝服务攻击点,某用户以独占方式打开它,然后永远不关闭就可以了。此外这还是一个数据篡改漏洞,用户可以打开文件,然后将数据用0全部覆写,或者对数据做一点细微的变动,你的音乐库里所有 Britney Spears 的歌就突然全部消失了。(说实话,这样都还算好的,更加鬼鬼祟祟的人会修改索引,这样等下次有人想播 Britney Spears 的歌时,放出来的却会是 Madonna 的了。)

A colleague from the security team pointed out another problem with this design: Disk quotas. Whoever created the file is charged for the disk space consumed by that file, even if most of the entries in the file belong to someone else. If you create the file in your Setup program, then it will most likely be owned by an administrator. Administrators are exempt from quotas, which means that everybody can party their data into the file for free! (Use alternate data streams so you can store your data there without affecting normal users of the file.) And if the file is on the system partition (which it probably is), then users can try to fill up all the available disk space and crash the system.

安全团队的一位同事还指出了这种设计制造出的另一个麻烦:磁盘配额。谁创建了这个文件,谁就为此付出了与文件大小等同自己的磁盘配额,即便文件内容中大多数的条目都属于其他人。如果这个程序是在你的安装程序中创建的,那么这个文件的所有人大概率会是系统管理员(Administrator)。系统管理员是从磁盘配额管制中豁免的,意味着任何人都可以将任何数据写到这个文件里,而且还不受配额的限制。(如果使用交换数据流(译注:alternate data stream,个人认为译为『备用数据流』更佳,此处采用通行译法)的方式,你还可以将自己的数据存进交换数据流里,而不会影响到其他人的数据)。如果这个文件存放在系统分区中(大概率会是这样),那么用户就可以尝试耗尽剩余的磁盘空间,让系统崩溃。

If you have a shared resource that you want to let people mess with, one way to do this is with a service. Users do not access the resource directly but rather go through the service. The service decides what the user is allowed to do with the resource. Maybe some users are permitted only to increment the “number of times played” counter, while others are allowed to edit the song titles. If a user is hogging the resource, the server might refuse connections for a while from that user.

如果你有一项共享资源想放开给用户折腾,一种比较可行的做法是通过服务。用户需要通过服务而不是直接去访问这项资源,而服务决定了允许用户对这项资源的所作所为。例如,一些用户只有权限增加『已播放次数』的计数器,而另一些用户则可以编辑歌曲的标题等等。如果某个用户对这项资源的访问过于贪婪,服务器可以决定暂停对这个用户提供服务。

A file doesn’t give you this degree of control over what people can do with it. If you grant write permission to a user, then that user can write to any part of the file. The user can open the file in exclusive mode and prevent anybody else from accessing it. The user can put fake data in the file in an attempt to confuse the other users on the machine.

单一一个文件无法给予这种等级的控制,来管制用户可以对其进行的操作。如果你授予用户写入的权限,那用户就可以对文件的任何部分进行写入。用户可以以独占方式打开这个文件,从而阻止其他人对其的访问。用户甚至可以在文件中写入伪造的数据,借此使同一机器上的其他用户感到困惑。

In other words, the user can make a change to the system that impacts how other users can use the system. This sort of “impact other users” behavior is something that is reserved for administrators. An unprivileged user should be allowed only to mess up his own life; he shouldn’t be allowed to mess up other users’ lives.

换句话说,某个用户可以对系统做出变更,而这些变更会影响其他用户对系统的使用。这类『影响其他用户』的行为是保留给系统管理员的权力。没有特权的用户应当只被允许对其自己的生活瞎折腾,而不应被允许去折腾其他用户的生活。

Armed with this information, perhaps now you can answer this question posted to comp.os.ms-windows.programmer a few months ago.

了解了这一点之后,大概现在你就有资格去回答这个几个月前贴在comp.os.ms-windows.programmer上的问题了。

TONT 37263 当文件夹和程序拥有相同的名字时,系统会优先考虑运行程序

原文链接:https://devblogs.microsoft.com/oldnewthing/20041118-00/?p=37263

If you have both a folder named, say, C:\Folder and a program named C:\Folder.exe and you type C:\Folder into the Start.Run dialog, you get the program and not the folder.

如果有一个文件夹,比如是 C:\Folder,与此同时有一个程序,其路径为 C:\Folder.exe,当你在开始—运行中输入 C:\Folder 时,会运行那个程序而不是打开那个文件夹。

Why is that?

这是为什么呢?

Because it is common to have D:\Setup.exe D:\Setup\… where there is a setup program in the root, as well as a setup folder containing files needed by the setup program.

因为同时在根目录下包含作为安装程序的 D:\Setup.exe 和作为存储安装程序所需文件的 D:\Setup\ 这个目录是很常见的情况。

Before Windows 95, you couldn’t open a folder by typing its name. (If you wanted to view it in File Manager, you had to run File Manager explicitly.) As a result, programs written for earlier versions of Windows would have instructions like

在 Windows 95 之前,你是不能通过输入目录的名字(译注:即输入到目录为止的路径)来打开一个目录的。(如果你想在“文件管理器”中查看目录的内容,你必须刻意去运行“文件管理器”)因此,为早期版本Windows撰写的程序通常都包含有类似如下的操作指南:

  • Insert the floppy disk labelled “Setup”. (CDs were for the rich kids.)
    插入标有『Setup』(安装)标签的软盘。(那年头CD是有钱人才用得起的东西)
  • From Program Manager, click File, then Run.
    在“程序管理器”中,单击“文件”,然后选择“运行”
  • In the dialog box, type “A:\SETUP” and press Enter.
    在弹出的对话框中,输入“A:\SETUP”,然后按下回车键

Since there was no such thing as “opening a folder”, the only option was to run the program A:\SETUP.EXE.

由于并没有(通过运行对话框来)“打开目录”这种操作,(向上面这样做)唯一的结果就是运行了程序 A:\SETUP.EXE。

Windows 95 was required to prefer the program over the folder in order that those instructions would remain valid (substituting the Start button for the File menu).

Windows 95 为了能让上面这样的操作指示仍然有效,(在遇到目录和程序同名的情况时)会优先选择运行程序而不是打开目录本身(当然还要将『文件菜单』替换为『开始菜单』)。

And each version of Windows that prefers the program over the folder creates an environment wherein people who write setup programs rely on that preference, thereby securing this behavior for the next version of Windows.

另外,每个版本的 Windows 这种优先运行程序而不是打开文件夹的环境,又进一步促使用户在设计安装程序时依赖这种设计,进一步保证了这种设计可以被延续到下一个版本的 Windows 中。

But what if you really want to open the folder?

不过,如果你真的想打开那个同名目录怎么办?

Append a backslash to force the path to be interpreted as a folder (A:\SETUP\).

在最后加一个反斜杠(\),使其强制被解释为文件夹(A:\SETUP\)就可以了。

TONT 37443 为什么桌面窗口的尺寸没有缩水,将任务栏排除在外?

原文链接:https://devblogs.microsoft.com/oldnewthing/20041029-00/?p=37443

The taskbar created all sorts of interesting problems, since the work area was not equal to the entire screen dimensions. (Multiple monitors created similar problems.) “Why didn’t the gui return the usable workspace as the root window (excluding the taskbar)?”

任务栏制造了各种各样有趣的问题,由于它的存在,工作区域的尺寸与整个屏幕的分辨率并不是相等的。(多显示器环境同样制造了类似的问题。)『为什么GUI不将可用的工作区域作为顶层窗口(排除任务栏)返回呢?』

That would have made things even worse.

那样会让事情变得更糟糕。

Lots of programs want to cover the entire screen. Games, for example, are very keen on covering the entire screen. Slideshow programs also want to cover the entire screen. (This includes both slideshows for digital pictures as well as business presentations.) Screen savers of course must cover the entire screen.

很多程序都有遮挡整个屏幕的需求。例如游戏,在这方面的需求就很强烈。幻灯片应用程序也需要遮挡整个画面(这类程序同时包含了展示数码图片和商业幻灯的场合)。屏幕保护程序则必须遮挡住整个画面。

If the desktop window didn’t include the taskbar, then those programs would leave a taskbar visible while they did their thing. This is particularly dangerous for screen savers, since a user could just click on the taskbar to switch to another program without going through the screen saver’s password lock!

如果桌面窗口没有包含任务栏区域,那么这些程序在运行时任务栏就会可见。这种情形对屏幕保护程序尤为危险,因为这样一来用户就可以通过点击任务栏来切换到其它程序,从而绕过屏幕保护程序的密码保护了!

And if the taskbar were docked at the top or left edge of the screen, this would have resulted in the desktop window not beginning at coordinates (0,0), which would no doubt have caused widespread havoc. (Alternatively, one could have changed the coordinate system so that (0, 0) was no longer the top left corner of the screen, but that would have broken so many programs it wouldn’t have been funny.)

此外,如果任务栏停靠在屏幕的上方或边缘(译注:应当指的是左侧),会使得桌面窗口的左上角坐标不再是(0,0),而这样毫无疑问将会制造出大笔的麻烦。(换个思路想想的话,把坐标系统进行一点『小小的改动』,让左上角的坐标不再是(0,0)也是一种做法,但这样大概会搞炸一大堆程序,所以这一点也不好玩。)

TONT 37453 在任务栏被发明出来之前,最小化的窗口都去了哪里?

原文链接:https://devblogs.microsoft.com/oldnewthing/20041028-00/?p=37453

Before Explorer was introduced in Windows 95, the Windows desktop was a very different place.

在资源管理器被引入到 Windows 95 之前,桌面是完全大相径庭的另一个物种。

The icons on your desktop did not represent files; rather, when you minimized a program, it turned into an icon on the desktop. To open a minimized program, you had to hunt for its icon, possibly minimizing other programs to get them out of the way, and then double-click it. (You could also Alt+Tab to the program.)

桌面上的图标不是用来代表文件的,与这种印象不同,当你最小化一个程序时,程序就会变成桌面上的一个图标。要打开一个最小化的程序,你得先找到它的图标,或许还得先把其他挡住视线的程序最小化,然后再双击图标(来打开这个最小化的程序)。(你也可以用Alt+Tab切换过去。)

Explorer changed the desktop model so that icons on your desktop represent objects (files, folders) rather than programs. The job of managing programs fell to the new taskbar.

资源管理器改变了桌面的模型,此后桌面上的图标就变成了代表对象(文件、文件夹)而不是程序。管理程序的任务就落到了新生的任务栏上。

But where did the windows go when you minimized them?

不过当程序最小化的时候,它的窗口去哪了呢?

Under the old model, when a window was minimized, it displayed as an icon, the icon had a particular position on the screen, and the program drew the icon in response to paint messages. (Of course, most programs deferred to DefWindowProc which just drew the icon.) In other words, the window never went away; it just changed its appearance.

在旧的模型之下,当一个窗口最小化时,其表现为一个图标,这个图标在屏幕上有特定的位置,而程序在响应描画的窗体消息时,会对这个图标进行绘制。(当然,大多数程序推迟了绘制图标的 DefWindowProc 方法。)换句话说,程序的窗口从来都没有消失,只是换了个表现形式。

But with the taskbar, the window really does go away when you minimize it. Its only presence is in the taskbar. The subject of how to handle windows when they were minimized went through several iterations, because it seemed that no matter what we did, some program somewhere didn’t like it.

但自从有了任务栏以后,最小化的程序的窗口就真的消失了,其存在就仅仅表现在任务栏中而已。对于如何管控最小化的窗口的设计经历了几次迭代,原因是不管我们做什么,总有一些应用程序不喜欢。

The first try was very simple: When a window was minimized, the Windows 95 window manager set it to hidden. That didn’t play well with many applications, which cared about the distinction between minimized (and visible) and hidden (and not visible).

第一次的尝试很简单:当一个窗口最小化后,Windows 95 的窗口管理器就将其设置为隐藏。这样的设计与很多程序配合都不好,因为这些程序会严格区分最小化(同时可见)与隐藏(同时不可见)的区别。

Next, the Windows 95 window manager minimized the window just like the old days, but put the minimized window at coordinates (-32000, -32000), This didn’t work because some programs freaked out if they found their coordinates were negative.

后来,Windows 95 的窗口管理器用以往的方法将窗口最小化,但将最小化的窗口移动到坐标(-32000,-32000)。这样做的效果依然不彰,因为有些程序发现自己的坐标是负值时感觉非常不安。

So the Windows 95 window manager tried putting minimized windows at coordinates (32000, 32000), This still didn’t work because some programs freaked out if they found their coordinates were positive and too large!

所以Windows 95的窗口管理器又转而尝试将最小化的窗口放到坐标(32000,32000)上。这样做的效果仍旧不好,因为有些程序发现自己的坐标是很大的正值时吓坏了。

Finally the Windows 95 window manager tried coordinates (3000, 3000), This seemed to keep everybody happy. Not negative, not too large, but large enough that it wouldn’t show up on the screen (at least not at screen resolutions that were readily available in 1995).

最终,Windows 95 窗口管理器将移动的坐标换成了(3000,3000),这样大家似乎都很满意了。不是负值,不是很大的值,但又足够大使得不会显示在屏幕上(至少以1995年较常见的屏幕分辨率来说如此)。

If you have a triple-monitor Windows 98 machine lying around, you can try this: Set the resolution of each monitor to 1024×768 and place them corner-to-corner. At the bottom right corner of the third monitor, you will see all your minimized windows parked out in the boonies.

如果你有一台三显示器配置的 Windows 98 机器在手边,你可以尝试这样操作一下:将每台显示器的分辨率设置为1024×768,然后将三台显示器底角对顶角地排列起来。在右下角的第三台显示器上,你就能看到那些最小化的窗口们在屏幕的『郊区』安居乐业的样子了。

(Windows NT stuck with the -32000 coordinates and didn’t pick up the compatibility fixes for some reason. I guess they figured that by the time Windows NT became popular, all those broken programs would have been fixed. In other words: Let Windows 95 do your dirty work!)

(出于某些原因,Windows NT 则保留了移动到坐标-32000的做法,没有采用(移动到坐标3000的)那个兼容性补丁。我猜开发人员们大概在想等 Windows NT 普及了,那些不听话的应用程序大概都已经修复这个问题了。换句话说:让 Windows 95 去做那些苦活吧!)

TONT 37503 为什么安装程序不会问你是否要保留较新版本的系统文件?

原文链接:https://devblogs.microsoft.com/oldnewthing/20041022-00/?p=37503

Windows 95 Setup would notice that a file it was installing was older than the file already on the machine and would ask you whether you wanted to keep the existing (newer) file or to overwrite it with the older version.

Windows 95 的安装程序会注意到正在安装的文件相较于机器上已有的版本比较旧,于是就会问你是否要保留现有(较新)的文件,或者用较旧的版本进行覆盖替换。(译注:用过Win95的人都知道这是一个很容易令人摸不到头脑的问题)

Asking the user this question at all turned out to have been a bad idea. It’s one of those dialogs that ask the user a question they have no idea how to answer.

事实上向用户提出这个问题本身就是一个很差劲的主意。这种对话框属于那种用户完全不知道如何作答的类型。

Say you’re installing Windows 95 and you get the file version conflict dialog box. “The file Windows is attempting to install is older than the one already on the system. Do you want to keep the newer file?” What do you do?

比如说,你正在安装Windows 95,然后碰到了一个版本冲突提示对话框,上面写着:『Windows 当前尝试安装的文件相较于您机器上的文件版本较旧,是否保留较新的文件?』你会怎么做?

Well, if you’re like most people, you say, “Um, I guess I’ll keep the newer one,” so you click Yes.

如果你跟大多数人的想法一样,会想:『唔,那就保留新版本的文件吧』,然后点击了『是』。

And then a few seconds later, you get the same prompt for some other file. And you say Yes again.

没过几秒钟,对话框又弹了出来,这次换了个文件,你又点了『是』。

And then a few seconds later, you get the same prompt for yet another file. Now you’re getting nervous. Why is the system asking you all these questions? Is it second-guessing your previous answers? Often when this happens, it’s because you’re doing something bad and the computer is giving you one more chance to change your mind before something horrible happens. Like in the movies when you have to type Yes five times before it will launch the nuclear weapons.

又过了几秒钟,对话框换了个文件又一次弹了出来,这下你开始变得焦虑起来了。为什么系统会问我这些问题?是想让我对前面的问题再作三思吗?类似这样的情况发生时,通常是因为你做错了什么事,而计算机正在事情变得无法挽留之前再给你一次改变主意的机会,就像在电影里,在发射核武器之前需要输入五次『Yes』一样。

Maybe this is one of those times.

而眼下大概就是那种场景。

Now you start saying No. Besides, it’s always safer to say No, isn’t it?

于是你开始点『否』了。说实话,说『否』更安全些,不是吗?

After a few more dialogs (answering No this time), Setup finally completes. The system reboots, and… it bluescreens.

又看过几个对话框之后(而你在面对这些对话框时都点了『否』),安装程序终于收工,计算机重启,然后……等着你的是一片蓝屏。

Why?

怎么会这样?

Because those five files were part of a matched set of files that together form your video driver. By saying Yes to some of them and No to others, you ended up with a mishmash of files that don’t work together.

因为你点了『否』的那五个文件是你的显卡驱动的一部分。对其中一部分文件选择了『是』,又对另一部分选择了『否』,结果新旧文件掺杂在一起,无法正常工作。

We learned our lesson. Setup doesn’t ask this question any more. It always overwrites the files with the ones that come with the operating system. Sure, you may lose functionality, but at least you will be able to boot. Afterwards, you can go to Windows Update and update that driver to the latest version.

我们接受了这个教训,安装程序再也不会问这类问题了,现在它总是会用随操作系统出厂的文件对其进行覆盖。的确,这样会损失一些新功能,但至少可以正常启动。在那之后,你总可以去Windows Update里将驱动更新到新版本。

Note, however, that this rule does not apply to hotfixes and Service Packs.

不过需要注意的是,这样的规则并不适用于系统补丁(Hotfix)和服务包(Service Packs)。

TONT 37523 资源管理器是如何检测你的程序是否支持长文件名的?

原文链接:https://devblogs.microsoft.com/oldnewthing/20041020-00/?p=37523

When you register your program with a file association, the shell needs to decide whether your program supports long file names so it can decide whether to pass you the long name (which may contains spaces! so make sure you put quotation marks around the “%1” in your registration) or the short name.

当你为自己的应用程序建立文件关联时,系统需要知道你的应用程序是否支持长文件名,这样才能决定是否向你的程序传递长文件名(可能会包含空格!所以请保证在注册文件关联时在%1前后包上西文引号)还是短文件名。

The rule is simple: The shell looks at your program’s EXE header to see what kind of program it is.

规则很简单:系统会依据程序exe文件的文件头来进行判断。

  • If it is a 16-bit program, then the shell assumes that it supports long file names if it is marked as Windows 95-compatible. Otherwise, the shell assumes that it does not support long file anmes.
    如果是16位应用程序,并且标记为Windows 95兼容,系统会认为其支持长文件名,否则认为不支持。(译注:anmes疑为names误植,此处原样保留)
  • If it is a 32-bit program (or 64-bit program for 64-bit systems), then the shell assumes that it supports long file names.
    如果是32位应用程序(或面向64位系统的64位应用程序),系统会认为其支持长文件名。
  • If it can’t find your program, then the shell plays it safe and assumes that the program doesn’t support long file names.
    如果找不到你的程序,系统会保守起见认为其不支持长文件名。

Note that third case. If you mess up your program registration, then the shell will be unable to determine whether your program supports long file names and assumes not. Then when your program displays the file name in, say, the title bar, you end up displaying some icky short file name alias instead of the proper long file name that the user expects to see.

注意第三种情况。如果你注册文件关联的时候玩脱了,那么系统就无法判定你的程序是否支持长文件名,并假定其不支持。这样如果你的程序在某些地方显示文件名(比如标题栏)的话,就会显示成那种看上去很讨厌的短文件名别名,而不是用户期望看到的长文件名。

The most common way people mess up their program registration is by forgetting to quote spaces in the path to the program itself! For example, an erroneous registration might go something like this:

最常见玩脱注册文件关联的方式,是忘记将程序路径包含在引号的里面!例如,某程序错误地注册文件关联的情况如下所示:

HKEY_CLASSES_ROOT\litfile\shell\open\command

(default) = C:\Program Files\LitWare Deluxe\litware.exe “%1”

Observe that the spaces in the path “C:\Program Files\Litware Deluxe\litware.exe” are not quoted in the program registration. Consequently, the shell mistakenly believes that the program name is “C:\Program”, which it cannot find. The shell therefore plays it safe and assumes no LFN support.

注意观察程序路径『C:\Program Files\Litware Deluxe\litware.exe』并没有用引号括起来。由此,系统便错误地认为程序的名称是『C:\Program』,并且也找不到对应的文件,所以系统决定保守起见,认为这个程序不支持长文件名。

Compatibility note: As part of other security work, the code in the shell that parses these command lines was augmented to chase down the “intended” path of the program. This presented the opportunity to fix that third case, so that the shell could find the program after all and see that it supported long file names, thereby saving the user the ignominy of seeing their wonderful file name turn into a mush of tildes.

这里有一个兼容性相关的小故事:作为安全方面考量的一环,系统中有关处理这些命令行的代码曾被要求可以主动找出对应程序『原本』的路径。这样的设计提供了一种对前述第三种情况补救的机会,系统可能因此找到对应的程序文件,并实际判断其是否支持长文件名,如此一来用户就不会对其美妙的文件名变成一堆小浪花(译注:~符号,长文件名压缩到短文件名时用以区别多个压缩后同名文件的机制之一,形如FILENA~1.DOC、FILENA~2.DOC等)而干瞪眼了。

And after we made the change, we had to take it out.

我们在做出这样的改进后,又不得不将其撤了下来。

Because there were programs that not only registered themselves incorrectly, but were relying on the shell not being smart enough to find their real location, resulting in the program receiving the short name on the command line. Turns out these programs wanted the short name, and doing this fake-out was their way of accomplishing it.

因为(这样改进之后)某些应用程序不光不用正确的方式去注册文件关联,甚至还依赖这种原先系统无法找到其程序文件的缺陷,由此使系统传短文件名进去。我们发现这个程序就是想要短文件名,而它则是用这种欺骗的手段来达成目的的。

(And to those of you who are already shouting, “Go ahead and break them,” that’s all fine and good as long as the thing that’s incompatible isn’t something you use. But if it’s your program, or a program your company relies on, I expect you’re going to change your tune.)

(此外,对那些此时已经在大喊『那就别管他们啊』的人们,只要这不是你非用不可的软件,怎么样都是好的。但如果这样做的就是你的软件,或者你们公司依赖的软件,我很乐意看到你收回你的说法。)

TONT 37533 即使是内部数据也会有兼容性约束的需求

原文链接:https://devblogs.microsoft.com/oldnewthing/20041019-00/?p=37533

The Listview control when placed in report mode has a child header control which it uses to display column header titles. This header control is the property of the listview, but the listview is kind enough to let you retrieve the handle to that header control.

当Listview控件置于报表模式下时,其内有一个表头控件,用来展示列标题。这个表头控件是属于Listview的“私有财产”,不过Listview很大方,允许你去获取这个表头空间的句柄。

And some programs abuse that kindness.

而有些应用程序就滥用了这种大方。

It so happens that the original listview control did not use the lParam of the header control item for anything. So some programs said, “Well, if you’re not using it, then I will!” and stashed their own private data into it.

凑巧,原始版本的Listview没有使用这个表头控件的lParam,这时个别程序就决定,『你不用的话,我就用一下好了!』然后将其内部数据塞了进去。

Then a later version of the listview decided, “Gosh, there’s some data I need to keep track of for each header item. Fortunately, since this is my header control, I can stash my data in the lParam of the header item.”

后续版本的Listview做了个决定:『哎呀,这些个表头有些数据我得跟踪监视,不过还好,这个表头控件是属于我的,只要把数据藏进lParam里去修好了。』

And then the application compatibility team takes those two ingredients (the program that stuffs data into the header control and the listview that does the same) to their laboratory, mixes them, and an explosion occurs.

后来,应用程序兼容性团队把这两种成分(把私有数据塞进表头控件里的应用、和同样这样做的Listview)带进了实验室,将二者混合,然后就炸了。

After some forensic analysis, the listview development team figures out what happened and curses that they have to work around yet another program that grovels into internal data structures. The auxiliary data is now stored in some other less convenient place so those programs can continue to run without crashing.

经过一番正经八百的论证,Listview控件的开发团队找到了问题的原因,并选择在这又一个随便挖进内部数据结构的应用程序面前退让一步。前面提到的辅助数据现在被放进了不太容易被乱来的地方,这样那个乱改(Listview)内部数据的程序就能继续运行而不崩溃了。

The moral of the story: Even if you change something that nobody should be relying on, there’s a decent chance that somebody is relying on it.

故事的寓意是:即便你修改了别人不该依赖的位置,还是有相当的可能有人是依赖它的。

(I’m sure there will be the usual chorus of people who will say, “You should’ve just broken them.” What if I told you that one of the programs that does this is a widly-used system administration tool? Eh, that probably wouldn’t change your mind.)

(我敢肯定肯定又有很多人要说,『别管那家伙不就好了』,要是我告诉你这样做的程序之一是某个被广泛使用的系统管理工具呢?不过,这大概也不会让你收回前言吧。)

TONT 37623 为什么会有一个单独的 GetSystemDirectory 方法?

原文链接:https://devblogs.microsoft.com/oldnewthing/20041008-00/?p=37623

If the system directory is always %windir%\SYSTEM32, why is there a special function to get it?

如果系统目录总会是 %windir%\SYSTEM32,为什么还有专门的一个方法来获取它?

Because it wasn’t always that.

因为并不总是如此。

For 16-bit programs on Windows NT, the system directory is %windir%\SYSTEM. That’s also the name of the system directory for Windows 95-based systems and all the 16-bit versions of Windows.

对于 Windows NT 下的 16 位应用程序来说,系统目录是 %windir%\SYSTEM。对于以 Windows 95 为基础、以及所有16位版本的 Windows 来说也是如此。

But even in the 16-bit world, if it was always %windir%\SYSTEM, why have a function for it?

然而,即便在16位的世界里,如果系统目录总会是 %windir%\SYSTEM 的话,那为什么还需要专门的一个方法呢?

Because even in the 16-bit world, it wasn’t always %windir%\SYSTEM.

因为即使在16位的世界里,也并不总是如此。

Back in the old days, you could run Windows directly over the network. All the system files were kept on the network server, and only the user’s files were kept on the local machine. What’s more, every single computer on the network used the same system directory on the server. There was only one copy of USER.EXE, for example, which everybody shared.

在过去,你可以直接通过网络来运行Windows。所有的系统文件都存放在网络服务器上,只有用户的文件保存在本地。另外,网络上的所有计算机都会调用服务器上相同的系统目录,例如,只会有一份USER.exe供所有人共享使用。

Under this network-based Windows configuration, the system directory was a directory on a server somewhere (\\server\share\somewhere) and the Windows directory was a directory on the local machine (C:\WINDOWS). Clients did not have write permission into the shared system directory, but they did have permission to write into the Windows directory.

在这种以网络为基础配置的Windows环境下,系统目录是存放在服务器上的某处的(例如\\server\共享名\共享目录),而Windows目录是本机上的某个文件夹(例如C:\WINDOWS)。客户端没有权限写入系统目录,不过的确有权限对Windows目录进行写入。

That’s why GetSystemDirectory is a separate function.

这就是为什么GetSystemDirectory是单独存在的一个方法。

TONT 37903 有时候游戏的 bug 不玩到后期是体现不出来的

原文链接:https://devblogs.microsoft.com/oldnewthing/20040910-00/?p=37903

I didn’t debug it personally, but I know the people who did. During Windows XP development, a bug arrived on a computer game that crashed only after you got to one of the higher levels.

我并不亲自进行调试工作,不过我认识做这项工作的人。在 Windows XP 的开发过程中,曾经有款游戏的一个 bug,只有玩到进度比较深入的时候才会显现,并使游戏崩溃。

After many saved and restored games, the problem was finally identified.

存档读档很多次之后,问题终于定位出来了。

The program does its video work in an offscreen buffer and transfers it to the screen when it’s done. When it draws text with a shadow, it first draws the text in black, offset down one and right one pixel, then draws it again in the foreground color.

游戏的程序在一处屏幕外的缓冲区中处理图像,当处理完成后,再传输到屏幕上。绘制带阴影的文字时,程序会先以黑色将文字绘制一遍,将其向右、向下移动各1个像素,然后再用前景色将文字绘制出来。

So far so good.

到此为止还没什么问题。

Except that it didn’t check whether moving down and right one pixel was going to go beyond the end of the screen buffer.

只是开发商忘了检查向右、向下移动各1个像素的时候,有没有超出屏幕缓冲区的边界。

That’s why it took until one of the higher levels before the bug manifested itself. Not until then did you accomplish a mission whose name contained a lowercase letter with a descender! Shifting the descender down one pixel caused the bottom row of pixels in the character to extend past the video buffer and start corrupting memory.

这就是为什么只有打到高等级的时候 bug 才会显现,因为直到那时才会完成一个任务,而这个任务的名字里有一个带下延部(注1)的小写字母!将这个字母的下延部下移1个像素,会导致底部一行的像素超出视频缓冲区,进而损毁了内存数据。

Once the problem was identified, fixing it was comparatively easy. The application compatibility team has a bag of tricks, and one of them is called “HeapPadAllocation”. This particular compatibility fix adds padding to every heap allocation so that when a program overruns a heap buffer, all that gets corrupted is the padding. Enable that fix for the bad program (specifying the amount of padding necessary, in this case, one row’s worth of pixels), and run through the game again. No crash this time.

找到问题的根源后,修复起来就相对比较容易了。应用程序兼容性团队有一口袋的戏法,其中之一名叫『HeapPadAllocation』。这一兼容性修复补丁会为每个堆分配增加一块补丁,这样当程序发生了堆缓冲区溢出的问题时,弄坏的就只是这块补丁而已。为这个惹事的程序启用这个补丁(指定所需的补丁大小,此处即1行像素的尺寸),再运行游戏,就不会再崩溃了。

What made this interesting to me was that you had to play the game for hours before the bug finally surfaced.

这件事让我感到有趣的一点是,你得把这款游戏先玩上4个小时,然后bug才会崭露头角。


注1:在西文字体排印学中,降部(英语:Descender)指的是一个字体中,字母向下延伸超过基线的笔画部分,也称为下延部。如图所示,字母y第二笔的“尾巴”部分就是降部。另外字母v两条对角线连接的时候也有超过基线的部分,虽然很少,但也是降部。(以上信息来自维基百科『降部』词条

TONT 37983 为什么Windows会将你的BIOS时间设定为本地时间?

原文链接:https://devblogs.microsoft.com/oldnewthing/20040902-00/?p=37983

Even though Windows NT uses UTC internally, the BIOS clock stays on local time. Why is that?

尽管 Windows NT 内部使用 UTC 时间,但 BIOS 的时钟却仍然设定为本地时间,这是为什么呢?

There are a few reasons. One is a chain of backwards compatibility.

原因有几个,其中之一是一系列向下兼容性问题。

In the early days, people often dual-booted between Windows NT and MS-DOS/Windows 3.1. MS-DOS and Windows 3.1 operate on local time, so Windows NT followed suit so that you wouldn’t have to keep changing your clock each time you changed operating systems.

早年间,人们经常在 Windows NT 和 MS-DOS / Windows 3.1 之间双启动。MS-DOS 和 Windows 3.1 使用本地时间,所以 Windows NT 延续了这一设计,以便你每次更换(启动的)操作系统的时候不用总是去修改时间。

As people upgraded from Windows NT to Windows 2000 to Windows XP, this choice of time zone had to be preserved so that people could dual-boot between their previous operating system and the new operating system.

后来人们从 Windows NT 升级到了 Windows 2000 直至 Windows XP,对于时区的设定也需要被保留,以便于人们可以在以前和后来的操作系统间进行双启动。

Another reason for keeping the BIOS clock on local time is to avoid confusing people who set their time via the BIOS itself. If you hit the magic key during the power-on self-test, the BIOS will go into its configuration mode, and one of the things you can configure here is the time. Imagine how confusing it would be if you set the time to 3pm, and then when you started Windows, the clock read 11am.

另一个将 BIOS 时钟设定为本地时间的原因是避免使人们通过 BIOS 设置程序设定时间时产生疑惑。如果在开机自检的时候按下相关的按键,BIOS 会进入设置模式,其中一项可以进行设置的就是当前时间。想象一下如果你把时间设置为下午3点,等进了 Windows 时却发现时间变成了早上11点的感觉。

“Stupid computer. Why did it even ask me to change the time if it’s going to screw it up and make me change it a second time?”

『电脑这玩意简直太蠢了,既然它又会把时间设置搞乱再让我去修改第二次,那从一开始为啥要让我去改呀?』

And if you explain to them, “No, you see, that time was UTC, not local time,” the response is likely to be “What kind of totally propeller-headed nonsense is that? You’re telling me that when the computer asks me what time it is, I have to tell it what time it is in London? (Except during the summer in the northern hemisphere, when I have to tell it what time it is in Reykjavik!?) Why do I have to remember my time zone and manually subtract four hours? Or is it five during the summer? Or maybe I have to add. Why do I even have to think about this? Stupid Microsoft. My watch says three o’clock. I type three o’clock. End of story.”

这时候如果你像这类人去解释:『不是的,那个时间是 UTC 时间,不是本地时间』,很可能会被回击『那又是他喵的什么鬼东西?你是说电脑让我设置时间的时候,我得把伦敦时间给输进去?(除了北半球夏季的时候,那就得输入雷克雅未克的时间了!?)凭什么我得记住我所在的时区,然后手工减去四个小时,或者夏令时的时候得减五个小时?哦,有时候还得用加的,为什么我非得做这些事不可啊?傻×微软,我的手表说现在是三点,我就输入三点,完事。』

(What’s more, some BIOSes have alarm clocks built in, where you can program them to have the computer turn itself on at a particular time. Do you want to have to convert all those times to UTC each time you want to set a wake-up call?)

(更有甚者,有些 BIOS 还有内置的闹钟功能,可以通过设置它来让计算机在特定的时间启动。你会乐意每次设置它之前,手工先将所有的时间转成 UTC 时间吗?)