Converting a class into a CommandHandler






4.67/5 (9 votes)
An article on creating a framework for command processing using the Command Design Pattern.
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:
- Motivation
- Where this concept can be used
- Concept explanation
- Major components involved in implementing the concept
- Overall class structure design
- Explanation of classes and their members
- How to create ActualCommandHandler
- 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:
- CommandHandler: This will be the class which should follow the specifications discussed in this article in order to be a CommandHandler class.
- 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:
- 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.
- 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
- Template
<typename ACTUALCOMMANDHANDLER>
classCXCommandHandler
- Class
CXCommand
- Class
CXData
- 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
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.
- Template
<typename ACTUALCOMMANDHANDLER>
classCXCommandHandler
: 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. - 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. - Class
CXData
: This class serves as the base class for the data which is used in executing individual commands. - Class
CActualCommandHandler
: This is the class which you want to convert to a CommandHandler. This class should derive from the classCXCommandHandler
. 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 byCActualCommandHandler
. This class is declared insideCActualCommandHandler
. This class defines two important things through which the client can populate the commands. enum COMMANDLIST
: Every CommandHandlerRoutine implemented byCActualCommandHandler
is given a unique ID.COMMANDLIST
holds these CommandIDs.struct DATA_COMMANDHANDLERROUTINE1
: This struct is derived from the classCXData
. This structure specifies the data which will be used by the relatedCommandHandlerRoutine1
which is implemented byCActualCommandHandler
.struct DATA_COMMANDHANDLERROUTINE2
: This struct is derived from the classCXData
. This structure specifies the data which will be used by the relatedCommandHandlerRoutine2
which is implemented byCActualCommandHandler
.struct DATA_COMMAND..
Note: CommandHandlerRoutines should strictly have the following signature:
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:
- Create two files AccountService.h and AccountService.cpp.
- Include the existing class structure:
- Create a class
CAccountService
and derive it fromCXCommandHandler
: - Place the existing macro
IMPLEMENT_GETTHISMEMBERFUNCTION
in the public section of the classCAccountService
. This macro can be found in the CommandHandler.h file. - Now create a class
COMMAND
inside the public section ofCAccountService
.COMMAND
will act as a namespace, and will keep all the details related to commands which will be handled byCAccountService
. Please refer to 5. Overall class structure design for an explanation of theCOMMAND
class. - 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.
//AccountService.h file
#include "CommandHandler.h"
//AccountService.h file
#include "CommandHandler.h"
class CAccountService : public CXCommandHandler <CAccountService>
{
};
//AccountService.h file
#include "CommandHandler.h"
class CAccountService : public CXCommandHandler <CAccountService>
{
public:
IMPLEMENT_GETTHISMEMBERFUNCTION
};
//AccountService.h file
#include "CommandHandler.h"
class CAccountService : public CXCommandHandler <CAccountService>
{
public:
IMPLEMENT_GETTHISMEMBERFUNCTION
public:
class COMMAND
{
};
};
Implementing CommandHandlerRoutines requires the following steps:
- Declaring the CommandHandlerRoutine:
- Declaring the data required by a CommandHandlerRoutine.
- Declaring the CommandID which will represent the CommandHandlerRoutine.
- Defining the CommandHandlerRoutine.
- Declaring the native data which is required by ActualCommandHandler.
- Registering the CommandHandlerRoutine in the CommandHandler.
//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.
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.
//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.
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
.
//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
.
Now define the CommandHandlerRoutine Register
which is declared in CAccountService
.
//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
.
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.
//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
.
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.
//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
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:
- CommandInfo: For an explanation of this, please refer to
struct CXCommand::COMMANDINFO
in the CommandHandler.h file. - 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.
CAccountService oService;
Clients can fire commands in one of two ways:
- 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.
- Create the command object:
- Create the command info object which will be set in the command object. Also, initialize it by using the
CXCommand::GetCommandInfo
member function. - Set the members of the
cmdinfo
object: - Set the
cmdinfo
object in the command objectcmd
. - Create the related command data on which the CommandHandlerRoutine will operate. Also, assign values to this data.
- Set the command data in the Command object.
- Fire the command to the ActualCommandHandler:
- 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.
//command object creation
CXCommand cmd;
//command object creation
CXCommand cmd;
//command info object creation
CXCommand::COMMANDINFO cmdinfo;
cmd.GetCommandInfo(cmdinfo);
//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"));
//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);
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
.
//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;
//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);
//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);
//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
- Once the value
CXCommand::EXTRA::COMMANDEXTRA_DELETEONCOMPLETIONOFEXECUTION
is specified forCXCommand::COMMANDINFO::enumExtraInfo
through theCXCommand::SetCommandInfo
member function, then the Command object should not be used after firing the command either through SendCommand or PostCommand. Once the valueCXData::OPERATIONONDATA::OPERATION_DELETEAFTERUSE
is specified forCXData::DATAINFO::enumOperationOnData
through theCXData::SetDataInfo
member function, then the command data should not be used after firing the command either through SendCommand or PostCommand. - 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.