精华内容
下载资源
问答
  • UCOSIII---消息传递

    2021-05-15 09:51:53
    UCOSIII任务之间通信 一个任务或者中断服务程序有时候需要和...一则消息包括几个部分:指向数据的指针,表明数据长度的变量和记录消息发布时刻的时间戳。指针指向的可以是一块数据区或者一个函数。显然,发布方和接收方

    UCOSIII任务之间通信

    一个任务或者中断服务程序有时候需要和另一个任务交流信息,这个信息传递的过程称为任务间的通信。任务间的信息传递可以通过两种途径来实现:一是通过全局变量,二是通过发布消息

    使用全局变量的时候,每个任务或者中断服务程序都必须保证其对全局变量的独占访问(通常的解决办法:关中断、临界区、信号量)。消息也可以通过消息队列作为中介发布给任务。

    消息

    一则消息包括几个部分:指向数据的指针,表明数据长度的变量和记录消息发布时刻的时间戳。指针指向的可以是一块数据区或者一个函数。显然,发布方和接收方需要对数据的内容和含义达成约定。换句话说,消息的接收方需要知道所接收的消息的含义才能对其进行处理。

    消息的内容必须一直保持可见性,因为发布数据采用的是引用传递,是指针传递而不是值传递。也就是说,发布的数据本身并不产生拷贝。

    消息队列

    消息队列是一种由用户程序分配的内核对象。用户可以分配任意数量的消息队列,唯一的限制就是可用的ARM区的容量

    在UCOSII中有消息邮箱和消息队列,但是在UCOSIII中只有消息队列。其实只能容纳一个消息的消息队列,可以看成是消息邮箱。消息队列是由用户创建的内核对[象,数量不限制,下图展示了用户可以对消息队列进行的操作:
    在这里插入图片描述
    从上图中可以看出,中断服务程序只能使用OSQPost()函数!

    对消息队列的读取方式采用先进先出(fifo)的方式。在UCOSIII中,也可以采用后进先出(LIFO)的方式发布消息。当任务或者中断服务程序需要向一个任务发布一则紧急的消息时,后进先出的机制就非常有用了。使用后进先出的方式,发布的消息会绕过所有其他已经位于消息队列中的消息优先传递给任务。消息队列的容量可以在运行时进行配置。

    在这里插入图片描述

    消息队列的结构

    UCOSIII中消息队列有关的结构体有三个,其关系如下:
    在这里插入图片描述

    OS_Q结构体

    struct  os_q {                                              /* Message Queue                                          */
                                                                /* ------------------ GENERIC  MEMBERS ------------------ */
        OS_OBJ_TYPE          Type;                              /* Should be set to OS_OBJ_TYPE_Q                         */
        CPU_CHAR            *NamePtr;                           /* Pointer to Message Queue Name (NUL terminated ASCII)   */
        OS_PEND_LIST         PendList;                          /* List of tasks waiting on message queue                 */
    #if OS_CFG_DBG_EN > 0u
        OS_Q                *DbgPrevPtr;
        OS_Q                *DbgNextPtr;
        CPU_CHAR            *DbgNamePtr;
    #endif
                                                                /* ------------------ SPECIFIC MEMBERS ------------------ */
        OS_MSG_Q             MsgQ;                              /* 消息队列    */
    };
    

    OS_MSG_Q结构体

    struct  os_msg_q {                                          /* OS_MSG_Q                                               */
        OS_MSG              *InPtr;                             /* 指向消息队列的头 */
        OS_MSG              *OutPtr;                            /* 指向消息队列尾 */
        OS_MSG_QTY           NbrEntriesSize;                    /* 消息队列最大长度   */
        OS_MSG_QTY           NbrEntries;                        /* 消息队列当前长度   */
        OS_MSG_QTY           NbrEntriesMax;                     /* 消息队列历史中最长的的长度    */
    };
    

    这个结构体才算是消息队列的本体了,它由一个指向消息队列头的指针、指向消息队列尾的指针、消息队列中消息的最大个数、当前个数等变量组成。

    OS_MSG结构体

    struct  os_msg {                                            /* MESSAGE CONTROL BLOCK                                  */
        OS_MSG              *NextPtr;                           /* 指向下一个消息的指针   */
        void                *MsgPtr;                            /* 消息内容    */
        OS_MSG_SIZE          MsgSize;                           /* 消息大小   */
        CPU_TS               MsgTS;                             /* 时间戳    */
    };
    

    这个结构体才算是消息队列中的消息的本体了。上文提到:消息一般包含:指向数据的指针,表明数据长度的变量和记录消息发布时刻的时间戳。

    消息队列的API函数

    在这里插入图片描述

    如何创建消息队列

    OSQCreate()函数用来创建一个消息队列,消息队列使得任务或者中断服务程序可以向一个或者多个任务发送消息。

    void  OSQCreate (OS_Q        *p_q,                            //指向一个消息队列
                     CPU_CHAR    *p_name,                        //消息队列的名字
                     OS_MSG_QTY   max_qty,                        //指定消息队列的长度,必须大于0
                     OS_ERR      *p_err)
     
    {
        CPU_SR_ALLOC();
     
        OS_CRITICAL_ENTER();
        p_q->Type    = OS_OBJ_TYPE_Q;                           /* Mark the data structure as a message queue             */
        p_q->NamePtr = p_name;
        OS_MsgQInit(&p_q->MsgQ,                                 /* Initialize the queue                                   */
                    max_qty);
        OS_PendListInit(&p_q->PendList);                        /* Initialize the waiting list                            */
     
        OSQQty++;                                               /* One more queue created                                 */
     
        OS_CRITICAL_EXIT_NO_SCHED();
       *p_err = OS_ERR_NONE;
    }
    

    max_qty:指定消息队列的长度,必须大于0。

    等待消息队列

    当一个任务想要从消息队列中接收一个消息的话就需要使用函数OSQPend()。

    void  *OSQPend (OS_Q         *p_q,                                //指向一个消息队列
                    OS_TICK       timeout,                            //等待消息的超时时间
                    OS_OPT        opt,                            //用来选择是否使用阻塞模式
                    OS_MSG_SIZE  *p_msg_size,                    //指向一个变量用来表示接收到的消息长度(字节数)
                    CPU_TS       *p_ts,                        //指向一个时间戳
                    OS_ERR       *p_err)
    {
        OS_PEND_DATA  pend_data;
        void         *p_void;
        CPU_SR_ALLOC();
     
        if (p_ts != (CPU_TS *)0) {
           *p_ts  = (CPU_TS  )0;                                /* Initialize the returned timestamp                      */
        }
     
        CPU_CRITICAL_ENTER();
        p_void = OS_MsgQGet(&p_q->MsgQ,                         /* Any message waiting in the message queue?              */
                            p_msg_size,
                            p_ts,
                            p_err);
        if (*p_err == OS_ERR_NONE) {
            CPU_CRITICAL_EXIT();
            return (p_void);                                    /* Yes, Return message received                           */
        }
     
        if ((opt & OS_OPT_PEND_NON_BLOCKING) != (OS_OPT)0) {    /* Caller wants to block if not available?                */
            CPU_CRITICAL_EXIT();
           *p_err = OS_ERR_PEND_WOULD_BLOCK;                    /* No                                                     */
            return ((void *)0);
        } else {
            if (OSSchedLockNestingCtr > (OS_NESTING_CTR)0) {    /* Can't pend when the scheduler is locked                */
                CPU_CRITICAL_EXIT();
               *p_err = OS_ERR_SCHED_LOCKED;
                return ((void *)0);
            }
        }
     
        OS_CRITICAL_ENTER_CPU_CRITICAL_EXIT();                  /* Lock the scheduler/re-enable interrupts                */
        OS_Pend(&pend_data,                                     /* Block task pending on Message Queue                    */
                (OS_PEND_OBJ *)((void *)p_q),
                OS_TASK_PEND_ON_Q,
                timeout);
        OS_CRITICAL_EXIT_NO_SCHED();
     
        OSSched();                                              /* Find the next highest priority task ready to run       */
     
        CPU_CRITICAL_ENTER();
        switch (OSTCBCurPtr->PendStatus) {
            case OS_STATUS_PEND_OK:                             /* Extract message from TCB (Put there by Post)           */
                 p_void     = OSTCBCurPtr->MsgPtr;
                *p_msg_size = OSTCBCurPtr->MsgSize;
                 if (p_ts  != (CPU_TS *)0) {
                    *p_ts   =  OSTCBCurPtr->TS;
                 }
                *p_err      = OS_ERR_NONE;
                 break;
     
            case OS_STATUS_PEND_ABORT:                          /* Indicate that we aborted                               */
                 p_void     = (void      *)0;
                *p_msg_size = (OS_MSG_SIZE)0;
                 if (p_ts  != (CPU_TS *)0) {
                    *p_ts   =  OSTCBCurPtr->TS;
                 }
                *p_err      = OS_ERR_PEND_ABORT;
                 break;
     
            case OS_STATUS_PEND_TIMEOUT:                        /* Indicate that we didn't get event within TO            */
                 p_void     = (void      *)0;
                *p_msg_size = (OS_MSG_SIZE)0;
                 if (p_ts  != (CPU_TS *)0) {
                    *p_ts   = (CPU_TS  )0;
                 }
                *p_err      = OS_ERR_TIMEOUT;
                 break;
     
            case OS_STATUS_PEND_DEL:                            /* Indicate that object pended on has been deleted        */
                 p_void     = (void      *)0;
                *p_msg_size = (OS_MSG_SIZE)0;
                 if (p_ts  != (CPU_TS *)0) {
                    *p_ts   =  OSTCBCurPtr->TS;
                 }
                *p_err      = OS_ERR_OBJ_DEL;
                 break;
     
            default:
                 p_void     = (void      *)0;
                *p_msg_size = (OS_MSG_SIZE)0;
                *p_err      = OS_ERR_STATUS_INVALID;
                 break;
        }
        CPU_CRITICAL_EXIT();
        return (p_void);
    }
    

    timeout:等待消息的超时时间,如果在指定的时间没有接收到消息的话,任务就会被唤醒,接着运行。这个参数也可以设置为0,表示任务将一直等待下去,直到接收到消息。

    opt:用来选择是否使用阻塞模式
    OS_OPT_PEND_BLOCKING:如果没有任何消息存在的话就阻塞任务,一直等待,直到接收到消息。OS_OPT_PEND_NON_BLOCKING:如果消息队列没有任何消息的话任务就直接返回。

    **p_ts:指向一个时间戳,**表明什么时候接收到消息。如果这个指针被赋值为NULL的话,说明用户没有要求时间戳。

    向消息队列发送消息

    可以通过函数OSQPost()向消息队列发送消息,如果消息队列是满的,则函数OSQPost()就会立刻返回,并且返回一个特定的错误代码。

    void  OSQPost (OS_Q         *p_q,                        //指向一个消息队列
                   void         *p_void,                        //指向实际发送的内容
                   OS_MSG_SIZE   msg_size,                    //设定消息的大小,单位为字节数
                   OS_OPT        opt,                        //用来选择消息发送操作的类型
                   OS_ERR       *p_err)
    {
        CPU_TS  ts;
     
        ts = OS_TS_GET();                                       /* Get timestamp                                          */
     
        OS_QPost(p_q,
                 p_void,
                 msg_size,
                 opt,
                 ts,
                 p_err);
    }
    

    p_void:指向实际发送的内容,p_void是一个执行void类型的指针,其具体含义由用户程序的决定

    展开全文
  • MFC消息映射宏

    千次阅读 2010-09-24 14:41:00
      MFC消息映射宏  消息的传递与发送是Windows...一个完整的MFC消息映射包括消息处理函数的原型声明、实现以及存在于消息映射中的消息入口。这几部分分别存在与类的头文件和实现文件中。一般情况下

     

    MFC消息映射宏 

    消息的传递与发送是Windows应用程序的核心所在,任何事件的触发与响应均要通过消息的作用才能得以完成。在SDK编程中,对消息的获取与分发主要是 通过消息循环来完成的,而在MFC编程中则是通过采取消息映射的方式对其进行处理的。相比而言,这样的处理方式要简单许多,这也是符合面向对象编程中尽可 能隐含实现细节的原则。一个完整的MFC消息映射包括对消息处理函数的原型声明、实现以及存在于消息映射中的消息入口。这几部分分别存在与类的头文件和实现文件中。一般情况下除了对自定义消息的响应外,对于标准Windows 消息的映射处理可以借助ClassWizard向导来完成。   在选定了待处理的Windows 消息后,向导将会根据消息的不同而生成具有相应函数参数和返回值的消息处理代码框架。下面这段代码给出了一个完成的MFC消息映射过程:

    // 在.h文件中的声明 //{{AFX_MSG(CMessageMapView) afx_msg void OnMove(int x, int y); //}}AFX_MSG DECLARE_MESSAGE_MAP() …… // 在.cpp文件中的实现 BEGIN_MESSAGE_MAP(CMessageMapView, CView) //{{AFX_MSG_MAP(CMessageMapView) ON_WM_MOVE() //}}AFX_MSG_MAP END_MESSAGE_MAP() …… void CMessageMapView::OnMove(int x, int y) { CView::OnMove(x, y); // TODO: Add your message handler code here }

    这里对Windows标准消息WM_MOVE做了消息映射,其中用到的BEGIN_MESSAGE_MAP、END_MESSAGE_MAP 和头文件中的DECLARE_MESSAGE_MAP等均是用于消息映射的宏。这些宏声明了在应用程序框架中可用于在系统中浏览所有对象映射的成员变量和 函数。除了以上三个比较常见的宏之外,MFC还提供了其他一些用于消息映射的宏,详情可参见下表:

    宏名

    说明

    DECLARE_MESSAGE_MAP

    在头文件声明源文件中所含有的消息映射

    BEGIN_MESSAGE_MAP

    标记源文件消息映射的开始

    END_MESSAGE_MAP

    标记源文件消息映射的结束

    ON_COMMAND

    将特定命令的处理委派给类的一个成员函数

    ON_COMMAND_RANGE

    把一定范围内的command IDs 映射到相应的函数上

    ON_CONTROL

    映射一个函数到一个定制控制通知消息。其中,定制控制通知消息是从一个控制发送到其父窗口的消息。

    ON_CONTROL_RANGE

    将一个控制ID的范围映射到一个消息处理函数

    ON_CONTROL_REFLECT

    映射一个由父窗口反射回控制的通知消息

    ON_MESSAGE

    将一个用户自定义消息映射到一消息处理函数

    ON_NOTIFY

    映射一个控制消息到一个函数

    ON_NOTIFY_RANGE

    映射一个控制ID范围内的控制消息到一个函数

    ON_NOTIFY_EX

    映射一个控制消息到一个函数,该成员函数返回FALSE或TRUE来表明通知是否应被传送到下一个对象以进行其他反应。

    ON_NOTIFY_EX_RANGE

    映射一个控制ID范围内的控制消息到一个函数,该成员函数返回FALSE或TRUE来表明通知是否应被传送到下一个对象以进行其他反应

    ON_NOTIFY_REFLECT

    映射一个控制消息到一个函数。该消息将会被控制的父窗口反射回来。

    ON_REGISTERED_MESSAGE

    映射一个唯一的消息到一个将要处理该注册消息的函数上。该消息是由RegisterWindowMessage()函数注册的。

    ON_UPDATE_COMMAND_UI

    映射一个函数来处理一个用户接口更新命令消息

    ON_UPDATE_COMMAND_UI_RANGE

    映射一个命令ID的范围到一个更新消息处理函数

    一般作为基类使用的CWnd类为Windows消息定义了大量窗口消息的缺省处理函数,这些函数大部分只是简单地调用了Windows的缺省 过程,可以在派生类中对其进行重载。但是MFC应用程序框架却并没有象使用普通虚函数那样使用Windows消息处理函数,而是通过宏将指定的消息映射到 派生类的成员函数。如果MFC仍象普通虚函数一样对消息响应函数进行处理,那么CWnd类就要为这上百个消息声明虚函数。而C++将为在程序中使用的每一 个派生类都提供一个被称作vtable的虚拟函数分配表,这个分配表需要为每一个虚函数提供一个4字节的入口,而不管这些函数在派生类中是否真正被重载, 这将不能有效利用存储空间。而且对于每一个不同类型的窗口或控件,应用程序都要为其提供一个超过400字节的虚拟函数分配表来实现对消息的响应。而采用 MFC的用宏将Windows消息映射到C++成员函数的方式则可避免产生庞大的虚拟函数分配表,其消耗的内存是同它所包含的消息入口数量成正比的。

     

    为了简化程序员的工作, MFC定义了一系列的消息映射宏和像AfxSig_vv这样的枚举变量,以及标准消息处理函数,并且具体地实现这些函数。这里主要讨论消息映射宏,常用的分为以下几类。

    1.用于 Windows消息的宏,前缀为“ON_WM_”。
    这样的宏不带参数,因为它对应的消息和消息处理函数的函数名称、函数原型是确定的。 MFC提供了这类消息处理函数的定义和缺省实现。每个这样的宏处理不同的Windows消息。
    例如:宏 ON_WM_CREATE()把消息WM_CREATE映射到OnCreate函数,消息映射条目的第一个成员nMessage指定为要处理的Windows消息的ID,第二个成员nCode指定为0。

    2.用于命令消息的宏 ON_COMMAND

    这类宏带有参数,需要通过参数指定命令 ID和消息处理函数。这些消息都映射到WM_COMMAND上,也就是将消息映射条目的第一个成员nMessage指定为WM_COMMAND,第二个成员nCode指定为CN_COMMAND(即0)。消息处理函数的原型是void (void),不带参数,不返回值。

    除了单条命令消息的映射,还有把一定范围的命令消息映射到一个消息处理函数的映射宏 ON_COMMAND_RANGE。这类宏带有参数,需要指定命令ID的范围和消息处理函数。这些消息都映射到WM_COMMAND上,也就是将消息映射条目的第一个成员nMessage指定为WM_COMMAND,第二个成员nCode指定为CN_COMMAND(即0),第三个成员nID和第四个成员nLastID指定了映射消息的起止范围。消息处理函数的原型是void (UINT),有一个UINT类型的参数,表示要处理的命令消息ID,不返回值。

    3.用于控制通知消息的宏

    这类宏可能带有三个参数,如 ON_CONTROL,就需要指定控制窗口ID,通知码和消息处理函数;也可能带有两个参数,如具体处理特定通知消息的宏ON_BN_CLICKED、ON_LBN_DBLCLK、ON_CBN_EDITCHANGE等,需要指定控制窗口ID和消息处理函数。

    控制通知消息也被映射到 WM_COMMAND上,也就是将消息映射条目的第一个成员的nMessage指定为WM_COMMAND,但是第二个成员nCode是特定的通知码,第三个成员nID是控制子窗口的ID,第四个成员nLastID等于第三个成员的值。消息处理函数的原型是void (void),没有参数,不返回值。

    还有一类宏处理通知消息 ON_NOTIFY,它类似于ON_CONTROL,但是控制通知消息被映射到WM_NOTIFY。消息映射条目的第一个成员的nMessage被指定为WM_NOTIFY,第二个成员nCode是特定的通知码,第三个成员nID是控制子窗口的ID,第四个成员nLastID等于第三个成员的值。消息处理函数的原型是void (NMHDR*, LRESULT*),参数1是NMHDR指针,参数2是LRESULT指针,用于返回结果,但函数不返回值。

    对应地,还有把一定范围的控制子窗口的某个通知消息映射到一个消息处理函数的映射宏,这类宏包括 ON__CONTROL_RANGE和ON_NOTIFY_RANGE。这类宏带有参数,需要指定控制子窗口ID的范围和通知消息,以及消息处理函数。

    对于 ON__CONTROL_RANGE,是将消息映射条目的第一个成员的nMessage指定为WM_COMMAND,但是第二个成员nCode是特定的通知码,第三个成员nID和第四个成员nLastID等于指定了控制窗口ID的范围。消息处理函数的原型是void (UINT),参数表示要处理的通知消息是哪个ID的控制子窗口发送的,函数不返回值。

    对于 ON__NOTIFY_RANGE,消息映射条目的第一个成员的nMessage被指定为WM_NOTIFY,第二个成员nCode是特定的通知码,第三个成员nID和第四个成员nLastID指定了控制窗口ID的范围。消息处理函数的原型是void (UINT, NMHDR*, LRESULT*),参数1表示要处理的通知消息是哪个ID的控制子窗口发送的,参数2是NMHDR指针,参数3是LRESULT指针,用于返回结果,但函数不返回值。

    4.用于用户界面接口状态更新的ON_UPDATE_COMMAND_UI宏

    这类宏被映射到消息 WM_COMMND上,带有两个参数,需要指定用户接口对象ID和消息处理函数。消息映射条目的第一个成员nMessage被指定为WM_COMMAND,第二个成员nCode被指定为-1,第三个成员nID和第四个成员nLastID都指定为用户接口对象ID。消息处理函数的原型是 void (CCmdUI*),参数指向一个CCmdUI对象,不返回值。

    对应地,有更新一定 ID范围的用户接口对象的宏ON_UPDATE_COMMAND_UI_RANGE,此宏带有三个参数,用于指定用户接口对象ID的范围和消息处理函数。消息映射条目的第一个成员nMessage被指定为WM_COMMAND,第二个成员nCode被指定为-1,第三个成员nID和第四个成员nLastID用于指定用户接口对象ID的范围。消息处理函数的原型是 void (CCmdUI*),参数指向一个CCmdUI对象,函数不返回值。之所以不用当前用户接口对象ID作为参数,是因为CCmdUI对象包含了有关信息。

    5.用于其他消息的宏

    例如用于用户定义消息的 ON_MESSAGE。这类宏带有参数,需要指定消息ID和消息处理函数。消息映射条目的第一个成员nMessage被指定为消息ID,第二个成员nCode被指定为0,第三个成员nID和第四个成员也是0。消息处理的原型是LRESULT (WPARAM, LPARAM),参数1和参数2是消息参数wParam和lParam,返回LRESULT类型的值。

     

     

    展开全文
  • MFC中的消息映射

    千次阅读 2011-04-28 16:01:00
    MFC消息映射 消息的... 一个完整的MFC消息映射包括消息处理函数的原型声明、实现以及存在于消息映射中的消息入口。这几部分分别存在与类的头文件和实现文件中。一般情况下除了对自定义消息的响应外,对于标准Wind

    WINDOWS 消息的种类

      WINDOWS中消息主要有以下三种类型:

      1、标准的WINDOWS消息:这类消息是以WM_为前缀,不过WM_COMMAND例外。 例如: WM_MOVE、WM_QUIT等.

      2、命令消息:命令消息以WM_COMMAND为消息名.在消息中含有命令的标志符ID,以区分具体的命令.由菜单,工具栏等命令接口对象产生.

      3、控件通知消息:控件通知消息也是以WM_COMMAND为消息名.由编辑框,列表框,子窗口发送给父窗口的通知消息.在消息中包含控件通知码.以区分具体控件的通知消息.

      其中标准的WINDOWS消息及控件通知消息主要由窗口类即直接或间接由CWND类派生类处理.相对标准WINDOWS消息及控件通知消息而言,命令消息的处理对象范围就广得多.它不仅可以由窗口类处理,还可以由文档类,文档模板类及应用类所处理。 

     

     

    MFC消息映射
      消息的传递与发送是Windows程序的核心所在,任何事件的触发与响应均要通过消息的作用才能得以完成。在SDK编程中,对消息的获取与分发主要是通过消息循环来完成的,而在MFC编程中则是通过采取消息映射的方式对其进行处理的。相比而言,这样的处理方式要简单许多,这也是符合面向对象编程中尽可能隐含实现细节的原则。
      一个完整的MFC消息映射包括对消息处理函数的原型声明、实现以及存在于消息映射中的消息入口。这几部分分别存在与类的头文件和实现文件中。一般情况下除了对自定义消息的响应外,对于标准Windows 消息的映射处理可以借助ClassWizard向导来完成。
      在选定了待处理的Windows 消息后,向导将会根据消息的不同而生成具有相应函数参数和返回值的消息处理代码框架。下面这段代码给出了一个完成的MFC消息映射过程:

    // 在.h文件中的声明
    //{{AFX_MSG(CMessageMapView)
    afx_msg void OnMove(int x, int y);
    //}}AFX_MSG

    DECLARE_MESSAGE_MAP()

    ……
    // 在.cpp文件中的实现
    BEGIN_MESSAGE_MAP(CMessageMapView, CView)
    //{{AFX_MSG_MAP(CMessageMapView)
    ON_WM_MOVE()
    //}}AFX_MSG_MAP

    END_MESSAGE_MAP()
    ……
    void CMessageMapView::OnMove(int x, int y) 
    {
    CView::OnMove(x, y);
    // TODO: Add your message handler code here
    }
      

     

           这里对Windows标准消息WM_MOVE做了消息映射,其中用到的BEGIN_MESSAGE_MAP、END_MESSAGE_MAP和头文件中的DECLARE_MESSAGE_MAP等均是用于消息映射的宏。这些宏声明了在应用程序框架中可用于在系统中浏览所有对象映射的成员变量和函数。

     

          一般作为基类使用的CWnd类为Windows消息定义了大量窗口消息的缺省处理函数,这些函数大部分只是简单地调用了Windows的缺省过程,可以在派生类中对其进行重载。但是MFC应用程序框架却并没有象使用普通虚函数那样使用Windows消息处理函数,而是通过宏将指定的消息映射到派生类的成员函数。如果MFC仍象普通虚函数一样对消息响应函数进行处理,那么CWnd类就要为这上百个消息声明虚函数。而C++将为在程序中使用的每一个派生类都提供一个被称作vtable的虚拟函数分配表,这个分配表需要为每一个虚函数提供一个4字节的入口,而不管这些函数在派生类中是否真正被重载,这将不能有效利用存储空间。而且对于每一个不同类型的窗口或控件,应用程序都要为其提供一个超过400字节的虚拟函数分配表来实现对消息的响应。而采用MFC的用宏将Windows消息映射到C++成员函数的方式则可避免产生庞大的虚拟函数分配表,其消耗的内存是同它所包含的消息入口数量成正比的。

        上面代码中的消息处理函数的具体实现为函数void CMessageMapView::OnMove(int x, int y) ,函数体里面调用了父类的处理函数CView::OnMove(x, y),那么在派生类需要WM_MOVE消息时,才构造对应的消息映射,而不需要把Windows消息的所有的缺省处理函数都在派生类中进行重载,正是这样就节省了很多存储空间。

     

     

     

          这里ON_COMMAND宏将特定命令的处理同一个类成员函数建立了关联。而宏ON_UPDATE_COMMAND_UI则负责对命令的更新,即通过CCmdUI对象控制菜单/控件的是否可用或其他一些状态变化的更新。对命令的更新也可以将其理解为存在一个含有每个菜单入口的大表,各菜单入口含有菜单是否可用的标志。在显示菜单时通过快速检查该表而做出其所对应的每一个菜单项是否可用的决定。如果可用标志发生了变化,该表也将得到及时的更新。

    命令和通知

     

            命令和通知实际都是一种特殊的消息类型。在SDK编程中,菜单和控件的动作均会产生一个WM_COMMAND命令消息,通过对消息参数wParam的区分可以识别出具体是哪个控件或菜单发出的命令。在MFC应用框架下,菜单和控件产生的消息将有所区分,选取菜单产生的消息被称作命令,而点击控件所产生的消息则被称作通知。由于命令和通知的本质仍是一种消息,因此在基本原理上仍是同消息一致的,即也是通过消息循环进入OnWndMsg()进而为对应的处理函数所响应。但是在使用上,命令和消息还是有区别的,其中一个最主要的区别是消息只有CWnd类的派生类所接收,而命令和通知则可以为所有从CCmdTarget派生出去的类对象所接收,从MFC类的继承关系可以看出,除CWnd外CWinThread、CDocument和CDocItem等也都可以接收命令和通知。
    // 头文件
    //{{AFX_MSG(CDIP_SystemView)
    afx_msg void OnEmboss();
    afx_msg void OnUpdateEmboss(CCmdUI* pCmdUI);
    //}}AFX_MSG
    DECLARE_MESSAGE_MAP()
    ……
    // 源文件
    BEGIN_MESSAGE_MAP(CDIP_SystemView, CScrollView)
    /{{AFX_MSG_MAP(CDIP_SystemView)
    ON_COMMAND(IDM_EMBOSS, OnEmboss)
    ON_UPDATE_COMMAND_UI(IDM_EMBOSS, OnUpdateEmboss)
    //}}AFX_MSG_MAP
    END_MESSAGE_MAP()
    ……
    void CDIP_SystemView::OnEmboss () 
    {
    return;
    }
    ……
    void CDIP_SystemView::OnUpdateStartPos(CCmdUI* pCmdUI) 
    {
    pCmdUI->Enable(m_bCanUse); 
    }
    展开全文
  • 一个进程池的服务器程序 下面做了非常简单的http服务器,该服务器只能接收Get请求。 流程大概如下: 1,父进程listen,创建pipe(下面所有父子进程之间的通信都用该pipe) 2,父进程预fork n个子进程 3,各个子...
  • 220. 升级论坛编辑器,主要从以下几个方面作了努力: .适当兼容Mozilla系列浏览器 .默认编辑方式全部设为高级(html)编辑方式,除非浏览器不支持,比如禁用JS .IE能享受所有功能,Mozilla系列拥有大部分编辑功能 ....
  • 口中的变化情况通知给另一个窗口,则一个窗口发送信号,另一个窗口的槽接收此信号并进 行相应的操作,即可实现两窗∏之间的通信。这比传统的图形化程序采用回调函数的方式 实现对象间通信要简单灵活得多。每Qt对象...
  • RabbitMQ+PHP演示实例

    2021-05-27 15:50:15
    RabbitMQ+PHP演示实例概念几个概念说明消息队列的使用过程大概如下消息队列持久化包括3个部分:介绍PHP+RabbitMQ实现消息队列config.phpBaseMQ.phpProductMQ.phpConsumerMQ.php 概念 RabbitMQ是个在AMQP基础上实现...

    概念

    RabbitMQ是一个在AMQP基础上实现的企业级消息系统。何谓消息系统,就是消息队列系统,消息队列是“”消费-生产者模型“”的一个典型的代表,一端往消息队列中不断写入消息,而另一端则可以读取或者订阅队列中的消息。

    在项目中,将一些无需即时返回且耗时的操作提取出来,进行了异步操作,而这种异步处理的方式大大的节省了服务器的请求时间,从而提高了系统的吞吐量。而且不影响服务器做其他相应,不独占服务器资源。

    如:注册用户这种服务,它可能解耦成好几种独立的服务(账号验证,邮箱验证码,手机短信码等)。它们作为消费者,等待用户输入数据,在前台数据提交之后会经过分解并发送到各个服务所在的url,分发的那个角色就相当于生产者。消费者在获取数据时候有可能一次不能处理完,那么它们各自有一个请求队列,那就是内存缓冲区了。做这项工作的框架叫做消息队列。

    又比如:电商系统中的订单处理系统,传统处理模式是:下订单的时候,订单系统可能会调用库存系统的接口,这样两个系统之间存在一个严重依赖关系,如果库存系统宕机,那么整个流程都会受到影响。现在大多公司的处理方法是:引入消息队列,下完订单,订单系统完成持久化处理,将消息写入消息队列,返回用户订单下单成功。

    对库存系统来说,采用拉/推的方式,获取下单信息,库存系统根据下单信息,进行库存操作。这样实现了两个系统间的解耦。

    即使在下单时库存系统不能正常使用。也不影响正常下单,因为下单后,订单系统写入消息队列就不再关心其他的后续操作了。

    给一张结构图:

    在这里插入图片描述

    几个概念说明

    Broker:简单来说就是消息队列服务器实体。
    Exchange:消息交换机,它指定消息按什么规则,路由到哪个队列。
    Queue:消息队列载体,每个消息都会被投入到一个或多个队列。
    Binding:绑定,它的作用就是把exchange和queue按照路由规则绑定起来。
    Routing Key:路由关键字,exchange根据这个关键字进行消息投递。
    vhost:虚拟主机,一个broker里可以开设多个vhost,用作不同用户的权限分离。
    producer:消息生产者,就是投递消息的程序。
    consumer:消息消费者,就是接受消息的程序。

    channel:消息通道,在客户端的每个连接里,可建立多个channel,每个channel代表一个会话任务。可以理解为一个连接中的子通道,想象一条高速公路,可能是 4车道的,也可能是 8 车道的,这些车道就是 Channel。

    消息队列的使用过程大概如下

    (1)客户端连接到消息队列服务器,打开一个channel。
    (2)客户端声明一个exchange,并设置相关属性。
    (3)客户端声明一个queue,并设置相关属性。
    (4)客户端使用routing key,在exchange和queue之间建立好绑定关系。
    (5)客户端投递消息到exchange。

    exchange接收到消息后,就根据消息的key和已经设置的binding,进行消息路由,将消息投递到一个或多个队列里。

    exchange也有几个类型,完全根据key进行投递的叫做Direct交换机,例如,绑定时设置了routing key为”abc”,那么客户端提交的消息,只有设置了key为”abc”的才会投递到队列。

    对key进行模式匹配后进行投递的叫做Topic交换机,符号”#”匹配一个或多个词,符号””匹配正好一个词。例如”abc.#”匹配”abc.def.ghi”,”abc.”只匹配”abc.def”。

    还有一种不需要key的,叫做Fanout交换机,它采取广播模式,一个消息进来时,投递到与该交换机绑定的所有队列。

    RabbitMQ支持消息的持久化,也就是数据写在磁盘上,为了数据安全考虑,我想大多数用户都会选择持久化。

    消息队列持久化包括3个部分:

    (1)exchange持久化,在声明时指定durable => 1
    (2)queue持久化,在声明时指定durable => 1
    (3)消息持久化,在投递时指定delivery_mode => 2(1是非持久化)

    如果exchange和queue都是持久化的,那么它们之间的binding也是持久化的。如果exchange和queue两者之间有一个持久化,一个非持久化,就不允许建立绑定。

    介绍

    config.php 配置信息
    BaseMQ.php MQ基类
    ProductMQ.php 生产者类
    ConsumerMQ.php 消费者类
    Consumer2MQ.php 消费者2(可有多个)
    

    PHP+RabbitMQ实现消息队列

    先安装PHP对应的RabbitMQ,这里用的是 php_amqp 不同的扩展实现方式会有细微的差异.
    php扩展地址: http://pecl.php.net/package/amqp
    具体以官网为准  http://www.rabbitmq.com/getstarted.html 
    

    config.php

    <?php
    return [
        //配置
        'host' => [
            'host' => '127.0.0.1',
            'port' => '5672',
            'login' => 'guest',
            'password' => 'guest',
            'vhost'=>'/',
        ],
        //交换机
        'exchange'=>'word',
        //路由
        'routes' => [],
    ];
    

    BaseMQ.php

    <?php
    /**
     * Created by PhpStorm.
     * Time: 14:28
     */
    
    namespace MyObjSummary\rabbitMQ;
    
    /** Member
     *      AMQPChannel
     *      AMQPConnection
     *      AMQPEnvelope
     *      AMQPExchange
     *      AMQPQueue
     * Class BaseMQ
     * @package MyObjSummary\rabbitMQ
     */
    class BaseMQ
    {
        /** MQ Channel
         * @var \AMQPChannel
         */
        public $AMQPChannel ;
    
        /** MQ Link
         * @var \AMQPConnection
         */
        public $AMQPConnection ;
    
        /** MQ Envelope
         * @var \AMQPEnvelope
         */
        public $AMQPEnvelope ;
    
        /** MQ Exchange
         * @var \AMQPExchange
         */
        public $AMQPExchange ;
    
        /** MQ Queue
         * @var \AMQPQueue
         */
        public $AMQPQueue ;
    
        /** conf
         * @var
         */
        public $conf ;
    
        /** exchange
         * @var
         */
        public $exchange ;
    
        /** link
         * BaseMQ constructor.
         * @throws \AMQPConnectionException
         */
        public function __construct()
        {
            $conf =  require 'config.php' ;
            if(!$conf)
                throw new \AMQPConnectionException('config error!');
            $this->conf     = $conf['host'] ;
            $this->exchange = $conf['exchange'] ;
            $this->AMQPConnection = new \AMQPConnection($this->conf);
            if (!$this->AMQPConnection->connect())
                throw new \AMQPConnectionException("Cannot connect to the broker!\n");
        }
    
        /**
         * close link
         */
        public function close()
        {
            $this->AMQPConnection->disconnect();
        }
    
        /** Channel
         * @return \AMQPChannel
         * @throws \AMQPConnectionException
         */
        public function channel()
        {
            if(!$this->AMQPChannel) {
                $this->AMQPChannel =  new \AMQPChannel($this->AMQPConnection);
            }
            return $this->AMQPChannel;
        }
    
        /** Exchange
         * @return \AMQPExchange
         * @throws \AMQPConnectionException
         * @throws \AMQPExchangeException
         */
        public function exchange()
        {
            if(!$this->AMQPExchange) {
                $this->AMQPExchange = new \AMQPExchange($this->channel());
                $this->AMQPExchange->setName($this->exchange);
            }
            return $this->AMQPExchange ;
        }
    
        /** queue
         * @return \AMQPQueue
         * @throws \AMQPConnectionException
         * @throws \AMQPQueueException
         */
        public function queue()
        {
            if(!$this->AMQPQueue) {
                $this->AMQPQueue = new \AMQPQueue($this->channel());
            }
            return $this->AMQPQueue ;
        }
    
        /** Envelope
         * @return \AMQPEnvelope
         */
        public function envelope()
        {
            if(!$this->AMQPEnvelope) {
                $this->AMQPEnvelope = new \AMQPEnvelope();
            }
            return $this->AMQPEnvelope;
        }
    }
    
    

    ProductMQ.php

    <?php
    //生产者 P
    namespace MyObjSummary\rabbitMQ;
    require 'BaseMQ.php';
    class ProductMQ extends BaseMQ
    {
        private $routes = ['hello','word']; //路由key
    
        /**
         * ProductMQ constructor.
         * @throws \AMQPConnectionException
         */
        public function __construct()
        {
           parent::__construct();
        }
    
        /** 只控制发送成功 不接受消费者是否收到
         * @throws \AMQPChannelException
         * @throws \AMQPConnectionException
         * @throws \AMQPExchangeException
         */
        public function run()
        {
            //频道
            $channel = $this->channel();
            //创建交换机对象
            $ex = $this->exchange();
            //消息内容
            $message = 'product message '.rand(1,99999);
            //开始事务
            $channel->startTransaction();
            $sendEd = true ;
            foreach ($this->routes as $route) {
                $sendEd = $ex->publish($message, $route) ;
                echo "Send Message:".$sendEd."\n";
            }
            if(!$sendEd) {
                $channel->rollbackTransaction();
            }
            $channel->commitTransaction(); //提交事务
            $this->close();
            die ;
        }
    }
    try{
        (new ProductMQ())->run();
    }catch (\Exception $exception){
        var_dump($exception->getMessage()) ;
    }
    
    

    ConsumerMQ.php

    <?php
    //消费者 C
    namespace MyObjSummary\rabbitMQ;
    require 'BaseMQ.php';
    class ConsumerMQ extends BaseMQ
    {
        private  $q_name = 'hello'; //队列名
        private  $route  = 'hello'; //路由key
    
        /**
         * ConsumerMQ constructor.
         * @throws \AMQPConnectionException
         */
        public function __construct()
        {
            parent::__construct();
        }
    
        /** 接受消息 如果终止 重连时会有消息
         * @throws \AMQPChannelException
         * @throws \AMQPConnectionException
         * @throws \AMQPExchangeException
         * @throws \AMQPQueueException
         */
        public function run()
        {
    
            //创建交换机
            $ex = $this->exchange();
            $ex->setType(AMQP_EX_TYPE_DIRECT); //direct类型
            $ex->setFlags(AMQP_DURABLE); //持久化
            //echo "Exchange Status:".$ex->declare()."\n";
    
            //创建队列
            $q = $this->queue();
            //var_dump($q->declare());exit();
            $q->setName($this->q_name);
            $q->setFlags(AMQP_DURABLE); //持久化
            //echo "Message Total:".$q->declareQueue()."\n";
    
            //绑定交换机与队列,并指定路由键
            echo 'Queue Bind: '.$q->bind($this->exchange, $this->route)."\n";
    
            //阻塞模式接收消息
            echo "Message:\n";
            while(True){
                $q->consume(function ($envelope,$queue){
                    $msg = $envelope->getBody();
                    echo $msg."\n"; //处理消息
                    $queue->ack($envelope->getDeliveryTag()); //手动发送ACK应答
                });
                //$q->consume('processMessage', AMQP_AUTOACK); //自动ACK应答
            }
            $this->close();
        }
    }
    try{
        (new ConsumerMQ)->run();
    }catch (\Exception $exception){
        var_dump($exception->getMessage()) ;
    }
    
    

    一开始队列是不存在的,我们需要先启动 ConsumerMQ.php 来初始化队列

    展开全文
  • Wicket的作者中有几个是原Sun公司Swing小组的开发人员(现在可能大部分已经不是了),因此Wicket的框架中带有浓厚的C/S色彩。而他们的开发计划中,还包括了Swing,Flash平台的支持,也就是说使用Wicket不仅可以...
  • 至于shouldConvert和后续的几个判断是为了防止value不是单纯的对象而是Regexp或者函数之类的,或者是vm实例再或者是不可扩展的,shouldConvert是某些特殊情况下为false,它的解释参见源码里尤...
  • 到今天,RTOS已经在全球形成了一个产业,每年RTOS和相关的开发工具(包括仿真器,逻辑分析仪,软件编译器和调试器)销售额已经达到十亿美元。(1999年已经达到13亿美元。) 嵌入式系统由硬件和软件两大部分构成,...
  • JMS与ActiveMQ

    2011-08-29 08:29:11
    许多厂商目前都支持 JMS,包括 IBM 的 MQSeries、BEA的 Weblogic JMS service和 Progress 的 SonicMQ,这只是几个例子。 JMS 使您能够通过消息收发服务(有时称为消息中介程序或路由器)从个 JMS 客户机向另个 ...
  • 本课程将以任务驱动的模式,介绍几个经典的应用案例,来介绍Sysinternals Suite的强大功能。 深入研究Windows内部原理系列之六:Vista新特性底层揭秘 讲师信息:彭爱华 2007年02月01日 14:00-15:30 Level: 400 ...
  • -一个典型应用,在Window控件中打开新页面,如果传递的参数不正确,首先提示参数不对然后关闭此弹出窗口。 -ExtAspNet.Alert.Show("参数错误!", String.Empty, ExtAspNet.ActiveWindow.GetCloseReference());...
  • discord-bots-源码

    2021-03-19 04:59:27
    几个聊天是预先编写的(加上可选的介绍),以使漫游器具有所需的个性,然后在频道的历史记录中添加最后的几则消息,以提供一些上下文信息。上下文可以包含机器人刚刚编写的消息,因此它可以“记住
  • 如果此参数为HWND_BROADCAST,则消息将被发送到系统中所有顶层窗口,包括无效或不可见的非自身拥有的窗口、被覆盖的窗口和弹出式窗口,但消息不被发送到子窗口。 Msg:指定被发送的消息。 wParam:指定附加的消息...
  • 指纹开发包文档包包含以下几个部分 1.这个文档(包括COM API 和DLL API 的说明及部分样例代码等) 2.COM 组件(Efatcom.dll)提供指纹模板的登记,识别等功能,该COM 组件必须在指纹仪的支持下 能使用,如果没有指纹...
  • PGP消息分成原始消息、签名部分和会话密钥部分三个部分。 PGP发送方处理消息的过程为:(1)签名:利用UserID作为索引,从私钥环中得到私钥;PGP提示输入口令短语,恢复私钥;构造签名部分;(2)加密:PGP产生个...
  • 我发布此消息的主要原因是,天前,当时的VB代码是一个称为DB创建器的表,但只能使用一个表。 数据库中有很多字符串操作,它们保存在纯文本文件中(是​​的,我知道很容易崩溃,我将采取措施来防止和解决崩溃) ...
  • starter-p5-源码

    2021-03-21 11:41:33
    您可以使用少数几个环境变量来自定义项目各个部分使用的端口。查看.env.sample以获取可用配置选项的列表。 问题? 此仓库主要供我个人使用,但是如果您使用它并有任何疑问,请在此存储库中打开个问题! 您也可以...
  • 若类之间具有继承关系,它们之间具有下列几个特性: (1) 类间具有共享特征(包括数据和操作代码的共享); (2) 类间具有差别或新增部分(包括非共享的数据和操作代码); (3) 类间具有层次结构。 【1.10】 【解】 从...
  • 微软JavaScript手册

    2009-04-08 22:54:53
    break 语句 终止当前循环,或者如果与一个label 语句关联,终止相关联的语句。 call 方法 应用对象的一个方法,用当前对象代替另对象。 callee 属性 返回正执行的函数对象,它是指定的函数对象的文本正文。 ...
  • 多播 Multicast Socket示例

    热门讨论 2007-10-05 16:18:02
    如果有的话,终端就会回应条IGMP消息,路由器继续转发这组播组的数据。如果没有人回应这条消息,那么路由器就认为已经没有终端对这组播组的数据感兴趣,就不会在转发关于这组播组的数据了。在IGMP第二版中...
  • JScript 语言参考

    2009-05-28 08:53:39
    break 语句 终止当前循环,或者如果与一个label 语句关联,终止相关联的语句。 call 方法 应用对象的一个方法,用当前对象代替另对象。 callee 属性 返回正执行的函数对象,它是指定的函数对象的文本正文。 ...
  • 多播 Multicast Socket

    2007-08-06 16:39:05
    如果有的话,终端就会回应条IGMP消息,路由器继续转发这组播组的数据。如果没有人回应这条消息,那么路由器就认为已经没有终端对这组播组的数据感兴趣,就不会在转发关于这组播组的数据了。在IGMP第二版中...
  • javascript文档

    2009-08-11 10:44:24
    break 语句 终止当前循环,或者如果与一个label 语句关联,终止相关联的语句。 call 方法 应用对象的一个方法,用当前对象代替另对象。 callee 属性 返回正执行的函数对象,它是指定的函数对象的文本正文。 ...
  • 相比之下,Zulip团队有30多个核心团队成员分布在十几个时区,并且仅使用Zulip和GitHub问题进行通信(没有电子邮件列表,视频会议等)。 当每个人都在线时,Slack非常适合私人消息(“ DM”),集成和快速提问。关于...
  • 它有几个不错的功能,包括: 图形用户界面 从串行部分创建图像 z 堆栈 动画 z 堆栈演练预览 自动细胞和背景检测 自动细胞识别和追踪 各种像素强度统计 图像和颜色通道比较 快速统计输出 开源开发! 预览 - 荧光...

空空如也

空空如也

1 2 3 4 5 ... 11
收藏数 218
精华内容 87
关键字:

一则消息包括几个部分