Introduction
This article describes a way of creating class objects by the name of its classes. This way permits you to create a new class object by calling a function or class method that receives as input parameters a pointer to the CRuntimeClass object. Thus, this method encapsulates the procedure of object creation inside another class or function.
In general, the article demonstrates how to design a mechanism that creates a user-selected dialog window in its own child thread. The child thread class is derived from CWinThread and its InitInstanse method encapsulates the procedure of object creation. A user can choose a type of dialog that is to be created.
The project architecture represented in Figure 1.
Figure 1. Project Architecture
Writing a Dialog Class
For dialog class creation, we can use a standard VC++ wizard. All we need to do is enter class name and choose the class that it is derived from. The first step is to adapt the newly created class to the correct MFC macros DECLARE_DYNAMIC and IMPLEMENT_DYNAMIC to DECLARE_DYNCREATE and IMPLEMENT_DYNCREATE in “.h” and “.cpp” files of class.
Use of the DECLARE_DYNCREATE macro in “.h” and the IMPLEMENT_DYNCREATE macro in “.cpp” permits classes to be created by the framework dynamically at run time.
The class should look like the following:
class CDialog1 : public CDialog { DECLARE_DYNCREATE(CDialog1) public: CDialog1(CWnd* pParent = NULL); // standard constructor virtual ~CDialog1(); // Dialog Data enum { IDD = IDD_DIALOG1 }; DECLARE_MESSAGE_MAP() public: afx_msg void OnClose(); };
It is convenient to add a WM_CLOSE event handler method to notify the thread which dialog is attached to the window that is going to be closed and it is necessary to call the ExitInstanse method. To let the thread know about this intention, the window has to send a WM_QUIT message.
void CDialog1::OnClose() { this->PostMessage( WM_QUIT ); CDialog::OnClose(); }
Writing a Child Thread Class
We also can create this class with the help of the VC++ class wizard; just set the derived class to CWinThread. It is necessary to repeat the procedure of changing the MFC macros DECLARE_DYNAMIC and IMPLEMENT_DYNAMIC to DECLARE_DYNCREATE and IMPLEMENT_DYNCREATE in the “.h” and “.cpp” files of the class. This class also is to be created dynamically at run time.
Obviously, we need some variables: pointer to dialog, dialog ID, pointer to CRuntimeClass object of attached dialog class.
class CChildThread : public CWinThread { friend class CMainFrame; DECLARE_DYNCREATE(CChildThread) public: CChildThread( CRuntimeClass* pClass = NULL, UINT id = 0 ); virtual ~CChildThread(); private: CRuntimeClass* m_pClass; UINT m_ID; protected: CDialog* m_pDialog; public: virtual BOOL InitInstance(); virtual int ExitInstance(); DECLARE_MESSAGE_MAP() };
The class constructor ought to be modified to get some class variable values from input parameters. We can initialise the pointer to CRuntimeClass and dialog ID.
CChildThread::CChildThread( CRuntimeClass* pClass, UINT id ) { m_pClass = pClass; m_ID = id; m_pDialog = NULL; }
Now, when the thread object already exists, the InitInstanse method might be called. The mechanism of class object creation by name is placed into this function.
BOOL CChildThread::InitInstance() { if ( m_pClass != NULL) { CObject* pObject = m_pClass->CreateObject(); if ( pObject->IsKindOf( RUNTIME_CLASS( CDialog ) ) ) { CWnd desktop; desktop.Attach( ::GetDesktopWindow() ); m_pDialog = ( CDialog* )pObject; m_pDialog->Create( m_ID, &desktop ); m_pMainWnd = m_pDialog; m_pActiveWnd = m_pDialog; m_pMainWnd->ShowWindow( SW_SHOW ); m_pMainWnd->UpdateWindow(); desktop.Detach(); } } return TRUE; }
First, the object will be created by using the CreateObject method of the CRuntimeClass class. Second, we should convince that just-created object is derived from CDialog using the IsKindOf method of the CObject class. And finally, the Create method of the CDialog class might be called.
The ExitInstance method of the class is to be extended. You should process such an operation like destroying the attached dialog.
int CChildThread::ExitInstance() { if ( m_pDialog != NULL) { m_pDialog->DestroyWindow(); delete m_pDialog; m_pDialog = NULL; } return CWinThread::ExitInstance(); }
Using a Class of the Child Thread Type
When a user performs actions to create a new dialog window by dialog selecting, we must take care of the handler that catches this event.
void CSampleDlg::OnDialogDialog1() { CChildThread* pThread = new CChildThread( RUNTIME_CLASS( CDialog1 ), IDD_DIALOG1 ); pThread->CreateThread(); m_aChildThreads.Add( pThread ); }
All we have to do is dynamically create a new CChildThread object, passing as input parameters to its constructor a pointer to the CRuntimeClass object and to call the CreateThread method of the newly created thread object. For convenience of subsequent destroying, the thread object pointers are to be stored in a special array. This array is useful when the application main window is going to be closed.
void CSampleDlg::OnClose() { for ( int i = 0; i < m_aChildThreads.GetSize(); i++ ) { if ( m_aChildThreads[i]->GetThreadPriority() == THREAD_PRIORITY_NORMAL ) { m_aChildThreads[i]->ExitInstance(); m_aChildThreads[i]->Delete(); } } m_aChildThreads.RemoveAll(); CDialog::OnClose(); }
Conclusion
Thus, it was cleared how to create a class object having only its class name and why this mechanism is needed. This project model is convenient to use when designing a multi thread application with a set of fixed dialog types when each thread has its own message queue. This model permits you to create windows with equal priority of overlapping. Nevertheless, there is an unresolved task to manage a behaviour of such dialogs, to control operations of minimise and restore.
The idea of using one child thread class for all types of dialogs is original. It actually optimises the project architecture when it is used in a thread-separated dialog.