MFC Registry Basics
By: Kenn Scribner for TechTalk
|Run the demo program (all demos require Visual C++ 5.0!)|
|Download the demo source|
|Access the Registry using ActiveX control (ATL/VB)|
Background: I'd always been one to use the Win32 Registry API to access the Registry once I ditched .ini files. This is one case where MFC is so large it's just impossible to fully understand everything it offers at first glance. Imagine my surprise when I found out MFC could not only handle Registry access but also provided me with accessor functions that resembled the old initialization file calls! All that custom MFC code...wasted. :) Aw, not really wasted. See the next article!
Ripping Through the Registry
Part I, The Quick Tour
(Part II, MFC and the Registry)
I can't think of anything that terrifies Windows users more than the thought of diving into the Registry to change a value or fix a problem. Most folks I know would sooner chew their arm off and eat it before they would run "RegEdit" and take a look at Window's "memory center". I suppose they have a valid concern--one wrong move in the wrong key and Windows is as dead as Tyrannosaurus Rex. As developers, though, we not only don't have the luxury of fear, but we must also be intimately familiar with the Registry and be capable of easily navigating through its labyrinths. Why? The Registry is where we store "persistent" application values, deal with Component Object Model objects, register our application's file types, and in general control the way in which Windows operates. Without the Registry and your knowledge of its workings, your applications will remain very limited indeed.
So what is the Registry really, anyway? Well, in reality it's nothing more than a file (its name and location differ depending upon the operating system, Windows 95/3.1 or NT). The file contains all of the information Windows requires to run, as well as values individual applications supply for their own correct operations. But let's not think of accessing the Registry as accessing a file. Rather, the Registry looks to us to be more like a file system, where we have directories and files ("keys") and file contents ("values"). You access a particular piece of information by supplying a key. Give the Registry a key, and you'll get back the value associated with that key.
The values may take several forms, but the more common data representations are strings, "multiings", double words, and raw binary information. A string value consists of the familiar "C style" null-terminated character buffer. A multiple string value would include several null-terminated strings "glued" together with a final, additional null terminator appended to the end of the buffer. Double word values are just that--32 bit unsigned integers. And finally, raw binary information can be as much of anything you like (there are limitations as to how large the Registry will grow, but any reasonable buffer could be stored).
So what type of information would we be most likely to store in the Registry? Well, that's application-specific, but in general you will want to store persistent information from one application execution to another. This issue's demonstration program, for example, shows you the proper way to store the window's screen location so the next time the application runs, the window restores itself to the former location (more on that next issue--how this is done is not obvious). It also keeps track of some user options, which dictate how it redraws the client area--I needed something interesting to show in the demo!.
There is, shall I say, an etiquette issue when dealing with the Registry. Feel free to store what you need there, but if it's a "lot" of buffered data, store that data to a file. In other words, don't pollute the Registry with a lot of data that might be better kept in a file somewhere. Instead, use the Registry for storing persistent state information. The larger the Registry grows the longer Windows takes to boot. And remember there is an upper Registry size boundary (what that is and how you set it is operating system dependent).
Knowing what values can be stored and what types of things we might store, the next question must certainly be "Where will we store the information?" It's best to address this question by referring you to "Windows 95 Application Setup Guidelines for Independent Software Vendors" on the MSDN CD-ROM or MSDN Online (http://msdn.microsoft.com/developer/). To summarize, though, there are three areas we generally touch when we install an application. We will install our file associations in HKEY_CLASSES_ROOT (think of this as a subdirectory), our general/global application information in HKEY_LOCAL_SYSTEM, and any user-specific information, such as "preferences", "options", or "customizations", in HKEY_CURRENT_USER. Ignoring the file associations MFC might register for us, our MFC-based application information is usually stored in HKEY_CURRENT_USER. You may sometimes see the "ROOT" keys referred to as "hives" Im sure there is an interesting story behind that! Once you've determined the key, you store the value--very much like writing to a file whose "filename", so to speak, is the key.
That's the nickel tour of the Registry. Feel free to fire up "RegEdit.exe" and examine your Registry. I've seen many different versions of RegEdit, but the one I like the best is supplied with Windows 95 and Windows NT 4.0. In this version, you'll see a tree view of the Registry which servers to reinforce the file system metaphor. Go ahead and expand a few keys, dig through and find a few values, and see what they contain. Open the "Edit" menu and search for "(insert your favorite application title here)" and see where it pops up. The goal here is to explore the Registry and get comfortable with RegEdit.
Okay, so now that we know a little bit about the Registry, how can we take advantage of it? I'll separate the answer to that question into two parts. The first part, the "quick and easy MFC way" we'll discuss briefly in this issue and in more detail in the next. I'll save some slightly more advanced topics for a future issue.
In a nutshell, whenever you create a MFC application, you will be using the Registry by default. Surprised? What you probably have not been doing is using the Registry explicitly. MFC has been doing some behind-the-scenes work on your behalf. Now, though, it's high time to jump into the Registry yourself and make it work for you. If you look up CWinApp in the online help file you'll find it has a member function called SetRegistryKey() (this will be inserted into your SDI/MDI application source file by AppWizard insert it manually in a dialog-based application). SetRegistryKey() takes a single parameter--a string value which MFC will insert into a standard AppWizard key value HKEY_CURRENT_USER\Software\(parameter)\(project name)\. Ostensibly, this parameter will be your company name, though in the demonstration program I used the string "TechTalk". AppWizard also creates for you a "Settings" subkey by default, though you may or may not want to use it (I did for the demo). SetRegistryKey() establishes this key value when you use the CWinApp functions Get/SetProfileInt() and Get/SetProfileString(). These function names are holdovers from the "old" days when we used to deal with .ini files. Well look specifically at these member functions next time. For now, its more important to feel comfortable with the Registry keep working with RegEdit to examine your Registry, where things are, and what types of data are stored there. DO NOT change any values unless you really know what you are doing!
Until next time, have fun with RegEdit, and be sure to download the demo program linked above. Well be ripping into it next. Happy Registry spelunking!
Ripping Through the Registry
Part II, MFC and the Registry
Last issue, we took a quick tour of the Registry. We saw it appears to us as a "file system", where "keys" equate to directories and files, and "values" equate to the contents. We saw certain information is typically stored in certain places, such as file associations being stored in HKEY_CLASSES_ROOT. If youve examined this (and last) issues demonstration program, youve seen it store information under HKEY_CURRENT_USER.
So how did I determine where things were to be stored, and how did I store them there? Well, if were using MFC, MFC predetermined which key we were to use. CWinApp is the MFC class that handles Registry access, and as we saw last issue, it stores information in the key HKEY_CURRENT_USER\Software\(parameter)\(project name)\, where (parameter) is the parameter string given to CWinApp::SetRegistryKey(). This allows us to use the "old" .ini file functions Get/SetProfileInt() and Get/SetProfileString().
Using these functions from MFC, then, makes accessing the Registry is as easy as a single function call! For example, you'll find this line of code in the demo (CMainFrame::PreCreateWindow()):
cs.x = (int)pApp->GetProfileInt(_T("Settings"),_T("LeftWinCoord"),cs.x);
Here, I'm loading the window's previous left corner coordinate. What if this is the first time the application has been run? The Registry doesn't have this value, so what happens then? GetProfileInt()'s third parameter is a "default" value to use if there is no such value stored. You'll either get the default value or the value stored in the Registry--the function doesn't fail, which is nice. Later, when we close the demonstration program, we'll write its window coordinates to the Registry. From then on, you'll get the value from the Registry rather than the default value. The "Settings" and "LeftWinCoord" strings are combined with the key SetRegistryKey() created to form the actual Registry key:
Once you run and terminate the demonstration program the first time, you'll find this key using RegEdit. In general, you should strive to make your Registry keys readable, so other folks perusing the Registry will (hopefully) be able to determine to what your key refers in case they want to modify the value directly for some reason.
I generally try to "pack in" a little something extra with my demonstration programs, and for this issue I also show you the "proper" method for saving an application's window coordinates. A common mistake, I believe, is to simply query the window for its location using CWnd::GetWindowRect(). While GetWindowRect() will indeed return to you the current window rectangle, it will not give you any information about the window's state--minimized, maximized, hidden, or whatever. Not only that, but suppose the window is maximized when you restore the window using mere rectangle coordinates, you simply make it as large as the screen. You didn't really "maximize" it (check out the system buttons using Windows 95 or Windows NT 4.0), you just made a big window. Close, but no cigar.
A better method, and one that is not so obvious, is to use CWnd::GetWindowPlacement(). This function uses the WINDOWPLACEMENT structure, which is filled with not only the window coordinates, but also the window's current state. If the window state is maximized or minimized, then the window coordinates are what the "restored" window size was before it was maximized/minimized. By storing this information, the application will truly come back to life maximized or minimized, and if it does, it will "Restore" to its proper size, which is a size the user specified in some previous execution. Notice I sprinkled the code to do this in a couple of source files. In RegDemo1.cpp (CWinApp::InitInstance()), I added one line of code and modified another to restore the window's state. This:
changed to this:
UINT iShowCmd = GetProfileInt(_T("Settings"),_T("ShowCmd"),SW_SHOW); m_pMainWnd->ShowWindow(iShowCmd);
In MainFrm.cpp, I handle the storage and retrieval of the window coordinates in
CMainFrame::DestroyWindow() and CMainFrame::PreCreateWindow() respectively.
If this were a real application, we'd supply a method to clean up the Registry if the user uninstalled our application. Since this is a small demonstration program we don't really "install" it in the usual sense. Therefore, we can't really uninstall it by deleting the demo directory (or something like that) and know the Registry was cleaned up properly. I've provided a "clean" program that simply deletes the key we created in the demonstration. Similarly, you could open the Registry using RegEdit and delete the key that way. In either case, I recommend you delete the key after you're through with the demonstration program. Why fill the Registry with demonstration information?
That's about it for the "quick and easy" Registry access using MFC. Because I know how sharp you are, you've no doubt noticed an inherent limitation with this method and are asking yourself a basic, though exceptionally good question. It's that limitation and question I'll address in the next issue. Haven't figured out what that is? Well, then you'll have that much more to look forward to! Of course, I used Visual C++ version 5.0 to create the demonstration program. If you're using an older version, you'll need to recreate the project file and "insert" the demo's source files in order to recompile and play around. Definitely do that, though accessing the Registry properly is a major capability you must master to produce truly professional Windows applications. You can check out the demonstration source code at (see top of page). Until next time, enjoy, but always be careful if you modify Registry contents by hand!
Comments? Questions? Find a bug? Please send me a note!