Click here to Skip to main content
Click here to Skip to main content
Add your own
alternative version
Go to top

Import/Export registry sections as XML

, 21 Jan 2003
Export registry sections as XML to simplify registry diffs
registryasxml_demo.zip
ReleaseMFCdll
registryasxml.exe
Release
registryasxml.exe
registryasxml_src.zip
registryxml.dsp
registryxml.dsw
res
Icon_Anchor.jpg
Icon_Form.jpg
Icon_Frame.jpg
Icon_Media.jpg
Icon_Root.jpg
ImageList.bmp
ImageList256.bmp
registryxml.ico
RegistryxmlDoc.ico
TocImageList.bmp
Toolbar.bmp
registryxml.opt
registryxml.plg
registryxml.suo
registryasxml__demo.zip
registryasxml.exe
registryasxml.exe
registryasxml__src.zip
registryxml.dsp
registryxml.dsw
Icon_Anchor.jpg
Icon_Form.jpg
Icon_Frame.jpg
Icon_Media.jpg
Icon_Root.jpg
ImageList.bmp
ImageList256.bmp
registryxml.ico
RegistryxmlDoc.ico
TocImageList.bmp
Toolbar.bmp
#include "stdafx.h"
#include "registryxmllib.h"

long Helper_OpenKey(CString &szMainKey, CString &szPath, HKEY *pKey)
{
	if (pKey==NULL) return 1; // generic error

	*pKey = NULL;

	// open the key now
	long hr;

	if (szMainKey.CompareNoCase("HKEY_CLASSES_ROOT")==0)
	{
		hr = ::RegOpenKey(HKEY_CLASSES_ROOT, szPath, pKey);
	}
	else if (szMainKey.CompareNoCase("HKEY_CURRENT_USER")==0)
	{
		hr = ::RegOpenKey(HKEY_CURRENT_USER, szPath, pKey);
	}
	else if (szMainKey.CompareNoCase("HKEY_LOCAL_MACHINE")==0)
	{
		hr = ::RegOpenKey(HKEY_LOCAL_MACHINE, szPath, pKey);
	}
	else if (szMainKey.CompareNoCase("HKEY_USERS")==0)
	{
		hr = ::RegOpenKey(HKEY_USERS, szPath, pKey);
	}
	else if (szMainKey.CompareNoCase("HKEY_CURRENT_CONFIG")==0)
	{
		hr = ::RegOpenKey(HKEY_CURRENT_CONFIG, szPath, pKey);
	}
	else 
		return 1; // break here

	return hr;
}


void AddChildren(CTreeCtrl &tree, HTREEITEM hItem, CString &szPath)
{
	// build path from item name
	//
	if (hItem==NULL || tree.GetParentItem(hItem)==NULL) return;

	if (szPath.IsEmpty()) return;


	CString szMainKeyname = szPath;

	int nSlash = szPath.Find(_T("\\") );
	if (nSlash>-1)
	{
		szMainKeyname = szPath.Left( nSlash );
		szPath = szPath.Right( szPath.GetLength()-(nSlash+1));
	}
	else
		szPath.Empty();

	// open the key now
	HKEY hKey;
	long hr;

	hr = Helper_OpenKey(szMainKeyname, szPath, &hKey);

	if (hr==ERROR_SUCCESS) // it's ok
	{
		AddChildRegistryKeys(tree, hItem, hKey);

		::RegCloseKey(hKey);
	}

}



void AddChildRegistryKeys(CTreeCtrl &tree, HTREEITEM hParent, HKEY hKey) 
{ 
    
    DWORD    cSubKeys;                 // number of subkeys 
    DWORD    cbMaxSubKey;              // longest subkey size 
    DWORD    cchMaxClass;              // longest class string 
    DWORD    cValues;              // number of values for key 
    DWORD    cchMaxValue;          // longest value name 
    DWORD    cbMaxValueData;       // longest value data 
    DWORD    cbSecurityDescriptor; // size of security descriptor 
    FILETIME ftLastWriteTime;      // last write time 
 
    DWORD i; 
    DWORD retCode; 
 

    // Get the class name and the value count. 
    retCode = ::RegQueryInfoKey(hKey,        // key handle 
        NULL,					// buffer for class name 
        NULL,					// length of class string 
        NULL,                    // reserved 
        &cSubKeys,               // number of subkeys 
        &cbMaxSubKey,            // longest subkey size 
        &cchMaxClass,            // longest class string 
        &cValues,                // number of values for this key 
        &cchMaxValue,            // longest value name 
        &cbMaxValueData,         // longest value data 
        &cbSecurityDescriptor,   // security descriptor 
        &ftLastWriteTime);       // last write time 
 

	if (retCode!=ERROR_SUCCESS) return;

    // Enumerate the child keys, until RegEnumKeyEx fails. 

    CHAR  *achKey = new CHAR[cbMaxSubKey+1]; 
	if (!achKey) return;

    for (i = 0, retCode = ERROR_SUCCESS; retCode == ERROR_SUCCESS; i++) 
    { 
		DWORD achKeyMaxLength = cbMaxSubKey + 1;

        retCode = ::RegEnumKeyEx(hKey, 
                     i, 
                     achKey, 
                     &achKeyMaxLength, 
                     NULL, 
                     NULL, 
                     NULL, 
                     &ftLastWriteTime); 

		if (retCode == ERROR_SUCCESS && achKey && achKeyMaxLength>0)
		{
			achKey[achKeyMaxLength]=0; // force EOL
			HTREEITEM hNewItem = 
				tree.InsertItem(achKey, ILI_FOLDER, ILI_FOLDERS, hParent, TVI_LAST);

			// now try to guess if this item has children ?
			// - if yes, ten add a fake item, just to make sure it is expandable
			// - if no, ok good bye!
			if (hNewItem)
			{
				HKEY hSubkey;
				if (::RegOpenKey(hKey, achKey, &hSubkey)==ERROR_SUCCESS)
				{

					DWORD cChildSubKeys = 0;  

					if (::RegQueryInfoKey(hSubkey,  // key handle 
						NULL,					// buffer for class name 
						NULL,					// length of class string 
						NULL,                   // reserved 
						&cChildSubKeys,         // number of subkeys 
						NULL,					// longest subkey size 
						NULL,					// longest class string 
						NULL,					// number of values for this key 
						NULL,					// longest value name 
						NULL,					// longest value data 
						NULL,					// security descriptor 
						NULL)==ERROR_SUCCESS )					// last write time 
					{
						if (cChildSubKeys>0)
							tree.InsertItem(FAKEDITEM, ILI_DEFAULT, ILI_DEFAULT, hNewItem, TVI_LAST);
					}


					::RegCloseKey(hSubkey);
				}
			}

		}
    } 

	delete [] achKey;


} 


void AddRegistryValues(HKEY hKey, CPtrArray &arrValues)
{

    DWORD    cSubKeys;                 // number of subkeys 
    DWORD    cbMaxSubKey;              // longest subkey size 
    DWORD    cchMaxClass;              // longest class string 
    DWORD    cValues = 0;              // number of values for key 
    DWORD    cchMaxValue;          // longest value name 
    DWORD    cbMaxValueData;       // longest value data 
    DWORD    cbSecurityDescriptor; // size of security descriptor 
    FILETIME ftLastWriteTime;      // last write time 
 
    DWORD j; 
    DWORD retValue; 
 
    // Get the class name and the value count. 
    retValue = ::RegQueryInfoKey(hKey,        // key handle 
        NULL,					// buffer for class name 
        NULL,					// length of class string 
        NULL,                    // reserved 
        &cSubKeys,               // number of subkeys 
        &cbMaxSubKey,            // longest subkey size 
        &cchMaxClass,            // longest class string 
        &cValues,                // number of values for this key 
        &cchMaxValue,            // longest value name 
        &cbMaxValueData,         // longest value data 
        &cbSecurityDescriptor,   // security descriptor 
        &ftLastWriteTime);       // last write time 
 
    // Enumerate the child keys, until RegEnumKeyEx fails.

    CHAR  *achValueName = new CHAR[cchMaxValue+1]; 
	if (!achValueName) return;

    CHAR  *achValueData = new CHAR[cbMaxValueData+1]; 
	if (!achValueData)
	{
		delete [] achValueName;
		return;
	}
	
	CString szDefaultName = IDS_DEFAULTVALUENAME; // (Default)
	CString szDefaultValue = IDS_DEFAULTVALUEVALUE; // (value not set)

 // Enumerate the key values. 

	if (cValues && cValues!=-1 && retValue==ERROR_SUCCESS)
	{
		for (j = 0, retValue = ERROR_SUCCESS; j < cValues; j++) 
		{ 
			DWORD cchValueName = cchMaxValue + 1;
			DWORD cchValueData = cbMaxValueData + 1;
			DWORD dwType;

			achValueName[0] = _T('\0'); 
			achValueData[0] = _T('\0'); 

			retValue = ::RegEnumValue(hKey, j, achValueName, 
				&cchValueName, 
				NULL, 
				&dwType, 
				(LPBYTE) achValueData,
				&cchValueData);

			if (retValue == ERROR_SUCCESS) 
			{ 

				KeyValue *p = new KeyValue();
				if (!p) continue; //
				
				CStringArray a;

				if (cchValueName==0 && strlen(achValueName)==0)
				{
					if (cchValueData==0 && strlen(achValueData)==0)
						p->SetKeyValue( szDefaultName,
										szDefaultValue,
										dwType);
					else
						p->SetKeyValue( szDefaultName,
										CString (achValueData),
										dwType);
				}
				else
				{
					p->SetKeyValue( CString(achValueName),
									ConvertToString(dwType,achValueData,cchValueData),
									dwType );
				}


				arrValues.Add( p );
				
			} 
 
		}

	} // end if (cValues && cValues!=-1)


	// now sort all this
	//
	int nNbItems = arrValues.GetSize();
	BOOL bSorted = FALSE;
	while (!bSorted)
	{
		bSorted = TRUE;

		if (nNbItems<=1) break; // no need to sort trivial arrays

		int i=0;
		while ( bSorted && i<(nNbItems-1) )
		{
			KeyValue *pCur = (KeyValue*) arrValues.GetAt(i);
			KeyValue *pNext = (KeyValue*) arrValues.GetAt(i+1);
			if (pCur && pNext)
			{
				if (pCur->GetName().CompareNoCase(szDefaultName)==0)
				{
					i++;
					continue; 
				}

				if (pCur->GetName().CompareNoCase( pNext->GetName() ) >0 )
				{
					bSorted = FALSE;
					arrValues.SetAt(i,   pNext); // swap values
					arrValues.SetAt(i+1, pCur);
				}
			}

			i++;

		} // first-pass
	} // second-pass


	// make sure we have at least a default value to play with
	BOOL bFound = FALSE;
	int i = 0;
	while (!bFound && i<nNbItems)
	{
		KeyValue *p = (KeyValue*) arrValues.GetAt(i++);
		if (p)
		{
			bFound = ( p->GetName().Compare(szDefaultName)==0 );
		}
		
	}

	if (!bFound)
	{
		// this is a fake add, just to visually match what's shown in regedit.exe
		KeyValue *p = new KeyValue();
		if (p)
		{
			p->SetKeyValue( szDefaultName,
							szDefaultValue,
							REG_SZ);

			arrValues.InsertAt(0, p);
		}
	}

	

	delete [] achValueData;
	delete [] achValueName;

}




CString ConvertToString(DWORD dwType, LPTSTR szRawBuffer, DWORD nLen)
{
	CString s;

	// conversion from number to string
	if ( (dwType>=REG_BINARY && dwType<=REG_DWORD_BIG_ENDIAN) || 
			dwType==11 || 
			dwType==REG_RESOURCE_LIST || 
			dwType==REG_RESOURCE_REQUIREMENTS_LIST)
	{
		switch (dwType)
		{
			case REG_BINARY :
			case REG_RESOURCE_LIST :
			case REG_RESOURCE_REQUIREMENTS_LIST :
			{
				CString sByte;
				for (int i=0; i<(long)nLen; i++)
				{
					byte c = szRawBuffer[i];
					sByte.Format(_T("%02x"), c);
					if (!s.IsEmpty()) s += _T(" ");
					s += sByte;
				}
			}
			break;
			case REG_DWORD : // == REG_DWORD_LITTLE_ENDIAN
			{
				byte a = szRawBuffer[3];
				byte b = szRawBuffer[2];
				byte c = szRawBuffer[1];
				byte d = szRawBuffer[0];
				s.Format(_T("0x%02x%02x%02x%02x"), a, b, c, d);
				DWORD n = (a<<24) | (b<<16) | (c<<8) | d;
				CString sDword;
				sDword.Format(_T(" (%d)"), n);
				s += sDword;
			}
			break;
			case REG_DWORD_BIG_ENDIAN :
			{
				byte a = szRawBuffer[0];
				byte b = szRawBuffer[1];
				byte c = szRawBuffer[2];
				byte d = szRawBuffer[3];
				s.Format(_T("0x%02x%02x%02x%02x"), a, b, c, d);
				DWORD n = (a<<24) | (b<<16) | (c<<8) | d;
				CString sDword;
				sDword.Format(_T(" (%d)"), n);
				s += sDword;
			}
			break;
			case 11 : // QWORD, QWORD_LITTLE_ENDIAN (64-bit integer)
			{
				byte a = szRawBuffer[7];
				byte b = szRawBuffer[6];
				byte c = szRawBuffer[5];
				byte d = szRawBuffer[4];
				byte e = szRawBuffer[3];
				byte f = szRawBuffer[2];
				byte g = szRawBuffer[1];
				byte h = szRawBuffer[0];
				s.Format(_T("0x%02x%02x%02x%02x%02x%02x%02x%02x"), a, b, c, d, e, f, g, h);
			}
			break;
		}
	}
	else
	{
		if (dwType==REG_LINK)
		{
			// convert the Unicode string to local charset string

			char *temps = new char[nLen+1];

			int nActualLength = ::WideCharToMultiByte(CP_ACP,
					0,
					(wchar_t *)szRawBuffer,
					-1,
					temps,
					nLen,
					NULL,
					NULL);

			temps[nActualLength]= _T('\0'); // EOL

			s = temps;

			delete [] temps;
		}
		else if (dwType==REG_MULTI_SZ)
		{
			// a MULTI_SZ value is a set of strings separated by a 0 char, and
			// finishes with a double 0

			for (int i=0; i<long(nLen-2); i++) // nLen-1 instead of nLen, because we don't care the second 0 of the double 0
			{
				if (szRawBuffer[i]==0)
					s += _T("\r\n");
				else
					s += szRawBuffer[i];
			}

		}
		else
		{
			s = szRawBuffer;
		}


	}
	

	return s;
}

// this function is the dual of ConvertToString
void ConvertFromString(CString &szValue, DWORD dwType, LPSTR *szRawBuffer, DWORD *nLen)
{
	if (szRawBuffer==NULL || nLen==NULL) return;

	*szRawBuffer = NULL; // defensive programming
	*nLen = 0;

	// real conversion starts here
	if (dwType==REG_SZ || dwType==REG_EXPAND_SZ)
	{

		long n = szValue.GetLength()+1;
		char *buffer = new char[n];
		if (buffer)
		{
			*nLen = n;
			strncpy(buffer, szValue.GetBuffer(0), n-1);

			buffer[n-1]=0; // EOL
			*szRawBuffer = buffer;
		}

	}
	else if (dwType==REG_BINARY || dwType==REG_RESOURCE_LIST || dwType==REG_RESOURCE_REQUIREMENTS_LIST)
	{
		if (szValue.GetLength()==0) // stupid case handling
		{
			char *buffer = new char[1];
			if (buffer)
			{
				buffer[0]=0;
				*nLen = 1;
				*szRawBuffer = buffer;
			}
		}
		else
		{
			long n = ( szValue.GetLength()+1 ) / 3;
			char *buffer = new char[n];
			if (buffer)
			{
				*nLen = n;
				long ncur = 0;

				for (long i=0; i<n; i++)
				{
					buffer[i] = ConvertToDecimal( szValue.GetAt(ncur), szValue.GetAt(ncur+1) );
					ncur += 3;
				}

				*szRawBuffer = buffer;
			}
		}
	}
	else if (dwType==REG_DWORD)
	{
		if (szValue.GetLength()==0) // stupid case handling
		{
			char *buffer = new char[4];
			if (buffer)
			{
				buffer[0]=0;buffer[1]=0;buffer[2]=0;buffer[3]=0;
				*nLen = 4;
				*szRawBuffer = buffer;
			}
		}
		else
		{
			long n = 4;
			char *buffer = new char[n];
			if (buffer)
			{
				*nLen = n;
				long ncur = 2; // starts of offset 2 since we have 0x

				for (long i=0; i<n; i++)
				{
					buffer[n-1-i] = ConvertToDecimal( szValue.GetAt(ncur), szValue.GetAt(ncur+1) );
					ncur += 2;
				}

				*szRawBuffer = buffer;
			}
		}
	}
	else if (dwType==11)
	{
		if (szValue.GetLength()==0) // stupid case handling
		{
			char *buffer = new char[8];
			if (buffer)
			{
				buffer[0]=0;buffer[1]=0;buffer[2]=0;buffer[3]=0;
				buffer[4]=0;buffer[5]=0;buffer[6]=0;buffer[7]=0;
				*nLen = 8;
				*szRawBuffer = buffer;
			}
		}
		else
		{
			long n = 8;
			char *buffer = new char[n];
			if (buffer)
			{
				*nLen = n;
				long ncur = 2; // starts of offset 2 since we have 0x

				for (long i=0; i<n; i++)
				{
					buffer[n-1-i] = ConvertToDecimal( szValue.GetAt(ncur), szValue.GetAt(ncur+1) );
					ncur += 2;
				}

				*szRawBuffer = buffer;
			}
		}
	}
	else if (dwType==REG_LINK)
	{
		char *buffer = new char[ (szValue.GetLength()+1)*2 ];
		if (buffer)
		{

			// local charset to widechar
			//

			int nActualWideLength = ::MultiByteToWideChar(CP_ACP,
					0,
					szValue.GetBuffer(0),
					szValue.GetLength(),
					(wchar_t*) buffer,
					szValue.GetLength());

			buffer[2*nActualWideLength] = 0; // EOL (two bytes)
			buffer[2*nActualWideLength+1] = 0;

			*nLen = nActualWideLength;
			*szRawBuffer = buffer;
		}
	}
	else if (dwType==REG_MULTI_SZ)
	{

		// szValue : the MULTI_SZ value is strings separated by \r\n (0x0A0x0D)

		// buffer : a MULTI_SZ value is a set of strings separated by a 0 char, and
		// finishes with a double 0

		// szValue has a greater length than buffer since \r\n is 2 chars, while a 0-EOL is only one
		// buf buffer has also a trailing EOL
		char *buffer = new char[ szValue.GetLength()+2 ];
		if (buffer)
		{
			long n = 0;

			long nszvaluelen = szValue.GetLength();
			for (long i=0; i<nszvaluelen; i++)
			{
				char c = szValue.GetAt(i);
				if (c!='\r' && c!='\n')
				{
					buffer[n++] = c;
				}
				else if (c=='\n')
				{
					buffer[n++] = 0; 
				}
			}
	
			buffer[n++] = 0; 
			buffer[n++] = 0; 

			if (nszvaluelen==0) n=1;

			*nLen = n;
			*szRawBuffer = buffer;
		}
	}

}

char ConvertToDecimal(char c1, char c2)
{
	char n1, n2;

	if (c1>='0' && c1<='9')
		n1 = c1-'0';
	else if (c1>='A' && c1<='F')
		n1 = c1-'A'+10;
	else if (c1>='a' && c1<='f')
		n1 = c1-'a'+10;

	if (c2>='0' && c2<='9')
		n2 = c2-'0';
	else if (c2>='A' && c2<='F')
		n2 = c2-'A'+10;
	else if (c2>='a' && c2<='f')
		n2 = c2-'a'+10;

	return n1*16+n2;
}



CString StringFromValueType(DWORD nType)
{
	CString s;

	switch (nType) // see winnt.h
	{
		case REG_BINARY: s = "REG_BINARY"; break;
		case REG_DWORD : s = "REG_DWORD"; break;
		case REG_DWORD_BIG_ENDIAN : s = "REG_DWORD"; break;
		case REG_EXPAND_SZ : s = "REG_EXPAND_SZ"; break;
		case REG_LINK : s = "REG_SZ"; break;
		case REG_MULTI_SZ : s = "REG_MULTI_SZ"; break;
		case REG_NONE : s = "REG_SZ"; break;
		case 11 : s = "REG_QWORD"; break; // QWORD (64-bit integer)
		case REG_RESOURCE_LIST : s = "REG_RESOURCE_LIST"; break;
		case REG_RESOURCE_REQUIREMENTS_LIST : s = "REG_RESOURCE_REQUIREMENTS_LIST"; break;
		case REG_SZ : s = "REG_SZ"; break;
	}
	return s;
}

// this function is the dual of StringFromValueType
DWORD TypeFromString(CString &szValueType)
{
	DWORD nType = REG_SZ; // default type

	if (szValueType.CompareNoCase("REG_BINARY")==0)
		nType = REG_BINARY;
	else if (szValueType.CompareNoCase("REG_DWORD")==0)
		nType = REG_DWORD;
	else if (szValueType.CompareNoCase("REG_SZ")==0)
		nType = REG_SZ;
	else if (szValueType.CompareNoCase("REG_EXPAND_SZ")==0)
		nType = REG_EXPAND_SZ;
	else if (szValueType.CompareNoCase("REG_MULTI_SZ")==0)
		nType = REG_MULTI_SZ;
	else if (szValueType.CompareNoCase("REG_QWORD")==0)
		nType = 11; // QWORD (64-bit integer)
	else if (szValueType.CompareNoCase("REG_RESOURCE_LIST")==0)
		nType = REG_RESOURCE_LIST;
	else if (szValueType.CompareNoCase("REG_RESOURCE_REQUIREMENTS_LIST")==0)
		nType = REG_RESOURCE_REQUIREMENTS_LIST;

	return nType;
}









BOOL SaveAsXml( XmlWriter &w, BOOL bFakedXml, CString &szInPath)
{
	CString szPath = szInPath;

	if (szPath.IsEmpty()) return FALSE;

	CString szMainKeyname = szPath;

	int nSlash = szPath.Find(_T("\\") );
	if (nSlash>-1)
	{
		szMainKeyname = szPath.Left( nSlash );
		szPath = szPath.Right( szPath.GetLength()-(nSlash+1));
	}
	else
		szPath.Empty();

	// open the key now
	HKEY hKey;

	long hr = Helper_OpenKey(szMainKeyname, szPath, &hKey);

	if (hr==ERROR_SUCCESS) // it's ok
	{

		// write the main key here
		//
		CStringArray arKeyPath;

		int nIndexSlash = 0, i;
		CString szTmpPath = szInPath;

		do
		{
			nIndexSlash = szTmpPath.Find("\\", 0);
			if (nIndexSlash>-1)
			{
				arKeyPath.Add( szTmpPath.Left(nIndexSlash++) );
				szTmpPath = szTmpPath.Right( szTmpPath.GetLength()-nIndexSlash );
			}
			else
				arKeyPath.Add( szTmpPath );
		}
		while ( nIndexSlash>-1);

		int nSize = arKeyPath.GetSize();
		BOOL bResult;

		if (!bFakedXml)
		{

			for (i=0; i<nSize; i++)
			{
				XmlElement wkey( CString(XML_KEY) );

				GetEscapedXmlString( arKeyPath.GetAt(i) );

				wkey.AddAttrib( GetEscapedXmlString( CString(XML_NAME) ), 
								GetEscapedXmlString( arKeyPath.GetAt(i) ) );

				wkey.Write(w,1);
			}

			bResult = SaveAsXml(w, bFakedXml, CString(""), hKey);

			for (i=0; i<nSize; i++)
			{
					XmlElement wkey( CString(XML_KEY) );
					wkey.WriteClosingTag(w,-1);
			}

		}
		else
		{
				
			nSize--;

			for (i=0; i<nSize; i++)
			{
				XmlElement wkey( arKeyPath.GetAt(i) );
				wkey.Write(w,1);
			}

			bResult = SaveAsXml(w, bFakedXml, arKeyPath.GetAt(nSize), hKey);
		
			for (i=0; i<nSize; i++)
			{
				XmlElement wkey( arKeyPath.GetAt(nSize-1-i) );
				wkey.WriteClosingTag(w,-1);
			}
		}


		::RegCloseKey(hKey);

		return bResult;
	}

	return FALSE;

}


long g_nSaveCounter = 0;



BOOL SaveAsXml( XmlWriter &w, BOOL bFakedXml, CString szKeyname, HKEY hKey)
{
	// write key name
	//
    DWORD    cSubKeys;                 // number of subkeys 
    DWORD    cbMaxSubKey;              // longest subkey size 
    DWORD    cchMaxClass;              // longest class string 
    DWORD    cValues;              // number of values for key 
    DWORD    cchMaxValue;          // longest value name 
    DWORD    cbMaxValueData;       // longest value data 
    DWORD    cbSecurityDescriptor; // size of security descriptor 
    FILETIME ftLastWriteTime;      // last write time 
 
    DWORD i; 
    DWORD retCode; 
 

    // Get the class name and the value count. 
    retCode = ::RegQueryInfoKey(hKey,        // key handle 
        NULL,					// buffer for class name 
        NULL,					// length of class string 
        NULL,                    // reserved 
        &cSubKeys,               // number of subkeys 
        &cbMaxSubKey,            // longest subkey size 
        &cchMaxClass,            // longest class string 
        &cValues,                // number of values for this key 
        &cchMaxValue,            // longest value name 
        &cbMaxValueData,         // longest value data 
        &cbSecurityDescriptor,   // security descriptor 
        &ftLastWriteTime);       // last write time 
 

	if (retCode!=ERROR_SUCCESS) return FALSE;


    // Enumerate the child keys, until RegEnumKeyEx fails. 

    CHAR  *achKey = new CHAR[cbMaxSubKey+1]; 
	if (!achKey) return FALSE;


	XmlElement wkey( CString(XML_KEY) );


	if (!szKeyname.IsEmpty())
	{
		if (!bFakedXml) // standard xml
		{
		
			wkey.AddAttrib( GetEscapedXmlString( CString(XML_NAME) ), 
							GetEscapedXmlString( szKeyname ) );

			if (cSubKeys>0 || cValues>0)
				wkey.Write(w,1);
			else
				wkey.WriteEmpty(w,1);
		}
		else // faked xml
		{
			wkey.SetName( GetEscapedXmlString( szKeyname ) );
		}
	}	


	// each 50 values, we pump a window message, to check out whether the user hit ESCAPE
	if ( (g_nSaveCounter++ % 50)==0 )
	{
		MSG msg ;
		if (::PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
		{
			::TranslateMessage(&msg);
			::DispatchMessage(&msg);

			if (msg.message==WM_KEYDOWN && msg.wParam==VK_ESCAPE) // abort if the user presses the ESC key
					return FALSE;
		}

	}


	// save values
	//
	CPtrArray arrValues;

	AddRegistryValues(hKey, arrValues);

	int nbItems = arrValues.GetSize();
	for (int j=0; j<nbItems; j++)
	{

		KeyValue *p = (KeyValue*) arrValues.GetAt(j);
		if (!p) continue;

		if (p->GetName().Compare(IDS_DEFAULTVALUENAME) || p->GetValue().Compare(IDS_DEFAULTVALUEVALUE) )
		{

			DWORD dwType = p->GetType();

			int nValueIcon = ILI_STRING;
			if ( (dwType>=REG_BINARY && dwType<=REG_DWORD_BIG_ENDIAN) || dwType==11)
				nValueIcon = ILI_BINARY;

			if (!bFakedXml) // standard xml
			{
				XmlElement wvalue( CString(XML_VALUE) );

				wvalue.AddAttrib( CString(XML_NAME), GetEscapedXmlString( p->GetName() ) );
				wvalue.AddAttrib( CString(XML_VALUE2), GetEscapedXmlString( p->GetValue() ) );

				if (dwType!=REG_SZ && dwType!=REG_NONE)
				{
					wvalue.AddAttrib( CString(XML_TYPE), GetEscapedXmlString( StringFromValueType(dwType) ) );
				}

				wvalue.WriteEmpty(w, 1);
			}
			else // faked xml
			{
				wkey.AddAttrib( GetEscapedXmlString( p->GetName() ), GetEscapedXmlString( p->GetValue() ) );
			}

		}

		delete p; // destroy the now useless temp struct
	}

	if (!szKeyname.IsEmpty())
	{

		if (bFakedXml)
		{
			if (cSubKeys>0)
				wkey.Write(w,1);
			else
				wkey.WriteEmpty(w,1);
		}

	}


    for (i = 0, retCode = ERROR_SUCCESS; retCode == ERROR_SUCCESS; i++) 
    { 
		DWORD achKeyMaxLength = cbMaxSubKey + 1;

        retCode = ::RegEnumKeyEx(hKey, 
                     i, 
                     achKey, 
                     &achKeyMaxLength, 
                     NULL, 
                     NULL, 
                     NULL, 
                     &ftLastWriteTime); 

		if (retCode == ERROR_SUCCESS && achKey && achKeyMaxLength>0)
		{
			achKey[achKeyMaxLength]=0; // force EOL

			// open sub keys
			//

			HKEY hSubkey;
			if (::RegOpenKey(hKey, achKey, &hSubkey)==ERROR_SUCCESS)
			{
		
				if (!SaveAsXml(w, bFakedXml, CString(achKey), hSubkey))
					return FALSE; //


				::RegCloseKey(hSubkey);
			}

		}

    }  // end for


	if (!szKeyname.IsEmpty())
	{

		if (!bFakedXml)
		{
			if (cSubKeys>0 || cValues>0)
				wkey.WriteClosingTag(w,-1);
		}
		else
		{
			// with faked xml, we only need to actually close the tag when there
			// are keys under it, otherwise, we did a WriteEmpty above.
			if (cSubKeys>0)
				wkey.WriteClosingTag(w,-1);
		}

	}

	delete [] achKey;

	return TRUE;
}



BOOL LoadAsXml( XmlReader &r, CString &szFilename)
{
	BOOL bReturn = TRUE;

	r.ShowMsgBoxOnError();
	r.Open( szFilename );

	CPtrArray arrInternalKeyLoads;

	CString szValueName, szValueValue, szValueType;


	while ( r.ReadString() && bReturn )
	{
		switch ( r.GetNodeType() )
		{
			case NODETYPE_BEGINELEMENT:
			{
				CString szElement;
				r.GetNodeName(szElement);
				szElement = GetUnescapedXmlString(szElement);
			} 
			break;
			case NODETYPE_ENDELEMENT:
			{
				CString szElement;
				r.GetNodeName(szElement);
				szElement = GetUnescapedXmlString(szElement);

				// close the latest key now
				if ( szElement.CompareNoCase(XML_KEY)==0 && arrInternalKeyLoads.GetSize()>0 )
				{
					// retrieve latest key
					InternalKeyLoad *p = (InternalKeyLoad*) arrInternalKeyLoads.GetAt( arrInternalKeyLoads.GetSize()-1 );
					if (p)
					{
						LoadAsXml_CloseKey( p->hKey );

						arrInternalKeyLoads.RemoveAt( arrInternalKeyLoads.GetSize()-1 );

						delete p;
					}

				}
				else if ( szElement.CompareNoCase(XML_VALUE)==0 && arrInternalKeyLoads.GetSize()>0 )  // <v .../>
				{
					// now set/create the value
					InternalKeyLoad *p = (InternalKeyLoad*) arrInternalKeyLoads.GetAt( arrInternalKeyLoads.GetSize()-1 );
					if (p)
						LoadAsXml_SetValue( p->hKey, szValueName, szValueValue, szValueType);

					szValueName.Empty();
					szValueValue.Empty();
					szValueType.Empty();
				}
			} 
			break;
			case NODETYPE_ATTRIB:
			{
				CString szName, szValue;
				r.GetAttrib(szName, szValue);
				szName = GetUnescapedXmlString(szName);
				szValue = GetUnescapedXmlString(szValue);

				CString szCurrentElement;
				r.GetNodeName(szCurrentElement);

				// <k name="..."> ?
				if (szName.CompareNoCase(XML_NAME)==0 && szCurrentElement.CompareNoCase(XML_KEY)==0)
				{
					InternalKeyLoad *p = new InternalKeyLoad();
					if (p)
					{
						p->szKeyname = szValue;

						arrInternalKeyLoads.Add( p );

						// open the key, and store the handle
						p->hKey = LoadAsXml_OpenKey( arrInternalKeyLoads );

						if (p->hKey==NULL)
							bReturn = FALSE; // abort the process

					} // end if (p)

				}
				else if (szCurrentElement.CompareNoCase(XML_VALUE)==0) // <v name="..." value="..." type="..." />
				{
					if (szName.CompareNoCase(XML_NAME)==0) // name="..."
					{
						szValueName = szValue;
						szValueValue.Empty();
						szValueType.Empty();
					}
					else if (szName.CompareNoCase(XML_VALUE2)==0) // value="..."
					{
						szValueValue = szValue;
						szValueType.Empty();
					}
					else if (szName.CompareNoCase(XML_TYPE)==0) // type="..."
					{
						szValueType = szValue;
					}

				}

			} 
			break;
		
			// other nodetypes : we don't care

		}
	} // end read lines one by one


	long nSize = arrInternalKeyLoads.GetSize();
	for (long i=0;i< nSize; i++)
		delete arrInternalKeyLoads.GetAt( i );

	r.Close();

	return bReturn;
}



HKEY LoadAsXml_OpenKey(CPtrArray &arrInternalKeyLoads)
{
	HKEY hKey = NULL;
	long hr;

	if (arrInternalKeyLoads.GetSize()==0) return NULL; // come on!

	// build the real path from the array of keynames
	// open the key now
	CString szMainKeyname, szPath;
	long nSize = arrInternalKeyLoads.GetSize();
	for (long i=0; i<nSize; i++)
	{
		InternalKeyLoad *p = (InternalKeyLoad*) arrInternalKeyLoads.GetAt(i);
		if (p)
		{
			if (i==0)
			{
				szMainKeyname = p->szKeyname;
			}
			else
			{
				if (!szPath.IsEmpty())
					szPath += "\\";

				szPath += p->szKeyname;
			}
		}
	} // end for


	hr = Helper_OpenKey(szMainKeyname, szPath, &hKey);

	// open the registry key now
	if (hr==ERROR_SUCCESS) // it's ok
		return hKey;

	// key does not exist, so let's create it !
	if (szMainKeyname.CompareNoCase("HKEY_CLASSES_ROOT")==0)
	{
		hr = ::RegCreateKey(HKEY_CLASSES_ROOT, szPath, &hKey);
	}
	else if (szMainKeyname.CompareNoCase("HKEY_CURRENT_USER")==0)
	{
		hr = ::RegCreateKey(HKEY_CURRENT_USER, szPath, &hKey);
	}
	else if (szMainKeyname.CompareNoCase("HKEY_LOCAL_MACHINE")==0)
	{
		hr = ::RegCreateKey(HKEY_LOCAL_MACHINE, szPath, &hKey);
	}
	else if (szMainKeyname.CompareNoCase("HKEY_USERS")==0)
	{
		hr = ::RegCreateKey(HKEY_USERS, szPath, &hKey);
	}
	else if (szMainKeyname.CompareNoCase("HKEY_CURRENT_CONFIG")==0)
	{
		hr = ::RegCreateKey(HKEY_CURRENT_CONFIG, szPath, &hKey);
	}
	else 
		return NULL; // break here
	
	if (hr==ERROR_SUCCESS) // it's ok
		return hKey;

	// 
	SetCursor(LoadCursor (NULL, IDC_ARROW)); // back to normal cursor

	CString s;
	s.Format("couldn't create key %s\\%s", szMainKeyname.GetBuffer(0), szPath.GetBuffer(0) );
	AfxMessageBox(s);

	return NULL;
}

void LoadAsXml_CloseKey(HKEY hKey)
{
	if (hKey) ::RegCloseKey(hKey);
}

void LoadAsXml_SetValue(HKEY hKey, CString &szName, CString &szValue, CString &szType)
{
	if (hKey==NULL) return;

	// handle the (Default) value
	LPCTSTR pName = szName.CompareNoCase(IDS_DEFAULTVALUENAME)==0 ? NULL : szName.GetBuffer(0);

	DWORD nType = TypeFromString(szType);

	LPSTR buffer = NULL;
	DWORD nLen = 0;

	ConvertFromString(szValue, nType, &buffer, &nLen);

	if (buffer && nLen>0)
	{
		long hr = ::RegSetValueEx(
			hKey,           // handle to key
			pName,			// value name
			0,				// reserved
			nType,			// value type
			(CONST BYTE *) buffer, //szValue.GetBuffer(0),  // value data
			(DWORD) nLen //szValue.GetLength() // size of value data
		);

		delete [] (char*)buffer;

		if (hr==ERROR_SUCCESS) // it's ok
			return;
	}

	// 
	SetCursor(LoadCursor (NULL, IDC_ARROW)); // back to normal cursor

	CString s;
	s.Format("couldn't set value %s", szName.GetBuffer(0) );
	AfxMessageBox(s);
}



CString GetEscapedXmlString(CString szInput)
{
	CString s;
	if (szInput.GetLength()==0) return s;

	long nLength = szInput.GetLength();
	for (long i=0; i<nLength; i++)
	{
		char c = szInput.GetAt(i);
		if (c=='&')
			s += "&amp;";
		else if (c=='"')
			s += "&quot;";
		else if (c=='<')
			s += "&lt;";
		else
			s += c;
	}

	return UTF8Conversion(s);
}


CString UTF8Conversion(CString &s)
{

	wchar_t *pWideString = new wchar_t[s.GetLength()+1];
	if (!pWideString) return s;

	// local charset to widechar
	//

	int nActualWideLength = ::MultiByteToWideChar(CP_ACP,
			0,
			s.GetBuffer(0),
			s.GetLength(),
			pWideString,
			s.GetLength());

	pWideString[nActualWideLength] = 0;

	// widechar to UTF8
	//		
	char *temps = new char[2*nActualWideLength+5]; // 2*x+5 (why not?)
	if (!temps)
	{
		delete [] pWideString;
		return s;
	}

	int nActualLength = ::WideCharToMultiByte(CP_UTF8,
			0,
			pWideString,
			-1,
			temps,
			2*nActualWideLength+5,
			NULL,
			NULL);

	CString st = temps;

	delete [] temps;

	delete [] pWideString;

	return st;

}


CString GetUnescapedXmlString(CString szInput) // Xml escape chars (&quot; ==> ", &lt; ==> <, &amp; ==> &);
{
	CString s;
	if (szInput.GetLength()==0) return s;

	s = FromUTF8Conversion(szInput);

	int nLt;
	while ( (nLt=s.Find("&lt;",0))>-1 )
		s = s.Left(nLt) + "<" + s.Right( s.GetLength()-(nLt+strlen("&lt;")) );

	int nQuot;
	while ( (nQuot=s.Find("&quot;",0))>-1 )
		s = s.Left(nQuot) + "\"" + s.Right( s.GetLength()-(nQuot+strlen("&quot;")) );

	int nAmp;
	while ( (nAmp=s.Find("&amp;",0))>-1 )
		s = s.Left(nAmp) + "&" + s.Right( s.GetLength()-(nAmp+strlen("&amp;")) );

	return s;
}

CString FromUTF8Conversion(CString &s) // UTF8 ==> local charset
{
	wchar_t *pWideString = new wchar_t[s.GetLength()+1];
	if (!pWideString) return s;

	// UTF8 to widechar
	//

	int nActualWideLength = ::MultiByteToWideChar(CP_UTF8,
			0,
			s.GetBuffer(0),
			s.GetLength(),
			pWideString,
			s.GetLength());

	pWideString[nActualWideLength] = 0;

	// widechar to local charset
	//		
	char *temps = new char[nActualWideLength+1];
	if (!temps)
	{
		delete [] pWideString;
		return s;
	}

	int nActualLength = ::WideCharToMultiByte(CP_ACP,
			0,
			pWideString,
			-1,
			temps,
			nActualWideLength+1,
			NULL,
			NULL);

	CString st = temps;

	delete [] temps;

	delete [] pWideString;

	return st;
}

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

Addicted to reverse engineering. At work, I am developing business intelligence software in a team of smart people (independent software vendor).
 
Need a fast Excel generation component? Try xlsgen.
 

| Advertise | Privacy | Mobile
Web01 | 2.8.140916.1 | Last Updated 22 Jan 2003
Article Copyright 2002 by Stephane Rodriguez.
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid