GetDeviceCaps函数的困惑

标签: , ,

关于GetDeviceCaps函数,《Windows程序设计》中有这样的文字:

Within a Windows program you can use the GetDeviceCaps function to obtain the assumed resolution in dots per inch that the user selected in the Display applet of the Control Panel. To get these values—which in theory could be different if the video display doesn’t have square pixels—you use the indices LOGPIXELSX and LOGPIXELSY. The name LOGPIXELS stands for "logical pixels," which basically means "not the actual resolution in pixels per inch."

The device capabilities that you obtain from GetDeviceCaps with the HORZSIZE and VERTSIZE indices are documented (as I indicated earlier) as "Width, in millimeters, of the physical screen" and "Height, in millimeters, of the physical screen." These should be documented as a "logical width" and a "logical height," because the values are derived from the HORZRES, VERTRES, LOGPIXELSX, and LOGPIXELSY values. The formulas are

Horizontal Size (mm) = 25.4 × Horizontal Resolution (pixels)/ Logical Pixels X (dots per inch)

Vertical Size (mm) = 25.4 × Vertical Resolution (pixels)/ Logical Pixels Y (dots per inch)

The 25.4 constant is necessary to convert from inches to millimeters.

This may seem backward and illogical. After all, your video display has a size in millimeters that you can actually measure with a ruler (at least approximately). But Windows 98 doesn’t care about that size. Instead it calculates a display size in millimeters based on the pixel size of the display the user selects and also the resolution the user selects for sizing the system font. Change the pixel size of your display and according to GetDeviceCaps the metrical size changes. How much sense does that make?

HORZSIZE和VERTSIZE并不像文档中写的那样是屏幕的物理宽度和物理高度,而是“逻辑宽度”和“逻辑高度”,因为它们的值是根据HORZRES、VERTRES、LOGPIXELSX、LOGPIXELSY通过计算得到的,公式如下:

HORZSIZE = 25.4 * HORZRES / LOGPIXELSX

VERTSIZE = 25.4 * VERTRES / LOGPIXELSY

问题在于这个公式是Windows 98下的公式,对于现在的系统似乎并不成立,测试程序如下:

#include <stdio.h>
#include <Windows.h>

/************************************************************************/
/* By Demon                                                             */
/* http://demon.tw                                                      */
/************************************************************************/

int main()
{
    HDC hdc = GetDC(NULL);
    int x_size, y_size, x_res, y_res, x_dpi, y_dpi;

    x_size = GetDeviceCaps(hdc, HORZSIZE);
    y_size = GetDeviceCaps(hdc, VERTSIZE);

    x_res  = GetDeviceCaps(hdc, HORZRES);
    y_res  = GetDeviceCaps(hdc, VERTRES);

    x_dpi  = GetDeviceCaps(hdc, LOGPIXELSX);
    y_dpi  = GetDeviceCaps(hdc, LOGPIXELSY);

    printf(
        "HORZSIZE: %d\n"
        "VERTSIZE: %d\n"
        "HORZRES: %d\n"
        "VERTRES: %d\n"
        "LOGPIXELSX: %d\n"
        "LOGPIXELSY: %d\n",
        x_size, y_size, x_res, y_res, x_dpi, y_dpi);
    return 0;
}

在Windows 7系统中测试,当分辨率为1280 × 800,DPI为96时,输出:

HORZSIZE: 452
VERTSIZE: 282
HORZRES: 1280
VERTRES: 800
LOGPIXELSX: 96
LOGPIXELSY: 96

保持分辨率不变,将DPI改为120,输出:

HORZSIZE: 452
VERTSIZE: 282
HORZRES: 1280
VERTRES: 800
LOGPIXELSX: 120
LOGPIXELSY: 120

LOGPIXELSX和LOGPIXELSY改变了,但是HORZSIZE和VERTSIZE并没有改变。

保持DPI不变,将分辨率改为800 × 600,输出:

HORZSIZE: 282
VERTSIZE: 212
HORZRES: 800
VERTRES: 600
LOGPIXELSX: 120
LOGPIXELSY: 120

说明HORZSIZE和VERTSIZE只跟HORZRES和VERTRES有关,与LOGPIXELSX和LOGPIXELSY无关;或者说与分辨率有关,与DPI无关。

经过观察可以发现计算公式:

HORZSIZE = 25.4 * HORZRES / 72

VERTSIZE = 25.4 * VERTRES / 72

也就是原公式中的LOGPIXELSX和LOGPIXELSY变成了固定值72,至于为什么,只有微软才知道。

查了一下《Windows程序设计》第五版是1998年12月出版的,所以内容一直都没有针对新的系统更新过。虽然今年初出版了《Windows程序设计》第六版,但是开发语言使用的是C#而不是C了。

随机文章:

  1. OpenWrt SSH远程端口转发
  2. GetDeviceCaps函数的困惑
  3. 用Visual C++ 6.0(VC6)编译Notepad2
  4. jQuery设置ajax请求中的User-Agent
  5. 暴风一号病毒VBS源码解密

一条评论 发表在“GetDeviceCaps函数的困惑”上

  1. 比尔盖子说道:

    M$ 的东西就是这样的……M$ 以外的人永远不会知道这个东西到底是怎么实现的,其充量就是做一些黑箱分析。

留下回复