Tuesday, March 20, 2012

[ MFC ] - Restoring MainFrameWnd in VC++ using GetWindowPlacement and Registry


1. Introduction

In this article, I will show how can we preserve the application main window in terms of its size and position with respect to the desktop. The structure win32 structure WINDOWPLACEMENT will hold the information related to the window size and its position. We can fill this structure for a given window by making a call to GetWindowPlacement. The same way we can set the new position and size of the window using the SetWindowPlacement. OK, how do we preserve the window size and position? We should write this structure to the registry when the window is closed. And when the application is re-opened, we will restore the window state by reading the structure that written to the registry when the window was closed.

2. Creating the Sample App

You can apply the technique going to be explained in this article to any application. For now, we will do it for the SDI type of MFC Application. Create a new MFC Application. In the wizard, state you need SDI Application without document and view support. Accept the default for the rest of wizard steps. As we do not ask for the document and view support we will get the below specified initial classes.



The application is changed with a comment tag: Sample <xx>
Here xx is the step no. You can use find in files to search for the Text //Sample<One Blank Space Here>. Below is the result of changes made the Initial Wizard generated application.



3. Setting the Key
Search Tag: //Sample 01

The first step is setting a registry key name for the application. Go to the application class InitInstance function and make the change as shown below:

//Sample 01: Set the Value to the meaningful as given in the above comment
//SetRegistryKey(_T("Local AppWizard-Generated Applications"));
SetRegistryKey(_T("MsTechArticles"));

The key we set is called MsTechArticles. You can set a different key as you like. Usually in an organization environment, it will be usually the company name. In this same function, we are going to read the registry to restore the window state. But, now we will move to writing the registry.

Before we move on, go to the application property page (Right client project, chose property) and set the character set to Multibyte as shown below:


Why are we doing this? To avoid specifying the string literals like _T("MsTechArticles") as in the above piece of code. Once we specify the above property we can specify the string as you do in the VS 6.0.



4. Writing WINDOWPLACEMENT to registry
Search Tag: //Sample 02, //Sample 03, //Sample 04

Before start writing our task we should add a function that will get called when the application window is closed. To do that first we should provide the handler for the message WM_CLOSE. This message will be sent when the application window is closed. Below video shows how we add a handler for this message.

Video 1: Watch Here


1) First, we asked to write the toolbar state to the application registry. Remember we already specified our application key in the Sample 01 tag.

//Sample 02: Save the tool bar state to Registry
this->SaveBarState("TBAR STATE");

As MainFrame window already provides a function SaveBarState the job of saving the toolbar position and docking side become easy. But, to store the window position and state we need to write data registry ourselves.

2) OK. To write the data of window position and its size we need to open the registry key first. Now, we may come to a situation that the Key does not exist (For the first time) or it already exists (From Next time onwards). So what we do here is first try to open the specific key on a specific path. If it is not there, then create the one. The functions RegOpenKeyEx, RegCreateKeyEx are used to make the key ready before start writing the data.

//Sample 03: Open the Registry Key when Exists or Create the One
HKEY reg_key;
DWORD disposition;
if (RegOpenKeyEx( HKEY_CURRENT_USER, "Software\\MsTechArticles\\WindowPos",           0,         KEY_WRITE,       &reg_key   )!=        ERROR_SUCCESS )
{
RegCreateKeyEx( HKEY_CURRENT_USER, "Software\\MsTechArticles\\WindowPos",  0,"", REG_OPTION_NON_VOLATILE ,                                   KEY_ALL_ACCESS, NULL,  &reg_key,  &disposition);
}

In the above code first, we tried to open the Registry key Software\\MsTechArticles\\WindowPos under the root key HKEY_CURRENT_USER. If that key not available in the registry then we are creating the one. The first parameter is the Root key and next parameter (Second) specifies all the sub-keys. The option REG_OPTION_NON_VOLATILE specifies that we want to persist the information written between the login and logout of the system. Have look at the Picture below to have an understanding of the registry structure:



In the above piece of code, we are trying to open key till the subkey level only. We are not yet coming to the Value Key and Value like the one shown above.

3) We used functions to open, create the registry keys. Once the key is ready, we can place Value Key and actual value in the opened key. Have a look at the below code:

//Sample 04: Query the Window Position,size then store it in Registry
WINDOWPLACEMENT strucWP;
this->GetWindowPlacement(&strucWP);
::RegSetValueEx(reg_key, "WinPos", 0, REG_BINARY, (BYTE *) &strucWP, sizeof(WINDOWPLACEMENT));
::RegCloseKey(reg_key);

First, we used the GetWindowPlacement function to read the Window position and size with other details. The information retrieved is packed in the structure called WINDOWPLACEMENT. So this packed information then is written to the registry using the RegSetValueEx win32 API function call. From function parameter, we can see that the Value Key is WinPos, the actual value is the content of the WINDOWPLACEMENT structure in Byte stream format.

OK. Now, whenever the user closes the window, its position and size are written to the registry. We will move to reading this structure from the registry so that we can restore its size and position.

5. Read WINDOWPLACEMENT from Registry
Search Tag: //Sample 05, //Sample 06, //Sample 07, //Sample 08

1) In the Application class, a private function is added. This function will take care of restoring the window position by reading the window placement structure from the registry. Below is the declaration:

//Sample 07: Declaration
private:
            void ReStoreWindow();

2) Next, the state of the toolbar is restored by making use of the LoadBarState member function available in the CMainFrame. The perfect place to make a call to that function is OnCreate as we need to load the toolbar status when the Main application window is created. Below is the code:

//Sample 08: Load the Toolbar State saved in the OnClose Handler
this->LoadBarState("TBAR STATE");

3) The below function is written to restore the Main window when the application is opened.

//Sample 06: Restore the Window Position and size
void CWinPlacementSampApp::ReStoreWindow()
{
            //Sample 06_1: Declaration & Initialization
            UINT retvalue;
            HKEY reg_key;
            DWORD type = REG_BINARY;
            WINDOWPLACEMENT strucWP;
            DWORD size = sizeof(WINDOWPLACEMENT);

            //Sample 06_2: Read the WindowPlacement from Registry and restore the Window position when
            //                                                            it was closed.              
            retvalue = ::RegOpenKeyEx(HKEY_CURRENT_USER, "Software\\MsTechArticles\\WindowPos", 0, KEY_READ, &reg_key);
            if (retvalue == ERROR_SUCCESS )
                        retvalue = ::RegQueryValueEx(reg_key, "WinPos", 0, &type, (LPBYTE) &strucWP, &size);

            //Sample 06_3: When the read of WINDOWPLACEMENT no succeeded, go with the default showwindow
            if(retvalue == ERROR_SUCCESS)
                        m_pMainWnd->SetWindowPlacement(&strucWP);
            else
                        m_pMainWnd->ShowWindow(SW_SHOW);

}

Here, we used the RegOpenKeyEx function to open the Key. Once the key is opened, we read the value for WinPos using the RegQueryValueEx function call. When any of these calls fails, we can restore the Window and hence we go with the default ShowWindow function call of the member variable of CMainFrame. When these calls succeed, we have a valid WINDOWPLACEMENT structure with us. Supplying this structure to SetWindowPlacement will restore the window to its previous (When it was closed) position with the same size.

If you want to know more details about the parameters, use the MSDN (If you own one) or go with Google and it will bring you to online MSDN.

4) OK. The function for restoring the window is ready! Where is it used? It is used in the Application class’s InitInstance    function. In that function, the call to ShowWindow is commented out and a call to the function described in the previous point is made. Below is the code:

// The one and only window has been initialized, so show and update it
//Sample 05: Read the stored values from the Registry and set it=>
//pFrame->ShowWindow(SW_SHOW);
ReStoreWindow();


The test run for the attached sample is recorded as a video.

Video 2: Watch Here


Source Code : Download

No comments:

Post a Comment

Leave your comment(s) here.

Like this site? Tell it to your Firend :)