Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Simple ATL dialog based exe

0.00/5 (No votes)
28 Apr 2004 2  
How to create a simple program based on a dialog box using nothing but ATL

Introduction

Did you ever have to write a simple light dialog based exe to do simple stuff? Great then, because I can tell you how to do it in a brief, using nothing but ATL.

Background

ATL is great to develop light COM components with a minimum of dependencies, those can be contained on a DLL or an EXE, also ATL wizard has an option to create an ATL Object based on CAxDialogImpl<> template, so why not to use these options to create an standalone exe? No reason, isn't it? So, let's do it.

The recipe

  • Start a new project.
  • Select ATL COM AppWizard
  • Choose a name for your project, I'll use DlgTest
  • Click Next
  • On the Server Type, choose the "Executable (EXE)" option
  • Finish and OK. On the ClassView tab of the workspace, right click on the tree root and select from the menu "New ATL Object..." From the ATL Object Wizard window, change to the Miscellaneous category
  • No many options there, so select Dialog and then click "Next >"
  • Choose a name, I'll use MainDlg, so my class will actually be CMainDlg, and the click OK. Hung on, almost there.

Let's now create an instance of our dialog class.

Expand the Global folder on the ClassView's tree and double click on _tWinMain function, it will open the DlgTest.cpp file and show the code for that function scroll down almost to the end until you see these lines

  MSG msg;
  while (GetMessage(&msg, 0, 0, 0))
    DispatchMessage(&msg);

Create an instance of the CMainDlg (remember this is the name I chose) just before the loop, and show the dialog.

  CMainDlg dlg;          //Create an istance of this class

  dlg.Create(NULL);        //Create the main window,

                  //the NULL parameter means the desktop 

                  //will be the parent window

  dlg.ShowWindow(SW_SHOWNORMAL); //Show it, 

         //acording with the SDK, the first time a window is showed 

         //the parameter should be SW_SHOWNORMAL instead of SW_SHOW

Let's not forget to add the
#include "MainDlg.h"        //the dialog's header file

at the beginning of this file.

I know, you want to taste it, ok, go a head compile it and run it. But is not ready yet...

So, it ran, it showed the dialog box with the 2 default buttons, but when you click either of them you got an assertion, told ya wasn't ready.

But is ok, so we can learn something while cooking this. What happened is, the dialog was created as a modeless using the member function Create(), but the default implementation from the wizard assumes it would be a modal one, so when the OnOK or OnCancel function are called, the default is just call EndDialog, which is not what a modeless is expecting. To fix this, let's replace the EndDialog for DestroyWindow which is the appropriated way to destroy a modeless dialog. Another little thing, this is our only window, so if it is destroyed the whole application should shutdown, so let do that also, will do it calling PostQuitMessage. Going back to our recipe.

Open the MainDlg.h file, look for
 LRESULT OnOK(WORD wNotifyCode, WORD wID, HWND hWndCtl, BOOL& bHandled)
 {
  EndDialog(wID);
  return 0;
 }

 LRESULT OnCancel(WORD wNotifyCode, WORD wID, HWND hWndCtl, BOOL& bHandled)
 {
  EndDialog(wID);
  return 0;
 }
and let's replace EndDialog by DestroyWindow and also add the PostQuitMessage, so, they should look like this
 LRESULT OnOK(WORD wNotifyCode, WORD wID, HWND hWndCtl, BOOL& bHandled)
 {
  DestroyWindow();   //Destroy this window

  PostQuitMessage(0);   
    //Tell's the main loop to stop and exit returning then code 0

  return 0;
 }

 LRESULT OnCancel(WORD wNotifyCode, WORD wID, HWND hWndCtl, BOOL& bHandled)
 {
  DestroyWindow();   //Destroy this window

  PostQuitMessage(0);   //Tell's the main loop to 

      //stop and exit returning then code 0

  return 0;
 }
Done. Now it should run and shutdown gracefully.

Nice, but... not very useful, ok, I know, we forgot the seasoning. Let's put some spices and see what we get.

A form window?

Yes, this dialog based application can look and feel like a form based window application, let me show how to do it.

First, let's get rid of the button. From the dialog template, delete the two buttons. But now we need another way to end the program. Let's add a menu, on the resources tree, right click and choose "Insert...", select menu and click New. Let add 4 items on the menu, &File as a top menu, then type &New, a separator and then E&xit. Open the items' properties and choose IDNEW as the id for &New and IDCLOSE for E&xit.

We'll use this clean up and to shutdown the application respectively. Go back to the dialog box template, open its properties and in the General tab select the menu id from the Menu combo box. Go to the Styles tab and change the Border to Resizing, you can also add the Minimize and Maximize boxes. Let's add also a text box, so it'll do something. Change the properties of the text box to Multiline, Vertical and Horizontal scroll and Auto HScroll and AutoVScroll. We are done with the template, let's go back to our class and add some code.

Open the class' header file, we don't need to process the IDOK and IDCANCEL handlers any more, remember we deleted the buttons, so delete these two lines from the message map section.

BEGIN_MSG_MAP(CMainDlg)
 MESSAGE_HANDLER(WM_INITDIALOG, OnInitDialog)
//DEL COMMAND_ID_HANDLER(IDOK, OnOK)

//DEL COMMAND_ID_HANDLER(IDCANCEL, OnCancel)

END_MSG_MAP()

and also the functions definitions

//DEL LRESULT OnOK(WORD wNotifyCode, WORD wID, HWND hWndCtl, BOOL& bHandled)

//DEL {

//DEL  DestroyWindow();   //Destroy this window

//DEL  PostQuitMessage(0);   //Tell's the main loop to

//DEL          // stop and exit returning then code 0

//DEL  return 0;

//DEL }


//DEL LRESULT OnCancel(WORD wNotifyCode, 

//DEL      WORD wID, HWND hWndCtl, BOOL& bHandled)

//DEL {

//DEL  DestroyWindow();   //Destroy this window

//DEL  PostQuitMessage(0);   

//DEL //Tell's the main loop to stop and exit returning then code 0

//DEL  return 0;

//DEL }

Now we want to process the messages from the menu, so let's add the the handler on the message map section

BEGIN_MSG_MAP(CMainDlg)
 MESSAGE_HANDLER(WM_INITDIALOG, OnInitDialog)
 MESSAGE_HANDLER(WM_COMMAND, OnMenu)
END_MSG_MAP()

and the implementation

 LRESULT OnMenu(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
 {
  WORD itemId = (WORD)(wParam & 0xFFFF); 
  //The low-order word specifies the identifier of the menu item, control, or 

//accelerator. 

  switch(itemId)
  {
  case IDNEW:
   //Process here the New menu item

   break;
   
  case IDCANCEL: //Process here the close window box on the Title bar

  case IDCLOSE: //Process here the Exit menu item

   {
    DestroyWindow();
    PostQuitMessage(0);
    break;
   }
  default:
   bHandled = FALSE;
  }
  return 0;
 }

Let's now clean the text box when the New menu item is selected

  case IDNEW:
   {
    //Process here the New menu item

    //Let's clean up the text box

    
    //Get the HWND of the text box

    HWND hWnd = GetDlgItem(IDC_EDIT1);

    //Set an empty string into it

    ::SendMessage(hWnd, WM_SETTEXT, 0, (LPARAM)_T(""));
    break;
   }  
Also we want to have the text box to resized when the window change its size, so let process the WM_SIZE message to.

BEGIN_MSG_MAP(CMainDlg)
 MESSAGE_HANDLER(WM_INITDIALOG, OnInitDialog)
 MESSAGE_HANDLER(WM_COMMAND, OnMenu)
 MESSAGE_HANDLER(WM_SIZE, OnSize)
END_MSG_MAP()

and the implementation

 LRESULT OnSize(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
 {
  //Get the size of the client rect and resize the text box

  RECT rect;
  GetClientRect(&rect); //The RECT structure receives the client coordinates. 

    //The left and top members are zero. 

    //The right and bottom members contain the width and height of the window.


  //Get the HWND of the text box

  HWND hWnd = GetDlgItem(IDC_EDIT1);

  //Resize it

  ::MoveWindow(hWnd, 0, 0, rect.right, rect.bottom, TRUE);
  return 0;
 }

And last but not least, a very tricky line. But before I tell you, just for the fun of it, compile and run it. The resize is working, and also the Exit menu is working, but is not possible to input any text to the text box.

Why? Because the message loop is not translating the virtual key for our dialog to understand the WM_KEYDOWN and the WM_KEYUP, so it is not getting the WM_CHAR.

So remember this?

  MSG msg;
  while (GetMessage(&msg, 0, 0, 0))
    DispatchMessage(&msg);

ok, we have to change to this

  MSG msg;
  while (GetMessage(&msg, 0, 0, 0))
  {
    TranslateMessage(&msg);   //TranslateMessage function translates 

         //virtual-key messages into character messages.

    DispatchMessage(&msg);
  }

and voil�. Now we have a little mini tiny notepad.

Points of Interest

I think the whole point here is not what the application is about, but how to put together a functional exe with minimum dependencies in a record time using only ATL. If this is not useful by itself at least I believe is a good exercise to see beyond what could be the standard use of ATL.

Note: I have done no error control what so ever, in a real application the code should have plenty of error controls, never forget that.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here