Click here to Skip to main content
15,896,557 members
Articles / Desktop Programming / MFC

How to Register a Program

Rate me:
Please Sign up or sign in to vote.
3.95/5 (9 votes)
28 Jan 2009CPOL8 min read 61.9K   2K   61  
How to register a program on end-user machine only and without end-user interference.
// ------------------------------------------------------------------------------------------------------------------------------------------------------
// - This file are split up in two parts:																												-
// -																																					-
// - 1. CLASSES/FUNCTIONS ETC. NEEDED FOR THE TEST PROJ'S ONLY.																							-
// - 2. CLASSES/FUNCTIONS ETC. NEEDED FOR THE LOCAL-MACHINE-REG. TO WORK AS DESCRIBED IN THE CP ARTICLE.												-
// -																																					-
// - Michael Mogensen, Copenhagen DK january 2009, michael-mogensen-danmark@hotmail.com.																-
// ------------------------------------------------------------------------------------------------------------------------------------------------------

// ------------------------------------------------------------------------------------------------------------------------------------------------------
// - Miscellaneous.																																		-
// ------------------------------------------------------------------------------------------------------------------------------------------------------

// ------------------------------------------------------------------------------------------------------------------------------------------------------
// - Header(s).																																			-
// ------------------------------------------------------------------------------------------------------------------------------------------------------
#include "stdafx.h"

#include "mfcutil2.h"

// ------------------------------------------------------------------------------------------------------------------------------------------------------
// - Miscellaneous.																																		-
// ------------------------------------------------------------------------------------------------------------------------------------------------------

// ------------------------------------------------------------------------------------------------------------------------------------------------------
// -																																					-
// -																																					-
// - 1. CLASSES/FUNCTIONS ETC. NEEDED FOR THE TEST PROJ'S ONLY.																							-
// -																																					-
// -																																					-
// ------------------------------------------------------------------------------------------------------------------------------------------------------

// ...all inlined...

// ------------------------------------------------------------------------------------------------------------------------------------------------------
// -																																					-
// -																																					-
// - 2. CLASSES/FUNCTIONS ETC. NEEDED FOR THE REG.																										-
// -																																					-
// -																																					-
// ------------------------------------------------------------------------------------------------------------------------------------------------------

// ------------------------------------------------------------------------------------------------------------------------------------------------------
// - CNTPTime.																																			-
// ------------------------------------------------------------------------------------------------------------------------------------------------------
const long CNTPTime::sm_lJan1St_1900 = 2415021;

// 1. Constructors. (alphabetical).
CNTPTime::CNTPTime() : 
	m_Time(0)
	// Default constructor.
	{}

CNTPTime::CNTPTime(const CNTPTime& time)
// Copy constructor.
{
	*this = time;
}

CNTPTime::CNTPTime(SNTPTimePacket& packet)
// Ex. constructor.
{
	DWORD dwLow = ::ntohl(packet.m_dwFractional);
	DWORD dwHigh = ::ntohl(packet.m_dwInteger);
	m_Time = ((unsigned __int64) dwHigh) << 32;
	m_Time += dwLow;
}

// 3. Methods. (alphabetical).
CNTPTime& CNTPTime::operator=(const CNTPTime& time)
// Assignment.
{
  m_Time = time.m_Time;
  return *this;
}

CNTPTime::operator SYSTEMTIME() const
// SYSTEMTIME cast operator. This operator only operates correctly in the 1900 - 2036 primary epoch defined by NTP.
{
	SYSTEMTIME st;
	DWORD s = Seconds();
	st.wSecond = (WORD)(s % 60);
	s /= 60;
	st.wMinute = (WORD)(s % 60);
	s /= 60;
	st.wHour = (WORD)(s % 24);
	s /= 24;
	long JD = s + sm_lJan1St_1900;
	st.wDayOfWeek = (WORD)((JD + 1) % 7);
	GetGregorianDate(JD, st.wYear, st.wMonth, st.wDay);
	return st;
}

CNTPTime::operator SNTPTimePacket() const
// SNTPTimePacket cast operator. 
{
	SNTPTimePacket ntp;
	ntp.m_dwInteger = ::htonl(Seconds());
	ntp.m_dwFractional = ::htonl(Fraction());
	return ntp;
}

void CNTPTime::GetGregorianDate(long lJD, WORD& dwYear, WORD& dwMonth, WORD& dwDay) // Is static.
// Convert long JD into year, month and day.
{
	long j = lJD - 1721119;
	long y = (4 * j - 1) / 146097;
	j = 4 * j - 1 - 146097 * y;
	long d = j / 4;
	j = (4 * d + 3) / 1461;
	d = 4 * d + 3 - 1461 * j;
	d = (d + 4) / 4;
	long m = (5 * d - 3) / 153;
	d = 5 * d - 3 - 153 * m;
	d = (d + 5) / 5;
	y = 100 * y + j;
	if (m < 10) 
	m = m + 3;
	else 
	{
		m = m - 9;
		y = y + 1;
	}
	// Set.
	dwYear = (WORD)y;
	dwMonth = (WORD)m;
	dwDay = (WORD)d;
}

// ------------------------------------------------------------------------------------------------------------------------------------------------------
// - CRegistryAccess.																																	-
// ------------------------------------------------------------------------------------------------------------------------------------------------------

// 1. Constructors. (alphabetical).
//
//
// HKEY_CURRENT_USER
//  +--- AppEvents <--- This is a main key.
//  +--- Console
//  +--- ControlPanel
//  +--- Enviroment
//  +--- ...
//  +--- Software
//        +--- MyProgram <--- This is a sub key.
//  +--- ...
//
CRegistryAccess::CRegistryAccess(const CString &cstrMainKey, const CString &cstrSubKey, const bool bHiddenName) : 
	m_hKey(NULL), 
	m_bHiddenName(bHiddenName), 
	m_cstrMainKey(cstrMainKey), 
	m_cstrSubKey(cstrSubKey)
	// Create.
	{
		// To use RegSaveKey(...), the current user must have the SE_BACKUP_NAME privilege
		// and your program must enable that privilege before executing RegSaveKey(...).
		// RegRestoreKey(...) requires that the SE_RESTORE_NAME be held and enabled.
		EnableProcessPrivilege(SE_BACKUP_NAME, TRUE);
		// Open or create main key.
		const CString cstrKey(FormatKey(m_cstrMainKey, m_cstrSubKey)); // Like "Software\MyProgram".
		m_hKey = OpenKey(cstrKey);
		if(!m_hKey)
			m_hKey = CreateKey(cstrKey);
		VERIFY(m_hKey);
	}

CRegistryAccess::~CRegistryAccess()
// Destroy.
{
	// Close main key.
	CloseKey(m_hKey);
}

// 3. Methods. (alphabetical).
const bool CRegistryAccess::CloseKey(HKEY hKey)
// Return key on created and NULL if not.
//
// Principle.
//
// HKEY_CURRENT_USER
//  +--- AppEvents
//  +--- Console
//  +--- ControlPanel
//  +--- Enviroment
//  +--- ...
//  +--- Software
//        +--- MyProgram <--- Close this.
//               +----------+----------+---------+
//               | Name     | Type     | Data    |
//               +----------+----------+---------+
//               | VAL0     | TYPE0    | 10      |
//               | VAL1     | TYPE1    | 20      |
//               | VAL2     | TYPE2    | 30      |
//               +----------+----------+---------+
//
// Result of CloseKey("MyProgram").
//
{
	TRY
	{
		LONG lResult = ::RegCloseKey(hKey);
		if(lResult == ERROR_SUCCESS)
			return true;
		return false;
	}
	CATCH_ALL(e)
	{
		WNDTRACEEXCEPTION_USERDLG("CRegistryAccess", "CloseKey", "", e);
		THROW_LAST();
	}
	END_CATCH_ALL
	return false;
}

HKEY CRegistryAccess::CreateKey(const CString &cstrKey)
// Return key on created and NULL if not.
//
// Principle.
//
// HKEY_CURRENT_USER
//  +--- AppEvents
//  +--- Console
//  +--- ControlPanel
//  +--- Enviroment
//  +--- ...
//  +--- Software
//        +--- MyProgram <--- Create this.
//               +----------+----------+---------+
//               | Name     | Type     | Data    |
//               +----------+----------+---------+
//               | VAL0     | TYPE0    | 10      |
//               | VAL1     | TYPE1    | 20      |
//               | VAL2     | TYPE2    | 30      |
//               +----------+----------+---------+
//
// Result of CreateKey("MyProgram").
//
{
	TRY
	{
		HKEY hKeyNew = (HKEY)NULL;
		TCHAR szName[MAX_KEY_LENGTH] = _T("");
		::wsprintf(szName, TEXT("%s"), (LPCTSTR)cstrKey); 
		DWORD dwDisp = 0;
		LONG lResult = ::RegCreateKeyEx(
			HKEY_CURRENT_USER, 
			szName, 
			0, 
			NULL, 
			REG_OPTION_NON_VOLATILE, 
			KEY_FULL_ACCESS, 
			NULL, 
			&hKeyNew, 
			&dwDisp);
		if(lResult == ERROR_SUCCESS)
			return hKeyNew;
		return NULL;
	}
	CATCH_ALL(e)
	{
		WNDTRACEEXCEPTION_USERDLG("CRegistryAccess", "CreateKey", "", e);
		THROW_LAST();
	}
	END_CATCH_ALL
}

const bool CRegistryAccess::DataExist(const CString &cstrName)
// Return T if data exist and F if not.
//
// Principle.
//
// HKEY_CURRENT_USER
//  +--- AppEvents
//  +--- Console
//  +--- ControlPanel
//  +--- Enviroment
//  +--- ...
//  +--- Software
//        +--- MyProgram
//               +----------+----------+---------+
//               | Name     | Type     | Data    |
//               +----------+----------+---------+
//               | VAL0     | TYPE0    | 10      | <--- Check existence of "VAL0".
//               | VAL1     | TYPE1    | 20      |
//               | VAL2     | TYPE2    | 30      |
//               +----------+----------+---------+
//
// Result of DataExist("VAL0") = { T, F }.
//
{
	TRY
	{
		const CString cstrNameEx(m_bHiddenName ? GetLongName(cstrName) : cstrName); // Make name long if hidden.
		CString cstrNameFound(_T(""));
		TCHAR szName[MAX_NAME_LENGTH] = _T("");
		DWORD dwSize = MAX_NAME_LENGTH;
		DWORD dwIndex = 0;
		while(::RegEnumValue(
			m_hKey, 
			dwIndex++, 
            szName, 
            &dwSize, 
            NULL, 
            NULL,
            NULL,
            NULL) != ERROR_NO_MORE_ITEMS)
		{
			cstrNameFound.Format(TEXT("%s"), szName);
			if(cstrNameFound.CompareNoCase(cstrNameEx) == 0)
				return true;
			dwSize = MAX_NAME_LENGTH;
		}
		return false;
	}
	CATCH_ALL(e)
	{ 
		WNDTRACEEXCEPTION_USERDLG("CRegistryAccess", "DataExist", "", e);
		THROW_LAST();
	}
	END_CATCH_ALL
	return false;
}

const bool CRegistryAccess::DeleteData(const CString &cstrName)
// Return T on deleted data and F if not.
//
// Principle.
//
// HKEY_CURRENT_USER
//  +--- AppEvents
//  +--- Console
//  +--- ControlPanel
//  +--- Enviroment
//  +--- ...
//  +--- Software
//        +--- MyProgram
//        |      +----------+----------+---------+
//        |      | Name     | Type     | Data    |
//        |      +----------+----------+---------+
//        |      | VAL0     | TYPE0    | 10      |
//        |      | VAL1     | TYPE1    | 20      | <--- Like delete this.
//        |      | VAL2     | TYPE2    | 30      |
//        |      +----------+----------+---------+
//
{
	TRY
	{
		// Delete data.
		LONG lResult = ::RegDeleteValue(
			m_hKey, 
			m_bHiddenName ? GetLongName(cstrName) : cstrName); // Make name long if hidden.
		if(lResult == ERROR_SUCCESS)
			return true;
		return false;
	}
	CATCH_ALL(e)
	{ 
		WNDTRACEEXCEPTION_USERDLG("CRegistryAccess", "DeleteData", "", e);
		THROW_LAST();
	}
	END_CATCH_ALL
}

const bool CRegistryAccess::DeleteData()
// Return T on deleted data and F if not.
//
// Principle.
//
// HKEY_CURRENT_USER
//  +--- AppEvents
//  +--- Console
//  +--- ControlPanel
//  +--- Enviroment
//  +--- ...
//  +--- Software
//        +--- MyProgram
//        |      +----------+----------+---------+ <--- Delete this table.
//        |      | Name     | Type     | Data    |
//        |      +----------+----------+---------+
//        |      | VAL0     | TYPE0    | 10      |
//        |      | VAL1     | TYPE1    | 20      |
//        |      | VAL2     | TYPE2    | 30      |
//        |      +----------+----------+---------+
//
{
	TRY
	{
		// Delete all data.
		bool bResult = true;
		CString cstrName(_T(""));
		TCHAR szName[MAX_NAME_LENGTH] = _T("");
		DWORD dwSize = MAX_NAME_LENGTH;
		while(::RegEnumValue(
			m_hKey, 
			0, // Remove only from top because we delete while we enum.
            szName, 
            &dwSize, 
            NULL, 
            NULL,
            NULL,
            NULL) != ERROR_NO_MORE_ITEMS)
		{
			cstrName.Format(TEXT("%s"), szName);
			bResult = bResult & DeleteData(cstrName);
			dwSize = MAX_NAME_LENGTH;
		}
		return bResult;
	}
	CATCH_ALL(e)
	{ 
		WNDTRACEEXCEPTION_USERDLG("CRegistryAccess", "DeleteData", "", e);
		THROW_LAST();
	}
	END_CATCH_ALL
}

const bool CRegistryAccess::DeleteKey()
// Return T on deleted data and F if not. Key must have no subkeys. Calling this is harakiri.
//
// Principle.
//
// HKEY_CURRENT_USER
//  +--- AppEvents
//  +--- Console
//  +--- ControlPanel
//  +--- Enviroment
//  +--- ...
//  +--- Software
//        +--- MyProgram <--- Delete this.
//               +----------+----------+---------+
//               | Name     | Type     | Data    |
//               +----------+----------+---------+
//               | VAL0     | TYPE0    | 10      |
//               | VAL1     | TYPE1    | 20      |
//               | VAL2     | TYPE2    | 30      |
//               +----------+----------+---------+
//
// Usage: DeleteKey("MyProgram").
//
{
	TRY
	{
		// Delete key itself.
		LONG lResult = 0;
		if(CloseKey(m_hKey))
		{
			m_hKey = OpenKey(m_cstrMainKey);
			if(m_hKey)
				lResult = ::RegDeleteKey(m_hKey, m_cstrSubKey);
		}
		if(lResult == ERROR_SUCCESS)
			return true;
		return false;
	}
	CATCH_ALL(e)
	{ 
		WNDTRACEEXCEPTION_USERDLG("CRegistryAccess", "DeleteKey", "", e);
		THROW_LAST();
	}
	END_CATCH_ALL
}

BOOL WINAPI CRegistryAccess::EnableProcessPrivilege(LPCTSTR lpPrivilege, BOOL bEnable) 
// This static method enables or disables a privilege for the current process. Common privileges include SE_SHUTDOWN_NAME, SE_BACKUP_NAME, 
// SE_SYSTEMTIME_NAME, etc. and are found in winnt.h. This function dynamically loads the required functions so as not to cause load
// problems on 9x where these functions may not be available.
{
	TRY
	{
		typedef BOOL (WINAPI * fnptr_openprocesstoken)(HANDLE,DWORD,PHANDLE);
		typedef BOOL (WINAPI * fnptr_adjusttokenprivileges)(HANDLE,BOOL,PTOKEN_PRIVILEGES,DWORD,PTOKEN_PRIVILEGES,PDWORD);
		typedef BOOL (WINAPI * fnptr_lookupprivilegevalue)(LPCTSTR,LPCTSTR,PLUID);
		TOKEN_PRIVILEGES tp = { 0 };
		HMODULE hAdvapi = NULL;
		HANDLE hToken = NULL;
		BOOL bResult = FALSE;
		fnptr_openprocesstoken openprocesstoken;
		fnptr_adjusttokenprivileges adjusttokenprivileges;
		fnptr_lookupprivilegevalue lookupprivilegevalue;
		// Load the required functions.
		if (!(hAdvapi = LoadLibrary(TEXT("Advapi32.dll"))) ||
			!(openprocesstoken = (fnptr_openprocesstoken)GetProcAddress(hAdvapi, "OpenProcessToken")) ||
			!(adjusttokenprivileges = (fnptr_adjusttokenprivileges)GetProcAddress(hAdvapi, "AdjustTokenPrivileges")) ||
			#ifndef UNICODE
			!(lookupprivilegevalue = (fnptr_lookupprivilegevalue)GetProcAddress(hAdvapi, "LookupPrivilegeValueA")))
			#else
			!(lookupprivilegevalue = (fnptr_lookupprivilegevalue)GetProcAddress(hAdvapi, "LookupPrivilegeValueW")))
			#endif
		{
			goto cleanup;
		}
		// Get the local id of our desired privilege.
		if(!lookupprivilegevalue(NULL, lpPrivilege, &tp.Privileges[0].Luid))
			goto cleanup;
		tp.PrivilegeCount = 1;
		tp.Privileges[0].Attributes = (bEnable ? SE_PRIVILEGE_ENABLED : 0);
		// Get the access token for the current process.
		if(!openprocesstoken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &hToken))
			goto cleanup;
		// Enable the privilege.
		if(!adjusttokenprivileges(hToken, FALSE, &tp, sizeof(TOKEN_PRIVILEGES), NULL, NULL))
			goto cleanup;
		bResult = TRUE;
		// Always do this.
		cleanup:
			if(hAdvapi)
				::FreeLibrary(hAdvapi);
			if(hToken)
				::CloseHandle(hToken);
		// Success.
		return bResult;
	}
	CATCH_ALL(e)
	{
		WNDTRACEEXCEPTION_USERDLG("CRegistryAccess", "EnableProcessPrivilege", "", e);
		THROW_LAST();
	}
	END_CATCH_ALL
	// Failure.
	return false;
}

const CString CRegistryAccess::FormatKey(const CString &cstrMainKey, const CString &cstrSubKey)
// Return sub key to main key - like "Software\MyProgram" or "Consol\MyProgram".
{
	TRY
	{
		CString cstrFullKey(_T(""));
		if(cstrSubKey.IsEmpty())
			cstrFullKey.Format(TEXT("%s"), cstrMainKey);
		else
			cstrFullKey.Format(TEXT("%s\\%s"), cstrMainKey, cstrSubKey);
		return cstrFullKey;
	}
	CATCH_ALL(e)
	{
		WNDTRACEEXCEPTION_USERDLG("CRegistryAccess", "FormatKey", "", e);
		THROW_LAST();
	}
	END_CATCH_ALL
}

const void CRegistryAccess::GetAll(CArray<SNameTypeData, SNameTypeData> &arrNameTypeData)
// Return array of name, type and data under a key (not its subkeys).
//
// Principle.
//
// HKEY_CURRENT_USER
//  +--- AppEvents
//  +--- Console
//  +--- ControlPanel
//  +--- Enviroment
//  +--- ...
//  +--- Software
//        +--- MyProgram
//               +----------+----------+---------+
//               | Name     | Type     | Data    |
//               +----------+----------+---------+
//               | VAL0     | TYPE0    | 10      | <--- Read this table.
//               | VAL1     | TYPE1    | 20      |
//               | VAL2     | TYPE2    | 30      |
//               +----------+----------+---------+
//
// Usage: GetAll("MyProgram") = { ( VAL0, TYPE0, DATA0 ), ( VAL1, TYPE1, DATA1 ), ( VAL2, TYPE2, DATA2 ) }.
//
{
	TRY
	{
		LONG lResult = 0;
		CString cstrNameFound(_T(""));
		TCHAR szName[MAX_NAME_LENGTH] = _T("");
		DWORD dwcbName= MAX_NAME_LENGTH;
		DWORD dwIndex = 0;
		DWORD dwType = MAX_PATH;
		BYTE btData[MAX_PATH];
		DWORD dwcbData = MAX_PATH;
		arrNameTypeData.SetSize(0, 1);
		arrNameTypeData.RemoveAll();
		while((lResult = ::RegEnumValue(
			m_hKey, 
			dwIndex++, 
            szName, 
            &dwcbName, 
            NULL, 
            &dwType, 
            btData, 
            &dwcbData) != ERROR_NO_MORE_ITEMS))
		{
			// Add name, type and data.
			SNameTypeData ntd;
			// The name.
			ntd.m_cstrName.Format(TEXT("%s"), szName);
			// The type and data.
			switch(dwType)
			{
				case REG_NONE: // No value type.
					ntd.m_cstrType = _T("REG_NONE");
					ntd.m_cstrData.Format(TEXT("%s"), _T("?"));
					break;
				case REG_SZ: // Unicode nul terminated string.
					ntd.m_cstrType = _T("REG_SZ");
					ntd.m_cstrData.Format(TEXT("%s"), (LPSTR)btData);
					break;
				case REG_EXPAND_SZ: // Unicode nul terminated string (with environment variable references).
					ntd.m_cstrType = _T("REG_EXPAND_SZ");
					ntd.m_cstrData.Format(TEXT("%s"), _T("?"));
					break;
				case REG_BINARY: // Free form binary.
					ntd.m_cstrType = _T("REG_BINARY");
					ntd.m_cstrData.Format(TEXT("%s"), _T("?"));
					break;
				case REG_DWORD: // 32-bit number (same as REG_DWORD_LITTLE_ENDIAN):
					ntd.m_cstrType = _T("REG_DWORD"); 
					ntd.m_cstrData.Format(TEXT("%d"), (int)*btData);
					break;
				case REG_DWORD_BIG_ENDIAN: // 32-bit number.
					ntd.m_cstrType = _T("REG_DWORD_BIG_ENDIAN");
					ntd.m_cstrData.Format(TEXT("%s"), _T("?"));
					break;
				case REG_LINK: // Symbolic Link (unicode).
					ntd.m_cstrType = _T("REG_LINK");
					ntd.m_cstrData.Format(TEXT("%s"), _T("?"));
					break;
				case REG_MULTI_SZ: // Multiple Unicode strings.
					ntd.m_cstrType = _T("REG_MULTI_SZ");
					ntd.m_cstrData.Format(TEXT("%s"), _T("?"));
					break;
				case REG_RESOURCE_LIST: // Resource list in the resource map.
					ntd.m_cstrType = _T("REG_RESOURCE_LIST");
					ntd.m_cstrData.Format(TEXT("%s"), _T("?"));
					break;
				case REG_FULL_RESOURCE_DESCRIPTOR: // Resource list in the hardware description.
					ntd.m_cstrType = _T("REG_FULL_RESOURCE_DESCRIPTOR");
					ntd.m_cstrData.Format(TEXT("%s"), _T("?"));
					break;
				case REG_RESOURCE_REQUIREMENTS_LIST:
					ntd.m_cstrType = _T("REG_RESOURCE_REQUIREMENTS_LIST");
					ntd.m_cstrData.Format(TEXT("%s"), _T("?"));
					break;
				case REG_QWORD: // 64-bit number (same as REG_QWORD_LITTLE_ENDIAN).
					ntd.m_cstrType = _T("REG_QWORD");
					ntd.m_cstrData.Format(TEXT("%s"), _T("?"));
					break;
				default:
					ntd.m_cstrType = _T("REG_UNKNOWN");
					ntd.m_cstrData.Format(TEXT("%s"), _T("?"));
					break;
			}
			// Append.
			arrNameTypeData.Add(ntd);
			// Next.
			dwcbName = MAX_NAME_LENGTH;
			dwType = MAX_PATH;
			dwcbData = MAX_PATH;
		}
	}
	CATCH_ALL(e)
	{
		WNDTRACEEXCEPTION_USERDLG("CRegistryAccess", "GetAll", "", e);
		THROW_LAST();
	}
	END_CATCH_ALL
}

const void CRegistryAccess::GetAllNames(CStringArray &arrNames)
// Return array of names. See also GetAll(...).
//
// Principle.
//
// HKEY_CURRENT_USER
//  +--- AppEvents
//  +--- Console
//  +--- ControlPanel
//  +--- Enviroment
//  +--- ...
//  +--- Software
//        +--- MyProgram
//               +----------+----------+---------+
//               | Name     | Type     | Data    |
//               +----------+----------+---------+
//               | VAL0     | TYPE0    | 10      |
//               | VAL1     | TYPE1    | 20      |
//               | VAL2     | TYPE2    | 30      |
//               +----------+----------+---------+
//
// Usage: GetAllNames("MyProgram") = { VAL0, VAL1, VAL2 }.
//
{
	TRY
	{
		CArray<CRegistryAccess::SNameTypeData, CRegistryAccess::SNameTypeData> arrNameTypeData;
		GetAll(arrNameTypeData);
		arrNames.SetSize(0, 1);
		arrNames.RemoveAll();
		for(int iId = 0; iId < arrNameTypeData.GetSize(); iId++)
		{
			CRegistryAccess::SNameTypeData ntd = arrNameTypeData.GetAt(iId);
			arrNames.Add(ntd.m_cstrName);
		}
	}
	CATCH_ALL(e)
	{
		WNDTRACEEXCEPTION_USERDLG("CRegistryAccess", "GetAllNames", "", e);
		THROW_LAST();
	}
	END_CATCH_ALL
}

const void CRegistryAccess::GetAllTypes(CStringArray &arrTypes)
// Return array of types. See also GetAll(...).Names
//
// Principle.
//
// HKEY_CURRENT_USER
//  +--- AppEvents
//  +--- Console
//  +--- ControlPanel
//  +--- Enviroment
//  +--- ...
//  +--- Software
//        +--- MyProgram
//               +----------+----------+---------+
//               | Name     | Type     | Data    |
//               +----------+----------+---------+
//               | VAL0     | TYPE0    | 10      |
//               | VAL1     | TYPE1    | 20      |
//               | VAL2     | TYPE2    | 30      |
//               +----------+----------+---------+
//
// Usage: GetAllTypes("MyProgram") = { TYPE0, TYPE1, TYPE2 }.
//
{
	TRY
	{
		CArray<CRegistryAccess::SNameTypeData, CRegistryAccess::SNameTypeData> arrNameTypeData;
		GetAll(arrNameTypeData);
		arrTypes.SetSize(0, 1);
		arrTypes.RemoveAll();
		for(int iId = 0; iId < arrNameTypeData.GetSize(); iId++)
		{
			CRegistryAccess::SNameTypeData ntd = arrNameTypeData.GetAt(iId);
			arrTypes.Add(ntd.m_cstrType);
		}
	}
	CATCH_ALL(e)
	{
		WNDTRACEEXCEPTION_USERDLG("CRegistryAccess", "GetAllTypes", "", e);
		THROW_LAST();
	}
	END_CATCH_ALL
}

const void CRegistryAccess::GetAllDatas(CStringArray &arrDatas)
// Return array of names. See also GetAll(...).
//
// Principle.
//
// HKEY_CURRENT_USER
//  +--- AppEvents
//  +--- Console
//  +--- ControlPanel
//  +--- Enviroment
//  +--- ...
//  +--- Software
//        +--- MyProgram
//               +----------+----------+---------+
//               | Data     | Type     | Data    |
//               +----------+----------+---------+
//               | VAL0     | TYPE0    | 10      |
//               | VAL1     | TYPE1    | 20      |
//               | VAL2     | TYPE2    | 30      |
//               +----------+----------+---------+
//
// Usage: GetAllDatas("MyProgram") = { 10, 20, 30 }.
//
{
	TRY
	{
		CArray<CRegistryAccess::SNameTypeData, CRegistryAccess::SNameTypeData> arrNameTypeData;
		GetAll(arrNameTypeData);
		arrDatas.SetSize(0, 1);
		arrDatas.RemoveAll();
		for(int iId = 0; iId < arrNameTypeData.GetSize(); iId++)
		{
			CRegistryAccess::SNameTypeData ntd = arrNameTypeData.GetAt(iId);
			arrDatas.Add(ntd.m_cstrData);
		}
	}
	CATCH_ALL(e)
	{
		WNDTRACEEXCEPTION_USERDLG("CRegistryAccess", "GetAllDatas", "", e);
		THROW_LAST();
	}
	END_CATCH_ALL
}


const bool CRegistryAccess::GetData(const CString &cstrName, DWORD &dwValue) // Ends up as a REG_DWORD.
// Return T on success and F if not.
{
	TRY
	{
		return GetDataDWORD(cstrName, dwValue);
	}
	CATCH_ALL(e)
	{
		WNDTRACEEXCEPTION_USERDLG("CRegistryAccess", "GetData", "", e);
		THROW_LAST();
	}
	END_CATCH_ALL
}

const bool CRegistryAccess::GetData(const CString &cstrName, CString& cstrValue) // Ends up as a REG_SZ.
// Return T on success and F if not.
{
	TRY
	{
		return GetDataSZ(cstrName, cstrValue);
	}
	CATCH_ALL(e)
	{
		WNDTRACEEXCEPTION_USERDLG("CRegistryAccess", "GetData", "", e);
		THROW_LAST();
	}
	END_CATCH_ALL
}

const bool CRegistryAccess::GetData(const CString &cstrName, int &iValue) // Ends up as a REG_DWORD.
// Return T on success and F if not.
{
	TRY
	{
		DWORD dwValue = 0;
		const bool bResult = GetData(cstrName, dwValue);
		if(bResult)
			iValue = (int)dwValue;
		else
			iValue = 0;
		return bResult;
	}
	CATCH_ALL(e)
	{
		WNDTRACEEXCEPTION_USERDLG("CRegistryAccess", "GetData", "", e);
		THROW_LAST();
	}
	END_CATCH_ALL
}

const bool CRegistryAccess::GetData(const CString &cstrName, bool &bValue) // Ends up as a REG_DWORD.
// Return T on success and F if not.
{
	TRY
	{
		int iValue = 0;
		const bool bResult = GetData(cstrName, iValue);
		if(bResult)
			bValue = (iValue != 0);
		else
			bValue = false;
		return bResult;
	}
	CATCH_ALL(e)
	{
		WNDTRACEEXCEPTION_USERDLG("CRegistryAccess", "GetData", "", e);
		THROW_LAST();
	}
	END_CATCH_ALL
}

const bool CRegistryAccess::GetDataDWORD(const CString &cstrName, DWORD &dwValue)
// Return T on success and F if not.
{
	TRY
	{
		HKEY hKey = (HKEY)NULL;
		DWORD dwQueryType = REG_DWORD;
		DWORD dwQuerySize = sizeof(DWORD);
		dwValue = 0;
		LONG lResult = ::RegQueryValueEx(
			m_hKey, 
			m_bHiddenName ? GetLongName(cstrName) : cstrName, // Make name long if hidden.
			NULL, 
			&dwQueryType, 
			(LPBYTE)&dwValue, 
			&dwQuerySize);
		return(lResult == ERROR_SUCCESS);
	}
	CATCH_ALL(e)
	{
		WNDTRACEEXCEPTION_USERDLG("CRegistryAccess", "GetDataDWORD", "", e);
		THROW_LAST();
	}
	END_CATCH_ALL
}

const bool CRegistryAccess::GetDataSZ(const CString &cstrName, CString &cstrValue)
// Return T on success and F if not.
{
	TRY
	{
		HKEY hKey = (HKEY)NULL;
		DWORD dwQueryType = REG_SZ;
		TCHAR szName[MAX_VALUE_LENGTH] = _T("");
		DWORD dwQuerySize = MAX_VALUE_LENGTH - 1;
		cstrValue.Empty();
		LONG lResult = ::RegQueryValueEx(
			m_hKey, 
			m_bHiddenName ? GetLongName(cstrName) : cstrName, // Make name long if hidden.
			NULL, 
			&dwQueryType, 
			(LPBYTE)szName, 
			&dwQuerySize);
		if(lResult == ERROR_SUCCESS)
		{
			cstrValue.Format(TEXT("%s"), szName);
			return true;
		}
		return false;
	}
	CATCH_ALL(e)
	{
		WNDTRACEEXCEPTION_USERDLG("CRegistryAccess", "GetDataSZ", "", e);
		THROW_LAST();
	}
	END_CATCH_ALL
}

const CString CRegistryAccess::GetLongName(const CString &cstrName)
// Return long name (for invisible entries).
{
	TRY
	{
		// 0         0         0         0          = 40
		// 0123456789012345678901234567890123456789
		// MAGGIEBARTLISAMARGEHOMERSIMPSONSPIDERPIG
		static const CString s_cstr255Chars(
				_T("MAGGIEBARTLISAMARGEHOMERSIMPSONSPIDERPIG")
				_T("MAGGIEBARTLISAMARGEHOMERSIMPSONSPIDERPIG")
				_T("MAGGIEBARTLISAMARGEHOMERSIMPSONSPIDERPIG")
				_T("MAGGIEBARTLISAMARGEHOMERSIMPSONSPIDERPIG")
				_T("MAGGIEBARTLISAMARGEHOMERSIMPSONSPIDERPIG")
				_T("MAGGIEBARTLISAMARGEHOMERSIMPSONSPIDERPIG")
				_T("ITCHYORSCRATCHY"));
		CString cstrNameLong(s_cstr255Chars + cstrName);
		return cstrNameLong;
	}
	CATCH_ALL(e)
	{
		WNDTRACEEXCEPTION_USERDLG("CRegistryAccess", "GetLongName", "", e);
		THROW_LAST();
	}
	END_CATCH_ALL
}

HKEY CRegistryAccess::OpenKey(const CString &cstrKey)
// Return opned key handle on success and NULL if not
//
// Principle.
//
// HKEY_CURRENT_USER
//  +--- AppEvents
//  +--- Console
//  +--- ControlPanel
//  +--- Enviroment
//  +--- ...
//  +--- Software
//        +--- MyProgram <--- Open this.
//               +----------+----------+---------+
//               | Name     | Type     | Data    |
//               +----------+----------+---------+
//               | VAL0     | TYPE0    | 10      |
//               | VAL1     | TYPE1    | 20      |
//               | VAL2     | TYPE2    | 30      |
//               +----------+----------+---------+
//
// Usage: OpenKey("MyProgram").
//
{
	TRY
	{
		HKEY hKeyNew = (HKEY)NULL;
		LONG lResult = ::RegOpenKeyEx(
			HKEY_CURRENT_USER, 
			cstrKey, 
			0, 
			KEY_FULL_ACCESS, 
			&hKeyNew);
		if(lResult == ERROR_SUCCESS)
			return hKeyNew;
		return NULL;
	}
	CATCH_ALL(e)
	{
		WNDTRACEEXCEPTION_USERDLG("CRegistryAccess", "OpenKey", "", e);
		THROW_LAST();
	}
	END_CATCH_ALL
}

const bool CRegistryAccess::SetData(const CString &cstrName, CONST DWORD &dwValue) // Ends up as a REG_DWORD.
// Return T on deleted data and F if not.
{
	TRY
	{
		return SetDataDWORD(cstrName, dwValue);
	}
	CATCH_ALL(e)
	{
		WNDTRACEEXCEPTION_USERDLG("CRegistryAccess", "SetData", "", e);
		THROW_LAST();
	}
	END_CATCH_ALL
}

const bool CRegistryAccess::SetData(const CString &cstrName, const CString &cstrValue) // Ends up as a REG_SZ.
// Return T on deleted data and F if not.
{
	TRY
	{
		return SetDataSZ(cstrName, cstrValue);
	}
	CATCH_ALL(e)
	{
		WNDTRACEEXCEPTION_USERDLG("CRegistryAccess", "SetData", "", e);
		THROW_LAST();
	}
	END_CATCH_ALL
}

const bool CRegistryAccess::SetData(const CString &cstrName, const int &iValue) // Ends up as a REG_DWORD.
// Return T on deleted data and F if not.
{
	TRY
	{
		return(SetData(cstrName, (DWORD)iValue));
	}
	CATCH_ALL(e)
	{
		WNDTRACEEXCEPTION_USERDLG("CRegistryAccess", "SetData", "", e);
		THROW_LAST();
	}
	END_CATCH_ALL
}

const bool CRegistryAccess::SetData(const CString &cstrName, const bool &bValue) // Ends up as a REG_DWORD.
// Return T on deleted data and F if not.
{
	TRY
	{
		const int iValue = (bValue ? 1 : 0);
		return(SetData(cstrName, iValue));
	}
	CATCH_ALL(e)
	{
		WNDTRACEEXCEPTION_USERDLG("CRegistryAccess", "SetData", "", e);
		THROW_LAST();
	}
	END_CATCH_ALL
}

const bool CRegistryAccess::SetDataDWORD(const CString &cstrName, CONST DWORD &dwValue)
// Return T on success and F if not.
//
// Principle.
//
// HKEY_CURRENT_USER
//  +--- AppEvents
//  +--- Console
//  +--- ControlPanel
//  +--- Enviroment
//  +--- ...
//  +--- Software
//        +--- MyProgram
//               +----------+----------+---------+
//               | Name     | Type     | Data    |
//               +----------+----------+---------+
//               | VAL0     | DWORD    | 10      | <--- Set this.
//               | VAL1     | SZ       | "Hey"   |
//               | VAL2     | TYPE2    | 30      |
//               +----------+----------+---------+
//
// Usage: SetDataDWORD("VAL0", 10);
//
{
	TRY
	{
		LONG lResult = ::RegSetValueEx(
			m_hKey, 
			m_bHiddenName ? GetLongName(cstrName) : cstrName, // Make name long if hidden.
			0, 
			REG_DWORD, 
			(BYTE*)&dwValue, 
			sizeof(PDWORD));
		return(lResult == ERROR_SUCCESS);
	}
	CATCH_ALL(e)
	{
		WNDTRACEEXCEPTION_USERDLG("CRegistryAccess", "SetDataDWORD", "", e);
		THROW_LAST();
	}
	END_CATCH_ALL
}

const bool CRegistryAccess::SetDataSZ(const CString &cstrName, const CString &cstrValue)
// Return T on success and F if not.
//
// Principle.
//
// HKEY_CURRENT_USER
//  +--- AppEvents
//  +--- Console
//  +--- ControlPanel
//  +--- Enviroment
//  +--- ...
//  +--- Software
//        +--- MyProgram
//               +----------+----------+---------+
//               | Name     | Type     | Data    |
//               +----------+----------+---------+
//               | VAL0     | DWORD    | 10      |
//               | VAL1     | SZ       | "Hey"   | <--- Set this.
//               | VAL2     | TYPE2    | 30      |
//               +----------+----------+---------+
//
// Usage: SetDataSZ("VAL1", "Hey");
//
{
	TRY
	{
		
		CString cstrValueEx(*const_cast<CString*>(&cstrValue));
		LONG lResult = ::RegSetValueEx(
			m_hKey, 
			m_bHiddenName ? GetLongName(cstrName) : cstrName, // Make name long if hidden.
			0, 
			REG_SZ, 
			(BYTE*)cstrValueEx.GetBuffer(), 
			(DWORD)(sizeof(TCHAR) * (cstrValueEx.GetLength() + 1)));
		return(lResult == ERROR_SUCCESS);
	}
	CATCH_ALL(e)
	{
		WNDTRACEEXCEPTION_USERDLG("CRegistryAccess", "SetDataSZ", "", e);
		THROW_LAST();
	}
	END_CATCH_ALL
}

// ------------------------------------------------------------------------------------------------------------------------------------------------------
// - CNTPClient.																																		-
// ------------------------------------------------------------------------------------------------------------------------------------------------------
// 1. Constructors. (alphabetical).
CNTPClient::CNTPClient() :
	m_dwTimeout(5000)
	// Create.
	{}

// 3. Methods. (alphabetical).
const bool CNTPClient::GetServerTime(const CString &cstrHostName, SNTPServerResponse& sntpResponse, const int iPort)
{
	// Create.
	CNTPSocket* pSocket = new CNTPSocket();
	if(!pSocket->Create())
	{
		DELETE_AND_DESTROY(pSocket);
		return false;
	}
	// Connect to SNTP server.
	if(!pSocket->Connect(cstrHostName, iPort))
	{
		DELETE_AND_DESTROY(pSocket);
		return false;
	}
	// Initialise the SNTPBasicInfo packet.
	SNTPBasicInfo sntpBasicInfo;
	int nSendSize = sizeof(SNTPBasicInfo);
	ZeroMemory(&sntpBasicInfo, nSendSize);
	// Encoded representation which represents NTP Client Request & NTP version 3.0.
	sntpBasicInfo.m_LiVnMode = 27;
	// Send.
	if(!pSocket->Send((LPCSTR)&sntpBasicInfo, nSendSize))
	{
		DELETE_AND_DESTROY(pSocket);
		return false;
	}
	// Need to determine readibilty of socket.
	if(!pSocket->IsReadable(m_dwTimeout))
	{
		DELETE_AND_DESTROY(pSocket);
		return false;
	}
	// Read back the response into the SNTPFullPacket.
	SNTPFullPacket sntpFullPacket;
	const int iReceiveSize = sizeof(SNTPFullPacket);
	::ZeroMemory(&sntpFullPacket, iReceiveSize);
	if(!pSocket->Receive((LPSTR)&sntpFullPacket, iReceiveSize))
	{
		DELETE_AND_DESTROY(pSocket);
		return false;
	}
	// Set result.
	sntpResponse.m_ReceiveTime = sntpFullPacket.m_Basic.m_ReceiveTimestamp;
	// Normal exit.
	DELETE_AND_DESTROY(pSocket);
	return true;
}

// ------------------------------------------------------------------------------------------------------------------------------------------------------
// - CNTPSocket.																																		-
// ------------------------------------------------------------------------------------------------------------------------------------------------------
// 1. Constructors. (alphabetical).
CNTPSocket::CNTPSocket() : 
	m_hSocket(INVALID_SOCKET)
	// Create.
	{}

CNTPSocket::~CNTPSocket()
// Destroy.
{ Close(); }

// 3. Methods. (alphabetical).
const void CNTPSocket::Close()
// Close socket.
{
	if(m_hSocket != INVALID_SOCKET)
	{
		VERIFY(SOCKET_ERROR != ::closesocket(m_hSocket));
		m_hSocket = INVALID_SOCKET;
	}
}

const bool CNTPSocket::Create()
// Create the socket. NTP uses UDP instead of the usual TCP.
{
	m_hSocket = socket(AF_INET, SOCK_DGRAM, 0);
	return(m_hSocket != INVALID_SOCKET);
}

const bool CNTPSocket::Connect(const CString &cstrHostAddress, const int iPort)
// Connect socket.
{
	ASSERT(m_hSocket != INVALID_SOCKET);
	USES_CONVERSION;
	LPSTR lpszHostAddress = T2A((LPTSTR)(LPCTSTR)cstrHostAddress);
	//Determine if the address is in dotted notation.
	SOCKADDR_IN sockAddr;
	::ZeroMemory(&sockAddr, sizeof(sockAddr));
	sockAddr.sin_family = AF_INET;
	sockAddr.sin_port = htons((u_short)iPort);
	sockAddr.sin_addr.s_addr = inet_addr(lpszHostAddress);
	//If the address is not dotted notation, then do a DNS lookup of it.
	if (sockAddr.sin_addr.s_addr == INADDR_NONE)
	{
		LPHOSTENT lphost;
		lphost = ::gethostbyname(lpszHostAddress);
		if (lphost != NULL)
			sockAddr.sin_addr.s_addr = ((LPIN_ADDR)lphost->h_addr)->s_addr;
		else
			return false;
	}
	// Call the protected version which takes an address in the form of a standard C style struct.
	return(Connect((SOCKADDR*)&sockAddr, sizeof(sockAddr)));
}

const bool CNTPSocket::Connect(const SOCKADDR* lpSockAddr, const int iSockAddrLen)
// Connect socket.
{
	ASSERT(m_hSocket != INVALID_SOCKET);
	int nConnect = connect(m_hSocket, lpSockAddr, iSockAddrLen);
	return(nConnect == 0);
}

const bool CNTPSocket::IsReadable(const DWORD dwTimeout)
// Return T if readable and F if not.
{
	timeval timeout;
	timeout.tv_sec = dwTimeout / 1000;
	timeout.tv_usec = dwTimeout % 1000;
	fd_set fds;
	FD_ZERO(&fds);
	FD_SET(m_hSocket, &fds);
	int iStatus = select(0, &fds, NULL, NULL, &timeout);
	if(iStatus == SOCKET_ERROR)
		return false;
	else
		return(iStatus != 0);
}

const int CNTPSocket::Receive(LPSTR pszBuffer, const int iLength)
// Read recieved data on socket. Return number of bytes received on success.
{
	ASSERT(m_hSocket != INVALID_SOCKET);
	return(::recv(m_hSocket, pszBuffer, iLength, 0)); 
}

const bool CNTPSocket::Send(LPCSTR pszBuffer, const int iLength)
// Send data to socket.
{
	ASSERT(m_hSocket != INVALID_SOCKET);
	return(::send(m_hSocket, pszBuffer, iLength, 0) != SOCKET_ERROR);
}

// ------------------------------------------------------------------------------------------------------------------------------------------------------
// - CProgramRegistration.																																-
// ------------------------------------------------------------------------------------------------------------------------------------------------------
const CString CProgramRegistration::sm_cstrSNDummy = _T("00000-00000-00000-00000-00000-00000");
const CString CProgramRegistration::sm_cstrFirstRunRegKeyPostfix = _T("_FIRST_RUN");
const CString CProgramRegistration::sm_cstrSNRegKeyPostfix = _T("_SN");

// 1. Constructors. (alphabetical).
CProgramRegistration::CProgramRegistration(const CString &cstrAppName, const int iDaysToUseFreeOfCharge, const bool bHidden) : 
	m_cstrAppName(cstrAppName), 
	m_iDays2UseFreeOfCharge(iDaysToUseFreeOfCharge), 
	m_pregAccess(NULL)
	// Create.
	{
		try
		{
			// Get official UTC-time.
			if(!::GetInternetTime(m_dtNow))
			{
				// Abort.
				AfxThrowUserException();
			}
			// From now on m_dtNow are the official UTC time!
			m_pregAccess = new CRegistryAccess(_T("Console"), _T(""), bHidden);
			VERIFY(m_pregAccess);
			m_cstrAppName.MakeUpper();
			// If first-time-run are present we read it back and if not we write it as m_dtNow.
			if(!GetFirstRunDate(m_dtFirstRun))
			{
				m_dtFirstRun = m_dtNow;
				SetFirstRunDate(m_dtFirstRun);
				UnRegister();
			}
			// Calc. expire date.
			m_dtExpire = m_dtFirstRun + COleDateTimeSpan(m_iDays2UseFreeOfCharge, 0, 0, 0);
		}
		catch(CUserException *pe)
		{
			pe->ReportError();
			pe->Delete();
		}
		catch(CException *pe)
		{
			pe->ReportError();
		}
	}

CProgramRegistration::~CProgramRegistration()
// Destroy.
{
	DELETE_AND_DESTROY(m_pregAccess);
}

// 3. Methods. (alphabetical).
const int CProgramRegistration::GetDaysLeft()
// Return number of days left in the trial period.
{
	int iDaysLeft = 0;
	if(m_dtNow < m_dtExpire)
		iDaysLeft = ::GetTimeDiffInDays(m_dtNow, m_dtExpire);
	return iDaysLeft;
}

const bool CProgramRegistration::GetFirstRunDate(COleDateTime &dtFirstRun)
// Return date of first run.
{
	CString cstrTime(_T(""));
	if(m_pregAccess->GetData(GetFirstRunRegName(), cstrTime))
	{
		if(cstrTime.GetLength() == 6)
		{
			// Format is expected to be "YYMMDD".
			const CString cstrYY(cstrTime.Left(2));
			const CString cstrMM(cstrTime.Mid(2, 2));
			const CString cstrDD(cstrTime.Right(2));
			int iYear = ::_wtoi((LPCTSTR)cstrYY) + 2000;
			int iMonth = ::_wtoi((LPCTSTR)cstrMM);
			int iDay = ::_wtoi((LPCTSTR)cstrDD);
			dtFirstRun = COleDateTime(iYear, iMonth, iDay, 0, 0, 0);
			return true;
		}
	}
	return false;
}

const bool CProgramRegistration::GetSerialNumber(CString &cstrSN)
// Return SN.
{
	cstrSN.Empty();
	return(m_pregAccess->GetData(GetSNRegName(), cstrSN));
}

const bool CProgramRegistration::IsLegalCopy()
// Return T if program is a legal copy and F if not.
{
	if(IsRegistred() || GetDaysLeft() > 0)
		return true;
	return false;
}

const bool CProgramRegistration::IsRegistred()
// Return T if program is registred and F if not.
{
	CString cstrSN(_T(""));
	GetSerialNumber(cstrSN);
	return(cstrSN.CompareNoCase(sm_cstrSNDummy) != 0);
}

const void CProgramRegistration::RemoveRegistration()
// Remove all program registration.
{
	m_pregAccess->DeleteData(GetFirstRunRegName());
	m_pregAccess->DeleteData(GetSNRegName());
}

const bool CProgramRegistration::Register(const CString &cstrSN)
// Write SN.
{ return(m_pregAccess->SetData(GetSNRegName(), cstrSN)); }

const bool CProgramRegistration::SetFirstRunDate(const COleDateTime &dtFirstRun)
// Set time of first run.
{
	CString cstrTime(_T(""));
	m_dtFirstRun = dtFirstRun;
	cstrTime = ::GetDateAsNiceHumanReadableString(m_dtFirstRun, true);
	return(m_pregAccess->SetData(GetFirstRunRegName(), cstrTime));
}

const bool CProgramRegistration::UnRegister()
// Write dummy SN.
{ return(m_pregAccess->SetData(GetSNRegName(), sm_cstrSNDummy)); }

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, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior)
Denmark Denmark
c/c++/c# -developer.

Comments and Discussions