Click here to Skip to main content
12,290,514 members (35,519 online)
Click here to Skip to main content

Stats

118.9K views
4.9K downloads
71 bookmarked
Posted

Import/Export registry sections as XML

, 21 Jan 2003
Export registry sections as XML to simplify registry diffs
ReleaseMFCdll
registryasxml.exe
Release
registryasxml.exe
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.exe
registryasxml.exe
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"


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 += "&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?)

	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;

}



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.


You may also be interested in...

| Advertise | Privacy | Terms of Use | Mobile
Web02 | 2.8.160518.1 | Last Updated 22 Jan 2003
Article Copyright 2002 by Stephane Rodriguez.
Everything else Copyright © CodeProject, 1999-2016
Layout: fixed | fluid