TONT 36483 为什么最小化的窗口具有形式上的160×31的尺寸?

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

We discussed a few months ago the issue of where windows minimized to before the taskbar was invented. In the modern taskbar world, why do minimized windows have an apparent size of 160×31?

几个月前,我们讨论过在任务栏发明之前的最小化窗口的问题。那么在如今任务栏一统天下的时代,为什么最小化的窗口会有一个形式上的160×31的尺寸呢?

The size isn’t just apparent. That’s really their size. You can see them, for example, if you fire up a program that uses the Multiple Document Interface.

这个尺寸不光是形式上的,实际上也是如此。你可以通过一些方式观察到这一点,例如启动一个食用了多文档界面(MDI)的程序。

(译注:原本这里有一张图片,但已经失效,请运用你的想象力)

Observe the appearance of the window “Book1”. This is a minimized window (though minimized to its MDI host rather than to the desktop). With the introduction of Windows Explorer, which put files on the desktop in the form of icons, it became necessary to change the appearance of minimized windows in order to avoid confusing a minimized program icon from a desktop icon. A minimized program, therefore, took the form of a miniature title bar.

观察这个叫『Book1』的窗口的外观。这是一个最小化的窗口(尽管是最小化到其MDI顶层窗口而不是桌面)。在引入了Windows资源管理器之后,桌面上允许以图标的形式放置文件了,修改最小化窗口的形式便变得有必要起来,以防人们将最小化的程序图标与文件图标相混淆(译注:Windows 95之前版本的Windows没有任务栏,最小化的程序会缩小为『桌面』上的一个图标,这里的桌面指的是『程序管理器』背后的整个屏幕空间,与Windows 95之后的『桌面』并不是同一个概念)。由此,最小化的程序使用了缩小的标题栏的形式。

The programming interface to minimized windows remained the same, for compatibility reasons. (And please let’s just agree to disagree on whether backwards compatibility is a good thing or not.) That’s why the function to tell whether a window is minimized continues to be called IsIconic, the message you receive when someone tries to restore a minimized program is still called WM_QUERYOPEN, and the OpenIcon function can still be used to “open” a minimized “icon”. All even though minimized windows haven’t looked like icons for nearly ten years.

面向最小化窗口的编程接口(与旧版Windows)保持了一致,这是出于兼容性的考虑。(至于向下兼容是一件好事还是坏事,请保持见仁见智的态度。)这就是为什么用来判断一个窗口是否最小化的方法名延续了 IsIconic 的称呼:当用户尝试复原最小化的程序时,程序获得信息仍然叫 WM_QUERYOPEN,而 OpenIcon 方法仍然可以用来『打开』一个最小化的『图标』,尽管保留了上述这些特性的同时,最小化的窗口已经跟图标的长相大相径庭将近十年了。

The OpenIcon function is just an old-fashioned way of saying ShowWindow(hwnd, SW_NORMAL), in the same way that the CloseWindow function (dating back to Windows 1.0) is an extremely old-fashioned way of saying ShowWindow(hwnd, SW_MINIMIZE).

OpenIcon 方法不过是 ShowWindow(hwnd, SW_NORMAL) 的一种旧式叫法,与此相同的还有 CloseWindow(可以追溯到 Windows 1.0)也只是 ShowWindow(hwnd, SW_MINIMIZE) 相对应的超级老的一种叫法而已。

TONT 36513 为什么 DS_SHELLFONT = DS_FIXEDSYS | DS_SETFONT?

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

You may have noticed that the numerical value of the DS_SHELLFONT flag is equal to DS_FIXEDSYS | DS_SETFONT.

你可能曾注意到 DS_SHELLFONT 这个 flag 的数字值是与 DS_FIXEDSYS | DS_SETFONT 相同的。

#define DS_SETFONT 0x40L /* 用于对话框控件的用户指定字体 */

#define DS_FIXEDSYS 0x0008L

#define DS_SHELLFONT (DS_SETFONT | DS_FIXEDSYS)

Surely that isn’t a coincidence.

这当然不是巧合。

The value of the DS_SHELLFONT flag was chosen so that older operating systems (Windows 95, 98, NT 4) would accept the flag while nevertheless ignoring it.

DS_SHELLFONT 这个 flag 的值这样设计,是为了让旧版操作系统(Windows 95、98、NT 4)可以接受这个值的同时忽略它。

This allowed people to write a single program that got the “Windows 2000” look when running on Windows 2000 and got the “classic” look when running on older systems. (If you make people have to write two versions of their program, one that runs on all systems and one that runs only on the newer system and looks slightly cooler, they will usually not bother writing the second one.)

这样的设计,使得人们可以在编写程序时,一次性做到程序在 Windows 2000 下运行时获得『Windows 2000 样式』的同时,在旧版系统上运行时获得『经典样式』。(如果你非要人们编写两个版本的程序,一个可以在所有版本的系统上运行,但另一个只是在新版系统上运行时看上去漂亮一些的话,估计人们根本就不会考虑去写后一个版本。)

The DS_FIXEDSYS flag met these conditions. Older systems accepted the flag since it was indeed a valid flag, but they also ignored it because the DS_SETFONT flag takes precedence.

DS_FIXEDSYS 这个 flag 就可以同时适应这两种情况。旧版系统可以接受这个值,因为它的确是个有效的 flag,但同时也会忽略它,因为 DS_SETFONT 这个 flag 会被优先使用。

This is one of those backwards-compatibility exercises: How do you design something so that it is possible to write one program that gets the new features on new systems while at the same time degrading gracefully on old systems?

这就是向下兼容措施的其中之一:如何设计一种机制,使得只需撰写一个版本的程序,便可以在新系统上获得新功能,同时又在旧系统上可以优雅地降级呢?

TONT 36543 Windows PowerToys 的故事

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

During the development of Windows 95, as with the development of any project, the people working on the project write side programs to test the features they are adding or to prototype a feature. After Windows 95 shipped, some of those programs were collected into the first edition of the Windows 95 Power Toys.

在 Windows 95 的开发过程中,与其他的项目一样,开发人员总会编写一些小工具,用来测试讲要加入的功能,或者为某个功能打造原型。Windows 95 发布之后,其中一些小工具被收录进了第一版的 Windows 95 PowerToys。

As I recall, the first edition contained the following toys:

根据我的回忆,第一版 PowerToys 里有以下这些玩具:

CabView

This was a handy internal tool which also served as a test of the shell folder design.

一个很有帮助的内部工具,同时也是对系统外壳文件夹设计的测试。

CDAutoPlay, DeskMenu, FlexiCD, QuickRes

These were side toys originally written by shell developers for their own personal use.

这些是系统外壳开发人员写来自用的小工具。

Command Prompt Here, Explore From Here(从此处打开命令提示符、从此处打开资源管理器)

These were proof-of-concept toys which tested the shell command extension design.

这些是对系统外壳命令扩展设计的概念论证。

Round Clock(圆形钟)

This was a program to test regional windows.

这是用来测试区域化窗口的程序。

Shortcut Target Menu

This was a feature idea that didn’t quite make it.

这是一个新功能的点子,不过没能实现。

I wasn’t around when the decision was made to package these toys up and ship them, so I don’t know what the rule was for deciding what was PowerToy-worthy and what wasn’t. Nor do I know where the name PowerToy came from. (Probably somebody just made it up because it sounded neat.)

以上这些工具打包发布的时候我并没有参与,所以我也不知道入选或不入选 PowerToy 的标准是什么,也不知道 PowerToy 这个名字是怎么来的(可能有人觉得这样念起来感觉不错,所以就这么愉快的决定了)。

Upon the enormous success of the PowerToys, a second edition was developed. This time, people knew that they were writing a PowerToy, as opposed to the first edition of the PowerToys which was merely cobbled together from stuff lying around. The second edition of the Windows 95 PowerToys added FindX, Send To X, the Telephony Locator Selector, XMouse, and Tweak UI.
Later, the kernel team released their own set of toys, known as the Windows 95 Kernel Toys. Alas, the original blurb text is not on the Microsoft downloads site, but here’s an archived copy. (In reality, it was I who wrote all of the Kernel Toys, except for the Time Zone Editor, which came from the Windows NT Resource Kit. I also wrote the somewhat whimsical original blurb.)

鉴于 PowerToys 的良好反响,这套工具又有了第二版。这一次,开发者们知道自己是在专门开发 PowerToy,而不是像第一版的 PowerToys 那样仅仅是将散落在各处的东西拼凑在一起。Windows 95 PowerToys 第二版增加了 FindX、Send To X(发送到X)、the Telephony Locator Selector(电话位置选择器,用于修改 Windows 95 模拟电话拨号的『所在位置』设置——译注)、XMouse,以及 Tweak UI。后来,内核开发组发布了他们自己的套件,并起名为 Windows 95 Kernel Toys。不巧的是,原始的那些絮絮叨叨的介绍文字已经不在微软的下载站上了,不过这里有一份备份。(实际上,除了时区编辑器之外,是我撰写了所有的 Kernel Toys,而时区编辑器是从 Windows NT Resource Kit 中拿来的。那个冗长而古怪的介绍文字也是我的作品。)

This was all back in the day when it was easy to put up something for download. No digital signatures, no virus checking, no paperwork. Just throw it up there and watch what happens. Today, things are very different. Putting something up for download is a complicated process with forms to fill out in triplicate and dark rooms with card readers. I wouldn’t be surprised if an abandoned salt mine in Montana were somehow involved.

那是个将什么东西发布出来供人下载十分简单易行的年代。没有什么数字签名,没有什么病毒检测,也没有什么文件要签署,只要把它传上去,就可以观察会发生什么化学反应了。现如今,事情已经变得完全不同,要把什么东西发出来供人下载变成了一个复杂的过程,要填一大堆一式三份的表格,还得在带着门禁卡的小黑屋之类的地方。这个过程中就算包含了蒙大拿废弃的盐矿我也不会觉得奇怪。(译注:这句没看懂,如有知道的读者请帮忙解释一下。)

Nowadays, every team at Microsoft seems to have their own PowerToys, trading on the good name of the Windows shell team who invented the whole PowerToys idea. (As far as I can tell, we don’t get any royalties from other divisions calling their toys “PowerToys”.) A quick check reveals the following PowerToys available for download from Microsoft; I may have missed some.

现在,每个微软的团队似乎都有自己的 PowerToys 套件了,这些套件也利用了发明了 PowerToys  这个主意的 Windows 系统外壳团队的名声。(据我所知,其他团队把他们的套件叫做 PowerToys 的时候,我们可一分钱版税都没收到。)快速地浏览一遍让我列出了可以从微软网站上下载到的 PowerToys,或许也有些遗漏了。

  • Microsoft PowerToys for Windows XP Tablet PC Edition [link fixed 11am]
  • PowerToys for the Pocket PC
  • PowerToys Fun Pack
  • PowerToys for Microsoft Office OneNote 2003
  • Microsoft PowerToys for Windows XP Media Center Edition 2004
  • PowerToys for Windows Media Player for Windows XP
  • Windows XP Creativity Fun Pack PowerToys Wallpaper Changer

(译注:以上原文链接已全部失效,故未再做链接,有兴趣可以去看原文)

(Plus, of course, the Windows XP PowerToys, which does come from the shell team. The Internet Explorer team originally called their stuff PowerToys, but they later changed the name to Web Accessories, perhaps to avoid the very confusion I’m discussing here.)

(另外,当然还应该算上 Windows XP PowerToys,也的确是由(Windows)系统外壳团队开发的。Internet Explorer 团队原本也把他们的套件叫做 PowerToys,不过后来改名叫 Web Accessories(网络附件)了,可能就是为了避免这里所说的混乱局面。)

What’s frustrating is that since they are all called “PowerToys”, questions about them tend to go to the shell team, since we are the ones who invented PowerToys. We frequently have to reply, “Oh, no, you’re having a problem with the XYZ PowerToys, not the classic Windows PowerToys. We’re the folks who do the classic Windows PowerToys.”

令人沮丧的是,因为这些套件都叫 PowerToys,有关各种(不同团队的)套件的问题常常会跑到系统外壳团队那里去,毕竟我们是发明了 PowerToys 的人。我们常常需要回复说,『哦,不是的,你的问题与 XYZ PowerToys 有关,而不是传统的 Windows PowerToys,我们是制作传统的 Windows PowerToys 的那批人。』

Even the blog name “PowerToys” has been co-opted by the Visual Studio team to promote their Powertoys for Visual Studio 2003.

甚至连『PowerToys』这个博客名称都被 Visual Studio 团队指派用来宣传 Visual Studio 2003 的 PowerToys 了。

Some people claim that Tweak UI was written because Microsoft got tired of responding to customer complaints. I don’t know where they got that from. Tweak UI was written because I felt like writing it.

有些人声称开发 Tweak UI 的原因是微软已经受够了回复客户的抱怨,我是不知道他们是从哪听来这种说法的。我写 Tweak UI 只是因为我想这么做而已。

That page also says that sometimes PowerToys vanish without warning. That’s true. A few years ago, all the Windows XP PowerToys were taken down so they could be given a security review. Some of them didn’t survive and didn’t come back. Other times, a PowerToy will be pulled because a serious bug was found. Since PowerToys are spare-time projects, it can take a very long time for a bug to get fixed, tested, and re-published. For example, the HTML Slide Show Wizard was pulled after a (somewhat obscure) data-loss bug was found. Fixing the bug itself took just a few days, but testing and filling out all the associated paperwork took six months.

同一篇文章里(译注:即上文中提到 Tweak UI 是微软懒得再回复客户问题的那篇博文,因原文链接已失效,未予链接)还提到有些 PowerToys 套件在未予告知的情况下就消失了,的确有这么回事。几年前,整个 Windows XP PowerToys 都下线了,以便接受安全评估,后来套件中的一些通过了评估,另一些则再也没能回来。其它场合下,某个 PowerToys 套件被撤下是因为发现了严重的 bug。由于 PowerToys 都是在空余时间制作的作品,可能需要很长的时间才能修复 bug、通过测试,然后重新上线。例如,一个叫 HTML 幻灯片向导的组件曾被撤了下来,因为其中发现了一个(隐藏得比较深的)数据丢失漏洞。修好 bug只花了几天时间,但跑测试和填写相关的文书则花了六个月。

There’s no moral to this story. Just a quick history lesson.

上面的故事没有什么寓意,只是讲点小历史而已。

TONT 36673 保持错误代码向下兼容的重要性

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

I remember a bug report that came on in an old MS-DOS program (from a company that is still in business so don’t ask me to identify them) that attempted to open the file “”. That’s the file with no name.

我记得有一个bug报告,是关于一个老旧的 MS-DOS 程序(开发这个程序的公司目前仍在存续中,所以不要问我具体是哪家公司)尝试打开文件“”,也就是一个没有名字的文件。

This returned error 2 (file not found). But the program didn’t check the error code and though that 2 was the file handle. It then began writing data to handle 2, which ended up going to the screen because handle 2 is the standard error handle, which by default goes to the screen.

这样做会使系统报告错误代码2(文件未找到),但程序没有检查错误代码,以为2就是文件句柄,然后就会开始向句柄2填充数据,而数据会显示在屏幕上,而这是因为句柄2是标准错误输出句柄,其默认行为就是输出到屏幕上。

It so happened that this program wanted to print the message to the screen anyway.

碰巧这个程序要做的就是向屏幕输出消息。

In other words, this program worked completely by accident.

换句话说,这个程序只是撞了大运正常工作了。

Due to various changes to the installable file system in Windows 95, the error code for attempting to open the null file changed from 2 (file not found) to 3 (path not found) as a side-effect.
Watch what happens.

在 Windows 95 可以安装在其上的文件系统设计的几次变动中,其中一个副作用是:尝试打开一个不存在的文件回报的错误代码从2(文件不存在)变成了3(找不到路径)。现在来看看会发生什么事。

The program tries to open the file “”. Now it gets error 3 back. It mistakenly treats the 3 as a file handle and writes to it.

程序尝试打开文件“”(空文件名)。现在它获得了错误代码3。程序照旧误打误撞将3作为文件句柄,并开始向其中写入数据。

What is handle 3?

那么句柄3是什么呢?

The standard MS-DOS file handles are as follows:

标准的 MS-DOS 文件句柄如下所示:

句柄 名称 含义
0 stdin 标准输入设备
1 stdout 标准输出设备
2 stderr 标准错误输出
3 stdaux 标准辅助设备(串口)
4 stdprn 标准打印机

What happens when the program writes to handle 3?

当程序尝试向句柄3写入时会发生什么呢?

It tries to write to the serial port.

会尝试向串口写数据。

Most computers don’t have anything hooked up to the serial port. The write hangs.

大多数计算机的串口上什么也没连,所以写操作挂起了。

Result: Dead program.

结果就是:程序死掉了。

The file system folks had to tweak their parameter validation so they returned error 2 in this case.

文件系统开发组对参数校验做了些调整,使其在这种情况下返回错误代码2(来解决这个问题)。

TONT 36683 MS-DOS 是如何报告错误代码的?

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

The old MS-DOS function calls (ah, int 21h), typically indicated error by returning with carry set and putting the error code in the AX register. These error codes will look awfully familiar today: They are the same error codes that Windows uses. All the small-valued error codes like ERROR_FILE_NOT_FOUND go back to MS-DOS (and possibly even further back).

旧式的 MS-DOS 功能调用(啊,INT 21h)通常通过在返回中设置carry标志、并将错误代码放在AX寄存器中来表明发生了错误。这些错误代码即使今天看起来也极其眼熟,因为 Windows 也使用了相同的错误代码。所有这些由小小的数字代表的错误代码——如 ERROR_FILE_NOT_FOUND ——都可以追溯到 MS-DOS(并且可能更早)。

Error code numbers are a major compatibility problem, because you cannot easily add new error code numbers without breaking existing programs. For example, it became well-known that “The only errors that can be returned from a failed call to OpenFile are 3 (path not found), 4 (too many open files), and 5 (access denied).” If MS-DOS ever returned an error code not on that list, programs would crash because they used the error number as an index into a function table without doing a range check first. Returning a new error like 32 (sharing violation) meant that the programs would jump to a random address and die.

错误代码是一项主要的兼容性问题,因为你无法简单地增加新的错误代码,而不影响已有的应用程序。例如,广为人知的是『调用 OpenFile 且失败时,可能的返回只会是3(找不到路径)、4(打开的文件数已超出上限)或者5(拒绝访问)』。如果 MS-DOS 返回了一个不在这个列表上的错误代码,(第三方)程序们就会崩溃,因为这些程序将错误代码用作了函数列表的索引,甚至连边界检查都没做。返回一个新的错误代码(例如32)会让这些程序跳到一个随机的地址,然后炸掉。

More about error number compatibility next time.

下次有机会时,我们再来说有关错误代码兼容性的事。

When it became necessary to add new error codes, compatibility demanded that the error codes returned by the functions not change. Therefore, if a new type of error occurred (for example, a sharing violation), one of the previous “well-known” error codes was selected that had the most similar meaning and that was returned as the error code. (For “sharing violation”, the best match is probably “access denied”.) Programs which were “in the know” could call a new function called “get extended error” which returned one of the newfangled error codes (in this case, 32 for sharing violation).

等到增加新的错误代码变得有必要时,兼容性需求会要求函数返回的错误代码不能改变。因此,当某个新型的错误发生时(例如共享违例),会返回一个之前『最广为人知』且含义最为接近的的错误代码。(对于『共享违例』来说,最佳的匹配项是『拒绝访问』)。那些『知道内情』的(新)程序可以通过调用名为『获取扩展错误代码』的方法来获取那些『新奇』的错误代码(在前面的例子中,程序会获得32——共享违例)。

The “get extended error” function returned other pieces of information. It gave you an “error class” which gave you a vague idea of what type of problem it is (out of resources? physical media failure? system configuration error?), an “error locus” which told you what type of device caused the problem (floppy? serial? memory?), and what I found to be the most interesting meta-information, the “suggested action”. Suggested actions were things like “pause, then retry” (for temporary conditions), “ask user to re-enter input” (for example, file not found), or even “ask user for remedial action” (for example, check that the disk is properly inserted).

这个『获取扩展错误代码』方法还返回了其它的信息,它会给你返回一个『错误类』来通知你关于问题的大致类别(资源不足?媒体硬件损坏?系统设置出错?),一个『错误核心』来告知你导致错误发生的具体设备类型(软驱?串口?内存?),以及我认为最有趣的元信息部分——『建议操作』。『建议操作』会是类似『暂停,然后重试』(对于暂时性的问题来说),『要求用户重新提供输入』(例如找不到文件这类错误),甚至『要求用户实行补救措施』(例如检查磁盘是否正确插入了)等等。

The purpose of these meta-error values is to allow a program to recover when faced with an error code it doesn’t understand. You could at least follow the meta-data to have an idea of what type of error it was (error class), where the error occurred (error locus), and what you probably should do in response to it (suggested action).

这些有关错误的元数据有助于程序在面对一个其不了解的错误代码时,从错误中恢复过来。至少你可以从元数据所描述中,知晓出错的类型(错误类)、出错的所在(错误核心)以及面对错误时可能应该进行的操作(建议操作)。

Sadly, this type of rich error information was lost when 16-bit programming was abandoned. Now you get an error code or an exception and you’d better know what to do with it. For example, if you call some function and an error comes back, how do you know whether the error was a logic error in your program (using a handle after closing it, say) or was something that is externally-induced (for example, remote server timed out)? You don’t.

可惜的是,这种丰富的错误信息设计随着16位程序退出历史舞台被遗弃了。现在当你面对错误代码或异常信息时,你最好知道自己应该做什么。例如,如果你调用了某个方法,然后返回了一个错误,你如何知道这是你程序设计中的逻辑错误(例如在关闭某个句柄后又去使用它),还是某些外界因素的导致的(例如远程服务器超时)?你没法知道。

This is particularly gruesome for exception-based programming. When you catch an exception, you can’t tell by looking at it whether it’s something that genuinely should crash the program (due to an internal logic error – a null reference exception, for example) or something that does not betray any error in your program but was caused externally (connection failed, file not found, sharing violation).

这种情形在面对以异常为错误机制的编程时尤为可惧。当你捕获了一个异常时,你没有办法通过观察异常信息,来判断是什么地方真的让你的程序崩溃了(来自内部的逻辑设计错误,例如空引用异常等等),还是某些实际上与你的程序无关、而是某些外界因素导致的(例如连接失败、未找到文件、共享违例等等)。