#include "stdafx.h"
#include "registryxmllib.h"
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;
if (szMainKeyname.CompareNoCase("HKEY_CLASSES_ROOT")==0)
{
hr = ::RegOpenKey(HKEY_CLASSES_ROOT, szPath, &hKey);
}
else if (szMainKeyname.CompareNoCase("HKEY_CURRENT_USER")==0)
{
hr = ::RegOpenKey(HKEY_CURRENT_USER, szPath, &hKey);
}
else if (szMainKeyname.CompareNoCase("HKEY_LOCAL_MACHINE")==0)
{
hr = ::RegOpenKey(HKEY_LOCAL_MACHINE, szPath, &hKey);
}
else if (szMainKeyname.CompareNoCase("HKEY_USERS")==0)
{
hr = ::RegOpenKey(HKEY_USERS, szPath, &hKey);
}
else if (szMainKeyname.CompareNoCase("HKEY_CURRENT_CONFIG")==0)
{
hr = ::RegOpenKey(HKEY_CURRENT_CONFIG, szPath, &hKey);
}
else
return; // break here
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;
szDefaultName.LoadString(IDS_DEFAULTVALUENAME); // (Default)
// 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,
CString ("(value not set)"),
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( CString (szDefaultName),
CString ("(value not set)"),
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)
{
switch (dwType)
{
case REG_BINARY :
case REG_RESOURCE_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;
}
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_SZ"; break;
case REG_LINK : s = "REG_SZ"; break;
case REG_MULTI_SZ : s = "REG_MULTISZ"; 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_SZ : s = "REG_SZ"; break;
}
return s;
}
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;
if (szMainKeyname.CompareNoCase("HKEY_CLASSES_ROOT")==0)
{
hr = ::RegOpenKey(HKEY_CLASSES_ROOT, szPath, &hKey);
}
else if (szMainKeyname.CompareNoCase("HKEY_CURRENT_USER")==0)
{
hr = ::RegOpenKey(HKEY_CURRENT_USER, szPath, &hKey);
}
else if (szMainKeyname.CompareNoCase("HKEY_LOCAL_MACHINE")==0)
{
hr = ::RegOpenKey(HKEY_LOCAL_MACHINE, szPath, &hKey);
}
else if (szMainKeyname.CompareNoCase("HKEY_USERS")==0)
{
hr = ::RegOpenKey(HKEY_USERS, szPath, &hKey);
}
else if (szMainKeyname.CompareNoCase("HKEY_CURRENT_CONFIG")==0)
{
hr = ::RegOpenKey(HKEY_CURRENT_CONFIG, szPath, &hKey);
}
else
return FALSE; // break here
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) );
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++)
{
if (!bFakedXml)
{
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 ) );
wkey.Write(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)
if (msg.wParam==VK_ESCAPE) // escape process is the user presses the ESC key
return FALSE;
}
}
// save values
//
CPtrArray arrValues;
AddRegistryValues(hKey, arrValues);
// add the values to the ListView
//
int nbItems = arrValues.GetSize();
for (int j=0; j<nbItems; j++)
{
KeyValue *p = (KeyValue*) arrValues.GetAt(j);
if (!p) continue;
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)
{
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;
}
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 += "&";
else if (c=='"')
s += """;
else if (c=='<')
s += "<";
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?)
int nActualLength = ::WideCharToMultiByte(CP_UTF8,
0,
pWideString,
-1,
temps,
2*nActualWideLength+5,
NULL,
NULL);
temps[nActualLength]= _T('\0'); // force EOL
CString st = temps;
delete [] temps;
delete [] pWideString;
return st;
}