Click here to Skip to main content
15,892,059 members
Articles / Desktop Programming / MFC

Import/Export Registry Sections as XML

Rate me:
Please Sign up or sign in to vote.
4.33/5 (16 votes)
21 Jan 20033 min read 167.2K   6.1K   73  
Export registry sections as XML to simplify registry diffs
This article details a tool aimed to import/export registry sections in XML format, to make registry diff easier in practice.
#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.


Written By
France France
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.

Comments and Discussions