Windows 核心编程笔记一:关于Error Show的总结(转)

 

第一:关于代码编译的问题  

第一次把ErrorShow实例从光盘上拷下来,用Microsoft Visual C++ 6.0编译,出现如下错误:

Compiling...
ErrorShow.cpp
You are not using the latest Platform SDK header/library
files. This may prevent the project from building correctly.
c:/documents and settings/administrator/桌面/01-errorshow/cmnhdr.h(212) : error C2065: 'GetWindowLongPtr' : undeclared identifier
c:/documents and settings/administrator/桌面/01-errorshow/cmnhdr.h(212) : error C2065: 'GWLP_HINSTANCE' : undeclared identifier
c:/documents and settings/administrator/桌面/01-errorshow/errorshow.cpp(83) : error C2065: 'PCTSTR' : undeclared identifier
c:/documents and settings/administrator/桌面/01-errorshow/errorshow.cpp(83) : error C2146: syntax error : missing ')' before identifier 'LocalLock'
c:/documents and settings/administrator/桌面/01-errorshow/errorshow.cpp(83) : error C2059: syntax error : ')'
c:/documents and settings/administrator/桌面/01-errorshow/errorshow.cpp(125) : error C2664: 'DialogBoxParamA' : cannot convert parameter 4 from 'long (struct HWND__ *,unsigned int,unsigned int,long)' to 'int (__stdcall *)(struct HWND__ *,unsigned in
t,unsigned int,long)'
        None of the functions with this name in scope match the target type
执行 cl.exe 时出错.

ErrorShow.obj - 1 error(s), 0 warning(s)

 

解决办法:

1.将GetWindowLongPtr改成GetWindowLong    GWLP_HINSTANCE改成GWL_HINSTANCE原因可能是If you are retrieving a pointer or a handle, this function supersedes the GetWindowLong function.(参照msdn的说法)

2.将PCTSTR改成PTSTR(这个类型包含在windows.h头文件中)

3. Dlg_Proc改成(DLGPROC)Dlg_Proc   类型要匹配

改完之后应该就可以编译成功了。

 

第二:关于代码中一些让人费解的代码

1.chSETDLGICONS(hwnd, IDI_ERRORSHOW);

  chHANDLE_DLGMSG(hwnd, WM_INITDIALOG, Dlg_OnInitDialog);
   chHANDLE_DLGMSG(hwnd, WM_COMMAND,    Dlg_OnCommand);

这些都是作者定义的宏,包含在CmnHdr.h中,如:#define chHANDLE_DLGMSG(hwnd, message, fn)   /
                                                                                          case (message): return (SetDlgMsgResult(hwnd, uMsg, /
                                                                                         HANDLE_##message((hwnd), (wParam), (lParam), (fn))))

其实这是一些消息分流器,包含在windowsx.h中,作者为了使用方便及避免一些问题自定义了它,有关消息分流器的知识,如下所示:

对于熟悉Win API编程的同志们来说,windowsx.h这个头文件应该不会太陌生吧,经常能在msdn上查到这样一些函数,明明是个函数,而且模样长得和一般的api函数也一样一样的,可却叫做macro,为什么呢?留意一下函数使用的requirement,你会发现,它的声明正是在windowsx.h这个头文件里。
  Windowsx.h包含了这样一些内容:
  宏API,窗口消息分流器,控件API;
  所有的这些宏定义,可以使你的程序更加安全,简洁,结构更清晰,大大提高程序的可读性;其中窗口消息分流器(message cracker)是我们今天要讨论的话题,它可以使我们的API程序变得更简洁。下面就进入我们的主题:(有关windowsx.h的更多内容,可以参考 MS Knowledge Base Article #83456.)
  消息分流器是Windows提供的一组宏定义,它的两个最大的作用,用MS的话来说,就是:
  ● 安全的数据类型,因为消息分流器完成了大量的类型转换的工作;
  ● 使程序向32位windows的转化更简单;
  当然,使用消息分流器会大大改变程序的面貌,你也可以选择不使用它。
  下面我们就以一个对话框窗口的消息处理过程为例,看看消息分流器到底是怎么运作的。
  1.消息分流器的基本使用
  先看一个普通的窗口消息处理函数,它可能需要处理一些窗口的初始化,无效客户区重绘等消息:
  LRESULT CALLBACK WndProc (HWND hwnd, UINT msg,
  WPARAM wParam, LPARAM lParam)
  {
  switch(msg)
  {
  case WM_CREATE:
  // ...
  return 0;
  case WM_PAINT:
  // ...
  return 0;
  case WM_DESTROY:
  //...
  return 0;
  }
  return DefWindowProc(hwnd, msg, wParam, lParam);
  }
  而通过使用消息分流器,我们可以把每个case都写到相应的消息处理函数中,就像下面这样:
  LRESULT CALLBACK WndProc (HWND hwnd, UINT msg,
  WPARAM wParam, LPARAM lParam)
  {
  switch(msg)
  {
  case WM_CREATE:
  return HANDLE_WM_CREATE(hwnd, wParam, lParam, Cls_OnCreate);
  case WM_PAINT:
  return HANDLE_WM_PAINT(hwnd, wParam, lParam, Cls_OnPaint);
  case WM_DESTROY:
  return HANDLE_WM_DESTROY(hwnd, wParam, lParam, Cls_OnDestroy);
  }
  return DefWindowProc(hwnd, msg, wParam, lParam);
  }
  这里用到了三个宏定义:HANDLE_WM_CREATE, HANDLE_WM_PAINT, HANDLE_WM_DESTROY;这三个宏定义就是我们的三个消息分流器(别看叫什么分流器,说穿了也不值几个钱,呵呵),它们在windowsx.h中的定义如下:
  #define HANDLE_WM_CREATE(hwnd, wParam, lParam, fn)
  ((fn)((hwnd), (LPCREATESTRUCT)(lParam)) ? 0L : (LRESULT)-1L)
  #define HANDLE_WM_PAINT(hwnd, wParam, lParam, fn)
  ((fn)(hwnd), 0L)
  #define HANDLE_WM_DESTROYCLIPBOARD(hwnd, wParam, lParam, fn)
  ((fn)(hwnd), 0L)
  把这三个宏定义替换回去,就变成:
  LRESULT CALLBACK WndProc (HWND hwnd, UINT msg,
  WPARAM wParam, LPARAM lParam)
  {
  switch(msg)
  {
  case WM_CREATE:
  return Cls_OnCreate(hwnd, (LPCREATESTRUCT)(lParam) ? 0L : (LRESULT)-1L;
  // 如果处理了消息,则Cls_OnCreate应返回TRUE,导致WndProc返回0,否则Cls_OnCreate返回FALSE,导致WndProc返回-1;
  case WM_PAINT:
  return Cls_OnPaint(hwnd), 0L;
  // 逗号表达式;Cls_OnPaint是void类型,这里返回0;
  case WM_DESTROY:
  return Cls_OnDestroy(hwnd), 0L; // 同Cls_OnPaint
  }
  return DefWindowProc(hwnd, msg, wParam, lParam);
  }
  之后我们就可以按照消息分流器的定义编写相应的消息处理函数了:
  BOOL Cls_OnCreate(HWND hwnd, LPCREATESTRUCT lpCreateStruct){…};
  void Cls_OnPaint(HWND hwnd){…};
  void Cls_OnDestroyClipboard(HWND hwnd){…};
  windowsx.h还提供了一个更加简化的方法:使用HANDLE_MSG宏,这个宏是这样定义的:
  #define HANDLE_MSG(hwnd, message, fn)
  case (message): return HANDLE_##message((hwnd), (wParam), (lParam), (fn))
  这个宏要做的就是根据不同的message(##用来连接前后的字符串),把自己“变成”相应的HANDLE_XXXXMESSAGE形式的宏,再通过相应的宏来执行消息处理代码;
  比如实际代码中写入:
  HANDLE_MSG(hwnd, WM_CREATE, Cls_OnCreate)
  则经过转换就变成:
  case (WM_CREATE): return HANDLE_WM_CREATE((hwnd), (wParam), (lParam), (Cls_OnCreate))
  这样,我们就可以直接把程序写为:
  LRESULT CALLBACK WndProc (HWND hwnd, UINT msg,
  WPARAM wParam, LPARAM lParam)
  {
  switch(msg)
  {
  HANDLE_MSG(hwnd, WM_CREATE, Cls_OnCreate);
  HANDLE_MSG(hwnd, WM_PAINT, Cls_OnPaint);
  HANDLE_MSG(hwnd, WM_DESTROY, Cls_OnDestroy);
  }
  return DefWindowProc(hwnd, msg, wParam, lParam);
  }
  之后直接编写相应的消息处理过程就可以了。是不是简洁多了?而且把消息处理封装到函数里面,就可以使用VS直接跳转到这个函数,再也不用费劲去找那个 case了。要注意的一点是,虽然windowsx.h里包括了所有消息对应的分流器,但它们的参数是宏定义显式说明的,在编写消息处理函数时,必须遵循宏定义中的参数类型,否则会导致错误;这么多消息分流器,我们每次新写一个消息处理函数时就得看看是否把参数设置正确了,整个过程繁琐冗长。好在已经有一个工具叫Message Cracker Wizard,可以帮助我们生成消息分流器和相关的处理过程,具体见:http://www.codeproject.com/win32/msgcrackwizard.asp
  2.在对话框中使用消息分流器
  在对话框消息处理中,窗口子类化是我们经常使用的手段,这也可以通过消息分流器实现,但是有点小问题 :>
  下面是一个使用了windowsx.h消息分流器的对话框及其处理过程:
  ……
  int WINAPI _tWinMain(HINSTANCE hinstExe, HINSTANCE, PTSTR pszCmdLine, int)
  {
  DialogBoxParam(
  hinstExe, MAKEINTRESOURCE(IDD_PASSTHRU), NULL, (DLGPROC)Dlg_Proc, 0);
  return(0);
  }
  ……
  LRESULT CALLBACK Dlg_Proc (HWND hwnd, UINT msg,
  WPARAM wParam, LPARAM lParam)
  {
  switch(msg)
  {
  HANDLE_MSG(hwnd, WM_INITDIALOG, Cls_OnInitDialog); // 不能直接使用HANDLE_MSG宏
  HANDLE_MSG(hwnd, WM_COMMAND, Cls_OnCommand); // 不能直接使用HANDLE_MSG宏
  }
  return false;
  }
  以上程序中直接使用HANDLE_MSG可能导致错误;为什么呢?问题出在子类化的消息处理过程的返回值上,msdn中对于对话框消息处理过程的返回值有如下说明:
  一般情况下,对话框过程函数应该在处理了消息的情况下返回TRUE,如果没有处理,则返回FALSE。如果对话框过程返回了FALSE,那么对话框管理器为这条消息准备默认的对话操作。
  如果对话框处理了一个需要特定返回值的消息,则对话框的返回值应该被设置为调用SetWindowLong(The SetWindowLong function changes an attribute of the specified window. The function also sets a 32-bit (long) value at the specified offset into the extra window memory of a window. )后的返回值,并在返回TRUE之前立即返回这个值。注意你必须立即调用SetWindowLong(这个函数用于调用窗口子类化的过程),这会导致DWL_MSGRESULT值被一个嵌套的对话框消息改写。返回值为特定值的消息有:
  ? WM_CHARTOITEM
  ? WM_COMPAREITEM
  ? WM_CTLCOLORBTN
  ? WM_CTLCOLORDLG
  ? WM_CTLCOLOREDIT
  ? WM_CTLCOLORLISTBOX
  ? WM_CTLCOLORSCROLLBAR
  ? WM_CTLCOLORSTATIC
  ? WM_INITDIALOG
  ? WM_QUERYDRAGICON
  ? WM_VKEYTOITEM
  看到没有? 我们的消息WM_INITDIALOG也在其中,对这个消息进行处理的过程不能简单的返回TRUE表示对消息进行了处理,而是另有其意;它将转化为:
  case (WM_INITDIALOG): return HANDLE_WM_INITDIALOG(hwnd, wParam, lParam, Cls_OnInitDialog);
  宏HANDLE_WM_INITDIALOG定义如下:
  #define HANDLE_WM_INITDIALOG(hwnd, wParam, lParam, fn)
  (LRESULT)(DWORD)(UINT)(BOOL)(fn)((hwnd), (HWND)(wParam), lParam)
  对WM_INITDIALOG的处理,如果返回TRUE,则表示设置键盘焦点到对话框的默认控件,否则返回FALSE;这时好像还看不出什么问题,而对于我们的另外一个消息WM_COMMAND,HANDLE_MSG简单的把它变成:
  case (WM_COMMAND): return HANDLE_WM_COMMAND(hwnd, wParam, lParam, Cls_OnCommand);
  宏HANDLE_WM_COMMAND定义如下:
  #define HANDLE_WM_COMMAND(hwnd, wParam, lParam, fn)
  ((fn)((hwnd), (int)(LOWORD(wParam)), (HWND)(lParam), (UINT)HIWORD(wParam)), 0L)
  问题出来了,我们的Cls_OnCommand由于是个void型的函数,是没有返回值的,因此windows默认这种消息处理过程必须返回一个0值,而返回0值不就表示我们的消息过程不处理这个消息么?这个矛盾是HANDLE_MSG无法解决的。怎么办才能使消息过程在处理完WM_COMMAND消息之后正确的返回一个TRUE呢? 答案是使用另一个windowsx.h中的宏:SetDlgMsgResult(hwnd, msg, result)
  这个宏定义如下:
  #define SetDlgMsgResult(hwnd, msg, result) ((
  (msg) == WM_CTLCOLORMSGBOX ||
  (msg) == WM_CTLCOLOREDIT ||
  (msg) == WM_CTLCOLORLISTBOX ||
  (msg) == WM_CTLCOLORBTN ||
  (msg) == WM_CTLCOLORDLG ||
  (msg) == WM_CTLCOLORSCROLLBAR ||
  (msg) == WM_CTLCOLORSTATIC ||
  (msg) == WM_COMPAREITEM ||
  (msg) == WM_VKEYTOITEM ||
  (msg) == WM_CHARTOITEM ||
  (msg) == WM_QUERYDRAGICON ||
  (msg) == WM_INITDIALOG
  ) ? (BOOL)(result) : (SetWindowLongPtr((hwnd), DWLP_MSGRESULT, (LPARAM)(LRESULT)(result)), TRUE))
  (有没有注意到,里面多了一个WM_CTLCOLORMSGBOX ? 这个消息是16位WinAPI中的消息,一度被转换为Win32 API的一个消息;现在在最新的32位API中已经被删除了;保留它可能考虑到兼容性的问题,这里不做进一步讨论)
  现在看到了,如果对话框过程处理的消息恰巧为返回特定值中的一个,则如实返回result;不要被前面的BOOL蒙蔽,BOOL在头文件中的定义实际上是一个int型,一旦需要返回非TRUE或FALSE的其他值,照样可以;这样,我们的Cls_OnInitDialog就能够正确的返回它的BOOL值了,而Cls_OnCommand在处理之后,也可以由后面的逗号表达式正确的返回一个TRUE表示消息已处理。
  在《Windows核心编程》一书中,大牛Jeffrey自己定义了一个宏,使SetDlgMsgResult宏的使用更加方便:
  #define chHANDLE_DLGMSG(hwnd, message, fn)
  case (message): return (SetDlgMsgResult(hwnd, uMsg,
  HANDLE_##message((hwnd), (wParam), (lParam), (fn))))
  可见这个宏只是简单的对SetDlgMsgRseult宏进行了封装。
  这样,我们最终的代码可以写成:
  LRESULT CALLBACK Dlg_Proc (HWND hwnd, UINT msg,
  WPARAM wParam, LPARAM lParam)
  {
  switch(msg)
  {
  chHANDLE_DLGMSG(hwnd, WM_INITDIALOG, Cls_OnInitDialog); // 使用大牛的chHANDLE_DLGMSG宏
  chHANDLE_DLGMSG(hwnd, WM_COMMAND, Cls_OnCommand);
  }
  return false;
  }
  下面把原来程序整个框架列出来:
  LRESULT CALLBACK Dlg_Proc(HWND hwnd, UNIT umsg, WPARAM wparam, LPARAM lparam)
  {
  switch(msg)
  {
  case WM_COMMAND: // 每个case都被一个message cracker代替,这里使用大牛同志的
  // do something; // chHANDLE_DLGMSG宏;这个宏负责对消息筛选,处理并返回相应的值
  return true;
  case WM_INITDIALOG:
  // do something;
  return xxxx;
  }
  return false; // 如果消息不在我们的DlgProc过程中被处理,则告诉调用这个DlgProc的消息,
  } //告诉系统的对话框管理器,这个消息我们不处理,交给你了
  对比一下,消息分流器的作用不言自明。

 

2. Edit_LimitText(GetDlgItem(hwnd, IDC_ERRORCODE), 5);

   // Look up the command-line passed error number
   SendMessage(hwnd, ESM_POKECODEANDLOOKUP, lParam, 0);

这段代码其初始化作用,是IDC_ERRORCODE初始时显示为0。

 

就知道这么多了,呵呵,如果大家有什么疑问,可以一起共同探讨啊~~~~~~~~~

  • 0
    点赞
  • 0
    评论
  • 0
    收藏
  • 一键三连
    一键三连
  • 扫一扫,分享海报

©️2021 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值