Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / desktop / MFC

Converting a class into a CommandHandler

4.67/5 (9 votes)
12 Jul 2010CPOL11 min read 23.2K   177  
An article on creating a framework for command processing using the Command Design Pattern.

commandhandler_2.png

Introduction

This article is all about creating a generic, extensible concept through which a class can respond to commands fired by its clients. The concept revolves around the CommandHandler which handles a Command which has configuration information and works on specific data which is required by the CommandHandlerRoutine which is implemented by the CommandHandler. We also emphasise is on keeping the CommandHandler, Command, and the Data generic, extensible, and flexible. This article is divided into the following sections:

  1. Motivation
  2. Where this concept can be used
  3. Concept explanation
  4. Major components involved in implementing the concept
  5. Overall class structure design
  6. Explanation of classes and their members
  7. How to create ActualCommandHandler
  8. How clients can fire the commands to ActualCommandHandler

1. Motivation

In my day-to-day routine development, there was a requirement were I needed something which could respond to commands that are fired from outside, probably from other parts of code or other sub modules. I gave a thought that this something should be a class, because a class can hold necessary data, provide proper encapsulation, and can serve as an abstraction for this requirement. Therefore, I tried to convert a class into a CommandHandler which could efficiently handle/respond to calls/commands of other entities.

2. Where this concept can be used

This concept can be utilized in developing server code, since servers respond to calls/commands of clients. Moreover, the class structure described in this article can find a place in implementing any client-server architecture.

3. Concept explanation

There are two frequently used terms to discuss the concepts mentioned in this article:

  1. CommandHandler: This will be the class which should follow the specifications discussed in this article in order to be a CommandHandler class.
  2. Client(s): All the other code which will utilize the CommandHandler or fire commands to the CommandHandler.

A CommandHandler will mostly get utilized in a multithreaded environment wherein all its clients will continuously fire commands. A CommandHandler can respond to commands in two ways:

  1. SendCommand: In this way, the clients fire the commands to the CommandHandler, and if the CommandHandler is idle, then the commands get responded to/executed quickly. If the CommandHandler is busy processing other commands, then the client needs to wait till the CommandHandler respondsto /executes the command. The client cannot continue its processing or fire another command till the previously sent command is completely executed in the CommandHandler.
  2. PostCommand: In the case of PostCommand, the Client does not need to wait for the completion of the execution of a command. It can continue its processing or fire another command while the previously posted command is getting executed in the CommandHandler.

This way, the command fired by clients are first stored in a queue which is present in the CommandHandler. The CommandHandler fetches the commands from the queue and processes them one by one in a separate thread. Depending on their priority, the commands are temporarily stored in one of the high, normal, or low priority command queues. Commands are executed according to their priority. Therefore, all the high priority commands will get executed before the normal priority commands, which will be executed before the low priority commands. By the way, commands are inserted in the queue from the back end, and fetched or removed for processing from the front end. This is as per the standard queue concept.

In the case of SendCommand and PostCommand, the main procedure/method inside of CommandHandler which:

  • fetches and removes commands from the queue,
  • processes the fetched commands,
  • notifies the client regarding the completion of execution of a command.

runs in a separate thread. This main method is CXCommandHandler::CommandProcessor. Therefore, after converting a class into a CommandHandler, this converted class acts as a worker thread which can continuously execute the commands.

4. Major components involved in implementing the concept

  1. Template <typename ACTUALCOMMANDHANDLER> class CXCommandHandler
  2. Class CXCommand
  3. Class CXData
  4. Class CActualCommandHandler - this can be any class which needs to get converted to a CommandHandler
    • class COMMAND
      • enum COMMANDLIST
      • struct DATA_COMMANDHANDLERROUTINE1
      • struct DATA_COMMANDHANDLERROUTINE2
      • struct DATA_COMMAND..

5. Overall class structure design

commandhandler_1.png

This class structure is implemented in the "CommandHandler.h" and "CommandHandler.cpp" files. To implement this concept, the Command Design Pattern is used. Clients create the command (instance of class CXCommand), populate it with the necessary data (instance of the class derived from CXData), and then fire it to the CommandHandler (instance of class derived from class CXCommandHandler) which executes those commands.

  1. Template <typename ACTUALCOMMANDHANDLER> class CXCommandHandler: This is the main component of the overall concept. This is a template class which will serve as the base class for the class which you want to convert to CommandHandler. This class provides and implements all the necessary things required to implement the CommandHandler.
  2. Class CXCommand: This is the component which is the medium of communication between the client and the CommandHandler. This class provides the necessary members through which the client can set information such as Command Priority, Command ID, etc., as well as the data that will be required to execute the command.
  3. Class CXData: This class serves as the base class for the data which is used in executing individual commands.
  4. Class CActualCommandHandler: This is the class which you want to convert to a CommandHandler. This class should derive from the class CXCommandHandler. The main responsibility of this class is to implement the CommandHandlerRoutines for the commands that will be fired by the clients.
    • Class COMMAND: This class mainly serves as a namespace to keep all the things related to commands which will be handled by CActualCommandHandler. This class is declared inside CActualCommandHandler. This class defines two important things through which the client can populate the commands.
      • enum COMMANDLIST: Every CommandHandlerRoutine implemented by CActualCommandHandler is given a unique ID. COMMANDLIST holds these CommandIDs.
      • struct DATA_COMMANDHANDLERROUTINE1: This struct is derived from the class CXData. This structure specifies the data which will be used by the related CommandHandlerRoutine1 which is implemented by CActualCommandHandler.
      • struct DATA_COMMANDHANDLERROUTINE2: This struct is derived from the class CXData. This structure specifies the data which will be used by the related CommandHandlerRoutine2 which is implemented by CActualCommandHandler.
      • struct DATA_COMMAND..

Note: CommandHandlerRoutines should strictly have the following signature:

C++
int CommandHandlerRoutineName(CXData *pdata);

6. Explanation of classes and their members

This can be directly found in the source files.

7. How to create ActualCommandHandler

Consider a class CAccountService. We will convert this class into an ActualCommandHandler which will then be able to respond to the various commands fired by clients (other code). The client code can fire commands to:

  • Register/unregister human users for the services provided by CAccountService.
  • Email users their account information.
  • Print account information of users.

In general, CAccountService should be able to handle all the existing commands as well be open to get extended to handle other types of commands which might come up in future.

Now, do the following step by step:

  1. Create two files AccountService.h and AccountService.cpp.
  2. Include the existing class structure:
  3. C++
    //AccountService.h file
    #include "CommandHandler.h"
  4. Create a class CAccountService and derive it from CXCommandHandler:
  5. C++
    //AccountService.h file
    #include "CommandHandler.h"
    
    class CAccountService : public CXCommandHandler <CAccountService>
    {
    };
  6. Place the existing macro IMPLEMENT_GETTHISMEMBERFUNCTION in the public section of the class CAccountService. This macro can be found in the CommandHandler.h file.
  7. C++
    //AccountService.h file
    #include "CommandHandler.h"
    
    class CAccountService : public CXCommandHandler <CAccountService>
    {
    public:
        IMPLEMENT_GETTHISMEMBERFUNCTION
    };
  8. Now create a class COMMAND inside the public section of CAccountService. COMMAND will act as a namespace, and will keep all the details related to commands which will be handled by CAccountService. Please refer to 5. Overall class structure design for an explanation of the COMMAND class.
  9. C++
    //AccountService.h file
    #include "CommandHandler.h"
    
    class CAccountService : public CXCommandHandler <CAccountService>
    {
    public:
        IMPLEMENT_GETTHISMEMBERFUNCTION
    
    public:
        class COMMAND
        {
        };
    };
  10. Now, let's say this class CAccountService provides some services which clients can access by firing the respective commands. We need to implement these services, providing routines which are called CommandHandlerRoutines. Therefore, the next step is to implement CommandHandlerRoutines.

Implementing CommandHandlerRoutines requires the following steps:

  1. Declaring the CommandHandlerRoutine:
  2. C++
    //AccountService.h file
    #include "CommandHandler.h"
    
    class CAccountService : public CXCommandHandler <CAccountService>
    {
    public:
        IMPLEMENT_GETTHISMEMBERFUNCTION
    
    public:
        class COMMAND
        {
        };
        
    protected:
        //------------ CommandHandlerRoutines -----------------------//
        int Register(CXData *pData = NULL  /*CAccountService::COMMAND::DATA_USERINFO*/);
        //------------ CommandHandlerRoutines -----------------------//
    };

    As you can see, the CommandHandlerRoutine Register takes a parameter in a very generic form. This parameter is a pointer to the CXData class, which will actually point to specific data on which the Register CommandHandlerRoutine will operate.

    For example, the Register CommandHandlerRoutine needs an instance of struct CAccountService::COMMAND::DATA_USERINFO. Please refer to 5. Overall class structure design for an explanation related to the data requirement of a CommandHandlerRoutine.

  3. Declaring the data required by a CommandHandlerRoutine.
  4. The Register CommandHandlerRoutine declared in the above step requires an instance of CAccountService::COMMAND::DATA_USERINFO. Therefore, create a struct DATA_USERINFO in the public section of the CAccountService::COMMAND class which is acting as a namespace.

    C++
    //AccountService.h file
    
    #include "CommandHandler.h"
    
    class CAccountService : public CXCommandHandler <CAccountService>
    {
    public:
        IMPLEMENT_GETTHISMEMBERFUNCTION
    
    public:
        class COMMAND
        {
        public:
            //------ data required by 'Register' CommandHandlerRoutine ------//
            struct DATA_USERINFO : public CXData
            {
                TCHAR  tcszUserName[260];
                int    iUserID;
            };    
            //------ data required by 'Register' CommandHandlerRoutine ------//
        };
        
    protected:
        //------------ CommandHandlerRoutines -----------------------//
        int Register(CXData *pData = NULL  /*CAccountService::COMMAND::DATA_USERINFO*/);
        //------------ CommandHandlerRoutines -----------------------//
    };

    This struct contains data which is required by the Register CommandHandlerRoutine to register a human user.

  5. Declaring the CommandID which will represent the CommandHandlerRoutine.
  6. The ActualCommandHandler, i.e., CAccountService will expose various CommandHandlerRoutines, each of which should have a unique ID called CommandID. To handle the CommandID management, create an enumeration COMMANDLIST in the public section of CAccountService::COMMAND which is acting as a namespace. This enumeration will hold the CommandIDs for the CommandHandlerRoutines implemented by CAccountService.

    C++
    //AccountService.h file
    #include "CommandHandler.h"
    
    class CAccountService : public CXCommandHandler <CAccountService>
    {
    public:
        IMPLEMENT_GETTHISMEMBERFUNCTION
    
    public:
        class COMMAND
        {
        public:
            //------ enumeration for holding CommandIDs ------// 
            enum COMMANDLIST
            {
                Register,              
                TOTALCOMMANDS  //----1
            };
            //------ enumeration for holding CommandIDs ------// 
    
    
            //------ data required by 'Register' CommandHandlerRoutine ------//
            struct DATA_USERINFO : public CXData
            {
                TCHAR  tcszUserName[260];
                int    iUserID;
            };    
            //------ data required by 'Register' CommandHandlerRoutine ------//
        };
        
    protected:
        //------------ CommandHandlerRoutines -----------------------//
        int Register(CXData *pData = NULL  /*CAccountService::COMMAND::DATA_USERINFO*/);
        //------------ CommandHandlerRoutines -----------------------//
    };

    Now, clients can access the Register CommandHandlerRoutine by specifying the CommandID CAccountService::COMMAND::COMMANDLIST::Register in the command which they will fire to CAccountService.

  7. Defining the CommandHandlerRoutine.
  8. Now define the CommandHandlerRoutine Register which is declared in CAccountService.

    C++
    //AccountService.cpp file
    #include "AccountService.h"
    
    //------ Defining the CommandhandlerRoutine -------//
    int CAccountService::Register(CXData *pData  /*CAccountService::COMMAND::DATA_USERINFO*/)
    {
        CAccountService::COMMAND::DATA_USERINFO *pUserInfo = 
                      (CAccountService::COMMAND::DATA_USERINFO *) pData;
    
        if(m_UserDBase.find(pUserInfo->iUserID) != m_UserDBase.end())
            return 2; //already registered.
    
        m_UserDBase[pUserInfo->iUserID] = pUserInfo->tcszUserName;
            return 1; //newly registered.
    }
    //------ Defining the CommandhandlerRoutine -------//

    Since the Register CommandHandlerRoutine works on CAccountService::COMMAND::DATA_USERINFO which it will receive as a parameter, we can safely do the typecasting. The existing class structure of CXCommandHandler makes sure that proper data is passed to the CommandHandlerRoutine. After type casting, user information can be accessed through pUserInfo.

  9. Declaring the native data which is required by ActualCommandHandler.
  10. In the definition of the Register CommandHandlerRoutine, a std::map m_UserDBase is used by CAccoountService to store the user information. Such native data or the data required by the ActualCommandHandler can also be kept in the ActualCommandHandler.

    C++
    //AccountService.h file
    #include "CommandHandler.h"
    #include <map>
    #include <string>
    
    using namespace std;
    
    class CAccountService : public CXCommandHandler <CAccountService>
    {
    public:
        IMPLEMENT_GETTHISMEMBERFUNCTION
    
    public:
        class COMMAND
        {
        public:
            //------ enumeration for holding CommandIDs ------// 
            enum COMMANDLIST
            {
                Register,              
                TOTALCOMMANDS  //----1
            };
            //------ enumeration for holding CommandIDs ------// 
    
    
            //------ data required by 'Register' CommandHandlerRoutine ------//
            struct DATA_USERINFO : public CXData
            {
                TCHAR  tcszUserName[260];
                int    iUserID;
            };    
            //------ data required by 'Register' CommandHandlerRoutine ------//
        };
        
    private :
        //------- native data for CAccountService -------//
        //m_UserDBase : map[UserID] = Username
        //to store information of registered user.
        map <int, string>  m_UserDBase;
        //------- native data for CAccountService -------//
        
    protected:
        //------------ CommandHandlerRoutines -----------------------//
        int Register(CXData *pData = NULL  /*CAccountService::COMMAND::DATA_USERINFO*/);
        //------------ CommandHandlerRoutines -----------------------//
    };

    Since the Register CommandHandlerRoutine works on CAccountService::COMMAND::DATA_USERINFO which it will receive as a parameter, we can safely do the typecasting. After type casting, user information can be accessed through pUserInfo.

  11. Registering the CommandHandlerRoutine in the CommandHandler.
  12. Till now, ActualCommandHandler, i.e., CAccountService provided the facility of the Register service or CommandHandlerRoutine by declaring the necessary things like CommandID and the data on which it will operate. Now, this CommandHandlerRoutine needs to be registered in the ActualCommandHandler's database which stores the information of all commands the ActualCommandHandler can respond to. For registering the CommandHandlerRoutine which is implemented by the ActualCommandHandler, call the RegisterCommand member function which is already implemented by the base class CXCommandHandler. The RegisterCommand member function can be called from the constructor of ActualCommandHandler.

    C++
    //AccountService.h file
    #include "CommandHandler.h"
    
    class CAccountService : public CXCommandHandler <CAccountService>
    {
    public:
        IMPLEMENT_GETTHISMEMBERFUNCTION
    
    public:
        class COMMAND
        {
        public:
            //------ enumeration for holding CommandIDs ------// 
            enum COMMANDLIST
            {
                Register,              
                TOTALCOMMANDS  //----1
            };
            //------ enumeration for holding CommandIDs ------// 
    
    
            //------ data required by 'Register' CommandHandlerRoutine ------//
            struct DATA_USERINFO : public CXData
            {
                TCHAR  tcszUserName[260];
                int    iUserID;
            };    
            //------ data required by 'Register' CommandHandlerRoutine ------//
        };
    
    private :
        //------- native data for CAccountService -------//
        //m_UserDBase : map[UserID] = Username
        //to store information of registered user.
        map <int, string>  m_UserDBase;
        //------- native data for CAccountService -------//
        
    public:
        //-------- Registering the CommandHandlerRoutine -------//
        CAccountService()
        {
            RegisterCommand(CAccountService::COMMAND::COMMANDLIST::Register, 
                            &CAccountService::Register);
        }
        //-------- Registering the CommandHandlerRoutine -------//
        
    protected:
        //------------ CommandHandlerRoutines -----------------------//
        int Register(CXData *pData = NULL  /*CAccountService::COMMAND::DATA_USERINFO*/);
        //------------ CommandHandlerRoutines -----------------------//
    };

    Note: The CommandHandlerRoutine handling/implementing a command should be registered before the clients can fire that particular command.

In this way, we can create other CommandHandlerRoutines like:

  • Unregister
  • Email
  • Print

Please refer to the files AccountService.h and AccountService.cpp for the implementation of these CommandHandlerRoutines.

8. How clients can fire the commands to the ActualCommandHandler

Command is nothing but an instance of the CXCommand class. This instance consists of two parts:

  1. CommandInfo: For an explanation of this, please refer to struct CXCommand::COMMANDINFO in the CommandHandler.h file.
  2. CommandData: This is the data which is declared in the CActualCommandHandler::COMMAND class. Please refer to 5. Overall class structure design and 6.6.ii Declaring the data required by CommandHandlerRoutine.

Clients create, populate, and fire the commands. Please refer to 3. Concept explanation. For example, lets fire a command to ActualCommandHandler (i.e., CAccountService) in order to 'Register' a user. For this, create an object of ActualCommandHandler.

C++
CAccountService oService;

Clients can fire commands in one of two ways:

  1. SendCommand: While using SendCommand, the client waits for the command to be executed. So most of the time, things can be allocated on the stack itself.
    1. Create the command object:
    2. C++
      //command object creation
      CXCommand cmd;
    3. Create the command info object which will be set in the command object. Also, initialize it by using the CXCommand::GetCommandInfo member function.
    4. C++
      //command object creation
      CXCommand cmd;
      
      //command info object creation
      CXCommand::COMMANDINFO cmdinfo;
      cmd.GetCommandInfo(cmdinfo);
    5. Set the members of the cmdinfo object:
    6. C++
      //command object creation
      CXCommand cmd;
      
      //member initialization
      CXCommand::COMMANDINFO cmdinfo;
      cmd.GetCommandInfo(cmdinfo);
      
      //member initialization
      cmdinfo.enumCommandPriority = CXCommand::PRIORITY::COMMANDPRIORITY_NORMAL;
      cmdinfo.enumCommandValidity = CXCommand::VALIDITY::COMMANDVALIDITY_VALID;
      
      /**
      If the command object is created on stack, then there is no need 
      to take extra care of deallocation of memory related to command object. 
      Therefore, inform the ActualCommandHandler that it should not delete 
      the command object after responding to the command.
      For this situation use 
      CXCommand::EXTRA::COMMANDEXTRA_DONOTDELETEONCOMPLETIONOFEXECUTION.
      /**/
      cmdinfo.enumExtraInfo = 
        CXCommand::EXTRA::COMMANDEXTRA_DONOTDELETEONCOMPLETIONOFEXECUTION;
      
      cmdinfo.iCommandID = CAccountService::COMMAND::COMMANDLIST::Register;
      _tcscpy(cmdinfo.tcszCommandName, _T("Register"));
    7. Set the cmdinfo object in the command object cmd.
    8. C++
      //command object creation
      CXCommand cmd;
      
      //command info object creation
      CXCommand::COMMANDINFO cmdinfo;
      cmd.GetCommandInfo(cmdinfo);
      
      //member initialization
      cmdinfo.iCommandID = CAccountService::COMMAND::COMMANDLIST::Register;
      cmdinfo.enumCommandPriority = CXCommand::PRIORITY::COMMANDPRIORITY_NORMAL;
      cmdinfo.enumCommandValidity = CXCommand::VALIDITY::COMMANDVALIDITY_VALID;
      cmdinfo.enumExtraInfo = 
        CXCommand::EXTRA::COMMANDEXTRA_DONOTDELETEONCOMPLETIONOFEXECUTION;
      _tcscpy(cmdinfo.tcszCommandName, _T("Register"));
      
      //initialize command object with necessary execution related information
      cmd.SetCommandInfo(cmdinfo);
    9. Create the related command data on which the CommandHandlerRoutine will operate. Also, assign values to this data.
    10. C++
      int Register(CXData *pData = NULL  /*CAccountService::COMMAND::DATA_USERINFO*/);

      It can be observed that the Register CommandHandlerRoutine expects an instance of struct CAccountService::COMMAND::DATA_USERINFO.

      C++
      //command object creation
      CXCommand cmd;
      
      //command info object creation
      CXCommand::COMMANDINFO cmdinfo;
      cmd.GetCommandInfo(cmdinfo);
      
      //member initialization
      cmdinfo.iCommandID = CAccountService::COMMAND::COMMANDLIST::Register;
      cmdinfo.enumCommandPriority = CXCommand::PRIORITY::COMMANDPRIORITY_NORMAL;
      cmdinfo.enumCommandValidity = CXCommand::VALIDITY::COMMANDVALIDITY_VALID;
      cmdinfo.enumExtraInfo = 
        CXCommand::EXTRA::COMMANDEXTRA_DONOTDELETEONCOMPLETIONOFEXECUTION;
      _tcscpy(cmdinfo.tcszCommandName, _T("Register"));
      
      //initialize command object with necessary execution related information
      cmd.SetCommandInfo(cmdinfo);
      
      //create and initilization of related command data
      CAccountService::COMMAND::DATA_USERINFO cmddata;
      _tcscpy(cmdddata.tcszUserName, _T("schneider")); 
      cmddata.iUserID = 10;
    11. Set the command data in the Command object.
    12. C++
      //command object creation
      CXCommand cmd;
      
      //command info object creation
      CXCommand::COMMANDINFO cmdinfo;
      cmd.GetCommandInfo(cmdinfo);
      
      //member initialization
      cmdinfo.iCommandID = CAccountService::COMMAND::COMMANDLIST::Register;
      cmdinfo.enumCommandPriority = CXCommand::PRIORITY::COMMANDPRIORITY_NORMAL;
      cmdinfo.enumCommandValidity = CXCommand::VALIDITY::COMMANDVALIDITY_VALID;
      cmdinfo.enumExtraInfo = 
        CXCommand::EXTRA::COMMANDEXTRA_DONOTDELETEONCOMPLETIONOFEXECUTION;
      _tcscpy(cmdinfo.tcszCommandName, _T("Register"));
      
      //initialize command object with necessary execution related information
      cmd.SetCommandInfo(cmdinfo);
      
      //create and initilization of related command data
      CAccountService::COMMAND::DATA_USERINFO cmddata;
      _tcscpy(cmdddata.tcszUserName, _T("schneider")); 
      cmddata.iUserID = 10;
      
      //setting the related command data
      cmd.SetCommandData(&cmddata);
    13. Fire the command to the ActualCommandHandler:
    14. C++
      //command object creation
      CXCommand cmd;
      
      //command info object creation
      CXCommand::COMMANDINFO cmdinfo;
      cmd.GetCommandInfo(cmdinfo);
      
      //member initialization
      cmdinfo.iCommandID = CAccountService::COMMAND::COMMANDLIST::Register;
      cmdinfo.enumCommandPriority = CXCommand::PRIORITY::COMMANDPRIORITY_NORMAL;
      cmdinfo.enumCommandValidity = CXCommand::VALIDITY::COMMANDVALIDITY_VALID;
      cmdinfo.enumExtraInfo = 
        CXCommand::EXTRA::COMMANDEXTRA_DONOTDELETEONCOMPLETIONOFEXECUTION;
      _tcscpy(cmdinfo.tcszCommandName, _T("Register"));
      
      //initialize command object with necessary execution related information
      cmd.SetCommandInfo(cmdinfo);
      
      //create and initilization of related command data
      CAccountService::COMMAND::DATA_USERINFO cmddata;
      _tcscpy(cmdddata.tcszUserName, _T("schneider")); 
      cmddata.iUserID = 10;
      
      //setting the related command data
      cmd.SetCommandData(&cmddata);
      
      //fire the command
      oService.SendCommand(&cmd);
  2. PostCommand: This method is similar to SendCommand, with the only difference that the client does not wait for the completion of execution of commands which it has fired. In PostCommand, commands are first stored in the appropriate command priority queue, and then a command is fetched from the queue and executed. Meanwhile, the client code is also executing in parallel. Therefore, if a thought is given here then, after posting the command to the ActualCommandHandler, the command and its related data should remain valid, i.e., the memory at which the command and its data are stored should remain valid/accessible. Therefore, while using PostCommand, the Client should allocate the Command object and all its related data on the heap so that they remain valid till the command is executed. But wait. In PostCommand, if the client does not wait for the completion of execution of commands then, who is going to deallocate the memory which is allocated for the Command object and its related data? In this case, the client can give the responsibility of deallocating the memory to the ActualCommandHandler.
  3. C++
    //create the command object on heap
    CXCommand *pcmd = new CXCommand;
    
    //command info object creation
    CXCommand::COMMANDINFO cmdinfo;
    pcmd->GetCommandInfo(cmdinfo);
    
    
    //member initialization
    cmdinfo.iCommandID = CAccountService::COMMAND::COMMANDLIST::Register;
    cmdinfo.enumCommandPriority = CXCommand::PRIORITY::COMMANDPRIORITY_NORMAL;
    cmdinfo.enumCommandValidity = CXCommand::VALIDITY::COMMANDVALIDITY_VALID;
    
    /**
    Since command object is created on heap therefore giving 
    the responsibility of deallocating the memory to ActualCommandHandler.
    For this situation use CXCommand::EXTRA::COMMANDEXTRA_DELETEONCOMPLETIONOFEXECUTIO.
    /**/
    cmdinfo.enumExtraInfo = CXCommand::EXTRA::COMMANDEXTRA_DELETEONCOMPLETIONOFEXECUTION;
    
    _tcscpy(cmdinfo.tcszCommandName, _T("Register"));
    
    
    //initialize command object with necessary execution related information
    pcmd->SetCommandInfo(cmdinfo);
    
    //creating and initilization of related command data on heap
    CAccountService::COMMAND::DATA_USERINFO *pcmddata = 
                     new CAccountService::COMMAND::DATA_USERINFO;
    CXData::DATAINFO stDataInfo;
    pcmddata->GetDataInfo(stDataInfo);
    /**
    Since command data is created on heap therefore giving 
    the responsibility of deallocating the memory to ActualCommandHandler.
    For this situation use CXData::OPERATIONONDATA::OPERATION_DELETEAFTERUSE.
    /**/
    stDataInfo.enumOperationOnData = CXData::OPERATIONONDATA::OPERATION_DELETEAFTERUSE;
    pcmddata->SetDataInfo(stDataInfo);
    
    //initializing members of command data
    _tcscpy(pcmdddata->tcszUserName, _T("schneider")); 
    pcmddata->iUserID = 10;
    
    //setting the related command data
    pcmd->SetCommandData(pcmddata);
    
    //fire the command
    oService.PostCommand(pcmd);

Note

  1. Once the value CXCommand::EXTRA::COMMANDEXTRA_DELETEONCOMPLETIONOFEXECUTION is specified for CXCommand::COMMANDINFO::enumExtraInfo through the CXCommand::SetCommandInfo member function, then the Command object should not be used after firing the command either through SendCommand or PostCommand. Once the value CXData::OPERATIONONDATA::OPERATION_DELETEAFTERUSE is specified for CXData::DATAINFO::enumOperationOnData through the CXData::SetDataInfo member function, then the command data should not be used after firing the command either through SendCommand or PostCommand.
  2. On exiting. i.e., when the ActualCommandHandler is about to terminate, all the commands present (if any) in the different priority queues are first executed and then the ActualCommandHandler exits.

History

  • Version 1.0:
    • Initial version of article.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)