Skip to content

Simple Windows Dialogs From C++ – Part 1

October 6, 2011

Introduction

Have you ever wanted to spice up your C++ console application with the odd bit of GUI magic? Perhaps pop up a dialog to ask a user for input, select a file, or display an error message? This two-part tutorial shows how you can do all that using the Windows API without having to go to the lengths of writing a full-blown Windows application.

To follow this tutorial, you will need a C++ compiler that you can use from the command line. I assume GCC (see here for how to install it), but you could also use the Microsoft command line compiler – I give brief details of how to do this at the end of the tutorial. You will also need a command line prompt, and a text editor of some sort – Notepad will do at a pinch.

A First Dialog

The easiest dialogs to use from console applications are the message boxes provided by the Windows API. Here’s about the simplest program you can write to create one:

#include <windows.h>
int main() {
  MessageBox( 0, "Hello World!", "Greetings", 0 );
}

Create this text using your editor, and save it as ex1.cpp. Now compile it with GCC (the ‘>’ in these examples indicates the command line prompt):

> g++ ex1.cpp -o ex1

This will compile the code, and create an executable called ex1.exe. Now run it:

> ex1

This should produce an impressive dialog:

Screenshot_Greetings_2011-10-04_09-33-00

 

Now take a look again at the code you wrote:

#include <windows.h>

This pulls in the declarations of most (but not all) of the commonly-used functions in the Windows C API. Almost all Windows programs that do anything with the API will need to include this header.

MessageBox( 0, "Hello World!", "Greetings", 0 );

This calls the MessageBox function which is part of the Windows API. I do not propose in this tutorial to explain all the parameters of such functions in detail, but here they are the handle of the owning window (using zero makes the desktop the owner), the message and the caption you want displayed, and some options, which have not been specified.

Now, let’s turn this into a semi-useful program:

#include <windows.h>
int main( int argc, char *argv[] ) {
  MessageBox( 0, 
              argc > 1 ? argv[1] : "",
              argc > 2 ? argv[2] : "Message" , 0 );
}

 

Save this as ex2.cpp and compile (this is the last time I’ll be giving specific instructions to do this):

> g++ ex2.cpp -o ex2

Now run it like this:

> ex2 "That's all folks"

Which should produce:

Screenshot_Message_2011-10-04_09-50-48

This can be quite a handy program to use in Windows batch (.CMD) files, where it can provide status information at various points in the processing.

Getting Input

Message boxes can also be used to get input from the user. Let’s create a version of the previous program which prompts the user for a yes/no response:

#include <windows.h>
#include <iostream>
using namespace std;

int main( int argc, char *argv[] ) {
  int btn = MessageBox( 0, 
               argc > 1 ? argv[1] : "" , 
               argc > 2 ? argv[2] : "Question" , 
               MB_YESNO + MB_ICONQUESTION );
  if ( btn == IDYES ) {
    cout << "yes" << endl;
  }
  else {
    cout << "no" << endl;
  }
}

This uses the options of the MessageBox function to specify that you would like a Yes and a No button, and a question mark icon. As you have specified the MB_YESNO option, the function will now return one of two special values – IDYES or IDNO, depending on which button the user hits, which get tested and results in the corresponding input on standard output, using the normal C++ stream output facilities. The dialog it displays looks like this:

Screenshot_Just Checking_2011-10-04_10-39-25

This is also a handy program (here called ‘ex3’ ) to use in a batch file:

FOR /F %%x IN ('ex3 "Really delete those pesky .ZOD files?" "Check" ') DO SET response=%%x
IF %response%==yes del *.zod

There are other button and icon combinations you can use  – see the MSDN documentation on MessageBox for details. However, very simple Yes/No and OK/Cancel responses is about all that MessageBox is capable off.

Getting File Names

Suppose you want to write a C++ program that performs line-numbering. You need some way of getting the name of the file you want to list. You could prompt the user using the command line, use command line parameters, or you could use something like this:

Screenshot_Number Which File__2011-10-04_12-19-56

Using the Windows API to pop up a standard file selection dialog from a program is pretty easy. Before doing it, let’s have a look at the pure command line version of the program:

#include <iostream>
#include <fstream>
#include <string>
#include <iomanip>
using namespace std;

string  GetFileName( const string & prompt ) {
  cout << prompt;
  string name;
  getline( cin, name );
  return name;
}

int main( int argc, char *argv[] ) {

  string fname = GetFileName( "Number which file: " );
  ifstream ifs( fname.c_str() );
  if ( ! ifs.is_open() ) {
    cerr << "cannot open " << fname << " for input" << endl;
  }
  else {
    string line;
    int lineno = 1;
    while( getline( ifs, line ) ) {
      cout << setw(5) << right << lineno++ << " : " << line << "\n";
    }
  }
}

 

There is nothing very exceptional here. I suggest you compile and run this program so you understand what it does, and then we’ll look at replacing the GetFileName function with one that pops up the file selection dialog.

The file selection dialog is one of a group of dialogs which were added to Windows quite late on in its history. Before than, all common tasks, like file selection, had to be completely coded by each application. This was a vast waste of time and effort, and everyone gave a sigh of relief when the Common Dialogs were introduced.

The open-for-read dialog is produced by the Windows API function GetOpenFileName and the associated OPENFILENAME struct. This is an immensely complicated structure that is fully described in the MSDN documentation. If you check out this documentation, you will see that things have got even more complicated, as (since Vista) MS would like you to use some COM interface to get file names, but I’m sticking with the older API.

The OPENFILENAME struct has a shed-load of fields, but luckily most of them can profitably be set to zero and ignored. The only ones you absolutely have to worry about are the input buffer and buffer size and the size of the struct itself. We’ll also set the caption field to be the prompt. The replacement for the GetFileName function in the code above then looks like this:

string  GetFileName( const string & prompt ) { 
    const int BUFSIZE = 1024;
    char buffer[BUFSIZE] = {0};
    OPENFILENAME ofns = {0};
    ofns.lStructSize = sizeof( ofns );
    ofns.lpstrFile = buffer;
    ofns.nMaxFile = BUFSIZE;
    ofns.lpstrTitle = prompt.c_str();
    GetOpenFileName( & ofns );
    return buffer;
}

Here the buffer size is set to something suitably large, and used to size the buffer, which is zero initialised. An instance of the OPENFILENAME struct is then created and also zero-initialised. Then the fields you are interested in get set up – the buffer, the buffer size  and the caption (title) string. Then the API function GetOpenFileName is called to display the dialog with the address of the struct you have just set up as its parameter. When the function returns, any selected file name will be in the buffer, which is then returned from the function.

If you now compile this new program (let’s say you called it ex5.cpp) you may be surprised to get this error message:

undefined reference to `GetOpenFileNameA@4'
collect2: ld returned 1 exit status

This is a message from the GCC linker saying that it knows nothing about the function GetOpenFileName. This happens because (as I mentioned earlier) the common dialogs were added late to Windows and are not in the libraries that most compilers search by default. In fact, the import library for common dialogs under GCC is called libcomdlg32.a and you have to tell GCC  to use it like this:

> g++ ex5.cpp -l comdlg32 -o ex5

The file open dialog has many more features. For example, you can tell it which directory to start in, which kind of files to list and many other things. These are all described in the MSDN documentation, and using them is left as an exercise for you! There are also a number of other common dialogs which support things like font and colour selection, and which work in a similar manner to GetOpenFileName.

Using The Microsoft C++ Compiler

To use the Microsoft command line compiler, you will have to start up a command prompt using the VS20xx Prompt shortcut that should get installed along with Visual C++. This will set up all the required environment variables for you. Then to compile the message box programs, use  a command line like this;

> cl ex1.cpp user32.lib

This will generate an executable called ex1.exe. Note that you have to specify the USER32 library, which contains the MessageBox function, which GCC will link with by  default.

To build the file open dialog code, you need to specify the common dialogs library:

> cl ex5.cpp user32.lib comdlg32.lib

Conclusion

This first tutorial has looked at the built-in dialogs that Windows provides for your applications. In the next tutorial I will look at what is entailed in creating a custom dialog.

From → c++, tutorial, windows

Leave a Comment

Leave a comment