第9章第1个程序的几个问题

strwolf 2009-09-12 11:45:43

#include <windows.h>

struct
{
int iStyle ;
TCHAR * szText ;
}
button[] =
{
BS_PUSHBUTTON, TEXT ("PUSHBUTTON"),
BS_DEFPUSHBUTTON, TEXT ("DEFPUSHBUTTON"),
BS_CHECKBOX, TEXT ("CHECKBOX"),
BS_AUTOCHECKBOX, TEXT ("AUTOCHECKBOX"),
BS_RADIOBUTTON, TEXT ("RADIOBUTTON"),
BS_3STATE, TEXT ("3STATE"),
BS_AUTO3STATE, TEXT ("AUTO3STATE"),
BS_GROUPBOX, TEXT ("GROUPBOX"),
BS_AUTORADIOBUTTON, TEXT ("AUTORADIO"),
BS_OWNERDRAW, TEXT ("OWNERDRAW")
} ;

#define NUM (sizeof button / sizeof button[0])

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

int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,
PSTR szCmdLine, int iCmdShow)
{
static TCHAR szAppName[] = TEXT ("BtnLook") ;
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 ("This program requires Windows NT!"),
szAppName, MB_ICONERROR) ;
return 0 ;
}

hwnd = CreateWindow (szAppName, TEXT ("Button Look"),
WS_OVERLAPPEDWINDOW,
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 HWND hwndButton[NUM] ;
static RECT rect ;
static TCHAR szTop[] = TEXT ("message wParam lParam"),
szUnd[] = TEXT ("_______ ______ ______"),
szFormat[] = TEXT ("%-16s%04X-%04X %04X-%04X"),
szBuffer[50] ;
static int cxChar, cyChar ;
HDC hdc ;
PAINTSTRUCT ps ;
int i ;

switch (message)
{
case WM_CREATE :
cxChar = LOWORD (GetDialogBaseUnits ()) ;
cyChar = HIWORD (GetDialogBaseUnits ()) ;

for (i = 0 ; i < NUM ; i++)
hwndButton[i] = CreateWindow ( TEXT("button"),
button[i].szText,
WS_CHILD | WS_VISIBLE | button[i].iStyle,
cxChar, cyChar * (1 + 2 * i),
20 * cxChar, 7 * cyChar / 4,
hwnd, (HMENU) i,
((LPCREATESTRUCT) lParam)->hInstance, NULL) ;
return 0 ;

case WM_SIZE :
rect.left = 24 * cxChar ;
rect.top = 2 * cyChar ;
rect.right = LOWORD (lParam) ;
rect.bottom = HIWORD (lParam) ;
return 0 ;

case WM_PAINT :
InvalidateRect (hwnd, &rect, TRUE) ; //问题2:这里使用InvalidateRect和下面ValidateRect产生了一个什么样的逻辑关系?能否把RECT从无效到有效并绘制出消息的这一过程为我解说一遍

hdc = BeginPaint (hwnd, &ps) ;
SelectObject (hdc, GetStockObject (SYSTEM_FIXED_FONT)) ;
SetBkMode (hdc, TRANSPARENT) ;

TextOut (hdc, 24 * cxChar, cyChar, szTop, lstrlen (szTop)) ;
TextOut (hdc, 24 * cxChar, cyChar, szUnd, lstrlen (szUnd)) ;

EndPaint (hwnd, &ps) ;
return 0 ;

case WM_DRAWITEM :
case WM_COMMAND :
ScrollWindow (hwnd, 0, -cyChar, &rect, &rect) ;

hdc = GetDC (hwnd) ;
SelectObject (hdc, GetStockObject (SYSTEM_FIXED_FONT)) ;

TextOut (hdc, 24 * cxChar, cyChar * (rect.bottom / cyChar - 1),
szBuffer,
wsprintf (szBuffer, szFormat,
message == WM_DRAWITEM ? TEXT ("WM_DRAWITEM") :
TEXT ("WM_COMMAND"),
HIWORD (wParam), LOWORD (wParam),
HIWORD (lParam), LOWORD (lParam))) ;

ReleaseDC (hwnd, hdc) ;
ValidateRect (hwnd, &rect) ; //问题2中的ValidateRect
break ; //问题1:这里为何用break而不用return 0,他再把WM_DRAWITEM和WM_COMMAND发给defwndProc有什么效果


case WM_DESTROY :
PostQuitMessage (0) ;
return 0 ;
}
return DefWindowProc (hwnd, message, wParam, lParam) ;
}

//问题3:为什么这个程序一运行就产生了WM_DRAWITEM


3个问题见代码注释处
谢谢
...全文
217 17 打赏 收藏 转发到动态 举报
写回复
用AI写文章
17 条回复
切换为时间正序
请发表友善的回复…
发表回复
dronly 2009-09-16
  • 打赏
  • 举报
回复
1.windows内部机制我也不知道所以不确定是否嵌套,WM_PAINT在一开始按的时候会有2次是个事实,说实在这个我也搞不清楚,但的确就SendMessage的阻塞运行方式来说,我觉得还是很类似嵌套递归的感觉。
strwolf 2009-09-15
  • 打赏
  • 举报
回复
[Quote=引用 15 楼 dronly 的回复:]
引用 8 楼 strwolf 的回复:
十分感谢 dronly 您耐心而详尽的解答
通过您的解答,以及动手去做了些测试
我总结了下一些以前我不知道的东西和清楚了一些模糊的东西:
1.windows调用窗口过程是并发的
2.BeginPaint用窗口类的刷子刷新无效区域
3.WM_PAINT的组合
4.ScrollWindow新滚动出的 行 为无效区域


1.窗口调用过程怎么可能是并发呢?就一个主线程在跑,而且也只有一个进程。
2.BeginPaint跟是否刷新没什么关系,他是“刷新”无效区域,并且告诉操作系统无效区域已经不存在了,因此操作系统停止产生WM_PAINT
3.不知道你说什么,WM_PAINT的组合是什么?无效区域的合并?
4.ScrollWindow本身就是个内部机构比较复杂的函数,只把新滚动出来的区域变成无效区域并且把该无效区域加入WM_PAINT的无效区域做合并操作。
[/Quote]

呵呵 又回复了 感谢哦
1.我没看到 多线程 那之类的 所以也不知道用词对不对 我是这样做的:
我设置了一个全局变量int count=0; 然后在WM_PAINT下 第一行加了count++语句
再在case WM_DRAWITEM case WM_COMMAND下 连续加了 两条MessageBoxPrintf(TEXT("test"),TEXT("%i"),count);第1次count=1,第2次count=2,那不是在这两条语句之间还处理了WM_PAINT消息么?那么是不是说windows可以同时调用几个窗口过程处理几个消息呢?听了你的话,我又想了想,难道这个类似于函数嵌套一样,弹出第一messagebox 子函数调用 再返回
2.恩
3.我就是在做一些测试的时候,感受到了书上所说的几个WM_PAINT压缩组合成一个
4.哦
MoXiaoRab 2009-09-14
  • 打赏
  • 举报
回复
LZ请看《深入浅出MFC》
tutu08 2009-09-14
  • 打赏
  • 举报
回复
楼主很用功
必成大牛
dronly 2009-09-14
  • 打赏
  • 举报
回复
[Quote=引用 8 楼 strwolf 的回复:]
十分感谢 dronly 您耐心而详尽的解答
通过您的解答,以及动手去做了些测试
我总结了下一些以前我不知道的东西和清楚了一些模糊的东西:
1.windows调用窗口过程是并发的
2.BeginPaint用窗口类的刷子刷新无效区域
3.WM_PAINT的组合
4.ScrollWindow新滚动出的 行 为无效区域

[/Quote]
1.窗口调用过程怎么可能是并发呢?就一个主线程在跑,而且也只有一个进程。
2.BeginPaint跟是否刷新没什么关系,他是“刷新”无效区域,并且告诉操作系统无效区域已经不存在了,因此操作系统停止产生WM_PAINT
3.不知道你说什么,WM_PAINT的组合是什么?无效区域的合并?
4.ScrollWindow本身就是个内部机构比较复杂的函数,只把新滚动出来的区域变成无效区域并且把该无效区域加入WM_PAINT的无效区域做合并操作。
xylicon 2009-09-13
  • 打赏
  • 举报
回复
问题1:WM_COMMAND 里面包含很多其他控件的消息,例如如果有按钮就会有BN_CLICKED等,一些消息。各个消息返回值的含义不一样。一般retrurn 0就相当于已经处理了,那样就相当于拦截了这些消息,而使得一些控件不能正常使用。
Randyqiu 2009-09-13
  • 打赏
  • 举报
回复
问题1:
我的理解是,因为执行WM_COMMAND或者WM_DRAWITEM时候,除了执行完它下面所列出的代码外,在某些情况下还需要进入DefWindowProc中去,所以这里不能直接return,只能break后跳出switch case去执行DefWindowProc
Randyqiu 2009-09-13
  • 打赏
  • 举报
回复
1.windows调用窗口过程是并发的

何以见得?
strwolf 2009-09-13
  • 打赏
  • 举报
回复
[Quote=引用 9 楼 xylicon 的回复:]
问题1:WM_COMMAND 里面包含很多其他控件的消息,例如如果有按钮就会有BN_CLICKED等,一些消息。各个消息返回值的含义不一样。一般retrurn 0就相当于已经处理了,那样就相当于拦截了这些消息,而使得一些控件不能正常使用。
[/Quote]

Return Values
If an application processes this message, it should return zero.

dronly 2009-09-12
  • 打赏
  • 举报
回复
InvalidateRect (hwnd, &rect, TRUE) ; //问题2:这里使用InvalidateRect和下面ValidateRect
这个是因为要把rect加入进去需要更新的区域,并且更新。

ValidateRect (hwnd, &rect) ; //问题2中的ValidateRect
这个是应为ScrollWindow (hwnd, 0, -cyChar, &rect, &rect) ;会绘制更新区域


break ; //问题1:这里为何用break而不用return 0,他再把WM_DRAWITEM和WM_COMMAND发给defwndProc
这个地方return也是可以的。
Conry 2009-09-12
  • 打赏
  • 举报
回复
1 不清楚,因为俺还没见过 WM_COMMAND 消息的这种用法呢
2 InvalidateRect 就是产生WM_PAINT消息从而达到更新窗口的,你在WM_PAINT再调用岂不是会有问题
strwolf 2009-09-12
  • 打赏
  • 举报
回复
十分感谢 dronly 您耐心而详尽的解答
通过您的解答,以及动手去做了些测试
我总结了下一些以前我不知道的东西和清楚了一些模糊的东西:
1.windows调用窗口过程是并发的
2.BeginPaint用窗口类的刷子刷新无效区域
3.WM_PAINT的组合
4.ScrollWindow新滚动出的 行 为无效区域
strwolf 2009-09-12
  • 打赏
  • 举报
回复
哎 第3个问题 问SB了 浪费了论坛资源
我在提问之前都看了MSDN几遍 却没仔细想

那另外2个问题呢?
求解答
MoXiaoRab 2009-09-12
  • 打赏
  • 举报
回复
当具有自绘风格的按钮、组合框、列表框或者菜单的可见部分发生改变时,就会发送WM_DRAWITEM消息给自绘控件所在的窗体。
I_NBFA 2009-09-12
  • 打赏
  • 举报
回复
在WM_PAINT里调用InvalidateRect等类似函数确实很奇怪,按说InvalidateRect会产生WM_PAINT,
在处理WM_PAINT时调用它会进入一个无限循环.
这里只有一种解释,GetMessage取得WM_PAINT消息时队列里的WM_PAINT并没有消失,
而是直到BeginPaint才将WM_PAINT移除,那么在此之前调用InvalidateRect实际上只更新了无效区域,
windows重新计算WM_PAINT.如果将InvalidateRect放到BeginPaint之后那就无限循环了.

查找MSDN, 这方面说的比较晦涩,
dronly 2009-09-12
  • 打赏
  • 举报
回复
我晕,这还不够详细么。。。

WM_PAINT 下面第一句就加InvalidateRect (hwnd, &rect, TRUE) ;为的就是在WM_PAINT产生的时候把rect刷新,就是除了标题打印出来的那部分外的区域,很多情况会引起WM_PAINT,但更新区域不可能那么准确,这一句就是一个保证。因为WM_PAINT还没被BeginPaint消除掉,因此InvalidateRect并没有再添加WM_PAINT的意思。但很明确,现在WM_PAINT就只能清空滚动区rect。

ScrollWindow 会产生WM_PAINT的,然后WM_PAINT现在给设置成了全部清空,只能加个ValidateRect把WM_PAINT去掉。

WM_DRAWITEM WM_COMMAND 你看看msdn就明白了,本来就建议return的.
strwolf 2009-09-12
  • 打赏
  • 举报
回复

可能是我没问得太清楚
单独一句我明白是什么意思
我不太明白的是这几句加之其间的消息组合起来是怎么个动态逻辑过程

具体点到问题的话:
我就问问
一、3楼说的 "2 InvalidateRect 就是产生WM_PAINT消息从而达到更新窗口的,你在WM_PAINT再调用岂不是会有问题"
二、我删除InvalidateRect (hwnd, &rect, TRUE) ,效果上丝毫没有影响,为什么

16,473

社区成员

发帖
与我相关
我的任务
社区描述
VC/MFC相关问题讨论
社区管理员
  • 基础类社区
  • Web++
  • encoderlee
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告

        VC/MFC社区版块或许是CSDN最“古老”的版块了,记忆之中,与CSDN的年龄几乎差不多。随着时间的推移,MFC技术渐渐的偏离了开发主流,若干年之后的今天,当我们面对着微软的这个经典之笔,内心充满着敬意,那些曾经的记忆,可以说代表着二十年前曾经的辉煌……
        向经典致敬,或许是老一代程序员内心里面难以释怀的感受。互联网大行其道的今天,我们期待着MFC技术能够恢复其曾经的辉煌,或许这个期待会永远成为一种“梦想”,或许一切皆有可能……
        我们希望这个版块可以很好的适配Web时代,期待更好的互联网技术能够使得MFC技术框架得以重现活力,……

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