Click here to Skip to main content
12,298,927 members (51,828 online)
Click here to Skip to main content

Stats

207.4K views
1.5K downloads
53 bookmarked
Posted

Save Application Settings to XML

, 16 Sep 2002
An easy to use class that reads, writes and deletes app settings to an XML file.
// XMLSettings.cpp : implementation file
//

#include "stdafx.h"
#include "XmlSettingsTest.h"
#include "XMLSettings.h"

/////////////////////////////////////////////////////////////////////////////
// CXMLSettings

// get a long value
long CXMLSettings::GetSettingLong(CString cstrBaseKeyName, CString cstrValueName, long lDefaultValue)
{
	/* 
		Since XML is text based and we have no schema, just convert to a string and 
		call the GetSettingString method.
	*/
	long lRetVal = lDefaultValue;
	CString cstrDefaultValue;

	cstrDefaultValue.Format("%d", lRetVal);

	lRetVal = atol(GetSettingString(cstrBaseKeyName, cstrValueName, cstrDefaultValue));

	return lRetVal;
}

// get a string value
CString CXMLSettings::GetSettingString(CString cstrBaseKeyName, CString cstrValueName, CString cstrDefaultValue)
{
	int iNumKeys = 0;
	CString cstrValue = cstrDefaultValue;
	CString* pCStrKeys = NULL;

	// Add the value to the base key separated by a '\'
	cstrBaseKeyName += "\\";
	cstrBaseKeyName += cstrValueName;

	// Parse all keys from the base key name (keys separated by a '\')
	pCStrKeys = ParseKeys(cstrBaseKeyName, iNumKeys);

	// Traverse the xml using the keys parsed from the base key name to find the correct node
	if (pCStrKeys)
	{
		if (LoadXML())  // load the xml document
		{
			MSXML2::IXMLDOMElementPtr rootElem = NULL;
			MSXML2::IXMLDOMNodePtr foundNode = NULL;

			XmlDocPtr->get_documentElement(&rootElem);  // root node

			if (rootElem)
			{
				// returns the last node in the chain
				foundNode = FindNode(rootElem, pCStrKeys, iNumKeys); 
				
				if (foundNode)
				{
					// get the text of the node (will be the value we requested)
					BSTR bstr = NULL;
					foundNode->get_text(&bstr);

					cstrValue = (CString)bstr;

					if (bstr) { SysFreeString(bstr); bstr = NULL; }

					foundNode = NULL;
				}

				rootElem = NULL;
			}
		}

		UnloadXML();  // dump the XML document

		delete [] pCStrKeys;
		pCStrKeys = NULL;
	}

	return cstrValue;
}

// set a long value
long CXMLSettings::SetSettingLong(CString cstrBaseKeyName, CString cstrValueName, long lValue)
{
	/* 
		Since XML is text based and we have no schema, just convert to a string and 
		call the SetSettingString method.
	*/
	long lRetVal = 0;
	CString cstrValue = "";

	cstrValue.Format("%d", lValue);

	lRetVal = SetSettingString(cstrBaseKeyName, cstrValueName, cstrValue);

	return lRetVal;
}

// set a string value
long CXMLSettings::SetSettingString(CString cstrBaseKeyName, CString cstrValueName, CString cstrValue)
{
	/*
		RETURN VALUES:
		 0 = SUCCESS
		-1 = LOAD FAILED
		-2 = NODE NOT FOUND
		-3 = PUT TEXT FAILED
		-4 = SAVE FAILED
	*/

	long lRetVal = 0;
	int iNumKeys = 0;
	CString* pCStrKeys = NULL;

	// Add the value to the base key separated by a '\'
	cstrBaseKeyName += "\\";
	cstrBaseKeyName += cstrValueName;

	// Parse all keys from the base key name (keys separated by a '\')
	pCStrKeys = ParseKeys(cstrBaseKeyName, iNumKeys);

	// Traverse the xml using the keys parsed from the base key name to find the correct node
	if (pCStrKeys)
	{
		if (LoadXML())  // load the xml document
		{
			MSXML2::IXMLDOMElementPtr rootElem = NULL;
			MSXML2::IXMLDOMNodePtr foundNode = NULL;

			XmlDocPtr->get_documentElement(&rootElem);  // root node
			
			if (rootElem)
			{
				// returns the last node in the chain
				foundNode = FindNode(rootElem, pCStrKeys, iNumKeys, TRUE); 

				if (foundNode)
				{
					// set the text of the node (will be the value we sent)
					HRESULT hr = foundNode->put_text(_bstr_t(cstrValue));
					if (SUCCEEDED(hr))
					{
						if (!SaveXML()) { lRetVal = -4; }  // save the changed XML
					}
					else
						lRetVal = -3;

					foundNode = NULL;
				}
				else
					lRetVal = -2;

				rootElem = NULL;
			}
		}
		else
			lRetVal = -1;

		UnloadXML();  // dump the XML document

		delete [] pCStrKeys;
		pCStrKeys = NULL;
	}

	return lRetVal;
}

// delete a key or chain of keys
BOOL CXMLSettings::DeleteSetting(CString cstrBaseKeyName, CString cstrValueName)
{
	BOOL bRetVal = FALSE;
	int iNumKeys = 0;
	CString* pCStrKeys = NULL;

	if (!cstrValueName.IsEmpty()) cstrBaseKeyName += "\\" + cstrValueName;

	// Parse all keys from the base key name (keys separated by a '\')
	pCStrKeys = ParseKeys(cstrBaseKeyName, iNumKeys);

	// Traverse the xml using the keys parsed from the base key name to find the correct node.
	if (pCStrKeys)
	{
		if (LoadXML())  // load the xml document
		{
			MSXML2::IXMLDOMElementPtr rootElem = NULL;
			MSXML2::IXMLDOMNodePtr foundNode = NULL;

			XmlDocPtr->get_documentElement(&rootElem);  // root node

			if (rootElem)
			{
				// returns the last node in the chain
				foundNode = FindNode(rootElem, pCStrKeys, iNumKeys); 

				if (foundNode)
				{
					// get the parent of the found node and use removeChild to delete the found node
					MSXML2::IXMLDOMNodePtr parentNode = NULL;
					
					foundNode->get_parentNode(&parentNode);

					if (parentNode)
					{
						HRESULT hr = parentNode->removeChild(foundNode, NULL);
						if (SUCCEEDED(hr))
						{
							if (SaveXML()) bRetVal = TRUE;  // save the changed XML
						}

						parentNode = NULL;
					}

					foundNode = NULL;
				}

				rootElem = NULL;
			}

			UnloadXML();  // dump the XML document
		}

		delete [] pCStrKeys;
		pCStrKeys = NULL;
	}

	return bRetVal;
}

// Parse all keys from the base key name.
CString* CXMLSettings::ParseKeys(CString cstrFullKeyPath, int &iNumKeys)
{
	CString cstrTemp;
	CString* pCStrKeys = NULL;
	
	// replace spaces with _ since xml doesn't like them
	cstrFullKeyPath.Replace(' ', '_');

	if (cstrFullKeyPath.GetAt(cstrFullKeyPath.GetLength() - 1) == '\\')
		cstrFullKeyPath.TrimRight('\\');  // remove slashes on the end

	cstrTemp = cstrFullKeyPath;

	iNumKeys = cstrTemp.Remove('\\') + 1;  // get a count of slashes

	pCStrKeys = new CString[iNumKeys];  // create storage for the keys

	if (pCStrKeys)
	{
		int iFind = 0, iLastFind = 0, iCount = -1;
		
		// get all of the keys in the chain
		while (iFind != -1)
		{
			iFind = cstrFullKeyPath.Find("\\", iLastFind);
			if (iFind > -1)
			{
				iCount++;
				pCStrKeys[iCount] = cstrFullKeyPath.Mid(iLastFind, iFind - iLastFind);
				iLastFind = iFind + 1;
			}
			else
			{
				// make sure we don't just discard the last key in the chain
				if (iLastFind < cstrFullKeyPath.GetLength()) 
				{
					iCount++;
					pCStrKeys[iCount] = cstrFullKeyPath.Right(cstrFullKeyPath.GetLength() - iLastFind);
				}
			}
		}
	}

	return pCStrKeys;
}

// load the XML file into the parser
BOOL CXMLSettings::LoadXML()
{
	VARIANT_BOOL vbSuccessful;

	// initialize the Xml parser
	HRESULT hr = XmlDocPtr.CreateInstance(MSXML2::CLSID_DOMDocument);
	
	if (XmlDocPtr == NULL) return FALSE;

	// see if the file exists
	CFile file;
	if (!file.Open(cstrConfigFile, CFile::modeReadWrite))  // if not
	{
		// create it
		XmlDocPtr->loadXML(_bstr_t("<?xml version=\"1.0\" ?><XMLConfigSettings></XMLConfigSettings>"), &vbSuccessful);
	}
	else  // if so
	{
		file.Close();

		// load it
		XmlDocPtr->load(CComVariant::CComVariant((LPCSTR)cstrConfigFile), &vbSuccessful);
	}

	if (vbSuccessful == VARIANT_TRUE)
		return TRUE;  // loaded
	else
	{
		// an XML load error occurred so display the reason
		MSXML2::IXMLDOMParseErrorPtr pIParseError = NULL;
		XmlDocPtr->get_parseError(&pIParseError);

		if (pIParseError)
		{
			long value;
			BSTR bstr = NULL;

			HRESULT hr = pIParseError->get_errorCode(&value);
			pIParseError->get_reason(&bstr);

			CString cstrMessage;
			cstrMessage.Format("%s", (char *)_bstr_t(bstr, TRUE));

			if (!cstrMessage.IsEmpty()) 
			{
				MessageBox(NULL, cstrMessage, "LoadXML Malformed XML Error", MB_OK);
			}

			if (bstr) { SysFreeString(bstr); bstr = NULL; }

			pIParseError = NULL;
		}

		return FALSE;
	}	
}

// save the XML file
BOOL CXMLSettings::SaveXML()
{
	HRESULT hr = XmlDocPtr->save(CComVariant::CComVariant(cstrConfigFile));

	if (SUCCEEDED(hr))
		return TRUE;
	else
		return FALSE;
}

// find a node given a chain of key names
MSXML2::IXMLDOMNodePtr CXMLSettings::FindNode(MSXML2::IXMLDOMNodePtr parentNode, 
												  CString* pCStrKeys, int iNumKeys, 
												  BOOL bAddNodes /*= FALSE*/)
{
	MSXML2::IXMLDOMNodePtr foundNode = NULL;
	MSXML2::IXMLDOMElementPtr rootElem = NULL, tempElem = NULL;

	XmlDocPtr->get_documentElement(&rootElem);  // root element

	for (int i=0; i<iNumKeys; i++)
	{
		// find the node named X directly under the parent
		HRESULT hr = parentNode->selectSingleNode(_bstr_t(pCStrKeys[i]), &foundNode);

		if (FAILED(hr) || foundNode == NULL) 
		{
			// if its not found...
			if (bAddNodes)  // create the node and append to parent (Set only)
			{
				XmlDocPtr->createElement(_bstr_t(pCStrKeys[i]), &tempElem);
				if (tempElem) 
				{
					parentNode->appendChild(tempElem, &foundNode);

					// since we are traversing the nodes, we need to set the parentNode to our foundNode
					parentNode = NULL;
					parentNode = foundNode;
					foundNode = NULL;
				}
			}
			else
			{
				foundNode = NULL;
				parentNode = NULL;
				break;
			}
		}
		else
		{
			// since we are traversing the nodes, we need to set the parentNode to our foundNode
			parentNode = NULL;
			parentNode = foundNode;
			foundNode = NULL;
		}
	}

	rootElem = NULL;

	return parentNode;
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here

Share

About the Author

Jason Henderson
Architect
United States United States
I have been a professional developer since 1996. I live in the middle of no where in Illinois, USA. I am married and have four children.

You may also be interested in...

| Advertise | Privacy | Terms of Use | Mobile
Web01 | 2.8.160525.2 | Last Updated 17 Sep 2002
Article Copyright 2002 by Jason Henderson
Everything else Copyright © CodeProject, 1999-2016
Layout: fixed | fluid