幻想森林

 找回密码
 立即注册
搜索
热搜: 活动 交友 discuz
查看: 4211|回复: 14

[通用编程] [原创]好久没用MFC了,就重新复习了一下,突然在发现Windows

[复制链接]

2

主题

26

帖子

288

积分

③业余

积分
288
QQ
发表于 2007-10-3 12:21:42 | 显示全部楼层 |阅读模式
现在发现Windows消息有点奇怪喔,以前总觉得用VC怪怪的,你看:
使用Button的时候,同样是发给子控件的消息.
WM_MOUSEMOVE消息传过来的时候,用的是接收该消息的窗口句柄找到该控件来处理事件.
而把按钮铵下去却用的是主窗口的句柄,外加WPARAM参数表示的控件ID号。
在程序中控件ID号包括了所有的窗口句柄。为什么不全都用ID号呢?或者全都用窗口句柄呢?

不过没多久就发现,尽管在主窗口中能从COMMAND消息中得到子窗口的BUTTON DOWN的事件.
但是,并不影响子窗口本身获得自已的Button Down事件。原来啊,WINDOWS发了两条消息过来喔。

现在,困扰我多年的问题终于解决.一开始我认为Windows所有的窗口都具有独立性.直到
在使用VC中碰到控件ID号,我开始困惑窗口之间的关系。然而现在,把它们分开来考虑,原来
的窗口之前的关系又清晰了(每个控件就是一个窗口,窗口有的都应该有)。

其实Button Down的这个事件完全可以由子窗口来获取,而之所以使用COMMAND又插了一脚,应该是为了方便吧。
但是,你要知道,对于一个窗口来说按下的鼠标的事件之类的消息是一类东西,处理起来应该是一样的。
而VC硬是把它们分开处理了,初学者会晕的!

最后,要说说VC与Delphi在做WINDOWS程序中.在这里就可以看出他们的分别了,
VC更接近Windows SDK,要是Windows SDK有些什么缺陷/特性,那么很容易在MFC中出现。
因此,学MFC必学SDK吧。Delphi封装得更好,更接近人类的直觉习惯,同类型的问题应该使用同类型的手段来处理。

经过比较,
最后,我们就在改动尽量少的情况下,试试让VC中的MouseMove接近于Delphi吧。
其实实现方式有很多种啦,使用裸体函数指针,又或使用事件/信号槽,最终还是决定
用老方法,使用接口的方式比较合理:
struct IButtonEvent
{
   virtual void OnButtonDown () = 0;
   virtual void OnButtonMove () = 0;
};

class CMyButton : public CButton
{
public:
   void SetEvent ( IButtonEvent *pEvent );
};

class CMyDialog : public CDialog, pulbic IButtonEvent
{
public:
   virtual void OnButtonDown ();
   virtual void OnButtonMove ();
};

结论:
学MFC要学SDK。
看WINDOWS消息中,COMMAND消息有些特别,要分开来考虑。
回复

使用道具 举报

50

主题

742

帖子

402

积分

版主

自定义头衔

Rank: 7Rank: 7Rank: 7

积分
402
发表于 2007-10-4 04:19:54 | 显示全部楼层
关键看消息是怎么在WIN32里面走的……
比如鼠标的消息通常是一组HITTEST来判断哪个窗口收到此消息,并且如果不行会交给父窗口来处理,很奇怪楼主为什么没有提到窗口树或者窗口层次……

使用ID可以说是为了方便,当时偶弄个20个按钮的CODE没有用ID真是累死了……
至于如何封装,一般了解了基本原理就可以自己写了……
其实对UI这种个人不是很感兴趣一直没有去深入过……
偶把FX拉过来…………她肯定知道~
Style-C
回复 支持 反对

使用道具 举报

2

主题

26

帖子

288

积分

③业余

积分
288
QQ
 楼主| 发表于 2007-10-4 10:03:40 | 显示全部楼层
引用第1楼lw于2007-10-04 04:19发表的  :
关键看消息是怎么在WIN32里面走的……
比如鼠标的消息通常是一组HITTEST来判断哪个窗口收到此消息,并且如果不行会交给父窗口来处理,很奇怪楼主为什么没有提到窗口树或者窗口层次……

使用ID可以说是为了方便,当时偶弄个20个按钮的CODE没有用ID真是累死了……
至于如何封装,一般了解了基本原理就可以自己写了……
.......

1>我认为不会交给父窗口处理,只会交给父类,除非你用代码指定交给父窗口,比如刚才的WM_MOUSEMOVE消息就不会交给父窗口,而且就算是MouseDown也不会交给父窗口。
父窗口收到的那个消息本来就是发给父窗口的COMMAND。

2>唉,上面的封装是有些问题,我忘了。象上面这样的话还是需要使用ID号来区分不同的控件。
    最方便的做法看来还是使用 事件/信号槽。
  
  1. //伪代码如下
  2. class CMyButton
  3. {
  4. public:
  5.    CSigV1<CPoint&> m_OnMouseMove; //只有一个参数的信号
  6. };
  7. class CMyDialog : public Dialog
  8. {
  9.    CMyButton m_btn1;
  10.    CMyButton m_btn2;
  11.    CSlots    m_slots; //槽
  12. public:
  13.    CMyDialog ()
  14.    {
  15.        //关连事件
  16.        m_slots.connect(m_btn1.m_OnMouseMove, this, &OnBtn1MouseMove);
  17.        m_slots.connect(m_btn2.m_OnMouseMove, this, &OnBtn2MouseMove);
  18.    }
  19.    void OnBtn1MouseMove(CPoint &point);
  20.    void OnBtn2MouseMove(CPoint &point);
  21. };
复制代码
回复 支持 反对

使用道具 举报

2

主题

26

帖子

288

积分

③业余

积分
288
QQ
 楼主| 发表于 2007-10-4 10:26:43 | 显示全部楼层
我下面顺便帖上裸体指针的使用方法,该代码可以在VC6.0下执行:
  1. #include <iostream>
  2. using namespace std;
  3. //在没有 事件/信号槽机制 的地方可以使用原始的裸体函数指针
  4. //裸体函数指针,因此不是类型安全的,在多继承的情况下要小心使用
  5. class CMyButton
  6. {
  7. public:
  8.     typedef void (CMyButton::*EVENT_BTN_DOWN)(void);
  9.     template <class T>
  10.     void SetMouseDownEvent ( T* pThis, void (T::*pFun)(void) )
  11.     {
  12.         m_event.pThis = reinterpret_cast<CMyButton*>     (pThis);   //这里就相当于裸体了,不过用MyButton是为了方便下面调用而已
  13.         m_event.pfn   = reinterpret_cast<EVENT_BTN_DOWN> (pFun);    //这里函数指针也退化了
  14.     }
  15.     void CallEvent ()
  16.     {
  17.         (m_event.pThis->*m_event.pfn)();
  18.     }
  19. private:
  20.     struct CALL_BACK
  21.     {
  22.         CMyButton       *pThis;
  23.         EVENT_BTN_DOWN     pfn;   
  24.     };
  25.     CALL_BACK m_event;
  26. };
  27. class CMyDialog
  28. {
  29. public:
  30.     void OnBtn1MouseDown ()
  31.     {
  32.         cout << "Btn1 down" << endl;
  33.     }
  34.     void OnBtn2MouseDown ()
  35.     {
  36.         cout << "Btn2 down" << endl;
  37.     }
  38. };
  39. int main(int argc, char* argv[])
  40. {
  41.     CMyDialog mydlg;
  42.     CMyButton btn1;
  43.     CMyButton btn2;
  44.     btn1.SetMouseDownEvent( &mydlg, &CMyDialog::OnBtn1MouseDown );
  45.     btn2.SetMouseDownEvent( &mydlg, &CMyDialog::OnBtn2MouseDown );
  46.     btn1.CallEvent();
  47.     btn2.CallEvent();
  48.     return 0;
  49. }
复制代码
回复 支持 反对

使用道具 举报

2

主题

26

帖子

288

积分

③业余

积分
288
QQ
 楼主| 发表于 2007-10-4 11:13:00 | 显示全部楼层
上面那个觉得不够裸,还是用了模板,这次来个更裸的.没有template的话BUTTON就可以写在dll中了 [s:4]
  1. #include <iostream>
  2. using namespace std;
  3. //在没有 事件/信号槽机制 的地方可以使用原始的裸体函数指针
  4. //裸体函数指针,因此不是类型安全的,在多继承的情况下要小心使用
  5. template <typename T>
  6. void* function_cast( T pfn )
  7. {
  8.     return *reinterpret_cast<void **>((&pfn));
  9. }
  10. class CMyButton
  11. {
  12. public:
  13.     typedef void (CMyButton::*EVENT_BTN_DOWN)(void);
  14.     void SetMouseDownEvent ( void* pThis, void *pfun )
  15.     {
  16.         m_event.pThis =   reinterpret_cast<CMyButton*>     (pThis);    //这里就相当于裸体了,不过用MyButton是为了方便下面调用而已
  17.         m_event.pfn   =  *reinterpret_cast<EVENT_BTN_DOWN*>(&pfun);    //这里函数指针也退化了
  18.     }
  19.     void CallEvent ()
  20.     {
  21.         (m_event.pThis->*m_event.pfn)();
  22.     }
  23. private:
  24.     struct CALL_BACK
  25.     {
  26.         CMyButton      *pThis;
  27.         EVENT_BTN_DOWN     pfn;   
  28.     };
  29.     CALL_BACK m_event;
  30. };
  31. class CMyDialog
  32. {
  33. public:
  34.     void OnBtn1MouseDown ()
  35.     {
  36.         cout << "Btn1 down" << endl;
  37.     }
  38.     void OnBtn2MouseDown ()
  39.     {
  40.         cout << "Btn2 down" << endl;
  41.     }
  42. };
  43. int main(int argc, char* argv[])
  44. {
  45.     CMyDialog mydlg;
  46.     CMyButton btn1;
  47.     CMyButton btn2;
  48.     btn1.SetMouseDownEvent( &mydlg, function_cast(&CMyDialog::OnBtn1MouseDown) );
  49.     btn2.SetMouseDownEvent( &mydlg, function_cast(&CMyDialog::OnBtn2MouseDown) );
  50.     btn1.CallEvent();
  51.     btn2.CallEvent();
  52.     return 0;
  53. }
复制代码
回复 支持 反对

使用道具 举报

136

主题

1751

帖子

548

积分

版主

Rank: 7Rank: 7Rank: 7

积分
548
发表于 2007-10-4 12:04:19 | 显示全部楼层
好像,现在新的GUi都是vcl那样的。MFC是出了名的难入门。反正是不懂。
え~え~お!!!
回复 支持 反对

使用道具 举报

50

主题

742

帖子

402

积分

版主

自定义头衔

Rank: 7Rank: 7Rank: 7

积分
402
发表于 2007-10-4 16:17:12 | 显示全部楼层
不明白为什么要
btn1.SetMouseDownEvent( &mydlg, function_cast(&CMyDialog::OnBtn1MouseDown) );
因为从概念来说,对话框的功能为什么会给按钮去使用呢?
总之实现应该没什么问题……

PS:另外印象中直接使用对象指针可能会有问题,具体记不清楚了,反正偶通常统一使用static类似的CB,至于对象化都是用结构的参数传递……
Style-C
回复 支持 反对

使用道具 举报

50

主题

742

帖子

402

积分

版主

自定义头衔

Rank: 7Rank: 7Rank: 7

积分
402
发表于 2007-10-4 16:28:27 | 显示全部楼层
WIN32:DefWndProc这个交给父窗口处理- -
通常一般会这么调用,而如果不交给父窗口处理的情况就好比把消息中途拦截掉类似……
具体的实现方法,偶说过方法很多的XD,所以不太在意……

就不知道LINUX下面怎么做,大概也是消息队列-窗口&lt;-&gt;传递 这样巴……
Style-C
回复 支持 反对

使用道具 举报

2

主题

26

帖子

288

积分

③业余

积分
288
QQ
 楼主| 发表于 2007-10-4 18:14:14 | 显示全部楼层
to lw:  

Q1:WIN32:DefWndProc这个交给父窗口处理
A1:没听说过DefWindowProc是会把消息传给父窗口的?原来我有跟踪源代码,最近MFC有点问题,
   跟踪的MFC源代码与执行的MFC DLL不匹配,所以就没有试成。


Q2:不明白为什么要
   btn1.SetMouseDownEvent( &mydlg, function_cast(&CMyDialog::OnBtn1MouseDown) );
   对话框的功能为什么会给按钮去使用呢?
A2: 因为对话框要响应 按钮窗口的事件,所以按钮窗口要回调给对话框响应.
    Delphi,VB不都是这样做的吗?
   
Q3: 反正偶通常统一使用static类似的CB,至于对象化都是用结构的参数传递……
A3: 是不是用static不是关键,你所说的方法最终应该与我的使用接口的方法类似。     
    都是       1个事件调用者调用1个或多个事件接收者.
    而现在是   多个同类的事件调用者调用1个或多个事件接收者. 就好比我有30个Button,就有相应的30 MouseDown事件函数.
   
    现在是不知道接收者的对象类型与函数指针类型,但是知道其调用方法.
    不知道是不是我对你所说的理解有偏差,如果是的话就指出来吧。


Q4: 另外印象中直接使用对象指针可能会有问题.
A4: 对象如果使用多继承的话就有能有问题,但是那种用法实在太BT了。(不是说多继承)
    只要保证对象与相应的对象函数配套就OK了。
    还有,使用裸指针肯定是很容易有问题的啦,比如参数不匹配之类的.很容易就内存越介.
    但是,如果要做成DLL的话只能这样了,因为没办法得到使用者的信息。
    如果不放心的话,可以参考MFC的做法,加个函数类型参数判断。
回复 支持 反对

使用道具 举报

2

主题

26

帖子

288

积分

③业余

积分
288
QQ
 楼主| 发表于 2007-10-4 18:26:09 | 显示全部楼层
使用裸指针除了参数的调用肯定能出问题之外,
我能想到的就是在多继承的情况下对象指针的不确定性:
测试代码如下:
  1. struct CBaseA{   virtual void OnBtn1MouseDown() = 0; };
  2. struct CBaseB{   virtual void OnBtn2MouseDown() = 0; };
  3. class CMyDialog : public CBaseA, public CBaseB
  4. {
  5. public:
  6.     virtual void OnBtn1MouseDown ()
  7.     {
  8.         cout &lt;&lt; &quot;Btn1 down&quot; &lt;&lt; endl;
  9.     }
  10.     virtual void OnBtn2MouseDown ()
  11.     {
  12.         cout &lt;&lt; &quot;Btn2 down&quot; &lt;&lt; endl;
  13.     }
  14. };
  15. int main(int argc, char* argv[])
  16. {
  17.     CMyDialog t1;
  18.     CMyButton btn1;
  19.    
  20.     CBaseB *pB = &t1; //pB与t1不是同一个指针了
  21.     //接口与函数指针不匹配,太BT了
  22.     btn1.SetMouseDownEvent( pB, function_cast(&CMyDialog::OnBtn1MouseDown) );
  23.     //结果就调用了 OnBtn2MouseDown 去了。
  24.     btn1.CallEvent();
  25.     return 0;
  26. }
复制代码
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

Archiver|手机版|小黑屋|幻想森林

GMT+8, 2024-5-2 14:25 , Processed in 0.022425 second(s), 21 queries .

Powered by Discuz! X3.4

© 2001-2017 Comsenz Inc.

快速回复 返回顶部 返回列表