Updated date:

A Vc++ Mfc Example: Preserve Sdi Window Size and State

Author:

I am a software engineer. I have been working with C++, MFC, and .net technologies for 15 years. I like video games and reading books.

1. The Goal of Preserving the Window Size and Position

When we work on windows-based applications, we look at lot of window's elements like a menu, toolbar, status bar. The position and arrangement of one or more toolbars depends on the size of the window. Furthermore, one can also arrange the toolbar vertical or horizontal.

Let us say we arranged 7 of the toolbars in two rows on top of the window and in addition one toolbar in the left side. When we close and return to the application, all the toolbar states are gone. To avoid this, we have to preserve the windows position and size along with toolbar state while closing the application.

In this example, we will preserve the window size and its position relative to the desktop window using the WINDOWPLACEMENT structure. We will also use the SaveBarState function of CFrameWnd class to save the toolbar state.

2. The Default Behavior of the Application

First, create an SDI MFC application by accepting all the defaults in wizard. Run it, and drag the toolbar so that it appears in the left of the window. Then, resize the window and leave it towards the bottom left corner of the desktop. The window now looks as shown below:

Resized SDI Window

Resized SDI Window

When we reopen the application, the toolbar stays below the menu horizontally, and window does not stay near the start menu as shown above. Additionally, we will not see our resized window and by all means the customization that we did is lost. This is the default behavior of the MFC SDI application. OK, let us start the code change. We are going the write WINDOWPLACEMENT structure in the registry while closing the application. And when we open it again we read the registry to remember out last customization.

Video 1: Default behaviour of SDI Application – Does not preserve window position

3. Saving SDI Window State

3.1 Set an Application Key in the Registry

We are using the SetRegistryKey function of the CWinApp to create a Key Root for our example. In our case, we are creating HubPages as the Key. Now, have a look at the below code which is written in the InitInstance of CWinApp:

//Sample 01: Change registry key as HubPages
//SetRegistryKey(
//_T("Local AppWizard-Generated Applications"));
SetRegistryKey(_T("Hubpages"));

We are passing the HubPages as a string to the function SetRegistryKey and this will create a Key for us in the windows registry. The path is: HKEY_CURRENT_USER\Software\HubPages.

3.2 Save Toolbar and Window Position

We have our Registry entry ready. Now, we will save the Toolbar and window position into the registry under the sub-keys of HubPages. The correct time to preserve the window state to a registry is application closure. Add a handler for WM_CLOSE Message in the CMainFrame and this is where we will write our code to save the window state. In the below we show how to create OnClose Handler for the WM_CLOSE message.

Video 2: Adding WM_CLOSE Handler for CMainFrame

The empty handler added by the Visual Studio IDE is below:

void CMainFrame::OnClose()
{
	// TODO: Add your message handler code 
// here and/or call default
	CFrameWnd::OnClose();
}

3.2.1 Declaration required for Registry access

We need to declare some variables to access the registry. We declared Registry_Key as an HKEY or in simple terms a Registry Handle which tells key location in the registry for which we need access. The WINDOWPLACEMENT is C++ structure which we will write into the Registry. The code is below:

//Sample 02: Required Declarations
LONG Ret;
HKEY Registry_Key;
DWORD disposition;
WINDOWPLACEMENT sWindow_Position;

3.2.2 Save the Toolbar State

The function SaveBarState will create one or more sub-key under the "HubPages". In our example, we are creating "MainToolBar" as sub-key for storing the toolbar state. The code is below:

//Sample 03: Save the toolbar state with existing mainframe 
//functionality
SaveBarState(_T("MainToolBar"));

At this stage closing the application will create registry entries for string the Toolbar states. The Registry entries are shown in the below picture.

Application Key in Registry

Application Key in Registry

Do not get confused about the "PreservedWindowsPos" key as we will write code for that soon. The screenshot is taken after that code is executed once.

3.2.3 Save Window position

To save window position, first we need to create a registry key. From the previous section, we know that the Parent key in the Registry is HubPages. Now, we will create a sub-key called PreservedWindowPos and inside this key we will write our Window Position. The below code first checks the Registry entry and when it doesn’t find one, it will create a new Registry entry for Window Size and Window Position. Below is the code:

//Sample 04: Open the Registry and check for 
//key existence
Ret = RegOpenKeyEx(
	HKEY_CURRENT_USER, 
	_T("Software\\Hubpages\\PreservedWindowPos"),
	NULL,
	KEY_WRITE,
	&Registry_Key);

//Sample 05: The key will not exists for the very 
//first time and hence create
if (Ret != ERROR_SUCCESS)
{
	RegCreateKeyEx(
		HKEY_CURRENT_USER,
		_T("Software\\Hubpages\\PreservedWindowPos"),
		NULL, 
		NULL,
		REG_OPTION_NON_VOLATILE,
		KEY_ALL_ACCESS,
		NULL,
		&Registry_Key,
		&disposition);
}

Once, we have a valid Registry Key; we capture the Windows Size and Position in a structure called WINDOWPLACEMENT. The GetWindowPlacement Function will retrieve this information and it takes the WINDOWPLACEMENT structure as a parameter. After the call, we take the WINDOWPLACEMENT structure and writes that to the Registry. Below is the code:

//Sample 06: Get WindowSize and its position
GetWindowPlacement(&sWindow_Position);

//Sample 07: Write this Structure to Registry
RegSetValueEx(
	Registry_Key, 
	_T("PosAndSize"), 
	NULL, 
	REG_BINARY, 
	(BYTE *) &sWindow_Position, 
	sizeof(WINDOWPLACEMENT));
RegCloseKey(Registry_Key);

Note that while we close the window, its size and position are persevered into the registry. In the coming section, we will read this registry entry, create the structure for window placement and restore the window exactly as it was.

4. Loading Window Position and Size

Now, we have our window position and size in the registry. In this section, we will load those registry values and position the window in the same location while it was closed along with the preserved size.

1) In the below code, we are first restoring the toolbar state. The LoadBarState will load the toolbar settings from the registry and arranges the toolbar in the mainframe window. We added this code to the OnCreate Handler of the WM_CREATE Message.

// Now load the saved toolbar state
//Sample 08: Load the Toolbar State saved 
//in the OnClose Handler
this->LoadBarState(_T("MainToolBar"));

2) In the application’s InitInstance, we declare the variables required to read the registry and load the WINDOWPLACEMENT structure. Below is the code:

//9.1 Declarations
LONG Ret;
HKEY RegistryKey;
DWORD type = REG_BINARY;
WINDOWPLACEMENT sWP;
DWORD sizewp = sizeof(WINDOWPLACEMENT);

3) While closing the application, we stored the WINDOWPLACEMENT structure in the registry key called PreservedWindowPos and now we open that key by calling RegOpenKeyEx. The handle to this registry key is stored in HKEY variable RegistryKey. We use this handle to query the Window placement information written as a structure in binary format.

//Sample 9.2 Check Key Exits
Ret = RegOpenKeyEx(
	HKEY_CURRENT_USER, 
	_T("Software\\Hubpages\\PreservedWindowPos"), 
	0, 
	KEY_READ, 
	&RegistryKey);

//Sample 9.3: Read the Window Placement Structure
if (Ret == ERROR_SUCCESS )
	Ret = ::RegQueryValueEx(RegistryKey, 
	_T("PosAndSize"), 
	0, 
	&type, 
	(LPBYTE) &sWP, 
	&sizewp);

4) At this moment, we have the registry information read into the structure called "sWP" and we can use this to restore our window to the previous state. Note that when the registry read is a success, we call SetWindowPlacement by supplying the structure we read from the registry. Below is the code for it:

//Sample 9.4 Now show the window from preserved state
if(Ret != ERROR_SUCCESS)
	m_pMainWnd->ShowWindow(SW_SHOW);
else
	m_pMainWnd->SetWindowPlacement(&sWP);

You can look at the video below which shows how Windows is restored to it previous state between application sessions.

Video 3: Testing the Window Placement from Registry

© 2018 sirama