Meta

Archive for April 2008

Last week, I rebuilt my computer again (making this the third machine I’ve run on since this site opened up). Here’s the list of what I upgraded in the system:

ABit IP35 Pro Motherboard

A great motherboard with passive coolers on the MCH, ICH, and PWM component areas. A very handy digital readout is available on the board to troubleshoot boot-up issues (no more beep codes), and the overall layout is excellent. However, I have a few minor problems with the board. The SATA connectors are angled at 90 degrees, making it very difficult to attach the cables from my drives. I’m currently using SATA ports 5 and 6, because ports 1 through 4 are physically inaccessible. Also, my wireless network card wasn’t happy with the board, and I had to switch back to my old one, which has lousy reception. I’m going to fix this problem in a novel way; stay tuned for further details.

Intel Core 2 Duo E8400 Wolfdale CPU @ 3.0 GHz

After much thought, I decided to go back to an Intel CPU (after using an AMD X2 4400+ for a while). The Intel chips are just faster right now, and they use less power, allowing them to run a little cooler. I had a difficult time getting the stock heat sink to snap into the mounting holes on the motherboard, causing the motherboard to bend around the CPU socket (which concerns me). I’ll probably spring for an after-market cooler at some point to remedy this situation.

2 GB of Geil DDR2 800 (PC2 6400) SDRAM

I’ve never used the Geil brand of memory before, but Anandtech recommended it on their last holiday buying guide, so I picked some up. The chips run at a default 5-5-5-15 timing, but they support 4-4-4-12. I made one attempt at overclocking the chips, but Windows wouldn’t boot, so I reverted back to the defaults. Changes in the default memory voltage is probably warranted here, which is something I failed to do on my first try. The copper-orange heat spreaders are very sexy.

BFG Tech GeForce 8800 GTS (G92) 512 MB

The 8800 GTS-512 line of graphics cards is based on the new nVidia G92 chipset (using a 65nm die). It’s notably faster than the 8800 GT, which is the card I had my eye on for a long time. The 8800 GTS has a larger cooler that’s not as loud, and it’s a beast of a graphics card. This is the first card I’ve had that takes up two slots on the motherboard, leaving me with only 2 out of the 3 available PCI slots. It runs a little warm (70 degrees Celsius at load), but I’m willing to live with that for now.

Corsair CMPSU-620HX 620W Power Supply

I was concerned that my old 500 W power supply wouldn’t have the juice required for this new build, so I bought this highly recommended Corsair model. It’s modular, which allows me to use as few cables as I need, and it’s incredibly quiet.

2 Seagate Barracuda SATA 250 GB Hard Drives

These Seagate drives are a little faster than my old ones, they’re 90 GB larger, and they have double the cache (16 MB).

I have been test driving this new setup with a few games. Call of Duty 4 looks amazing in high resolution with all the eye candy turned on, and it has elevated my opinion of the game. The Crysis demo ran great at 1280 x 1024 at the ‘High’ graphics setting, and I may be able to push it a little farther (I’ve ordered the full game, by the way; I’m very excited). Team Fortress 2 is thrilling at 1600 x 1200, and even Half-Life 2, which will be four years old later this year, looks great running at 1600 x 1200 with 4x FSAA. Half-Life 2: Episode 2 should be equally as nice.

The last few Firefox 3 nightly builds have changed the way SSL URI’s are displayed to the user. In Firefox 2, accessing a secure site results in a yellow background for the address bar (which I think is a particularly elegant solution). For reasons I don’t fully understand, Mozilla is getting rid of this implementation. In new Firefox 3 builds, the background of the ‘favicon’ will change depending on the security of the site. A blue background indicates an SSL secured site, while a green background indicates an EV SSL secured site. Moving the color to the favicon, in my opinion, makes things a little harder to understand. A heated debate about this inevitably appeared in the corresponding bug, and there will likely be more confusion over this in the future, as more public users begin to explore the Firefox 3 world. I fully expect an extension to ‘fix’ this feature, so all may not be lost. This is a very strange decision on Mozilla’s part, and it should be interesting to see what the end result is.

I am now an official WordPress contributor! While converting Born Geek to WordPress, I noted that the Movable Type importer did not obey the WordPress database case constraints. Movable Type posts export themselves as “Published” while WordPress expects the value to be lower case (“published”). After finding and verifying this bug, I wrote it up and submitted a simple patch, which was then accepted. The fix will be included in 2.5.1!

There’s a great article over at ShackNews offering a preview of the new Team Fortress 2 updates coming in the next week or two. In addition to discussing the new Goldrush game type, they also take a look at the brand new unlockable weapons for the Medic class. Some of the new weapons really sound exciting, and I’m really looking forward to this update.

I have (mostly) completed the conversion of this site from Movable Type to WordPress 2.5. As always, there will be plenty of tweaking over the next few days. If you find anything out of the ordinary, please leave a comment on this post, or contact me directly. I’ve done my best to prevent broken links on the site, but there’s always a chance I missed something.

Moving Born Geek to WordPress offers several benefits:

Articles and posts are easier to update.

Static content and news postings no longer need to be separated into multiple “blogs.”

Comment spam is much easier to manage (and prevent).

The administration interface is much nicer.

I have yet to turn on any WordPress caching, so please excuse any slow performance for the next few days. The WP-Cache plugin apparently has a few issues with WordPress 2.5, and I haven’t yet investigated the alternate WP Super Cache plugin.

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:

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:

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.

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.

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.

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:

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:

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.

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):

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:

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:

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:

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 16×16 icon you create for your project does not get used at all! Instead, Windows squishes the 32×32 icon down to fit into a 16×16 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 32×32 icon, while the other one will be used for our 16×16 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 32×32. 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:

First, we load up the 32×32 icon using the same LoadIcon() method. Then we load the 16×16 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!

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.

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.

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.

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):

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!