Click here to Skip to main content
15,881,092 members
Articles / Programming Languages / C++

The Windows Access Control Model Part 4

Rate me:
Please Sign up or sign in to vote.
4.86/5 (29 votes)
7 Sep 200543 min read 227.8K   6.8K   100  
The final article in the access control series presents a guide to the access control editor and its associated ISecurityInformation interface.
/**
*	This code is by OShah. all code will have the following licence.
*	Copyright Shexec32. All code bears the following licence:
**/

/**
*	Copyright Shexec32 2004-2005. All rights reserved.
*
*	THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
*	ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED
*	TO THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
*	PARTICULAR PURPOSE.
**/



/** Registry_Value.h. Contains the reg class by D. Anderson and OShah.
*	D. Anderson's code is apparently public domain.
**/
#ifndef REGISTRY_VALUE_H
#define REGISTRY_VALUE_H
#pragma once

class registry_value
{/**
*	This is the reg class provided by D. Anderson (From codeproject).
*	This code was altered in 2004 by OShah to use CStdStrs instead of std::strings.
*	I have completed the exercise to make the class unicode-aware.
*	Also, the class throws exceptions instead of returning status values.
*
*	This class does not work for HKEY_PERFORMANCE_DATA. It will except if you attempt this.
**/
public:
	registry_value(HKEY base_ = HKEY_CURRENT_USER, const std::basic_string<TCHAR> & name = _T("Software\\Shexec32\\FilePermsBox"))
		: key(0), base(base_), keyname(name), TError(_T(""))
	{/* [Default] constructor. Stores the key name in base\name, using the init helper function. */
		::InitializeCriticalSection(&RegSection);
	};

	registry_value(const registry_value &OldClass)
		: key(0), base(OldClass.base), keyname(OldClass.keyname)
	{/* Copy constructor and assignment operator. Initialize our own critical section and Feed Terror into here. */
		TError << OldClass.TError.str().c_str();
		::InitializeCriticalSection(&RegSection);
	};

	registry_value &operator=(const registry_value &OldClass)
	{/* Assignment operator. Erase this object and copy the new contents into here. */
		key = 0;
		base = OldClass.base;
		keyname = OldClass.keyname;
		TError << OldClass.TError.str().c_str();
	};

	virtual void InitPath(HKEY base_ = HKEY_CURRENT_USER, const std::basic_string<TCHAR> & name = _T("Software\\Shexec32\\FilePermsBox"))
	{/* [Re-]Constructor Helper function. Recycle this object for another cause. */
		::EnterCriticalSection(&RegSection);
		{
			if(key != 0) ::RegCloseKey(key);
			key = 0;
		}
		::LeaveCriticalSection(&RegSection);

		base = base_;
		keyname = name;
	};

	virtual HKEY get_key(REGSAM samDesired = KEY_WRITE | KEY_READ)
	{/* Accessor for key. Transfer of ownership semantics. */
		::EnterCriticalSection(&RegSection);
		{/* Caution: do not call any other methods for this object once you do this. They will invalidate this HKEY. */
			if(key != 0) ::RegCloseKey(key);
			key = 0;
			::RegOpenKeyEx(base, keyname.c_str(), 0, samDesired, &key);
		}
		::LeaveCriticalSection(&RegSection);
		return key;
	};

	virtual ~registry_value()
	{/* Destructor. Closes the key when finished. */
		::EnterCriticalSection(&RegSection);
		{
			if(key != 0) ::RegCloseKey(key);
			key = 0;
		}
		::LeaveCriticalSection(&RegSection);
		::DeleteCriticalSection(&RegSection);
	};

	virtual const std::basic_stringstream<TCHAR> &get_TError(void)
	{/* TError accessor */
		return TError;
	};

	BOOL exists(REGSAM samDesired = KEY_READ)
	{/* Tests for access */
		DWORD dwErr = open(samDesired);

		::EnterCriticalSection(&RegSection);
		{
			if(key != 0) ::RegCloseKey(key);
			key = 0;
		}
		::LeaveCriticalSection(&RegSection);

		return (dwErr==ERROR_SUCCESS);
	};

	DWORD create(REGSAM samDesired = KEY_READ | KEY_WRITE)
	{/* If we want write access, we can just create the key. The key will open up once we do it. */
		DWORD RegErr = 0;
		DWORD disposition = 0;

		{/* Determine if we need to create it deep. */
			size_t Anchor = keyname.find_last_of(_T("\\"));
			if(Anchor < keyname.size())
			{/* It was a child. Get its parent. */
				std::basic_string<TCHAR> keynameold = keyname;/* save off the old string. */
				keyname = keyname.substr(0, Anchor);

				if( !exists(KEY_READ) )
				{/* The parent don't exist, create the parent. */
					create(samDesired);
				}/* It does exist (doesn't matter if we can't access it). Carry on creating us. */
				keyname = keynameold;
			}
		}

		::EnterCriticalSection(&RegSection);
		{/* Create key with RegCreateKeyEx() */
			if(key != 0) ::RegCloseKey(key);
			key = 0; /* Close the old key (if applicable) */
			RegErr = ::RegCreateKeyEx(base, keyname.c_str(), 0, _T(""), REG_OPTION_NON_VOLATILE,
				samDesired,	0, &key, &disposition);
		}
		::LeaveCriticalSection(&RegSection);

		::SetLastError(RegErr);	/* Make Reg error consistent with Win32 */
		if(RegErr != ERROR_SUCCESS)
			TError << keyname.c_str() << _T(": This key could not be created. ")
				<< static_cast<const TCHAR *>(::GetLastErrorText(RegErr)) << std::endl;

		::EnterCriticalSection(&RegSection);
		{/* We no longer need this key. close it. */
			if(key != 0) ::RegCloseKey(key);
			key = 0;
		}
		::LeaveCriticalSection(&RegSection);

		return RegErr;
	};

	DWORD remove_value(const std::basic_string<TCHAR> &ValueName)
	{
		DWORD dwErr = 0;
		if(ValueName.empty() == true)
			throw STD(_T("Cannot delete default value"));

		/* get a handle to the key */
		dwErr = open(KEY_READ | KEY_WRITE | DELETE);
		if(dwErr != ERROR_SUCCESS)/* status? */
			return dwErr;

		::EnterCriticalSection(&RegSection);
		{/* Call RegDeleteValue to delete it */
			dwErr = ::RegDeleteValue(key, ValueName.c_str());
			if(dwErr == ERROR_FILE_NOT_FOUND) dwErr = ERROR_SUCCESS;
			if(dwErr != ERROR_SUCCESS)
				TError << keyname.c_str() << _T("\\") << ValueName.c_str() << _T(": this value could not be deleted. ")
					<< static_cast<const TCHAR *>(::GetLastErrorText(dwErr)) << std::endl;
		}
		::LeaveCriticalSection(&RegSection);
		return dwErr;
	};

	DWORD remove_key(BOOL bRecurse)
	{/* Remove key. bRecurse indicates whether we should delete subkeys too. */
		DWORD dwErr = 0;
		::EnterCriticalSection(&RegSection);
		{
			if(key != 0)
			{/* Close the old key (if applicable). */
				::RegCloseKey(key);
				key = 0;
			}

			if(bRecurse == TRUE)
			{/* Remove subkeys too. */
				try {/**Not Much of a worry, shlwapi is already mapped into the process,
					*	so it won't make much difference
					**/
					dwErr = RemoveKeyRecursive(base, keyname);
					if(dwErr == ERROR_FILE_NOT_FOUND) dwErr = ERROR_SUCCESS;
					if(dwErr == ERROR_SUCCESS)
					{/* All done, let's leave. */
						::LeaveCriticalSection(&RegSection);
						base = 0;
						key = 0;
						return dwErr;
					}
				} catch(...) {/* If Shlwapi is unavailable, use older advapi32 instead. */
				}
			}
			else
			{/* Use Older advapi32 (has different behaviour on Win9x and NT). */
				::RegDeleteKey(base, keyname.c_str());
				if(dwErr == ERROR_FILE_NOT_FOUND) dwErr = ERROR_SUCCESS;
				if(dwErr != ERROR_SUCCESS)
					TError << keyname.c_str() << _T(": this key could not be deleted. ")
						<< static_cast<const TCHAR *>(GetLastErrorText(dwErr)) << std::endl;
			}
		}
		::LeaveCriticalSection(&RegSection);
		base = 0;
		key = 0;
		return dwErr;
	};

protected:

	DWORD RemoveKeyRecursive(HKEY hKey, const std::basic_string<TCHAR> &KeyName)
	{/** This is a static method that is called in the context of the critical section.
	*	It serves as a emulator (or wrapper if available) for SHLWAPI's SHDeleteKey().
	*	Returns the error from the registry of the last operation.
	**/
		DWORD dwErr = 0;

		try {
			dwErr = ::SHDeleteKey(hKey, KeyName.c_str());
		} catch (...) {/* shlwapi not found, or old. */
			HKEY hKey2 = NULL;
			dwErr = ::RegOpenKeyEx(hKey, KeyName.c_str(), 0, KEY_READ | DELETE, &hKey2);
			if(dwErr != ERROR_SUCCESS)
			{/* so that's why we failed. */
				return dwErr;
			}
			FILETIME LastWriteTime = {0};

			/* We only want lpcSubKeys from RegQueryInfoKey */
			DWORD NumSubKeys;
			dwErr = ::RegQueryInfoKey(hKey2, NULL, NULL, NULL, &NumSubKeys, NULL, NULL, NULL, NULL, NULL,
				NULL, &LastWriteTime);
			if(dwErr != ERROR_SUCCESS)
			{/* Set the number of subkeys to 16383 (the biggest number in the msdn article: Registry limits). */
				NumSubKeys = 16383;
			}
			for(DWORD dwIndex = 0; dwIndex < NumSubKeys; dwIndex++)
			{
				DWORD dwSize = 0;
				::RegEnumKeyEx(hKey2, dwIndex, NULL, &dwSize, NULL, NULL, NULL, &LastWriteTime);
				/* We expect this to fail. */
				sized_array<TCHAR> lpName (dwSize);
				dwErr = ::RegEnumKeyEx(hKey2, dwIndex, lpName.get(), &dwSize, NULL, NULL, NULL, &LastWriteTime);
				if(dwErr == ERROR_SUCCESS)
				{/* delete depth first */
					std::basic_string<TCHAR> NewKeyName (KeyName);
					NewKeyName.append(_T("\\"));
					NewKeyName.append(lpName.get());
					dwErr = this->RemoveKeyRecursive(hKey2, NewKeyName);
				}
				else if(dwErr == ERROR_NO_MORE_ITEMS)
				{/* Now it is empty. RegDeleteKey now works. */
					break;
				}

				dwIndex++;/* and loop */
			}

			::RegCloseKey(hKey2); hKey2 = NULL;
			dwErr = ::RegDeleteKey(hKey, KeyName.c_str());
		}

		return dwErr;
	};

	DWORD open(REGSAM samDesired = KEY_READ)
	{/* Opens a key: (optionally writable, with write_access==true). */
		DWORD RegErr = 0;
		::EnterCriticalSection(&RegSection);
		{/* Call RegOpenKeyEx on the value. */
			if(key != 0)
			{/* Close the old key (if applicable). */
				::RegCloseKey(key);
				key = 0;
			}

			RegErr = ::RegOpenKeyEx(base, keyname.c_str(), 0, samDesired, &key);
			::SetLastError(RegErr); /* Make registry error tally with Win32. */

		}
		::LeaveCriticalSection(&RegSection);
		if(RegErr != ERROR_SUCCESS)
		{
			TError << keyname.c_str() << _T("could not open key. ") <<
				static_cast<const TCHAR *>(::GetLastErrorText(RegErr)) << std::endl;
		}
		return RegErr;
	};

	DWORD query_value(const std::basic_string<TCHAR> &ValueName, DWORD &type, DWORD &size, BYTE *buffer)
	{/* Caution: size will be returned in bytes (you may have to divide in sizeof() */
		DWORD dwErr = 0;
		if(key == 0) throw STD(_T("Tried to read from a uninitialised key"));
		::EnterCriticalSection(&RegSection);
		{
			dwErr = ::RegQueryValueEx(key, ValueName.c_str(), 0, &type, buffer, &size);
		}
		::LeaveCriticalSection(&RegSection);
		if(dwErr != ERROR_SUCCESS)
			TError << keyname.c_str() << _T("\\") << _T(": this value could not be read. ")
				<< static_cast<const TCHAR *>(::GetLastErrorText(dwErr)) << std::endl;
		return dwErr;
	};

	DWORD set_value(const std::basic_string<TCHAR> &ValueName, DWORD type, DWORD size, const BYTE * buffer)
	{/* Caution: size MUST be in bytes (use a sizeof()) */
		DWORD dwErr = 0;
		if(key == 0) throw STD(_T("Tried to set value on an uninitialised key"));
		::EnterCriticalSection(&RegSection);
		{
			dwErr = ::RegSetValueEx(key, ValueName.c_str(), 0, type, buffer, size);
		}
		::LeaveCriticalSection(&RegSection);
		if(dwErr != ERROR_SUCCESS)
			TError << keyname.c_str() << _T("\\") << _T(": this value could not be set. ")
				<< static_cast<const TCHAR *>(::GetLastErrorText(dwErr)) << std::endl;

		return dwErr;
	};


	HKEY key;
	HKEY base;
	std::basic_string<TCHAR> keyname;

	CRITICAL_SECTION RegSection;
	std::basic_stringstream<TCHAR> TError;
};



class registry_string : public registry_value
{
	public:
		registry_string(HKEY base, const std::basic_string<TCHAR> & name) : registry_value(base, name)
		{
		};

		std::basic_string<TCHAR> get_value(const std::basic_string<TCHAR> ValueName = _T(""))
		{
			std::basic_string<TCHAR> returnval;
			DWORD type = 0, size = 0, dwErr = 0;

			if(open(KEY_READ)) return _T("FilePermsBox Error");
			if(query_value(ValueName, type, size, 0) != ERROR_SUCCESS)
			{
				TError << keyname.c_str() << _T("\\") << ValueName.c_str() << _T(": could not read registry value");
				return _T("FilePermsBox Error");
			}
			if(type != REG_SZ) throw STD(_T("Registry value is not a string"));
			sized_array<TCHAR> data ( (size + 1)/ sizeof(TCHAR) );


			dwErr = query_value( ValueName, type, size, reinterpret_cast<BYTE *>(data.get()) );
			if(dwErr != ERROR_SUCCESS)
			{
				TError << keyname.c_str() << _T("") << ValueName.c_str() << ("Registry value contains unreliable data");
				return _T("FilePermsBox Error");
			}

			returnval = data.get();
			return returnval;
		};

		DWORD put_value(const std::basic_string<TCHAR> &ValueName = _T(""), const std::basic_string<TCHAR> & value = _T(""))
		{
			DWORD dwErr = open(KEY_READ | KEY_WRITE);
			if(dwErr != ERROR_SUCCESS) return dwErr;
			dwErr = set_value( ValueName, REG_SZ, DWORD(value.length() + 1) * sizeof(TCHAR), reinterpret_cast<const BYTE *>(value.c_str()) );
			return dwErr;
			/* Let it throw on error. */
		};
};


template<class T>
class registry_int : public registry_value
{
	public:
		registry_int(HKEY base, const std::basic_string<TCHAR> & name) : registry_value(base, name)
		{
		};

		const T get_value(const std::basic_string<TCHAR> &ValueName = _T(""))
		{
			DWORD returnval = DWORD(0);
			DWORD type = 0, size = sizeof(DWORD);

			open(KEY_READ);
			if(query_value(ValueName, &type, &size, &returnval) == ERROR_SUCCESS)
			{/* We already know how big this type is. */
				if(type != REG_DWORD) throw STD(_T("Registry value is not an integer"));
			}
			else throw STD(_T("Error reading integer registry value"));

			return T(returnval);
		};

		DWORD put_value(const std::basic_string<TCHAR> &ValueName = _T(""), const T & value)
		{
			DWORD data(value), dwErr;
			dwErr = open(KEY_READ|KEY_WRITE);
			if(dwErr != ERROR_SUCCESS) return dwErr;
			dwErr = set_value(ValueName, REG_DWORD, sizeof(DWORD), &data);
			return dwErr;
		};
};

#endif /* REGISTRY_VALUE_H */

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


Written By
Web Developer
United States United States
Mr. Shah is a reclusive C++/C# developer lurking somewhere in the depths of the city of London. He learnt physics at Kings' College London and obtained a Master in Science there. Having earned an MCAD, he teeters on the brink of transitioning from C++ to C#, unsure of which language to jump to. Fortunately, he also knows how to use .NET interop to merge code between the two languages (which means he won't have to make the choice anytime soon).

His interests (apart from programming) are walking, football (the real one!), philosophy, history, retro-gaming, strategy gaming, and any good game in general.

He maintains a website / blog / FAQ / junk at shexec32.serveftp.net, where he places the best answers he's written to the questions you've asked. If you can find him, maybe you can hire Mr. Shah to help you with anything C++[/CLI]/C#/.NET related Smile | :) .

Comments and Discussions