Browsing all posts tagged visual-c++

Code Articles Moved

Jun 21, 2012

In further preparation for merging my blog with Born Geek, the "Code" section of this website has been relocated. You will now find all of the old Visual-C++ articles filed as back-dated posts beneath the Visual-C++ tag. If you spot a problem with any of them, let me know by leaving a comment here. Below is a master list of the articles, for convenience:

General Articles

Dialog Based Applications

Window Placement and Sizing

This article was written back when Visual Studio 6 was new. As such, I cannot guarantee that the content of this article still applies in modern Windows environments.

Sending the contents of a CString object to the clipboard is a quick and easy task when the following function is used:

BOOL SendTextToClipboard(CString source)
{
    // Return value is TRUE if the text was sent
    // Return value is FALSE if something went wrong
    if(OpenClipboard())
    {
        HGLOBAL clipbuffer;
        char* buffer;

        EmptyClipboard(); // Empty whatever's already there

        clipbuffer = GlobalAlloc(GMEM_DDESHARE, source.GetLength()+1);
        buffer = (char*)GlobalLock(clipbuffer);
        strcpy(buffer, LPCSTR(source));
        GlobalUnlock(clipbuffer);

        SetClipboardData(CF_TEXT, clipbuffer); // Send the data

        CloseClipboard(); // VERY IMPORTANT
        return TRUE;
    }
    return FALSE;
}

So what's going on here? We first test to see if we can successfully open the clipboard from our program. Note that once we have called the OpenClipboard() function, no other program can modify the clipboard contents. Because of this, it is very important that later on in our code we call the CloseClipboard() function. This way, others can access the clipboard as needed.

We create a local handle to a global memory block as well as a local character buffer. A call to the EmptyClipboard() function clears out anything that might already be on the clipboard. Following that, we allocate a specified number of bytes in the global memory heap. This is where the string will be stored for future use. We then lock the global memory block (so we don't lose our storage), assigning the memory pointer as necessary. Then we copy the text from the CString into the character buffer, unlock the global memory storage chunk and send the data to the clipboard.

Finally, we call the CloseClipboard() function! The code then ends by returning true (since we successfully copied the text).

This article was written back when Visual Studio 6 was new. As such, I cannot guarantee that the content of this article still applies in modern Windows environments.

Context menus (or "right-click popup" menus) come in two flavors: application-wide or control-specific. This article focuses on the latter. If you are interested in application-wide menus, be sure to check out my article on that very subject.

Adding the Menu Resource

The first step in creating a context menu is simple: use the Visual Studio resource editor to create the menu you wish to display. Make sure that you give it a unique ID; I will use the value IDR_POPUP_MENU for this example. Add to the menu the various menu items that you want to include. Note that your context menu should only have one top-level item (under which all other menu items will be placed). The caption for this top-level item can be whatever you like since the user will never see it. If you are mapping menu items in this context menu to commands that already exist in the program (as is likely), make sure that you give each menu item the appropriate ID. For example, if the ID to my "Open File" command in my main application menu is ID_FILE_OPEN, I would give that same ID to the corresponding item in my context menu. If you aren't mapping commands, simply add any message handlers as normal.

Displaying the Menu

After you create the menu resource, we need to write the code that's responsible for showing the menu when the user right-clicks a specific control. For this example, I will assume that the control we are adding a menu to is a CListCtrl. Add the NM_RCLICK event handler to your list control, and edit the resulting handler method to look like the following:

void CYourDlg::OnRclickYourList(NMHDR* pNMHDR, LRESULT* pResult)
{
    CPoint point;
    GetCursorPos(&point);

    CMenu mnuTop;
    mnuTop.LoadMenu(IDR_POPUP_MENU);

    CMenu* pPopup = mnuTop.GetSubMenu(0);
    ASSERT_VALID(pPopup);

    pPopup->TrackPopupMenu(TPM_LEFTBUTTON | TPM_RIGHTBUTTON |
        TPM_LEFTALIGN, point.x, point.y, AfxGetMainWnd(), NULL);

    *pResult = 0;
}

Understanding the Code

Let's examine this code so that we know exactly what's going on. We first create a CPoint object so that we can store the location of the mouse when the user clicks the right mouse button. Since the message is only fired when the user clicks within the list control, we can simply use a call to the GetCursorPos() method to get the mouse's current screen coordinates. We then create a menu object and attach the menu resource we created earlier to it. The parameter to the LoadMenu() method is simply the unique ID that we assigned to our menu.

Next, we set up a pointer to a menu object (this is what will actually control the popup menu). The pointer gets assigned to the return value of the GetSubMenu() method. The parameter that is passed here is the zero-based index of the submenu that will be used as the popup menu. Since our menu resource has only one top-level item, we need only to pass a parameter value of zero.

We finally use the TrackPopupMenu() method to create and draw the menu itself. The first parameter sets a screen position flag and a mouse button flag. As you can see from the flags I pass in, I allow the user to select menu items with either the left or right mouse buttons (which are the only two choices), and I align the left of the menu with the coordinate specified by the x parameter.

The next parameter is indeed that x value, which specifies the horizontal screen coordinate where the popup menu should appear. Likewise, they y (vertical) parameter follows the x value. In both cases, we use the CPoint object's values that we got from our call to GetCursorPos().

The fourth parameter identifies the window that owns this popup menu. This window will receive all the messages sent from the menu, so we pass AfxGetMainWnd() in order to get the application's main window object.

The final parameter is either a RECT structure or CRect object that specifies a rectangular area in which the user can click without dismissing the popup menu. If you just leave this set to NULL, things will work properly (i.e., clicking outside of the menu will dismiss it).

This article was written back when Visual Studio 6 was new. As such, I cannot guarantee that the content of this article still applies in modern Windows environments.

Context menus (or "right-click popup" menus) come in two flavors: application-wide or control-specific. This article focuses on the former. If you are interested in control-specific menus, be sure to check out my article on that very subject.

Implementing the Application Popup Menu

The first step in creating a context menu is simple: use the Visual Studio resource editor to create the menu you wish to display. Make sure that you give it a unique ID; I will use the value IDR_POPUP_MENU for this example. Add to the menu the various menu items that you want to include. Note that your context menu should only have one top-level item (under which all other menu items will be placed). The caption for this top-level item can be whatever you like since the user will never see it. If you are mapping menu items in this context menu to commands that already exist in the program (as is likely), make sure that you give each menu item the appropriate ID. For example, if the ID to my "Open File" command in my main application menu is ID_FILE_OPEN, I would give that same ID to the corresponding item in my context menu. If you aren't mapping commands, simply add any message handlers as normal.

In order to display the context menu to the user when they click the right mouse button, we need to add a handler for the WM_CONTEXTMENU message. This handler should be added to the window class that will handle the menu's message calls. Most often, this class will be your view class, but note that any CWnd based class will do.

Once the message handler has been added, insert the following code into the WM_CONTEXTMENU handler's method:

// We might need to adjust the origination point for the
// keyboard context menu
if(point.x == -1 && point.y == -1)
{
    CRect rect;
    GetClientRect(&rect);
    point = rect.TopLeft();
    point.Offset(5,5);
    ClientToScreen(&point);
}

// Load the top level menu from the resource we created
CMenu myMenu;
myMenu.LoadMenu(IDR_POPUP_MENU);

// Now extract the (one and only) popup menu item from
// this menu resource
CMenu* myPopup = myMenu.GetSubMenu(0);

// Let's display the menu!
myPopup->TrackPopupMenu(TPM_LEFTALIGN | TPM_LEFTBUTTON,
    point.x, point.y, AfxGetMainWnd(), NULL);

Understanding the Code

The first thing we do is to handle the case where the user invokes our context menu via the keyboard. In this scenario, we adjust the origination point for the menu. Next, we load the menu resource that we created earlier and extract the popup menu item (the one and only top-level item). The popup menu extraction is performed by using the GetSubMenu() method, and passing the (zero-based) index of the item to extract. Since we only had one top-level menu item, a value of zero gets passed in.

Finally, we make a call to the TrackPopupMenu() method to display the menu to the user. The first parameter in this method call is a series of flags used to specify where the menu shows up in relation to the mouse cursor, as well as what buttons can be used to select the menu items. The screen position may only be one of the following:

  • TPM_CENTERALIGN - Centers the menu horizontally relative to the coordinate specified by x.
  • TPM_LEFTALIGN - Positions the menu so that its left side is aligned with the coordinate specified by x.
  • TPM_RIGHTALIGN - Positions the menu so that its right side is aligned with the coordinate specified by x.

The mouse button flag can be any combination of the following two values:

  • TPM_LEFTBUTTON - Causes the menu to track the left mouse button.
  • TPM_RIGHTBUTTON - Causes the menu to track the right mouse button.

The next two parameters in the TrackPopupMenu() call specify the horizontal and vertical locations of the menu (the horizontal position depending on the flag used in the first parameter). Next comes a pointer to the window that owns the popup menu. Since we want our messages to be routed to our top-most parent, we use a call to AfxGetMainWnd() to get the top level window. The final parameter defines a CRect in which the user can click without dismissing the popup menu. A value of NULL here will do the right thing: clicking outside of the menu will dismiss it.

This article was written back when Visual Studio 6 was new. As such, I cannot guarantee that the content of this article still applies in modern Windows environments.

If you are interested in how to access arbitrary locations in the Windows registry, this is the article for you. However, if you would prefer to learn a simpler method of registry access to store and retrieve program settings, consult my simple registry access article.

Opening (Reading) Keys

Unlike the simpler, application based method, we don't need to enable registry access via a special function call. Instead, the functions we will be using interface directly with the registry itself. Let's look at the code required to open an arbitrary key value:

HKEY hKey;
RegOpenKeyEx(HKEY_CURRENT_USER, TEXT("Control Panel\\Desktop"),
             0, KEY_QUERY_VALUE, &hKey);

As you can see, we are using the RegOpenKeyEx() function. Let's take a look at its various parameters:

  • Parameter 1: A handle to a currently open key or any of the following predefined reserved handle values:
    • HKEY_CLASSES_ROOT
    • HKEY_CURRENT_CONFIG
    • HKEY_CURRENT_USER
    • HKEY_LOCAL_MACHINE
    • HKEY_USERS
    • HKEY_PERFORMANCE_DATA (Windows NT only)
    • HKEY_DYN_DATA (Windows 95 and 98 only)
  • Parameter 2: A pointer to a null-terminated string that specifies the name of the sub-key to open.
  • Parameter 3: Must be 0. This is a reserved value.
  • Parameter 4: Specifies an access mask that describes the security access for the new key. Take a look at the MFC documentation for all the possible values.
  • Parameter 5: Pointer to a variable that receives the handle of the open key.

As can be seen in the example code provided above, we are opening a registry key in the HKEY_CURRENT_USER branch. What's more is that we're explicitly opening the "Control Panel\Desktop" sub-key. We want to have permission to query sub-key data values (hence the value of KEY_QUERY_VALUE for parameter 4), and we store the resulting handle in the hKey variable.

The RegOpenKeyEx() method returns a value indicating success or failure, so make sure you test it appropriately. Let's assume that the key was opened successfully and see how to query its value:

unsigned char buffer[_MAX_PATH];
unsigned long datatype;
unsigned long bufferlength = sizeof(buffer);

RegQueryValueEx(hKey, "Wallpaper", NULL, &datatype, buffer, &bufferlength);

Again, let's take a look at the parameters for this new RegQueryValueEx() function:

  • Parameter 1: A handle to a currently open key.
  • Parameter 2: A pointer to a null-terminated string that specifies the name of the value to be queried.
  • Parameter 3: Must be NULL. This is a reserved value.
  • Parameter 4: Pointer to a variable that receives the type of data associated with the specified value. Check out the MFC documentation to see what possible values are stored in this variable. This isn't too critical, so you can just skip over it for now.
  • Parameter 5: Pointer to a buffer that will receive the value's data.
  • Parameter 6: Pointer to a variable that specifies the size, in bytes, of the available buffer.

The first parameter accepts the same handle we got from the RegOpenKeyEx() function. The second parameter in this example is the string "Wallpaper", which means this is the specific key we will be querying. The fourth parameter is an unsigned long that accepts the data type, but we won't ever actually use its value. Next is an unsigned character buffer, into which the value of the registry key will be read. I use the Windows constant _MAX_PATH to create a buffer large enough to store the longest possible file path (since this particular key I'm querying stores a file path value). We finally pass in the length of the buffer that we just created.

One last step is required before we can successfully say we have read a registry key. The key that got opened must be closed (after we read its value, of course), so that its handle gets freed. The code we use to do this is:

RegCloseKey(hKey);

Using all of the methods just mentioned, you can read any value from the registry.

Creating (Writing) Keys

So now we know how to read a value. But what about writing a value? The process is slightly different. First, we open the key we want to write (whether it exists or not) using the RegCreateKeyEx() function:

HKEY hKey;
unsigned long dwDisp;

RegCreateKeyEx(HKEY_LOCAL_MACHINE, TEXT("Control Panel\\Desktop"),
               0, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS,
               NULL, &hKey, &dwDisp);

Let's examine this function's parameters:

  • Parameter 1: A handle to a currently open key or any of the following predefined reserved handle values:
    • HKEY_CLASSES_ROOT
    • HKEY_CURRENT_CONFIG
    • HKEY_CURRENT_USER
    • HKEY_LOCAL_MACHINE
    • HKEY_USERS
    • HKEY_PERFORMANCE_DATA (Windows NT only)
    • HKEY_DYN_DATA (Windows 95 and 98 only)
  • Parameter 2: A pointer to a null-terminated string that specifies the name of the sub-key to open.
  • Parameter 3: Must be 0. This is a reserved value.
  • Parameter 4: A pointer to a null-terminated string that specifies the class (object type) of this key. If the key already exists, this parameter is ignored. For our purposes, just set it to a value of NULL.
  • Parameter 5: Set this parameter to REG_OPTION_NON_VOLATILE for our purposes.
  • Parameter 6: Specifies an access mask that describes the security access for the new key. Take a look at the MFC documentation for all of the possible values.
  • Parameter 7: Set this parameter to NULL for our purposes.
  • Parameter 8: Pointer to a variable that receives the handle of the opened or created key.
  • Parameter 9: A pointer to a variable that receives a disposition value of the key. Either the value will be REG_CREATED_NEW_KEY (a new key was created) or REG_OPENED_EXISTING_KEY (the key was already there and has been opened).

Once the desired key is opened, we can set its value with the RegSetValueEx() function:

unsigned char tilewallpaper[2];
tilewallpaper[0] = '0';
tilewallpaper[1] = '\0';

RegSetValueEx(hKey, TEXT("TileWallpaper"), NULL, REG_SZ, tilewallpaper, 1);

Let's look a little closer at this final function's parameter list:

  • Parameter 1: A handle to a currently open key.
  • Parameter 2: A pointer to a null-terminated string that specifies the name of the value to be set.
  • Parameter 3: Must be NULL. This is a reserved value.
  • Parameter 4: Pointer to a variable that specifies that type of data that will be written. We use REG_SZ since we will be writing a null-terminated string.
  • Parameter 5: Pointer to a buffer that contains the data to be written.
  • Parameter 6: Pointer to a variable that specifies the size, in bytes, of the buffer.

Here we are explicitly writing a value to the TileWallpaper key (we are going to write a value of '0' so that our wallpaper is not tiled). We use the hKey as before, and an unsigned character buffer. Fairly straightforward.

Don't forget to close the key once you've finished:

RegCloseKey(hKey);

I've now shown you how to read and write to any location in the registry. This is a powerful means of programming, so be careful! You don't want to accidentally remove or overwrite anything that you shouldn't.

This article was written back when Visual Studio 6 was new. As such, I cannot guarantee that the content of this article still applies in modern Windows environments.

The Windows registry is an excellent place to store program information. From recent file lists to program settings, the registry provides programmers with a central location to store information for future use. Registry access can be quite simple, provided that you accept a few limitations. First, you must be willing to store your registry values on a per-user basis (rather than for all users). Second, you may only read and write values to your own application's registry branch. Poking around in arbitrary places in the Windows registry is somewhat more complicated than the method provided in this article. If you are interested in the advanced method of access, feel free to read the article I've written on the subject.

Do not assume that the constraints mentioned above render this method of registry access useless. I personally use this registry access method for all of my applications, using the advanced access method only when necessary. The simple method described below will do everything you need it to do for simple storage of application specific information.

Enabling Registry Access

The method we will use to access the registry comes through the legacy CWinApp Initialization File (*.ini) functionality. This means of access gives us read and write permissions to the following registry key tree branch:

HKEY_CURRENT_USER\Software\<Your_Company_Name>\<Your_Program_Name>\

We first need to add the following line of code to the InitInstance() function of our program. If you are writing an SDI or MDI based application, this function call should have already been inserted by the AppWizard (when you first created the project). For dialog based programs, you must manually insert the function call. Here's the code:

SetRegistryKey(_T("Your Company Name or Identity"));

You should obviously replace the "Your Company Name or Identity" string with an appropriate value. This string identifies the registry branch in which your application settings will be stored. By making this call to SetRegistryKey, we set the value of the m_pszRegistryKey variable, which tells our CWinApp object to write all preference to the registry; not to an INI file.

Setting the Application Title

Now we need to either add or modify the following entry in the string table resource:

AFX_IDS_APP_TITLE

This string's value should be set to what you want your program to be called in the registry. As you probably can deduce, this will take the place of the <Your_Program_Name> entry in the registry branch mentioned at the beginning of this article. I usually use the program name itself (for example, I use the string "Paper Plus" for my wallpaper changing program).

Reading and Writing Keys

You will find an instance variable of your application called theApp in your CWinApp derived class file (usually named <projectname>.cpp). This instance is a global object and we will use it to read and write values from the registry. Select this variable declaration and copy it to the clipboard (make sure you copy rather than cut). Then decide which class you will be doing your registry reading and writing in. For a dialog based application, I usually place my registry reading and writing code in the CDialog derived class. Specifically, the InitDialog() method is where I read my stored values and the OnDestroy() method is where I do the writing.

Open up the .cpp file for the class that you have selected. At the top of this file (outside of any class definitions), paste the declaration of the CWinApp object we just copied, and make it an extern variable, just like this (obviously, CYourApplication will be whatever type your application variable is):

extern CYourApplication theApp;

Once you've declared this variable to be an extern, you can begin reading and writing values. Before we discuss how, let's first take a look at how keys are stored. We already know what registry branch we have access to, but what structure gets used within that branch? It's actually quite simple:

\<Section Name>\<Entry Name>

Think of the section as being a folder, and think of the entry name as being a file in that folder. Using this analogy, envisioning the structure of the registry is easy. Sections are merely a way to organize your entries, and they don't have to be used (read on to find out how and why). But they make things nice and neat, so it's good practice to use them. To write or read some values, use code similar to the following:

// Code to write some values
// Usually occurs right before the application exits

theApp.WriteProfileInt("", "Keep On Top", m_KeepOnTop);
theApp.WriteProfileString("Files", "Last Saved", m_LSFile);

// Code to read some values
// Usually occurs right after the application starts

m_KeepOnTop = theApp.GetProfileInt("", "Keep On Top", 0);
m_LSFile = theApp.GetProfileString("Files", "Last Saved", "");

Understanding the Code

These lines of code deserve an explanation. Each of these functions take 3 parameters:

  1. Points to a null-terminated string that contains the section name.
  2. Points to a null-terminated string that contains the entry name.
  3. The default value of the entry (if reading), or the value to store for the entry (if writing).

So, as you can see from my example code, the section name can be empty ("") or it can point to a name you specify ("Files"). If you decide to use an empty section value, the registry entries you write to will appear in the branch specified by:

HKEY_CURRENT_USER\Software\<Your_Company_Name>\<Your_Program_Name>\

The entry name can be whatever you want it to be, but it is strongly recommended that you make it readable, so that users of your program can edit the registry values manually should they so desire.

The context of the third parameter in the functions mentioned above changes between reading and writing values. If you are writing a value, this third parameter is the variable name that contains the value to be written. Conversely, if you are reading a value, the third parameter specifies the default value to use should the key not already exist. This is an incredibly handy way to initialize program settings after a user installs your application for the first time. Also note that, when reading values, the return value of the GetProfileXXXXX() calls is the value of the entry that was read.

As you can see, Windows registry access on an application level isn't a difficult task. Accessing other areas of the registry is a little more involved, however, and I cover that very topic in my advanced registry article.

Minimize All Windows

Apr 12, 2008
This article was written back when Visual Studio 6 was new. As such, I cannot guarantee that the content of this article still applies in modern Windows environments.

Minimizing all windows that are currently open is an extremely easy task. And undoing the minimize all operation (restoring all windows to their previous state) is just as easy. Just use the corresponding code snippets below to do it.

// Minimize All Windows
PostMessage(FindWindow("Shell_TrayWnd", NULL), WM_COMMAND,
            MAKELONG(415, 0), 0);

// Undo Minimize All Windows
PostMessage(FindWindow("Shell_TrayWnd", NULL), WM_COMMAND,
            MAKELONG(416, 0), 0);

Since the operating system has the minimize all feature built in, we use that instead of enumerating all of the currently opened windows and sending a minimize message to each one. You'll note that the first parameter to the PostMessage() method above is a call to the FindWindow() method. We are essentially obtaining a handle to the window indicated by the "Shell_TrayWnd" class name, which happens to be the Windows task bar. We send a WM_COMMAND message to that window, and pass in a value of 415 for the wParam value. I'm not 100% sure where the 415 and 416 values come from, as I found these in an obscure news posting. My guess is that these are the unique ID numbers for the task bar's minimize all and restore all commands.

This article was written back when Visual Studio 6 was new. As such, I cannot guarantee that the content of this article still applies in modern Windows environments.

Creating a window with size restraints is an easier task than you might initially think. Interestingly enough, it only involves overriding the WM_GETMINMAXINFO event handler. Since the user resizes a frame window that contains a view (rather than resizing the view itself), we need to add this override to the appropriate parent frame class. This is, in most cases, usually the CMainFrame class, so this article will use that as the example. Here is how we handle limiting the size of an SDI program to a minimum of 600 pixels by 300 pixels:

void CMainFrame::OnGetMinMaxInfo(MINMAXINFO FAR* lpMMI)
{
    CFrameWnd::OnGetMinMaxInfo(lpMMI);

    lpMMI->ptMinTrackSize.x = 600;
    lpMMI->ptMinTrackSize.y = 300;
}

First, we allow the default window processing to fill in the lpMMI structure (which keeps information on how large or small a window can get). Then, we simply modify the size fields in this structure to limit the window's size. The ptMinTrackSize field is where the minimum window size information is kept. Likewise, the ptMaxTrackSize field is where the maximum window size information is kept. You would therefore use that field to keep a window from becoming too large.

Handling Child Windows

Let's look at an example of this field in an MDI based application that limits the size of its child windows:

void CChildFrame::OnGetMinMaxInfo(MINMAXINFO FAR* lpMMI)
{
    CFrameWnd::OnGetMinMaxInfo(lpMMI);

    // Calculate the nonclient overhead of the frame window
    CRect client;
    GetClientRect(&client);

    CRect window;
    GetWindowRect(&window);

    int widthOverhead = window.Width() - client.Width();
    int heightOverhead = window.Height() - client.Height();

    lpMMI->ptMaxTrackSize.x = 800 + widthOverhead;
    lpMMI->ptMaxTrackSize.y = 600 + heightOverhead;
}

Here we are calculating the "nonclient" overhead associated with the parent frame: toolbars, scroll bars, frame widths, etc. Then we limit the maximum size of a child window to 800 pixels by 600 pixels. Fairly simple, right? Using this method, child windows can be limited just like their parents.

Keep a Window on Top

Apr 12, 2008
This article was written back when Visual Studio 6 was new. As such, I cannot guarantee that the content of this article still applies in modern Windows environments.

This article covers how to force your main program window to stay at the top of the Z-order. Making child views in an MDI application remain on top of other views is a completely different beast, so do not try to use this code for that purpose.

By using the single line of code below, you can force any dialog window to remain on top of all other windows (in the Z-order):

SetWindowPos(&wndTopMost, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE | SWP_SHOWWINDOW);

The first parameter passed to the SetWindowPos() function is a pointer to a window that will precede the current CWnd object in the Z-order. Since I want my window to be top most, a pointer to the value wndTopMost does the trick.

Forcing a frame window for an SDI or MDI application to remain on top uses a similar process, with one slight modification:

CWnd* pMainWnd = AfxGetMainWnd();
pMainWnd->SetWindowPos(&wndTopMost, 0, 0, 0, 0,
                       SWP_NOSIZE | SWP_NOMOVE | SWP_SHOWWINDOW);

Note that we first have to get a pointer to the main window frame, then use that pointer to call the frame window's SetWindowPos() method.

Restoring Windows to Normal

To restore a dialog window back to a normal state, use the following code:

SetWindowPos(&wndNoTopMost, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE | SWP_SHOWWINDOW);

In this case, a pointer to the value wndNoTopMost is used. Likewise, the following code will work with a frame window:

CWnd* pMainWnd = AfxGetMainWnd();
pMainWnd->SetWindowPos(&wndNoTopMost, 0, 0, 0, 0,
                       SWP_NOSIZE | SWP_NOMOVE | SWP_SHOWWINDOW);

In order to fully understand the other parameters used in this method, you should check out the online documentation for the SetWindowPos() method at the MSDN library.

This article was written back when Visual Studio 6 was new. As such, I cannot guarantee that the content of this article still applies in modern Windows environments.

The CListView class (a simple variant of the CListCtrl class) is a useful way to provide a resizable, list control application. But you may have noticed that the columns in "report" mode do not resize themselves when the user resizes the program window. So how can we make the columns fit nicely in the list view, without the need for a horizontal scroll bar? The answer is simpler than you may have thought!

Sizing the Columns

By overriding the WM_SIZE event for the view class, we can handle sizing the columns neatly. The following example will have four columns total, two of which will be proportional to the window size, and two of which will be a fixed size (no matter how large the window gets). Here is the code:

void CPaperplusView::OnSize(UINT nType, int cx, int cy) 
{
    CListView::OnSize(nType, cx, cy);

    if(m_List->GetSafeHwnd() != NULL)
    {
        // Get the list control size
        CRect r;
        m_List->GetClientRect(&r);

        // Figure out how large the column interval should be
        int scrollwidth = GetSystemMetrics(SM_CXVSCROLL);
        int listwidth = r.Width() - scrollwidth - 80 - 80;
        int col_interval = listwidth / 10;

        m_List->SetColumnWidth(0, col_interval * 6);
        m_List->SetColumnWidth(1, col_interval * 4);
        m_List->SetColumnWidth(2, 80);
        m_List->SetColumnWidth(3, 80);

        m_List->ShowScrollBar(SB_HORZ, FALSE);
    }
}

The first method call that appears in this code block was inserted by ClassWizard and is simply a call to the base class (to allow the normal processing to get handled). I then make sure that the pointer to my list control (m_List in the above code block) is not an illegal value. This prevents some hairy problems when the program is first starting up.

Understanding the Code

First, I get the client size of the list control and store it in a CRect variable. The next call is to the GetSystemMetrics() function, into which I pass the SM_CXVSCROLL parameter. This method call is used to get the width of the Windows vertical scroll bar. We need to provide space for this scroll bar so that the columns will fit nicely when the vertical scroll bar does indeed show up. I next determine how much space I have left over to work with. After subtracting out the scroll bar width, as well as the two fixed size columns (both of which are 80 pixels wide), I have the size available to the first two, proportional columns. I then divide that remaining space into 10 parts, for use with the variable-sized columns.

Next, I actually set the column widths. You will note that the first column will take up 60% of the available space (excluding the vertical scroll bar and fixed-size columns). Likewise, the second column will take up the remaining 40% of "free" space. After those two columns are dealt with, I make sure that the last two columns are sized appropriately (to their fixed values of 80 pixels).

The final step is to make sure that the horizontal scroll bar never shows up. I'm not 100% sure if we only need to make this call one time, but I figure that it doesn't hurt to turn off the horizontal scroll bar every time a size event gets called. Why is this a necessary line of code if we are going to great pains to size things correctly? Well, for some strange reason, when a window is horizontally sized smaller than its initial value, the scroll bar still shows up, even though there is nothing that is scrolled off screen to either the left or right! There is definitely a strange bug at work here, so I took this means of 'fixing' it.

You may notice that even though we have done a great deal of work to make things fit nicely, there will occasionally be some left over space in the list control header. This space always shows between the vertical scroll bar and the last column. Why isn't this space used like it should be? The simple answer is that we are doing integer division when dividing up the list size into "available" space. So, that little bit that was truncated from that integer division problem constitutes for this remainder. The easiest fix for this is to simply add the "left over" pixel width to the final column. Then your columns will always fit perfectly!

So there you have it. Dynamic column sizing is a quick addition to any program that utilizes the CListView and is sure to make your program look that much more professional.

This article was written back when Visual Studio 6 was new. As such, I cannot guarantee that the content of this article still applies in modern Windows environments.

Snapping a window to the screen edge has long been an interesting effect. Winamp is probably the best application to use as an example, as all of its associated windows can snap to the screen edge (and to each other). So how can you make your application do the same thing? It's actually not too difficult!

You will find that some "window snap" code snippets require that the "Show Window Contents While Dragging" option be turned on in Windows. Thankfully, the following code doesn't have that restriction. This article will use a dialog based application as the example, and will refer to the "dialog's class" as the class to add code to. This same effect can be added to document-view based applications (SDI / MDI), adding the necessary code to the appropriate frame class (e.g., CMainFrame).

Making the window snap to the screen requires that we take control of a few messages that are normally sent by the application to the operating system. We won't prevent the messages from reaching the OS, but we will be the first to manipulate them. Here's how to do it:

Override the Default WindowProc Method

First, we need to override the standard WindowProc() message in the dialog's class. Add this override via ClassWizard, and then add the following line of code to the corresponding method:

OnMessage(this, message, wParam, lParam);

The OnMessage() line we added is how we will "hijack" the messages being sent to the OS, and the return line is the code that actually sends the messages onward to the OS.

Trapping the Necessary Messages

Manually add the OnMessage() method to your dialog's class (make sure you put the declaration in the .h file). The method should have code similar to the following:

LRESULT CPaperplusDlg::OnMessage(CWnd* wnd, UINT msg, WPARAM wp, LPARAM lp)
{
    switch(msg)
    {
        case WM_MOVING : OnMoving(wnd, (LPRECT) lp); break;
        case WM_ENTERSIZEMOVE : OnEnterSizeMove(wnd); break;
    };
    return 0;
}

Handling the Trapped Messages

As you can see, the two messages we need to trap are the WM_MOVING message (which indicates that the user is currently moving the window), and the WM_ENTERSIZEMOVE message (which indicates that the window has just entered the modal loop that deals with the window moving). We will route these two messages through their respective methods (which we will write) shown in the code above; OnMoving() for the moving message, and OnEnterSizeMove() for the other one. To begin, manually add the OnEnterSizeMove() method, which should look like the following:

void CPaperplusDlg::OnEnterSizeMove(CWnd* wnd)
{
    POINT cur_pos;
    CRect rcWindow;
    wnd->GetWindowRect(rcWindow);
    GetCursorPos(&cur_pos);
    cx = cur_pos.x - rcWindow.left;
    cy = cur_pos.y - rcWindow.top;
}

Again, don't forget to add the declaration of this method to the class .h file. This code first gets the rectangle associated with the window we want to move (in this example the window is our program's main dialog). It then gets the mouse cursor position, and stores the x and y values in two member variables, cx and cy. Declare two variables in your class .h file that look like this:

int cx;
int cy;

Now that we have that method taken care of, and the mouse coordinate member variables are in place, we can add the OnMoving() method:

BOOL CPaperplusDlg::OnMoving(CWnd* wnd, LPRECT rect)
{
    POINT cur_pos;
    RECT wa;
    GetCursorPos(&cur_pos);
    OffsetRect(rect, cur_pos.x - (rect->left + cx),
               cur_pos.y - (rect->top + cy));
    SystemParametersInfo(SPI_GETWORKAREA, 0, &wa, 0);

    if(isClose(rect->left, wa.left))
        OffsetRect( rect, wa.left - rect->left, 0 );
    else if(isClose(wa.right, rect->right))
        OffsetRect( rect, wa.right - rect->right, 0 );

    if(isClose(rect->top, wa.top))
        OffsetRect(rect, 0, wa.top - rect->top);
    else if(isClose(wa.bottom, rect->bottom))
        OffsetRect(rect, 0, wa.bottom - rect->bottom);

    return TRUE;
}

Before we discuss the previous block of code, there is one point of confusion that should be cleared up. You may have noticed that we removed the values "cx" and "cy" in the OnEnterSizeMove() method and are adding them back in this method. Clearly this seems like a wasted effort, right? Wrong! These calculations are necessary to "un-snap" the window from the edge of the screen. If you play around with the code by commenting out the first OffsetRect() call above, you will notice that the window does some strange stuff.

Testing the Window's Location

The OnMoving() block of code is a bit more involved. We are getting the cursor position again (as we move the window), and are offsetting the main dialog rectangle appropriately. We then get the rectangle of the "work area" (using the SystemParametersInfo() call), which basically retrieves the rectangle that is defined by the screen edges (and the start bar edge). We then call a method that we have yet to write, isClose(), which basically just tests to see if we've gotten close enough to snap to the edge of the screen. Let's now take a look at that method:

BOOL CPaperplusDlg::isClose(int a, int b)
{
    return abs(a-b) < m_SnapWidth;
}

This method is very simple. All it does is return whether or not the difference in the passed parameters is less than the width that we want to snap by. As you can see in the code example above, I use a variable in my Paper Plus program that allows the user to set that snap distance: m_SnapWidth. But any value can be used here (so you can hard-wire a value in, without allowing the user a choice of a snap distance).

Now you're done! That wasn't too bad, was it? Just make sure you have all the function declarations in place, and you should be set to go. Test out the snapping, and see how well it works.

System Menu Icon Bug

Apr 12, 2008
This article was written back when Visual Studio 6 was new. As such, I cannot guarantee that the content of this article still applies in modern Windows environments.

Within the code generated by Microsoft's MFC AppWizard, is a means of displaying the system menu icon (the little icon that appears in the upper left corner of all Windows programs). This code contains an annoying bug, however. The 16x16 icon you create for your project does not get used at all! Instead, Windows squishes the 32x32 icon down to fit into a 16x16 region, often times producing nasty results. So how can we fix this problem? First, we need to make a change in our main dialog's class definition (.h) file. Here is the line in question:

HICON m_hIcon;

Replace that line of code with the following two lines:

HICON m_hIcon32x32;
HICON m_hIcon16x16;

Note that we are essentially creating two icon handles instead of the one which we had before. As you can see from their names, one will be for our 32x32 icon, while the other one will be used for our 16x16 icon. Now that we have those two handles available, we need to change the code responsible for actually loading the icon. This code can be found in the constructor for your application's main dialog (in the class .cpp file):

m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);

The LoadIcon() method only deals with icons which are SM_CXICON pixels wide by SM_CYICON pixels high, which almost always results in icons of size 32x32. Since this method uses these system constants (thereby displaying the wrong icon), we should make use of the LoadImage() method instead. Change the line of code above into the following two lines:

m_hIcon32x32 = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
m_hIcon16x16 = (HICON)LoadImage(AfxGetInstanceHandle(),
                MAKEINTRESOURCE(IDR_MAINFRAME), IMAGE_ICON,
                16, 16, LR_DEFAULTCOLOR);

First, we load up the 32x32 icon using the same LoadIcon() method. Then we load the 16x16 icon using the more robust LoadImage() method. Once both icons have been loaded, we must fix two other problems. The first is located in the default OnPaint() method. In the automatically generated code, you will find the following two lines:

// Draw the icon
dc.DrawIcon(x, y, m_hIcon);

Change the m_hIcon variable in that method call to m_hIcon16x16 instead. The result will look like this:

// Draw the icon
dc.DrawIcon(x, y, m_hIcon16x16);

Likewise, we need to change the icon name in the OnQueryDragIcon() method. The m_hIcon variable will again become m_hIcon16x16, resulting in the following code:

return (HCURSOR) m_hIcon16x16;

Once these changes have been made, your small icon will appear properly, and the larger icon will also be used as it should. A simple solution to an annoying problem!

This article was written back when Visual Studio 6 was new. As such, I cannot guarantee that the content of this article still applies in modern Windows environments.

The UpdateCommandUI functionality is great for disabling menu items when data is set to a certain value. Adding this functionality to a dialog based application is a little tricky, but it can be done.

We first need to add the OnInitMenuPopup() member function to our dialog box class. Here is the code that should get generated by default for this event:

void CTestDlg::OnInitMenuPopup(CMenu* pPopupMenu, UINT nIndex, BOOL bSysMenu)
{
    CDialog::OnInitMenuPopup(pPopupMenu, nIndex, bSysMenu);
}

Adding the Necessary Code

We need to change this code, so delete (or comment out) the single line of code in this function, replacing it with the following:

ASSERT(pPopupMenu != NULL);
CCmdUI state; // Check the enabled state of various menu items
state.m_pMenu = pPopupMenu;
ASSERT(state.m_pOther == NULL);
ASSERT(state.m_pParentMenu == NULL);

// Is the menu in question a popup in the top-level menu? If so,
// set m_pOther to this menu. Note that m_pParentMenu == NULL
// indicates that the menu is a secondary popup.

HMENU hParentMenu;
if(AfxGetThreadState()->m_hTrackingMenu == pPopupMenu->m_hMenu)
    // Parent == child for tracking popup.
    state.m_pParentMenu = pPopupMenu;
else if((hParentMenu = ::GetMenu(m_hWnd)) != NULL)
{
    CWnd* pParent = this;
    // Child windows don't have menus--need to go to the top!
    if(pParent != NULL && (hParentMenu = ::GetMenu(pParent->m_hWnd)) != NULL)
    {
        int nIndexMax = ::GetMenuItemCount(hParentMenu);
        for (int nIndex = 0; nIndex < nIndexMax; nIndex++)
        {
            if(::GetSubMenu(hParentMenu, nIndex) == pPopupMenu->m_hMenu)
            {
                // When popup is found, m_pParentMenu is containing menu.
               state.m_pParentMenu = CMenu::FromHandle(hParentMenu);
               break;
            }
        }
    }
}

state.m_nIndexMax = pPopupMenu->GetMenuItemCount();
for(state.m_nIndex = 0; state.m_nIndex < state.m_nIndexMax; state.m_nIndex++)
{
    state.m_nID = pPopupMenu->GetMenuItemID(state.m_nIndex);
    if(state.m_nID == 0)
        continue; // Menu separator or invalid cmd - ignore it.
    ASSERT(state.m_pOther == NULL);
    ASSERT(state.m_pMenu != NULL);
    if(state.m_nID == (UINT)-1)
    {
        // Possibly a popup menu, route to first item of that popup.
        state.m_pSubMenu = pPopupMenu->GetSubMenu(state.m_nIndex);
        if(state.m_pSubMenu == NULL ||
          (state.m_nID = state.m_pSubMenu->GetMenuItemID(0)) == 0 ||
           state.m_nID == (UINT)-1)
        {
            continue; // First item of popup can't be routed to.
        }
        // Popups are never auto disabled.
        state.DoUpdate(this, TRUE);
    }
    else
    {
        // Normal menu item.
        // Auto enable/disable if frame window has m_bAutoMenuEnable
        // set and command is _not_ a system command.
        state.m_pSubMenu = NULL;
        state.DoUpdate(this, FALSE);
    }

    // Adjust for menu deletions and additions.
    UINT nCount = pPopupMenu->GetMenuItemCount();
    if(nCount < state.m_nIndexMax)
    {
        state.m_nIndex -= (state.m_nIndexMax - nCount);
        while(state.m_nIndex < nCount &&

              pPopupMenu->GetMenuItemID(state.m_nIndex) == state.m_nID)
        {
            state.m_nIndex++;
        }
    }
    state.m_nIndexMax = nCount;
}

The code presented above comes from the MFC Documentation, and is adapted from CFrameWnd::OnInitMenuPopup() in WinFrm.cpp. With this code in place, you can now call the UpdateCommandUI interface for an embedded menu control. A handy addition to any dialog box that contains a menu.

This article was written back when Visual Studio 6 was new. As such, I cannot guarantee that the content of this article still applies in modern Windows environments.

Enabling tool tips on dialog controls can be extremely useful, especially when a large number of controls are used. In this scenario, they could be used to describe what a particular control does, as well as any corresponding shortcut keys. Adding this feature is a relatively simple procedure. First we need to enable the tool tips by calling the following line of code in our OnInitDialog() method:

EnableToolTips(TRUE);

This function simply enables the tool tip control in our dialog window. We now need to add the following line of code to the message map in our dialog class. Note that the text OnToolTip in the code below is the name of the method that will be associated with this message. Feel free to change the name of this method if you like. For the purposes of this article, we will stick with using OnToolTip as the method name.

ON_NOTIFY_EX(TTN_NEEDTEXT, 0, OnToolTip)

Handling the Tool Tips

Next, add the OnToolTip() method declaration to the dialog class header file:

afx_msg BOOL OnToolTip(UINT id, NMHDR* pTTTStruct, LRESULT* pResult);

After we have declared this method, we need to add its code to the dialog class CPP file. The definition for this method is shown below:

BOOL CYourDlg::OnToolTip(UINT id, NMHDR* pNMHDR, LRESULT* pResult)
{
    TOOLTIPTEXT *pTTT = (TOOLTIPTEXT*)pNMHDR;
    UINT nID = pNMHDR->idFrom;
    if(pTTT->uFlags & TTF_IDISHWND)
    {
        // idFrom is actually the HWND of the tool
        nID = ::GetDlgCtrlID((HWND)nID);
        if(nID)
        {
            pTTT->lpszText = MAKEINTRESOURCE(nID);
            pTTT->hinst = AfxGetResourceHandle();
            return(TRUE);
        }
    }
    return(FALSE);
}

Now that our code is in place, we need to add the tool tip strings to the string table resource. In the resource editor, open the string table (insert a new one if your project doesn't already have a string table). Now add one string for each control that you want to show a tool tip. Each string's value should be set to the text that you want to appear in the tool tip. Most importantly, each string's ID should be set to the same ID of the corresponding control.

This article was written back when Visual Studio 6 was new. As such, I cannot guarantee that the content of this article still applies in modern Windows environments.

To add accelerator key functionality to a dialog based program, we first must insert an accelerator resource into the project. In Visual Studio, this can be accomplished by right clicking the top-most folder in the resource view, and selecting the Insert... option. Make sure you give this new resource a unique ID. For this example, I will be using an ID value of IDR_MY_ACCELERATOR.

Next, we need to add a member variable to our main dialog class. This variable is a handle to the accelerator table, and will be used several places later on. It should look like the following:

HACCEL m_hAccel;

Loading the Table

Now add the following lines of code to the OnInitDialog() function. As you can see, we initialize our accelerator table handle by calling the LoadAccelerators() method. Also, note that the text IDR_MY_ACCELERATOR in the code below should be the ID that you gave to your accelerator resource.

// Load accelerator keys...
m_hAccel = ::LoadAccelerators(AfxGetInstanceHandle(),
                              MAKEINTRESOURCE(IDR_MY_ACCELERATOR));
ASSERT(m_hAccel);

Translating the Accelerator Keys

Now, add the PreTranslateMessage message to your main dialog class. Then place the following lines of code in the newly created method:

if(m_hAccel)
{
    if(::TranslateAccelerator(m_hWnd, m_hAccel, pMsg))
        return(TRUE);
}

return CDialog::PreTranslateMessage(pMsg);

All that's left at this point is to add the key mappings to the accelerator resource that we inserted into the project at the beginning. Make sure you give each accelerator key the same ID as the command you want the key to execute.

As an example, let us assume that our program has a “Refresh View” menu item. The corresponding ID value is ID_VIEW_REFRESH, and we want the [F5] key to mimic this menu command. To do this, we simply add the [F5] key to the accelerator resource and set its ID value to ID_VIEW_REFRESH, just as it is for the menu item. Now, whenever the [F5] key is pressed, the “Refresh View” menu item will be invoked, just as we want it to be.

A Cleaner MRU List

Apr 12, 2008
This article was written back when Visual Studio 6 was new. As such, I cannot guarantee that the content of this article still applies in modern Windows environments.

The default means of adding a most recently used (MRU) list to a program is through a call to the LoadStdProfileSettings() method. One call to this method takes care of creating the MRU and populating it with stored data. But this method call contains one minor annoyance: a single query to a registry value that may or may not already exist under your program's registry branch. Should the latter scenario be the case (the registry item does not exist), it's parent branch will be created; regardless of whether or not you will ever use it. The branch that gets created is entitled "Settings", which can be a useful place to store your program specific registry values. But suppose you would rather use other branch names and never have the "Settings" heading appear in your program's registry branch? The solution is surprisingly easy. Our first course of action is to remove the call to the LoadStdProfileSettings() method. This call is usually found in your application's InitInstance() method. The simplest way to remove the call is to simply comment it out.

Creating the Most Recently Used List

The next step involves a call to the RecentFileList class constructor. A "hidden" variable exists in your CWinApp derived class (the same class that included the InitInstance() method), and has the name m_pRecentFileList. This variable is simply a pointer to a CRecentFileList object. So we can create a new one with a call like the following (add this code to your InitInstance() method):

CString strSection = "Recent File List";
CString strEntryFormat = "File%d";

m_pRecentFileList = new CRecentFileList(0, strSection, strEntryFormat, 4);

The constructor here takes four parameters: the first is an offset for the numbering in the most recently used list. It's best to just leave this value at 0. Next is a string which points to the name of the section in the registry where the MRU is read and written. In this example, I've used the default "Recent File List" heading. The third parameter given to the constructor points to a format string, which is used in creating the names of the entries stored in the registry. Here, I've used the value "File%d". So, as files get added to the MRU list, they will follow the format of "File0", "File1", etc. The final parameter is the number of files to keep track of. As you can see, I've used a value of 4 in this example. This value must range between 0 and 16 (inclusive), with a value of zero meaning that no MRU list should be kept at all.

Reading the List

One final call is needed to do the actual reading of the stored values. It should look like the following and, again, be placed in the InitInstance() method, right after the code we inserted above:

m_pRecentFileList->ReadList();

Once this call has been made, you will be able to use your MRU list just as in any other program. There is no need to worry about saving the values, as the application will take care of that for you when it closes. Pretty nice!