WM_NOTIFY消息

当自定义控件中发生了特殊的事件需要通知父窗口时,可以向父窗口发送消息,最简单的方法就是直接向父窗口直接发送自定义消息:
   this->GetParent()->SendMessage(WM_USR1, wParam, lParam);
这种方法简单虽然简单,但是不太干净。因为这样做的前提是要保证WM_USR1消息ID在父窗口的范围内保持唯一,否则父窗口就可能会混淆该消息的来源。而一个窗口中的控件会有很多,并不能保证所有控件的所有消息ID都是唯一的。这就会留下了隐患。
 
  其实有一种更好的,也是标准的解决方案,那就是利用 WM_NOTIFY消息向父窗口发送通知。
  顾名思义,WM_NOTIFY是专门用于控件向父窗口发送消息的。这个消息与其它的消息不同,用户可以自行定义通知的内容,但传递消息的方式是统一的,程序的处理非常规范、简洁。以下是一点点总结和心得:
 
   【如何发送WM_NOTIFY消息】
  发送的方法如下所示:

    NMHDR nmhdr;

    nmhdr.hwndFrom = this->m_hWnd;

    nmhdr.idFrom = this->GetDlgID();

    nmhdr.code = WM_USR1;  // 用户自定义消息

  this->GetParent()->SendMessage(WM_NOTIFY, (WPARAM)nmhdr.idFrom,(LPARAM)&nmhdr);

从上面可以看出无论是发送什么通知,其格式都是统一的:首先利用NMHDR结构体进行包装,然后再作为WM_NOTIFY消息发送出去。而NMHDR结构,既可以使用系统提供的默认结构,也可以根据需要自行进行扩充。
 
   【NMHDR结构体的扩充】
  如果仅仅是向父窗口发送一个“通知”,那么使用默认的NMHDR结构体就已经足够了;但若需要同时传递一些附加的信息,就需要对NMHDR结构体进行扩充了。使用WM_NOTIFY发送通知的魅力就在于,用户可以根据自己的需要扩充任何附加信息进去。下面就是自定义的NMHDR结构体:

  typedef struct tagMyNMHDR {

        NMHDR  nmhdr;

        int  user_data1;

        int  user_date2;

        ......

    } MyNMHDR;

注意,这个结构体内包含了NMHDR结构体。这里利用到了C/C++的一个著名的特性: C/C++语言不做内存越界检查。因此,对于自定义的结构体,只要把NMHDR放在第一个元素的地方,就可以 安全地把该结构体转型为NMHDR。这样就可以很方便地在WM_NOTIFY消息中发送自定义的附加信息了。
 
   【父窗口如何进行消息影射】
  消息发送出去后,父窗口(也就是消息接受方)如何接受这个消息呢?首先是要做消息映射:

  ON_NOTIFY(WM_USR1, CTRLID, memberfun1)  // 单控件

    ON_NOTIFY_RANGE(WM_USR1, CTRLID1, CTRLIDn, memberfun2)  // 控件组

  然后要做的是定义消息接受函数:

    afx_msg void memberfun1(NMHDR *nmhdr, LRESULT *result);  // 单控件

  afx_msg void memberfun2(UINT id, NMHDR *nmhdr, LRESULT *result);  // 控件组

 (以上第二项是针对控件组的消息映射,这对于那些需要动态生成控件组的应用来说非常有用。)
    从消息映射可以看出,每个用户自定义的消息,都被限定在指定的控件上了,并且在NMHDR结构中还有 消息源的窗口句柄消息源的控件ID。因此,用这种方法向父窗口发送通知是不会混淆消息源的,也不需要什么额外的处理。
 
  到此为止,WM_NOTIFY消息从生成、发送,到接受的过程就完成了。再回过头来看看生成NMHDR结构的地方,是否会发现什么问题呢?是的,那就是:
   NMHDR结构体的内存应该什么时机进行分配,什么时机进行释放呢?
  在上例中,NMHDR结构体是建立在栈上的,在方法执行完毕后,NMHDR所占用的内存将自动被释放,不用担心内存泄露的问题。但是也因此引发一个新问题:如果以异步的形式( PostMessage)发送消息会有什么结果呢?可想而知,在接受方处理消息的时候,发送方很有可能已经结束处理,NMHDR结构也已被自然释放掉了。因此接受方所收到的NMHDR指针已经变成了“ 野指针”!从而必然引发 内存崩溃危险
  那么,如果把NMHDR建立在堆上,又会如何呢?这样做虽然可以避免了上述野指针的问题,但是已经分配给NMHDR结构体的内存,由谁来释放呢?什么时机释放呢?发送方固然已经无力对其进行释放,由接收方进行释放也是很麻烦的。因为有时会存在消息反射和消息传递的情况,对接收方来说很难判断自己是否是消息处理链的最后一个环节。这使问题复杂化了,同时也违背了“ 谁分配谁释放”的内存处理原则,稍不留神就会引发内存泄露。
   结论: 最好不要在堆上建立NMHDR结构体,也不要用PostMessage方法异步发送WM_NOTIFY消息。

你可能感兴趣的:(Date,语言)