The topics today are related to visual changes to my auto emailer program and background processing in a modal dialog box.
Visual Changes
The auto email program now sports a shiny new display. It looks very much like an SMS text message app on a smart phone. A few things were added to make this a workable display; the email address of the “other” user, not the email addresses used by this program, are displayed wherever they change. If an incoming message on the left has a different email address than the previous incoming or outgoing message then the address is displayed.
The messages above are some test messages that I generate in the program itself although they are sent to the SMTP mail server and then gathered up by the normal processing loop of this program.
This brings me to the second topic…
Background Processing in a Modal Dialog Box
The test emails are sent by clicking on the Help menu and then clicking on Send Test Messages. This brings up a modal dialog box that display the status of the test message process. To make this work so that the dialog box can be dragged around and to keep Window from displaying a “Not responding” warning, I needed to make sure that the SMTP email handling was done in another thread. As we discovered in an earlier lesson, you cannot do things to the user interface of the program in another thread outside of the main UI thread. Here’s some code:
CTestEmailDialog::CTestEmailDialog(CWnd* pParent /*=NULL*/) : CDialog(CTestEmailDialog::IDD, pParent) { m_hThread = 0; } CTestEmailDialog::~CTestEmailDialog() { TerminateThread( m_hThread, 0 ); } static unsigned int __stdcall ThreadFunction( void* pInfo ) { CTestEmailDialog* pWindow = (CTestEmailDialog*)pInfo; if( pWindow == 0 ) { _endthread(); return 0; } [Reading the configuration file and sedning emails in this area of the code. Each time an email is sent, the CString variables are set with the status information that should be displayed in the window. then the WM_TIMER message is sent as shown below.] pWindow->SendMessage( WM_TIMER, 0, 0 ); pWindow->SendMessage( WM_TIMER, 1, 1 ); _endthread(); return 0; } void CTestEmailDialog::OnTimer(UINT_PTR nIDEvent) { m_Line1Control.SetWindowText( m_Line1 ); m_Line2Control.SetWindowText( m_Line2 ); m_Line3Control.SetWindowText( m_Line3 ); UpdateWindow(); if( nIDEvent != 0 ) EndDialog( 0 ); } BOOL CTestEmailDialog::OnInitDialog() { CDialog::OnInitDialog(); unsigned int ThreadID; m_hThread = (HANDLE)_beginthreadex( NULL, 0, &ThreadFunction, this, 0, &ThreadID ); return TRUE; } void CTestEmailDialog::OnClose() { }
The above code, with lots of stuff removed to make it easier to read here, represents the test message dialog box code for thread handing.
I removed much of the email sending code in the thread function and stuck in a comment of sorts.
I decided that I didn’t want to bother with handling a WM_USER message. Instead, I used the WM_TIMER message. If no timer is set elsewhere then this message is free for my own use. Just don’t do this then start setting timers or things might break.
The WM_TIMER event ID value is used to tell the timer function what to do. If the event ID is non-zero, the dialog is closed.
Note that the handling of WM_CLOSE in the OnClose() function causes the window to be stuck open. The user cannot close it and it must finish it’s work before it goes away automatically. I did make sure to create the dialog box without a close button as seen here:
The only really dirty code is the termination code in the dialog destructor. I need to remove this because the handle may not be valid by the time this code is called. I wanted to make sure that the thread is not left running once the Window, or at least the class, is destroyed. This is probably not necessary since it seems impossible to close the dialog box before the thread completes without simply terminating the entire process.