While working on a Windows batch script earlier today, I ran across an interesting side effect of the call and exit commands. Let's take this simple example, which we'll name script_a.bat:

@echo off
SETLOCAL

call :function
cd %SOME_PATH%

goto :functionEnd
:function
    set foobar=1
    if "%foobar%" == "1" exit /B 1
    goto :EOF
:functionEnd

Unlike Bash, Windows batch files have no function capabilities. Clever hacks like the above can be used to fake out functions, but these hacks hide some subtle quirks. You see that exit call within the 'function'? It only gets called if the %foobar% variable is equal to 1 (which is always the case, in our example). Also note that we exit with an error code of 1. So, in short, this script should always return an exit code of 1. Now, let's create another batch script which we'll name script_b.bat:

@echo off

call script_a.bat
echo Exit Code = %ERRORLEVEL%

This second script is very simple. All we do is call script_a.bat, and then print its resulting return code. What do you expect the return code to be? One would expect it to be 1, but it's not! Our second script will actually print out Exit Code = 0. Why is this?

The answer lies in the call command. Again, unlike Bash scripts, stand-alone batch files do not create their own context when executed. But if you use the call command, the thing you call does get its own context. How weird is that? So, let's trace the first script we wrote to figure out where the error code gets changed.

After some initial setup, we call our function (call :function). Inside our function, we create a variable, initialize it to 1, then test to see if the value is 1. Since the value is indeed 1, the if test succeeds, and the exit command is called. But we don't exit the script; instead, we exit the context that was created when we called our function. Note that immediately after we call our function, we perform a cd operation. This line of code gets executed, succeeds, and sets the %ERRORLEVEL% global to 0.

In order to exit properly, we have to exit our initial script twice, like this:

@echo off
SETLOCAL

call :function
if "%ERRORLEVEL%" == "1" exit /B 1

cd %SOME_PATH%

goto :functionEnd
:function
    set foobar=1
    if "%foobar%" == "1" exit /B 1
    goto :EOF
:functionEnd

See the new exit call after our initial function call? Then, and only then, will our second script print out what we expected. This subtle behavior stymied me for several hours today; hopefully this short post will help someone else avoid this frustration.

The season finale for The Office was aired last night, and it really threw me for a loop. A lot of what I expected to happen didn't, and some surprising twists and turns occurred throughout the entire episode. Some of my predictions are coming to fruition, though others may be increasingly off the mark. Like before, there are spoilers ahead so be forewarned.

Jim and Pam
I'm surprised that the Jim-Pam storyline took a turn at the end, with Jim failing to propose to Pam (as I expected would happen). Hopefully we'll see the couple take this step at the beginning of next season. I'm not sure I can take the tension any longer!
Jim taking career risks
Jim's two voice-mails to Ryan don't bode well for the future of his career. If Ryan is able to dodge his current criminal problems, Jim could be in trouble.
Ryan gets his comeuppance
This prediction came true, though not in the way I expected. It seemed to me that Ryan's drug problem would catch up to him, but it turns out that his business fraud was what got him into trouble with the law.
Michael grows up
For the first three-quarters of this episode, Michael was maturing at an incredible pace. His behavior around Holly, the new HR rep, was remarkable. Unfortunately, he relapses after finding out Jan is pregnant. I truly hope he ditches Jan and doesn't fall back into his old ways.
Angela, Andy, and Dwight
I certainly didn't see the Angela-Andy proposal coming. And what's up with Angela and Dwight at the very end of the episode? Who knew that they have apparently been seeing one another? This storyline should be very interesting to follow.
Holly and Kevin
I absolutely loved the running joke where Holly thought that Kevin was slow-minded. The entire gag was genuinely funny, and Brian Baumgartner did an amazing job as Kevin (this is his best performance so far in this show).

What did you think of the finale? Are you as excited about next year's season as I am?

Kensington Digital FM Transmitter

Back in November, I picked up a Kensington Digital FM Transmitter for my iPod Classic. And on my way back from Dustin and Sarah's wedding yesterday (congratulations, you guys!), it occurred to me that I hadn't yet posted a review of the device.

Having never used an FM transmitter before, I was unsure about the reception quality. Thankfully, this specific Kensington model is top notch. It's rare that I encounter static, and I've noted that it most often happens when driving under a particularly large overpass. The audio quality is excellent, though I find that I have to turn up the volume on my car stereo a little higher than I normally would with the corresponding audio CD. This might simply be related to the compression that MP3's provide, but it's a minor nuisance.

The unit provides 3 preset buttons, which is very useful to lock in multiple unused stations. This feature was really handy when I went to the mountains last Thanksgiving; one of the preset stations I was using in the RTP area was being used in the Asheville area, and switching was simply a matter of pressing a button (and then tuning to the right place on the receiver).

While your iPod is attached, the unit charges the battery. Unfortunately, there's no option to not charge the battery, which would be useful for battery conditioning purposes. I don't use this unit every day, so this minor problem doesn't impact me as much. One other minor annoyance is that the iPod-style connector doesn't lock into place. This makes it much easier for the cable to fall out, though I have only seen this happen a time or two.

Overall, I really like this unit. According to the Amazon product page, this particular model is being phased out and replaced by the Kensington LiquidFM Transmitter, which has much lower reviews. If you want one of these models, I suggest picking it up ASAP. You will not be disappointed.

Crysis Mini-Review

May 11, 2008

I completed Crysis yesterday, so I thought I would post a few quick thoughts on my experience:

What I Liked

  • As can be expected, the graphics in this game are insane. The jungle atmosphere is spot-on, physics are incredible, lighting is superb, and character modeling is excellent. Visually, this game is a real treat to play.
  • The nano-suit which the player wears is an interesting game play mechanic. 'Health' is handled via the nano-suit, and the abilities which the suit provides (speed, strength, armor, and cloak) are fun to play with.
  • Level design is excellent. All of the locations feel incredibly realistic, and there's a ton to explore. I'll probably play through the game once more just to fully explore each map, because I know there's a ton of stuff I missed.

What I Didn't Like

  • Crysis isn't near as long as Far Cry, which really disappointed me. Likewise, the maps in Crysis aren't as large either.
  • Crysis has an incredibly weak story line, much like Far Cry did. This comes as no surprise, especially in the FPS genre, but I was hoping for a little more meat than what I was given.
  • Again, just like Far Cry, the game turns into a battle against alien forces. I really enjoyed battling the human forces in the early parts of this game, and I wish Crytek had stuck to that theme. The later alien-based levels are, for the most part, not very fun. Why can't we get a game that uses this style engine and doesn't devolve into a 'save-the-world-from-alien-attack' kind of story? Perhaps Far Cry 2 will provide the kind of experience I'm looking for.
  • Difficulty is really uneven. I played through on the 'Normal' difficulty, and found myself stuck at a few places (though I never got stuck permanently). Some battles are surprisingly easy, while others are incredibly hard to survive.
  • Some of the vehicles the player gets to drive are woefully difficult to maneuver, which is frustrating. I'm not entirely sure why this is, because the vehicles in Far Cry were a pleasure to drive.
  • This game is a system hog. I played at 1280x1024 on the High setting (no anti-aliasing or anything fancy), and there were still a few moments where things really chugged (entering and exiting buildings especially). Overall my experience was smooth, but these moments of stuttering killed some of the immersion factor.

I'm not sure if I can recommend this game or not. It's worth playing through for the eye candy, but some of the game's frustrations cancel out that fun. My final verdict for Crysis? C+

A recent Lifehacker article on the Top 10 Tools to Get Blogging Done featured my oh-so-handy CoLT extension. Unfortunately the article linked to the old URL, for which I failed to provide a redirect. This problem is now fixed, and the broken URL now redirects to the new location.

You've got to tell me about this kind of stuff people!

Doing Credits Right

May 6, 2008

I just finished Half-Life 2: Episode Two again. Playing it through on my new computer was a real treat, and the ending of this episode still brings a tear to my eye. One thing jumped out at me for the first time on this play-through: the end credits. Valve has chosen to present the end credits in a short, yet elegant way. Every contributor is listed in alphabetical order by last name, with only a few notable exceptions (all the voice talent is singled out, for example). The result is a short, concise credits sequence that isn't a bore to sit through. Compare this with the end credits for Call of Duty 4, which go on for so long that a rap song is performed to break the monotony (I believe the end credits are on the order of 3 or 4 minutes long in that particular title).

I'd really like to see more end credit sequences patterned after Valve's design. Others may disagree, but knowing who did what on a project isn't very important. Listing each contributor in a single group levels the playing field and gives everyone an equal measure of thanks.

I'm a big fan of The Office, the comedy television show on NBC (hopefully everyone else is too). Last night's episode was great, and it provided a glimpse of what might be coming down the pipeline (there are only 2 episodes left this season). Here are a few predictions I have for where this series is headed; note the possible spoilers ahead:

Jim proposes to Pam
This seems pretty obvious, and I wouldn't be surprised if it happens during the last episode of this season (and on top of the Dunder-Mifflin building). The proposal will most likely occur either during or immediately after the events of my next prediction...
Jim to be fired (or transferred)
Ryan had some harsh words of warning for Jim last night, thanks to a spiteful Toby (who's scheduled to depart in the coming weeks). I predict that Jim will indeed either (a) be let go from the company, or (b) be transferred away from the Scranton branch, due to his 'wasting time' at work. The latter option in my opinion is most likely, and I predict that he gets transferred to the Dunder-Mifflin branch in Utica, which is now managed by his previous girlfriend Karen. Awkward situations are bound to follow, especially if Pam comes along for the ride (as Jim's fiancée).
Ryan gets his comeuppance
It's pretty obvious that Ryan is a jerk, and we've now learned that he has a drug problem. I predict that this will catch up to him, and he will fall from his current heights, providing him with some much needed payback.
Roy to return?
If we assume that Jim leaves the Scranton office, and Pam doesn't come along, his relationship with her will be subsequently strained. I predict that Roy will make another appearance (perhaps hired by Vance refrigeration?), and will cast some doubt in Pam's mind as to who she should be with.
Michael grows up
For the first time ever, we are seeing some growth in Michael. He finally stood up to Jan, and he showed much more maturity with Stanley in last night's episode. I think Michael will continue to mature, and the show will slowly stop revolving around his character (it's time to flesh out some of the other characters in the show).

NBC has ordered an entire season of The Office for next year, so I'm excited to see where things are headed. What are your predictions?

The long-awaited update to Team Fortress 2 was released Tuesday evening, so I've only had a brief amount of time to play around with Gold Rush, the map showcasing the new payload game type. In a word, the new game play theme is awesome. Gold Rush is much more enjoyable than Dustbowl (which I love, by the way), and I really hope more maps of this type are released over time.

Here's how Gold Rush works. A mine cart, carrying a Fat Man style bomb, must be moved from point A to point B through 3 different stages. The attacking team (BLU) moves the cart simply by standing near it; the more team members surround the cart, the faster it moves. If the cart is left unattended for 30 seconds, it begins to move backwards towards the starting point. The map is played just like Dustbowl; there are a total of 3 stages in which BLU attacks and RED defends. Once either BLU successfully attacks all stages, or RED successfully defends a stage, the teams swap sides and the stages are played again.

Gold Rush is definitely a tough map. The defending team almost always has a height advantage, making it difficult to make quick progress. A number of choke points slow things down even more. But like every other map in Team Fortress 2, things are very well balanced.

The other major change to the game comes through the new unlockable weapons and achievements. Medic is the only player class that currently supports these new features, so everyone is inevitably playing as a Medic. A total of 36 new achievements are available, and for every 12 that you unlock, a new weapon option becomes available. First up is "The Blutsauger," a syringe gun mod that leeches health from each enemy you hit. Next is "The Kritzkrieg," a medi-gun mod that gives the person you use it on a 100% chance of firing critical damage (instead of the standard Ubercharge). Finally, "The Ubersaw" bone-saw mod provides a 25% charge to your Ubercharge meter for every hit you make on an enemy. I've only gotten 4 out of the 36 achievements so far (Surgical Prep, Trauma Queen, Intern, and Group Health), so I've got quite a ways to go. Some of the achievements look impossible to get, so I doubt I'll make it all the way.

If you'd like to try out Team Fortress 2, along with the new updates, Valve is offering a free weekend this weekend (May 5-6). I can't recommend this game highly enough, and the Steam service is great, so check it out. If you do check it out, look for me; my username is jgbCodeMonkey.

Jonah’s Rig 3.0

Apr 29, 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.

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.