Click here to Skip to main content
15,892,480 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 229K   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.
**/

#include "../FilePermsBoxDLL/SecurityCore.h"
#include "FilePermissionsBox.h"

bool g_AddWritability = FALSE;	/* Application Setting (set by user). */


int APIENTRY _tWinMain(HINSTANCE hInst, HINSTANCE, LPTSTR lpCmdLine, int nCmdShow)
{/* This program dumps the acls for the service. */
#ifdef _DEBUG
	::_CrtSetDbgFlag(_CRTDBG_CHECK_ALWAYS_DF | _CRTDBG_LEAK_CHECK_DF |
		_CRTDBG_ALLOC_MEM_DF | ::_CrtSetDbgFlag(_CRTDBG_REPORT_FLAG));
#endif /* _DEBUG. This detects memory leaks */

	intptr_t Result = 0;	/* main will return this result. */

	try {
		std::basic_string<TCHAR> sCmdLine(lpCmdLine);
		CoAutoInitializer autoCom;

		::InitCommonControls();	/* Bring in Themed common controls */
		/* Our command line needs to be a case insensitive string. */

		if(CheckVersion() != TRUE)
		{/* Win2k warning (and other misbehaving compiler warning). */
			_bstr_t Win98Hack = _T("This program requires Windows 2000 or later. ")
				_T("It may not work on your system. Do you want to continue?");
			/* WORKAROUND: If OleAut32 isn't loaded, Win9x will crash due to delay load in DllMain(DLL_DETACH) */
			Result = ::MessageBox(NULL, static_cast<const TCHAR *>(Win98Hack), _T("FilePermBox"), MB_YESNO | MB_DEFBUTTON2);
			if(Result != IDYES)
			{
				return 0;
			}
		}


		::SetLastError(ERROR_SUCCESS);

		Result = ::ParseCmdLine(sCmdLine);	/* Send the command line off to the separate procedure */
		if(Result != 2)/* If result isn't 2, then we've done what we've needed to do, so quit. */
			return int(Result);

		{/* Otherwise, start the program */
			MSG msg = {0};
			HWND hWndDlg = ::CreateDialogParam(hInst, MAKEINTRESOURCE(IDD_DIALOG1), NULL,
				FileSelectBox, reinterpret_cast<LPARAM>(&sCmdLine));

			if (hWndDlg == NULL)
				throw STD(_T("Error creating dialog box"));

			HACCEL hAccelTable = ::LoadAccelerators(hInst, MAKEINTRESOURCE(IDR_ACCELERATOR1));
			if(hAccelTable == NULL)
				throw STD(_T("Error creating keyboard shortcuts"));

			::ShowWindow(hWndDlg, nCmdShow);
			::UpdateWindow(hWndDlg);

			/* Now for the Dialog Message Loop. */
			while (::GetMessage(&msg, NULL, 0, 0) > 0)
			{
				if(!::TranslateAccelerator(hWndDlg,hAccelTable,&msg))
				{
					if (hWndDlg == NULL || ::IsDialogMessage(hWndDlg, &msg) == FALSE)
					{
						::TranslateMessage(&msg);
						::DispatchMessage(&msg);
					}
				}
			}

			Result = msg.wParam;
			if(Result == 2)
			{/* 2 indicates we caught an exception. */
				throw STD(_T("An unknown error occurred with program"));
			}

		}

	} catch (const std::basic_string<TCHAR> &Tout) {/* One of our exceptions was caught. Complete the program */
		std::basic_stringstream<TCHAR> TError;
		TError << Tout << _T("\nThe Last Win32 Error is ") << ::GetLastError() << _T(": ") <<
			static_cast<const TCHAR *>(GetLastErrorText(::GetLastError()));
		::MessageBox(NULL, TError.str().c_str(), _T("Problem Detected - FilePermsBox"), MB_OK | MB_ICONERROR);
	}
#if !defined (_DEBUG)
	catch (...) {
		/** some other exception occurred, complete the program. Don't catch this in debug builds,
		*	Let the debugger handle it.
		**/
		std::basic_stringstream<TCHAR> TError;
		TError << _T("An error occurred in the program. FilePermsBox could not determine the nature of the problem.\n")
			<< _T("The Last Win32 Error is ") << ::GetLastError() << _T(": ") <<
			static_cast<const TCHAR *>(GetLastErrorText(::GetLastError()));
		::MessageBox(NULL, TError.str().c_str(), _T("Problem Detected - FilePermsBox"), MB_OK | MB_ICONERROR);
	}
#endif /* _DEBUG. Only in release builds do we catch all. */

	return 0;
}


INT_PTR CALLBACK FileSelectBox(HWND TheirhWnd, UINT message, WPARAM wParam, LPARAM lParam)
{/**	This frontend consists of a File input box (with browse button), where the user enters their file.
*	When the user presses OK, out pops the security tab for that file.
**/
	try {/* for catch (...) */
		switch (message)
		{
			case WM_INITDIALOG:
			{/* We'll need to apply resize properties. */

				/* Load the application icon into this dialog box. */
				HANDLE hIcon = ::LoadImage(::GetModuleHandle(NULL), MAKEINTRESOURCE(IDI_ICON1),
					IMAGE_ICON, 0, 0, LR_SHARED | LR_DEFAULTSIZE);
				::SetClassLongPtr(TheirhWnd, GCLP_HICON, LONG_PTR2(hIcon));

				if( !::IsBadReadPtr(reinterpret_cast<const void *>(lParam), sizeof(std::basic_string<TCHAR>)) )
				{/* lParam contains a pointer to sCmdLine in main(). Put sCmdLine into the edit box. */
					std::basic_string<TCHAR> *sCmdLinePtr = reinterpret_cast<std::basic_string<TCHAR> *>(lParam);
					::SetDlgItemText(TheirhWnd, IDC_EDIT1, sCmdLinePtr->c_str());
				}

				/* Put in the text. */
				::SetDlgItemText(TheirhWnd, IDC_TITLE,
					_T("Enter the name of a filesystem object to view its security descriptor and permissions."));
				::SetWindowText(TheirhWnd, _T("FilePermsBox - Filename Required"));

				/* Default to read only. */
				::CheckDlgButton(TheirhWnd, IDC_CHECK1, !g_AddWritability);
				::DragAcceptFiles(TheirhWnd, TRUE);

				{/* Finally, send a WM_SIZE message */
					RECT RectDlg = {0};
					::GetWindowRect(TheirhWnd, &RectDlg);
					::SetWindowPos(TheirhWnd, NULL, 0, 0, RectDlg.right - RectDlg.left + 1, 200, SWP_NOZORDER | SWP_NOMOVE);
				}
				return TRUE;
			}

			case WM_DROPFILES:
			{/* WM_DROPFILES - The user has dropped a file into our dialog. */
				int len = ::DragQueryFile(reinterpret_cast<HDROP>(wParam), 0, NULL, 0);
				if(len == 0) return FALSE;
				/* Get the required size and allocate a buffer of that size. */
				sized_array<TCHAR> sFName ( len + 1 );

				::DragQueryFile(reinterpret_cast<HDROP>(wParam), 0, sFName.get(), len + 1);

				/* Just the first file. */
				::DragFinish(reinterpret_cast<HDROP>(wParam));

				/* and put the first file into IDC_EDIT1 */
				::SetDlgItemText(TheirhWnd, IDC_EDIT1, sFName.get());
				return FALSE;
			}

			case WM_GETMINMAXINFO:
			{/* The Window needs to be 300x160 */
				MINMAXINFO *MinMaxInfo = reinterpret_cast<MINMAXINFO *>(lParam);
				MinMaxInfo->ptMinTrackSize.x = 300;
				MinMaxInfo->ptMinTrackSize.y = 160;
				MinMaxInfo->ptMaxTrackSize.y = 160;
				return TRUE;
			}

			case WM_SIZE:
			{/* WM_SIZE. */
				const int space = 7;
				RECT RectDlg = {0}, RectCtl = {0};
				int x = RectCtl.left;
				HDWP hdwp = ::BeginDeferWindowPos(7);	/* 7's a bit of an underflow. There's really only about 5. */

				{/* Get The Client area of the Window. */
					::GetWindowRect(TheirhWnd, &RectDlg);
					::MapWindowPoints(NULL, TheirhWnd, (LPPOINT)&RectDlg, sizeof(RECT) / sizeof(POINT));
					RectDlg.left += space;
					RectDlg.top += space;
					RectDlg.bottom -= space;
					RectDlg.right -= space;

					/* Resize IDC_TITLE to right of RectDlg. */
					::GetWindowRect(::GetDlgItem(TheirhWnd, IDC_TITLE), &RectCtl);
					::MapWindowPoints(NULL, TheirhWnd, (LPPOINT)&RectCtl, sizeof(RECT) / sizeof(POINT));
					RectCtl.left = space;
					RectCtl.right = RectDlg.right - RectDlg.left - RectCtl.left;
					::DeferWindowPos(hdwp, ::GetDlgItem(TheirhWnd, IDC_TITLE), NULL, RectCtl.left, RectCtl.top,
						RectCtl.right, RectCtl.bottom - RectCtl.top, SWP_NOCOPYBITS | SWP_NOZORDER);

					/* Move IDC_BUTTON1 to right of window (retain size). */
					::GetWindowRect(::GetDlgItem(TheirhWnd, IDC_BUTTON1), &RectCtl);
					::MapWindowPoints(NULL, TheirhWnd, (LPPOINT)&RectCtl, sizeof(RECT) / sizeof(POINT));
					RectCtl.left = RectDlg.right - RectCtl.right + RectCtl.left - space;
					::DeferWindowPos(hdwp, ::GetDlgItem(TheirhWnd, IDC_BUTTON1),
						NULL, RectCtl.left, RectCtl.top, 0, 0, SWP_NOSIZE | SWP_NOCOPYBITS | SWP_NOZORDER);

					/* Make IDC_EDIT1 fill up the rest of the space. */
					x = RectCtl.left - space;
					::GetWindowRect(::GetDlgItem(TheirhWnd, IDC_EDIT1), &RectCtl);
					::MapWindowPoints(NULL, TheirhWnd, (LPPOINT)&RectCtl, sizeof(RECT) / sizeof(POINT));
					RectCtl.left = space;
					RectCtl.right = x - RectDlg.left - RectCtl.left;
					::DeferWindowPos(hdwp, ::GetDlgItem(TheirhWnd, IDC_EDIT1), NULL, RectCtl.left, RectCtl.top,
						RectCtl.right, RectCtl.bottom - RectCtl.top, SWP_NOCOPYBITS | SWP_NOZORDER);

					/* Move IDCANCEL to bottom right of window. */
					::GetWindowRect(::GetDlgItem(TheirhWnd, IDOK), &RectCtl);
					::MapWindowPoints(NULL, TheirhWnd, (LPPOINT)&RectCtl, sizeof(RECT) / sizeof(POINT));
					RectCtl.left = RectDlg.right - RectCtl.right + RectCtl.left - space;
					RectCtl.top = RectDlg.bottom - RectCtl.bottom + RectCtl.top - space;
					::DeferWindowPos(hdwp, ::GetDlgItem(TheirhWnd, IDOK),
						NULL, RectCtl.left, RectCtl.top, 0, 0, SWP_NOSIZE | SWP_NOCOPYBITS | SWP_NOZORDER);
					x = RectCtl.left;

					/* Move IDC_ABOUT beside IDC_BUTTON1 (to the left). */
					::GetWindowRect(::GetDlgItem(TheirhWnd, IDC_ABOUT), &RectCtl);
					::MapWindowPoints(NULL, TheirhWnd, (LPPOINT)&RectCtl, sizeof(RECT) / sizeof(POINT));
					RectCtl.left = x - RectCtl.right + RectCtl.left - space;
					RectCtl.top = RectDlg.bottom - RectCtl.bottom + RectCtl.top - space;
					::DeferWindowPos(hdwp, ::GetDlgItem(TheirhWnd, IDC_ABOUT),
						NULL, RectCtl.left, RectCtl.top, 0, 0, SWP_NOSIZE | SWP_NOCOPYBITS | SWP_NOZORDER);

					/* IDC_COMBO1 Needs to be offset 10 pixels to the right. */
					x = RectCtl.left;
					::GetWindowRect(::GetDlgItem(TheirhWnd, IDC_CHECK1), &RectCtl);
					::MapWindowPoints(NULL, TheirhWnd, (LPPOINT)&RectCtl, sizeof(RECT) / sizeof(POINT));
					RectCtl.left = space + 10;
					RectCtl.right = x - RectCtl.left;
					::DeferWindowPos(hdwp, ::GetDlgItem(TheirhWnd, IDC_CHECK1), NULL, RectCtl.left, RectCtl.top,
						RectCtl.right, RectCtl.bottom - RectCtl.top, SWP_NOCOPYBITS | SWP_NOZORDER);
				}

				/* Now update the window */
				::EndDeferWindowPos(hdwp);
				::InvalidateRgn(TheirhWnd, NULL, FALSE);
				::UpdateWindow(TheirhWnd);
				return TRUE;
			}

			case WM_COMMAND:
			{
				switch(LOWORD(wParam))
				{
					case IDC_ABOUT:
					{
						ShowAbout(TheirhWnd);
						return FALSE;
					}
					case IDOK :
					{
						/* [QueryServiceConfig] Get The name of the service. */
						int len = 0;
						len = 1 + ::GetWindowTextLength(::GetDlgItem(TheirhWnd, IDC_EDIT1));
						if(len == 1)
						{/* For an empty box, exit the program. */
							::PostMessage(TheirhWnd, WM_CLOSE, 0, 0);
							return TRUE;
						}
						::sized_array<TCHAR> lpszString (len);
						::GetDlgItemText(TheirhWnd, IDC_EDIT1, lpszString.get(), len);
						/* Now that we've retrieved the filename, show it. */

						/* Disable all controls */
						EnableFrontendControls(TheirhWnd, FALSE);

						std::list< const std::basic_string<TCHAR> > FileNamesCollection;
						if((len > 3) && (lpszString.get()[len - 2] == _T('*')))
						{/* Partial WildCard support. Hand the filename to the external procedure. */
							if( lpszString.get()[len - 3] == _T('*') )
							{/* "**" means choose dirs. */
								lpszString.get()[len - 2] = _T('\0'); /* remove one of the zeros */
								WalkDirectory(lpszString.get(), FileNamesCollection, TRUE);
							}
							else WalkDirectory(lpszString.get(), FileNamesCollection, FALSE);
						}
						else
						{/* No wildcards mean just add the file to FileNamesCollection */
							FileNamesCollection.push_back(lpszString.get());
						}

						if( FileNamesCollection.size() > 0)
						{
							if(DoSecurityBox(TheirhWnd, SE_FILE_OBJECT, FileNamesCollection, g_AddWritability) != TRUE)
							{/* If a problem occurred, exit the program. */
								::PostMessage(TheirhWnd, WM_CLOSE, 0, 0);
							}
						}

						/* reenable all controls */
						EnableFrontendControls(TheirhWnd, TRUE);
						return TRUE;
					}
					case IDC_BUTTON1:
					{/** The browse button was selected, show the common dialog box.
					*	(note, an explorer control: use it to your advantage).
					**/
						OPENFILENAME ofn = {0};
						sized_array<TCHAR> lpFileNames ( 32768 );/* Big buffer for multi-select. */

						if(HandleOfnStruct(TheirhWnd, ofn, lpFileNames.get(), 32 * FILENAME_MAX) == TRUE)
							/* Now we can set the file text. */
							::SetDlgItemText(TheirhWnd, IDC_EDIT1, lpFileNames.get());
						return TRUE;
					}
					case IDC_CHECK1:
					{/* Alter the state of AddWritability. */
						g_AddWritability = !g_AddWritability;
						::CheckDlgButton(TheirhWnd, IDC_CHECK1, !g_AddWritability);
						/* Make sure the checkbox reflects the current state of AddWritability. */
						return TRUE;
					}
					default:
						break;
				}
				break;
			}

			case WM_CLOSE:
			{/* We're a dialog box: We call EndDialog. */
				::DestroyWindow(TheirhWnd);
				return FALSE;
			}
			case WM_DESTROY:
			{/* If we posted this from the exception, use PostQuitMessage() to send the result code to _tWinMain. */
				if(wParam == 2)
				{
					::PostQuitMessage(2);
				}
				else
				{
					::PostQuitMessage(0);
				}
				break;
			}
			default:
			{
				break;
			}
		}
#ifdef _DEBUG
	} catch(const std::basic_string<TCHAR> &) {/* An unknown error occurred, signal the main function. */
#else /* means: if defined NDEBUG */
	} catch(...) {
#endif /* _DEBUG */
		::PostMessage(TheirhWnd, WM_DESTROY, 2, 0);
	} /* two tries */
	return FALSE;
}


BOOL EnableFrontendControls(HWND TheirhWnd, BOOL bEnable)
{
	BOOL Result = TRUE;
	Result = Result & ::EnableWindow(::GetDlgItem(TheirhWnd, IDOK), bEnable);
	Result = Result & ::EnableWindow(::GetDlgItem(TheirhWnd, IDC_EDIT1), bEnable);
	Result = Result & ::EnableWindow(::GetDlgItem(TheirhWnd, IDC_CHECK1), bEnable);
	Result = Result & ::EnableWindow(::GetDlgItem(TheirhWnd, IDC_BUTTON1), bEnable);
	Result = Result & ::EnableWindow(::GetDlgItem(TheirhWnd, IDC_ABOUT), bEnable);
	return Result;
}




BOOL HandleOfnStruct(HWND TheirhWnd, OPENFILENAME &ofn, LPTSTR FileNames, size_t FileNamesLength)
{/**
*	This is ::HandleOfnStruct() taken from the Shexec32 program. The purpose of this function is to
*	create the common dialog box. If the browse button was selected, and the user chose his file(s)
*	to open, those files would be stored in ofn->lpstrFile.
*
*   ofn.lpstrFile only has 32 * FILENAME_MAX characters. There lies the possibility of a buffer overflow here.
*   This will occur if ::GetOpenFileName() returns a value greater than 32 * FILENAME_MAX characters [but it
*   should also return a ::CommDlgExtendedError() if that is the case].
*
**/
	DWORD ThisErr=0;
	BYTE DidRetry=0;
	if(::IsBadStringPtr(FileNames, FileNamesLength)) throw STD(_T("The FileNames parameter is incorrect"));

	ofn.lStructSize = sizeof(OPENFILENAME);   /* See MSDN notes about this member in Pre-Win2K systems. */
	ofn.Flags = OFN_ENABLESIZING | OFN_FILEMUSTEXIST | OFN_PATHMUSTEXIST | OFN_EXPLORER;


	ofn.Flags = ofn.Flags | OFN_ALLOWMULTISELECT;


OfnRetry:
	ofn.hwndOwner = TheirhWnd;
	ofn.lpstrFile = FileNames;
	ofn.nMaxFile = static_cast<DWORD>(FileNamesLength);/* Bad Commdlg, Bad! */

	ofn.lpstrFileTitle = NULL;
	ofn.nMaxFileTitle = 0;
	/* Fill out filters for text documents, executables and all files. */
	ofn.lpstrFilter = _T("All Files (*.*)\0*.*\0\0");
	ofn.nFilterIndex = 2;
	/* REMEMBER: When you alter ofn->lpstrFilter, don't forget to update ofn->nFilterIndex. */

	ofn.lpstrInitialDir = NULL;
	ofn.lpfnHook = NULL;
	/* Only allow existent files, allow any number and use the new style open dialogs. */

	ThisErr = ::GetOpenFileName(&ofn);

	FileNames = ofn.lpstrFile;
	/** Handle the filename once received from GetOpenFileName(). TODO: We may want to assert whether
	*	the files exist. That TODO is not necessary in FilePermsBox (The assertion takes place elsewhere).
	**/
	if(ThisErr != FALSE)
		return TRUE;
	else
	{/** GetOpenFileName() returned an error. FileNames should always be the same as
	*	ofn.lpstrFile regardless.
	**/
		switch(::CommDlgExtendedError())
		{/* Handle specific cases where GetOpenFileName() failed. eg. CDERR_DIALOGFAILURE */
			case CDERR_INITIALIZATION:
			{/* Initialisation Failed. Happens on old Windows Versions. */
				::SetLastError(ERROR_SUCCESS);
				if(! (DidRetry & 1) )
				{/* Give the flags a windows 3.x style dialog box. */
					ThisErr |= 1;
					ofn.Flags = OFN_FILEMUSTEXIST | OFN_PATHMUSTEXIST;
					goto OfnRetry;
				}
				else
				{/* That didn't help; simply inform the user that this functionality is unavailable. */
					throw STD(_T("Error creating \"open...\" common dialog box"));
				}/* Clean Up. */

			}

			case CDERR_MEMALLOCFAILURE:
			case FNERR_SUBCLASSFAILURE:
			case CDERR_MEMLOCKFAILURE:
			{/* Bad Memory. */
				throw STD(_T("Could not browse file system due to memory or window creation problem"));
			}
			case CDERR_STRUCTSIZE:
			{/**    The structsize member is not the correct size. This occurs because in Win2K+,
			*   the ofn structure was changed to something bigger. However, this new structure does not
			*   work in earlier windows versions. When we call ::GetOpenFileName(), it will return this
			*   error. If we are trying this Win2K+ application in Win9x-, we'll need to call
			*   ::GetOpenFileName() with the value sizeof(ofn) Win9x- is expecting.
			*   OPENFILENAME_SIZE_VERSION_400 will help us here. It returns the sizeof(OPENFILENAME)
			*   we would get if the app was targeted for Win9x-.
			**/
				if( ! (ThisErr & (1 << 1)))
				{
					::SetLastError(ERROR_SUCCESS);
					DidRetry |= 1 << 1;
					ofn.lStructSize = OPENFILENAME_SIZE_VERSION_400;
					goto OfnRetry;
				}
			}
			case CDERR_GENERALCODES:
			{/* General Codes. (Happens when user presses cancel). Continue normally in this case. */
				return FALSE;
			}
			case FNERR_INVALIDFILENAME:
			case FNERR_BUFFERTOOSMALL:
			{/* BIG_STRING characters not enough, or there is an invalid filename. */
				throw STD(_T("The filename is too long"));
			}
			default:
			{/* Currently Unhandled Error. */
				throw STD(_T("Could not browse due to an unknown error"));
			}
		}
	}
}




intptr_t ParseCmdLine(std::basic_string<TCHAR> &sCmdLine)
{/** This procedure is what handles the command line. The return value is either not 2 (we did something).
*	or 2 (the command line was empty). This function may throw a tstring exception on error.
*
*	Currently handled switches: /? /reg /unreg /dump "FileName"
**/

	if(sCmdLine.find(_T("/?")) < sCmdLine.size())
	{/* ?: Display Help/ */
		::MessageBox(NULL, _T("FilePermsBox.exe [/{un}reg] [/silent] [/allusers] [/meonly] [/?] \"FileName\" [/dump]\n")
			_T("/?: Shows this help.\n\"FileName\": Name of the file to edit security. Must be quoted.\n")
			_T("/{un}reg: Enables the shell property sheet extension, or removes it.\n")
			_T("\t/silent: registers silently. Only valid with /reg.\n")
			_T("\t/allusers: registers for all users (this is the default). Only valid with /reg.\n")
			_T("\t/meonly: registers for this user only. Only valid with /reg. Do not mix with /allusers\n")
			_T("/dump: Displays the security descriptor for the file. Only valid when a filename has been specified.\n\n")
			_T("eg. FilePermsBox.exe \"C:\\Windows\\System\" /dump\n\nDumps the security descriptor for ")
			_T("\"C:\\Windows\\System\"."),
			_T("Command Line Options."), MB_OK | MB_ICONINFORMATION);
		return 0;
	}


	DWORD Flags = 0;
	const DWORD RegFlag = 1 << 0, UnregFlag = 1 << 1;
	if(sCmdLine.find(_T("/reg")) < sCmdLine.size())
	{/* reg: get ready to register */
		Flags |= RegFlag;
	}

	if(sCmdLine.find(_T("/unreg")) < sCmdLine.size())
	{/* unreg: get ready to unregister */
		Flags |= UnregFlag;
	}

	if( (Flags & RegFlag) || (Flags & UnregFlag) )
	{/* Were we suppose to [un]register? do it here. */
		intptr_t dwErr = 0;
		HMODULE hFilePermsBoxDll = ::LoadLibrary(_T("FilePermsBox.DLL"));
		/* We're required by Microsoft to dynamically link at runtime to DllRegisterServer */

		HRESULT (STDAPICALLTYPE *pfnDllInstall)(BOOL, LPCWSTR);

		if(!hFilePermsBoxDll) throw STD(_T("A required DLL, FilePermsBox.DLL was not found"));

		/* We need DCOM too (according to Microsoft). */

		/* Now that the library is loaded, GetProcAddress DllInstall. */
		pfnDllInstall = reinterpret_cast<HRESULT (STDAPICALLTYPE *)(BOOL, LPCWSTR)>
			(::GetProcAddress(hFilePermsBoxDll, "DllInstall"));
		if(!pfnDllInstall)
		{/* If we didn't get it, throw an exception */
			::FreeLibrary(hFilePermsBoxDll); hFilePermsBoxDll = NULL;
			throw STD(_T("The procedure entry point \"DllRegisterServer\" could not be found in FilePermsBox.DLL.")
				_T(" Perhaps there is a version mismatch"));
		}
		else
		{/* OK, we got it, call it. */
			BOOL bInstall = (Flags & UnregFlag) ? FALSE : TRUE;
			dwErr = pfnDllInstall(bInstall, sCmdLine.c_str());
		}

		/* Cleanup now. */
		::FreeLibrary(hFilePermsBoxDll); hFilePermsBoxDll = NULL;

		if(dwErr == 2)/* This conflicts with another code, so offset it. */
			dwErr = HRESULT_FROM_WIN32(2);
		return dwErr;
	}
	else if( sCmdLine.find(_T('\"')) < sCmdLine.size() &&
		sCmdLine.find(_T('\"'), sCmdLine.find(_T('\"')) + 1) < sCmdLine.size() )
	{/* There's a possible filename in the command line (at least two quotes). */
		size_t start = 1 + sCmdLine.find(_T('\"')) ,
			end = sCmdLine.find(_T('\"'), start );
		/* Anchor start and end to the two quotes. */

		if (::GetFileAttributes(sCmdLine.substr(start, end - start).c_str()) != INVALID_FILE_ATTRIBUTES)
		{/* It seems to be a legal filename. */
			if(sCmdLine.find(_T("/dump")) < sCmdLine.size())
			{/* They want to dump it. Dump it. */
				std::basic_string<TCHAR> SDDLString;
				if(::GetSDForObject(sCmdLine.substr(start, end - start),
					SDDLString, TRUE))
				{/* Once the security is obtained, show it to the user. */
					std::basic_stringstream<TCHAR> TError;
					TError << _T("Security descriptor for object \"") << sCmdLine.substr(start, end - start) << _T("\"\n")
						<< SDDLString.c_str();
					::MessageBox(NULL, TError.str().c_str(), _T("FilePermsBox - Security descriptor"), MB_OK);
					return 0;
				}
			}

			/* Use the net wrapper interface to get to DoSecurityBox */
		#if (_MSC_VER >= 1400)
			return UseManagedCOMWrapperLib(sCmdLine.substr(start, end - start));
		#else /* _MSC_VER */
			/* They don't want to dump its SD. Go straight to DoSecurityBox() (no frontend UI). */
			std::list<const std::basic_string<TCHAR> > FileNamesCollection;
			FileNamesCollection.push_back(sCmdLine.substr(start, end - start));
			return DoSecurityBox(NULL, SE_FILE_OBJECT, FileNamesCollection, g_AddWritability);
		#endif /* Use the managed C++ / CLI interface for VS2005, and the unmanaged interface otherwise */
		}
	}
	return 2;
}





BOOL WalkDirectory(const std::basic_string<TCHAR> &lpszString,
	std::list< const std::basic_string<TCHAR> > &FileNamesCollection, BOOL DirsOrFiles)
{/** This procedure walks the object lpszString and returns the resulting directory tree in FileNamesCollection.
*	If DirsOrFiles is TRUE, we walk directories only; if false, we walk files only
**/
	HANDLE hDirCmd = NULL;
	WIN32_FIND_DATA DirCmdStruct = {0};
	DWORD ListErr = ERROR_SUCCESS, dwErr = FALSE;

	::SetLastError(ERROR_SUCCESS);
	/* We use the last Win32 error code in this function. */
	hDirCmd = ::FindFirstFile(lpszString.c_str(), &DirCmdStruct);
	if(hDirCmd == INVALID_HANDLE_VALUE || hDirCmd == NULL)
	{/* Can't find it eh? just use lpszString as the filename. */
		FileNamesCollection.push_back(lpszString.c_str());
		return TRUE;
	}

	do {/* Foreach file object in this FindFirst/FindNext/FindClose handle: */
		if(DirCmdStruct.cFileName[0] != _T('.'))
		{/* skip . or .. directories */
			const std::basic_string<TCHAR> FullFileName =
				lpszString.substr(0, 1 + lpszString.find_last_of(_T("\\"))) +
				static_cast< const std::basic_string<TCHAR> >(DirCmdStruct.cFileName);
			DWORD FileAttrib = ::GetFileAttributes(FullFileName.c_str());
			/* Construct a full file pathname, and get its current attributes */

			if(FileAttrib != INVALID_FILE_ATTRIBUTES)
			{/* This filename is valid. */

				if(FileAttrib & FILE_ATTRIBUTE_DIRECTORY)
				{/* It's a directory. Do we want it? */
					if(DirsOrFiles == TRUE)
					{/* Walkit. */
						WalkDirectory(FullFileName, FileNamesCollection, TRUE);
					}
				}
				FileNamesCollection.push_back(FullFileName);
			}
		}

		::SetLastError(ERROR_SUCCESS); ListErr = ERROR_SUCCESS;
		/* Use FindNextFile to get the next file, and loop. */
		dwErr = ::FindNextFile(hDirCmd, &DirCmdStruct);
		/* This will set last error to ERROR_NO_MORE_FILES on Failure. */
		if(dwErr != TRUE)
			ListErr = ::GetLastError();

	} while (ListErr == ERROR_SUCCESS);
	/* finally, cleanup. */
	if(hDirCmd != INVALID_HANDLE_VALUE && hDirCmd != NULL)
		::FindClose(hDirCmd); hDirCmd = NULL;
	return TRUE;
}

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