windows程序设计(第5版)第4章,最后一个程序中看不懂,帮解释一下,谢谢!

philmei 2010-01-09 06:40:26
windows程序设计(第5版)第4章,最后一个程序
我把书中的代码贴出来,然后在其中指出我要问的问题

/*----------------------------------------------------
SYSMETS3.C -- System Metrics Display Program No. 3
(c) Charles Petzold, 1998
----------------------------------------------------*/

#include <windows.h>
#include "sysmets.h"

LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM) ;

int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,
PSTR szCmdLine, int iCmdShow)
{
static TCHAR szAppName[] = TEXT ("SysMets3") ;
HWND hwnd ;
MSG msg ;
WNDCLASS wndclass ;

wndclass.style = CS_HREDRAW | CS_VREDRAW ;
wndclass.lpfnWndProc = WndProc ;
wndclass.cbClsExtra = 0 ;
wndclass.cbWndExtra = 0 ;
wndclass.hInstance = hInstance ;
wndclass.hIcon = LoadIcon (NULL, IDI_APPLICATION) ;
wndclass.hCursor = LoadCursor (NULL, IDC_ARROW) ;
wndclass.hbrBackground = (HBRUSH) GetStockObject (WHITE_BRUSH) ;
wndclass.lpszMenuName = NULL ;
wndclass.lpszClassName = szAppName ;

if (!RegisterClass (&wndclass))
{
MessageBox (NULL, TEXT ("Program requires Windows NT!"),
szAppName, MB_ICONERROR) ;
return 0 ;
}

hwnd = CreateWindow (szAppName, TEXT ("Get System Metrics No. 3"),
WS_OVERLAPPEDWINDOW | WS_VSCROLL | WS_HSCROLL,
CW_USEDEFAULT, CW_USEDEFAULT,
CW_USEDEFAULT, CW_USEDEFAULT,
NULL, NULL, hInstance, NULL) ;

ShowWindow (hwnd, iCmdShow) ;
UpdateWindow (hwnd) ;

while (GetMessage (&msg, NULL, 0, 0))
{
TranslateMessage (&msg) ;
DispatchMessage (&msg) ;
}
return msg.wParam ;
}

LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
static int cxChar, cxCaps, cyChar, cxClient, cyClient, iMaxWidth ;
HDC hdc ;
int i, x, y, iVertPos, iHorzPos, iPaintBeg, iPaintEnd ;
PAINTSTRUCT ps ;
SCROLLINFO si ;
TCHAR szBuffer[10] ;
TEXTMETRIC tm ;

switch (message)
{
case WM_CREATE:
hdc = GetDC (hwnd) ;

GetTextMetrics (hdc, &tm) ;
cxChar = tm.tmAveCharWidth ;
cxCaps = (tm.tmPitchAndFamily & 1 ? 3 : 2) * cxChar / 2 ;
//第1个问题:cxCaps得出的是大写字符的宽度,是通过小写字符计算出来的,
//这里为什么要弄的这么复杂呢,如果这样cxCaps=1.5*cxChar不是更直接么?
cyChar = tm.tmHeight + tm.tmExternalLeading ;

ReleaseDC (hwnd, hdc) ;

// Save the width of the three columns

iMaxWidth = 40 * cxChar + 22 * cxCaps ;
return 0 ;

case WM_SIZE:
cxClient = LOWORD (lParam) ;
cyClient = HIWORD (lParam) ;

// Set vertical scroll bar range and page size

si.cbSize = sizeof (si) ;
si.fMask = SIF_RANGE | SIF_PAGE ;
si.nMin = 0 ;
si.nMax = NUMLINES - 1 ;
si.nPage = cyClient / cyChar ;
SetScrollInfo (hwnd, SB_VERT, &si, TRUE) ;

// Set horizontal scroll bar range and page size

si.cbSize = sizeof (si) ;
si.fMask = SIF_RANGE | SIF_PAGE ;
si.nMin = 0 ;
si.nMax = 2 + iMaxWidth / cxChar ;
si.nPage = cxClient / cxChar ;
SetScrollInfo (hwnd, SB_HORZ, &si, TRUE) ;
return 0 ;

case WM_VSCROLL:
// Get all the vertical scroll bar information

si.cbSize = sizeof (si) ;
si.fMask = SIF_ALL ;
GetScrollInfo (hwnd, SB_VERT, &si) ;

// Save the position for comparison later on

iVertPos = si.nPos ;

switch (LOWORD (wParam))
{
case SB_TOP:
si.nPos = si.nMin ;
break ;

case SB_BOTTOM:
si.nPos = si.nMax ;
break ;

case SB_LINEUP:
si.nPos -= 1 ;
break ;

case SB_LINEDOWN:
si.nPos += 1 ;
break ;

case SB_PAGEUP:
si.nPos -= si.nPage ;
break ;

case SB_PAGEDOWN:
si.nPos += si.nPage ;
break ;

case SB_THUMBTRACK:
si.nPos = si.nTrackPos ;
break ;

default:
break ;
}
// Set the position and then retrieve it. Due to adjustments
// by Windows it may not be the same as the value set.

si.fMask = SIF_POS ;
SetScrollInfo (hwnd, SB_VERT, &si, TRUE) ;
GetScrollInfo (hwnd, SB_VERT, &si) ;

// If the position has changed, scroll the window and update it

if (si.nPos != iVertPos)
{
ScrollWindow (hwnd, 0, cyChar * (iVertPos - si.nPos),
NULL, NULL) ;
UpdateWindow (hwnd) ;
}
return 0 ;

case WM_HSCROLL:
// Get all the vertical scroll bar information

si.cbSize = sizeof (si) ;
si.fMask = SIF_ALL ;

// Save the position for comparison later on

GetScrollInfo (hwnd, SB_HORZ, &si) ;
iHorzPos = si.nPos ;

switch (LOWORD (wParam))
{
case SB_LINELEFT:
si.nPos -= 1 ;
break ;

case SB_LINERIGHT:
si.nPos += 1 ;
break ;

case SB_PAGELEFT:
si.nPos -= si.nPage ;
break ;

case SB_PAGERIGHT:
si.nPos += si.nPage ;
break ;

case SB_THUMBPOSITION:
si.nPos = si.nTrackPos ;
break ;

default :
break ;
}
// Set the position and then retrieve it. Due to adjustments
// by Windows it may not be the same as the value set.

si.fMask = SIF_POS ;
SetScrollInfo (hwnd, SB_HORZ, &si, TRUE) ;
GetScrollInfo (hwnd, SB_HORZ, &si) ;

// If the position has changed, scroll the window

if (si.nPos != iHorzPos)
{
ScrollWindow (hwnd, cxChar * (iHorzPos - si.nPos), 0,
NULL, NULL) ;
}
return 0 ;

case WM_PAINT :
hdc = BeginPaint (hwnd, &ps) ;

// Get vertical scroll bar position

si.cbSize = sizeof (si) ;
si.fMask = SIF_POS ;
GetScrollInfo (hwnd, SB_VERT, &si) ;
iVertPos = si.nPos ;

// Get horizontal scroll bar position
GetScrollInfo (hwnd, SB_HORZ, &si) ;
iHorzPos = si.nPos ;

// Find painting limits

iPaintBeg = max (0, iVertPos + ps.rcPaint.top / cyChar) ;
//第2个问题:ps.rcPaint.top表示什么?我知道cyChar代表的是显示一行所需的距离。
//用ps.rcPaint.top/cyChar得到的是什么呢?
//然后又用+iVertPos得到的又是什么?我在提问题之前上网查了不少别人的回答,但都不满意。
iPaintEnd = min (NUMLINES - 1,
iVertPos + ps.rcPaint.bottom / cyChar) ;

for (i = iPaintBeg ; i <= iPaintEnd ; i++)
{
x = cxChar * (1 - iHorzPos) ;
y = cyChar * (i - iVertPos) ; //第3个问题,这两个坐标顺便帮解释一下

TextOut (hdc, x, y,
sysmetrics[i].szLabel,
lstrlen (sysmetrics[i].szLabel)) ;

TextOut (hdc, x + 22 * cxCaps, y,
sysmetrics[i].szDesc,
lstrlen (sysmetrics[i].szDesc)) ;

SetTextAlign (hdc, TA_RIGHT | TA_TOP) ;

TextOut (hdc, x + 22 * cxCaps + 40 * cxChar, y, szBuffer,
wsprintf (szBuffer, TEXT ("%5d"),
GetSystemMetrics (sysmetrics[i].iIndex))) ;

SetTextAlign (hdc, TA_LEFT | TA_TOP) ;
}

EndPaint (hwnd, &ps) ;
return 0 ;

case WM_DESTROY :
PostQuitMessage (0) ;
return 0 ;
}
return DefWindowProc (hwnd, message, wParam, lParam) ;
}
...全文
243 23 打赏 收藏 转发到动态 举报
写回复
用AI写文章
23 条回复
切换为时间正序
请发表友善的回复…
发表回复
酱油专业户 2011-08-13
  • 打赏
  • 举报
回复
受教了...............
philmei 2010-01-10
  • 打赏
  • 举报
回复
[Quote=引用 14 楼 yxwsbobo 的回复:]
比如窗口被遮挡了一部分,然后移开,这部分就变成了无效区域就是
BeginPaint 得到的区域  也就是 ps.rcPaint 的区域
[/Quote]
无效区域我是知道的。
无效矩形啥的我都理解了。

只是具体的top,bottom什么的,翻译过来是无效矩形的上方,下方,这我知道。
但上方是哪个范围呢?
用ps.rsPaint.top(上方)除以cyChar,得到的是什么?还是没解释清楚呀
您就告诉我,上方下方表示的是哪个范围就行了,谢谢啊
yxwsbobo 2010-01-10
  • 打赏
  • 举报
回复
很难理解吗?

无效矩形是一个矩形。。。。 它有4个点,而事实上,只需要2个点就能确定这个矩形了,因为矩形的边都是和显示器平行的

ps.rsPaint 是个结构 有4个成员 top left right bottom 其中 (top,left)是起点 (right,bottom)是结束点,这2个点确定无效矩形区域


cyClient/cyChar 知道是什么吗? 就是屏幕内能显示的 行数,

同样是 像素/cyChar 的 ps.rsPaint.top/cyChar 得到无效行数的起点,换句话说就知到了不需要重绘的上面的多少行,也就是从多少行开始需要重绘,这个是屏幕范围的 再加上 iVertPos 就是整个文本内的需要重绘的起点
yxwsbobo 2010-01-10
  • 打赏
  • 举报
回复
1-iHorzPos是为了在屏幕的左边保留一个字符的空白区域。

不是为了保留一个字符的空白区域,最主要的目的是负号,

也就是说 当水平滚动条向右滚动的时候,屏幕上的字实际是向左滚动,

默认水平滚动条为0的时候,那么 x = cxChar * 1 所以你会看到会空出一个字符,

如果水平滚动条向右滚动10,那么 x = cxChar * -9 TextOut起始x变得更靠左,所以输出的字也就靠左,从而实现滚动条向右滚动的效果
philmei 2010-01-10
  • 打赏
  • 举报
回复
知道就帮解释一下,不知道也请不要批评我
毕竟每个人对知识的渴望是不同的,我也知道3-iHorzPos是可以在屏幕的左边空出3个字符的宽度,这些我都试验过了。
但为什么会这样?有些人会思考,有些人就选择不去思考,这恐怕和耽误时间没什么关系吧?
我认为程序的每个细节都是重要的,因为它代表的是一种能力,如果不弄清楚,那么可能以后在自己的代码中永远也不会用到,自己也就少学会了一种编程方法。是的,我对小问题上扣的细了,但我就是这样的人。

如果一遇困难就略过,我想我也没必要再往下学了
对不起,朋友,我的话可能有些让您觉得反感,但我却实是这样想的。
damo_xu 2010-01-10
  • 打赏
  • 举报
回复
[Quote=引用 19 楼 philmei 的回复:]
谢谢,这回看懂了
再帮我解释一下这个吧
x = cxChar * (1 - iHorzPos) ;中的1-iHorzPos
iHorzPos是一个数值吧,代表的应该是水平滚动条的当前位置。
为什么要用1来减它呢?从输出中可以知道,1-iHorzPos是为了在屏幕的左边保留一个字符的空白区域。
但为什么用1减它就可以得出这样的结果呢?
[/Quote]

作者认为1这个数值输出时很耐眼,就这么简单。我当年改成了(3 - iHorzPos),不为什么,3是我的幸运数字。

编程是一门艺术,程序会有这个程序员的个性特征。如果你这些也要问,那么你是时候该打住了。楼上的大哥是过来人,说的话很中肯:人生是短暂的,不必要为小事耽误时间。
damo_xu 2010-01-10
  • 打赏
  • 举报
回复
[Quote=引用 15 楼 philmei 的回复:]
只是具体的top,bottom什么的,翻译过来是无效矩形的上方,下方,这我知道。
但上方是哪个范围呢?
用ps.rsPaint.top(上方)除以cyChar,得到的是什么?还是没解释清楚呀
您就告诉我,上方下方表示的是哪个范围就行了,谢谢啊
[/Quote]

上方下方左右就是无效矩形的图素坐标,比如在我的笔记本上top就是0,bottom就是536。。。
用ps.rsPaint.top除以cyChar,我猜应该是如果这个窗口是子窗口的话(比如一个程序,里面有个子窗口,你按个按钮显示SYSMETS数据,那么ps.rsPaint.top就有可能不是0,本程序就是0)

你一定先要看看Debug(程序调试的知识)!!! 没有这个知识学C++ console程序还凑合,学windows程序简直就是不可能。网上搜搜,教材一大堆,看个几天再试试就差不多了。

然后你以后下断点,单步跟踪,7,8个窗口看变量,9,10个窗口看内存 。 VS2008(2005)谁与争锋?!!!


philmei 2010-01-10
  • 打赏
  • 举报
回复
谢谢,这回看懂了
再帮我解释一下这个吧
x = cxChar * (1 - iHorzPos) ;中的1-iHorzPos
iHorzPos是一个数值吧,代表的应该是水平滚动条的当前位置。
为什么要用1来减它呢?从输出中可以知道,1-iHorzPos是为了在屏幕的左边保留一个字符的空白区域。
但为什么用1减它就可以得出这样的结果呢?
philmei 2010-01-10
  • 打赏
  • 举报
回复
谢谢大家,谢谢
yxwsbobo 2010-01-09
  • 打赏
  • 举报
回复
比如窗口被遮挡了一部分,然后移开,这部分就变成了无效区域就是
BeginPaint 得到的区域 也就是 ps.rcPaint 的区域
damo_xu 2010-01-09
  • 打赏
  • 举报
回复
兄弟,你这可不是一章章接着看吖。这是SYSMETS3.C,你看懂SYSMETS1.C了吗?因为在SYSMETS1程序有这样一句话啊: (都说了让你英文版对照着看了。呵呵)

SYSMETS1 also saves an average width of uppercase letters in the static variable cxCaps. For a fixed-pitch font, cxCaps would equal cxChar. For a variable-width font, cxCaps is set to 150 percent of cxChar. The low bit of the tmPitchAndFamily field in the TEXTMETRIC structure is 1 for a variable-width font and 0 for a fixed-pitch font.

我的山寨版译文:如果是变宽字体,那么tmPitchAndFamily 为1;如果是固定pitch字体,那么tmPitchAndFamily 为0。 作者定义cxCaps取cxChar的150%为一个平均宽度。所以你的第一个问题就解决了。至于为什么不用1.5乘以某某值,我想是因为作者是C语言绝顶高手,先*3然后/2肯定比double值要快。


暂时有事,而且水平有限,不知你满意吗?
philmei 2010-01-09
  • 打赏
  • 举报
回复
[Quote=引用 11 楼 yxwsbobo 的回复:]
就算是大写的话 等宽的话大写宽度也是固定的

ps.rcPaint  是表示 失效的区域 ,也就是说那块失效就重绘那块
[/Quote]
谢谢您的回答,那么ps.rcPaint.top表示的是哪个区域呢?
yxwsbobo 2010-01-09
  • 打赏
  • 举报
回复
就算是大写的话 等宽的话大写宽度也是固定的

ps.rcPaint 是表示 失效的区域 ,也就是说那块失效就重绘那块
philmei 2010-01-09
  • 打赏
  • 举报
回复
[Quote=引用 8 楼 wanggang999 的回复:]
一点题外话,看书要认真不错,但是想步步为营就不实际了,
有时候,一个细节无法理解也没什么大不了的,先跨过去,
很可能几天、几周或者几个月之后,自然而然就理解了,
因为随着学习的深入,理解也就更深刻,甚至有时候放下不管了,
过一段时间也有可能突然就“顿悟”了的。

细节扣得太认真,就是浪费时间,生命是有限的啊,
年轻或许还不觉得,但到了三十岁就知道了,时间需要节约的。
[/Quote]
谢谢您的教诲!

如果不是有这个论坛,我可能真的要先跨过去,以后再理解了,呵呵
正好看到这里,从网上又找不到舒服的解答,所以就打算上来问问试试看的
我每天都到论坛里回贴只为了每天可以+10个可用分,为了就是这一刻呀,现在真遇到困难了如果不提问题我还接分干嘛呀!
but thank u anyway!
songerzhou 2010-01-09
  • 打赏
  • 举报
回复
[Quote=引用 8 楼 wanggang999 的回复:]
一点题外话,看书要认真不错,但是想步步为营就不实际了,
有时候,一个细节无法理解也没什么大不了的,先跨过去,
很可能几天、几周或者几个月之后,自然而然就理解了,
因为随着学习的深入,理解也就更深刻,甚至有时候放下不管了,
过一段时间也有可能突然就“顿悟”了的。

细节扣得太认真,就是浪费时间,生命是有限的啊,
年轻或许还不觉得,但到了三十岁就知道了,时间需要节约的。
[/Quote]
同意!
windsting 2010-01-09
  • 打赏
  • 举报
回复
一点题外话,看书要认真不错,但是想步步为营就不实际了,
有时候,一个细节无法理解也没什么大不了的,先跨过去,
很可能几天、几周或者几个月之后,自然而然就理解了,
因为随着学习的深入,理解也就更深刻,甚至有时候放下不管了,
过一段时间也有可能突然就“顿悟”了的。

细节扣得太认真,就是浪费时间,生命是有限的啊,
年轻或许还不觉得,但到了三十岁就知道了,时间需要节约的。
philmei 2010-01-09
  • 打赏
  • 举报
回复
[Quote=引用 1 楼 loaden 的回复:]
① tm.tmPitchAndFamily & 1 这一句的作用?
② 查MSDN吧。
[/Quote]
&是按位操作符,这个我知道的
cxCaps = (tm.tmPitchAndFamily & 1 ? 3 : 2) * cxChar / 2
这句话是比较tm.tmPitchAndFamily的1位,与1相比,如果tm.tmPitchAndFamily的1位是1,那么表达式为真,tm.tmPitchAndFamily就用3的值。如果不是1,那么结果为假,用2的值

如果tm.tmPitchAndFamily是大写字符,那么cxCaps=3*cxChar/2
如果不是大写字符,那么cxCaps=2*cxChar/2
cxChar是小写字符的宽度,这段代码是要通过小写字符的宽度,算出大写字符的宽度没错吧?

我要问的是为什么作者要定义这个cxCaps,我猜是他想定义出大写字符的宽度,
那么为什么不直接这样cxCaps=1.5*cxChar呢?
tm.tmPitchAndFamily & 1可能为假吗?如果可能,在什么情况下会为假?
philmei 2010-01-09
  • 打赏
  • 举报
回复
[Quote=引用 3 楼 yxwsbobo 的回复:]
如果是等宽字符的话 就不需要乘以1.5了
[/Quote]
其实我知道如果不是等宽字符就不需要乘以1.5的意思。
但作者是出于什么目的要建立cxCaps呢?
看这段代码:ciMaxWidth = 40 * cxChar + 22 * cxCaps ;
说明作者在写程序的时候已经知道要为第一列的大写字符的输出留出空间,如果我没理解错的话,cxCaps就是为了表示大写字符才建立的。那么为什么还是用判断语句呢?我觉得完全可以用cxCaps=1.5*cxChar代替呀?

[Quote=引用 3 楼 yxwsbobo 的回复:]
ps.rcPaint.top 是需要重绘的起始高度, 除以 cyChar 是把像素换成行,加上位置 就成了起始要重绘的起始行
[/Quote]
重绘的起始高度指的是哪块?它应该是一个像素值吧?那它具体代表哪个地方?
如果ps.rcPaint.top我理解不了,那么除以cyChar就理解不了。
si.cbSize = sizeof (si) ;
si.fMask = SIF_POS ;
GetScrollInfo (hwnd, SB_VERT, &si) ;
iVertPos = si.nPos ;
从中可以知道,iVertPos应该代表的是滚动条当前的位置,它应该是一个数值,
所以才会用它加上ps.rcPaint.top/cyChar得出的值。
但iVertPos具体表代的是什么状态?是当窗口过程收到WM_PAINT信息后滚动条的位置吗?这个位置是相对窗口顶端吗?还是什么样子的?
呵呵,不好意思,我实在是不明白,麻烦了
philmei 2010-01-09
  • 打赏
  • 举报
回复
[Quote=引用 2 楼 loaden 的回复:]
不要纠缠第四章,看完前三章后,就从第九章看起。
[/Quote]
我看书从来不跳着看啊,我喜欢一点一点从头看,第四章可以说已经看完了。只差这几个问题还不是很清晰。第四章也讲了好些前三章里没有讲的问题啊,我觉得一点一点学还是有好处的。我也不那么着急的。
yxwsbobo 2010-01-09
  • 打赏
  • 举报
回复
y = cyChar * (i - iVertPos) ; //第3个问题,这两个坐标顺便帮解释一下

就是说 只重绘 需要重绘的,因为拉动了滚动条,所以第一行在 负的位置上重绘,也就是不重绘
加载更多回复(3)

64,653

社区成员

发帖
与我相关
我的任务
社区描述
C++ 语言相关问题讨论,技术干货分享,前沿动态等
c++ 技术论坛(原bbs)
社区管理员
  • C++ 语言社区
  • encoderlee
  • paschen
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
  1. 请不要发布与C++技术无关的贴子
  2. 请不要发布与技术无关的招聘、广告的帖子
  3. 请尽可能的描述清楚你的问题,如果涉及到代码请尽可能的格式化一下

试试用AI创作助手写篇文章吧