Click here to Skip to main content
15,880,796 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.7K   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 "SecurityCore.h"
#include "resource2.h"
/* Contains the core security functions and methods. */






BOOL __stdcall DoSecurityBox(HWND TheirhWnd, SE_OBJECT_TYPE seObjType,
	const std::list<const std::basic_string<TCHAR> > &FileNamesIn, BOOL AddWritability /* = FALSE */)
{/** The API to actually call the ACLUI box. This API forms the root of all our program.
*
**/
	if( (TheirhWnd != NULL && !::IsWindow(TheirhWnd)) ||
		(::IsBadReadPtr(&FileNamesIn, sizeof(std::list<const std::basic_string<TCHAR> > ))) ||
		FileNamesIn.size() < 1)
	{/* As an API, validate inputs. */
		::SetLastError(ERROR_INVALID_PARAMETER);
		return FALSE;
	}

	/* TODO: We currently only support file objects. Change this. */
	seObjType = SE_FILE_OBJECT;

	if( FileNamesIn.size() < 1)
		return FALSE;

	std::list<const std::basic_string<TCHAR> > FileNames = FileNamesIn;

	for(std::list<const std::basic_string<TCHAR> >::iterator Iter = FileNames.begin();
		Iter != FileNames.end(); Iter++)
	{/* Convert all filenames to full paths */
		sized_array<TCHAR> FileName (Iter->size() + 1 + FILENAME_MAX);
		FileName = *Iter;

		if(Iter->size() < 1 ||
			::GetFileAttributes(Iter->c_str()) == INVALID_FILE_ATTRIBUTES )
		{/* skip any non valid filenames. */
			const std::list< const std::basic_string<TCHAR> >::iterator eraseIter = Iter;
			FileNames.erase(eraseIter);
			if(FileNames.empty()) break;
			else continue;
		}

		/* convert relative paths to full paths. */
		if(::_wfullpath(FileName.get(), Iter->c_str(), FILENAME_MAX + 1) != NULL)
			Iter->assign(FileName.get());
	}


	if(FileNames.size() < 1)
	{/* We've determined the list contains all invalid names */
		int mBoxRes = ::MessageBox(TheirhWnd, _T("None of the objects apparently exist. Do you want to continue? ")
			_T("(may cause a crash)"), _T("File Not Found."), MB_ICONEXCLAMATION | MB_APPLMODAL | MB_YESNO | MB_DEFBUTTON1);
		if(mBoxRes != IDYES) return TRUE;
		FileNames.push_back(_T(""));
	}


	{/* ASSUMPTION: CoInitialize() has been called (FilePermsbox calls it at the start). */
		std::auto_ptr<CObjSecurity> SecPage;
		try {
			SecPage.reset(new CObjSecurity(FileNames, seObjType, TheirhWnd));

			if(AddWritability == TRUE)
			{/* Test if we have the "SeSecurityPrivilege". */
				PrivMgrImpl PrivClassImpl;
				{
					PrivMgr SeAutoPrivObj(SE_SECURITY_NAME, &PrivClassImpl);
					SecPage->MakeWritable(TRUE);
					if(SeAutoPrivObj.TurnOn() != TRUE)
						SecPage->DisableSacl();
				}
			}

			::EditSecurity(TheirhWnd, SecPage.get());
		} catch(const std::basic_string<TCHAR> &e) {/* We know the error here, tell the user. */
			while(SecPage->get_currenthWnd() != TheirhWnd)
			{/* Close all our windows. */
				SecPage->CloseCurrenthWnd();
			}
			::MessageBox(TheirhWnd, e.c_str(),
				_T("Unhandled Error."), MB_ICONEXCLAMATION | MB_OK);
			return FALSE;
		}
	}
	return TRUE;
}



INT_PTR CALLBACK TreeErrorFunc(HWND TheirhWnd, UINT Msg, WPARAM wParam, LPARAM lParam)
{/** This dialog box is shown when the system has a problem resetting the tree for security info.
*	It presents 4 options: Stop, Retry, Ignore, Ignore All.
**/
	switch(Msg)
	{
		case WM_INITDIALOG:
		{/* Access the structure in lParam. */
			const TCHAR *TError = reinterpret_cast<const TCHAR *>(lParam);
			if(!::IsBadStringPtr(TError,1))
			{
				::SetDlgItemText(TheirhWnd, IDC_EDIT1, TError);
			}
			::SetWindowText(TheirhWnd, _T("FilePermsBox - Error Setting Security."));
			return TRUE;
		}
		case WM_CLOSE:
		{/* By default, close means IDCANCEL (Hitting enter will send a WM_COMMAND message). */
			::EndDialog(TheirhWnd, IDCANCEL);
			return TRUE;
		}
		case WM_COMMAND:
		{/* End Dialog with the IDCONTROL */
			switch(LOWORD(wParam))
			{
				case IDCANCEL:/* Stop */
				case IDRETRY:/* Retry */
				case IDIGNORE:/* Ignore */
				case IDC_BUTTON1:/* Ignore All */
				{/* in each case, close this box. */
					::EndDialog(TheirhWnd, LOWORD(wParam));
					return FALSE;
				}
				default:
					return FALSE;
			}
		}
		default:
			break;
	}
	return FALSE;
}



VOID WINAPI TreeCallBackFunc(wchar_t *pObjectName, DWORD Status, PROG_INVOKE_SETTING *pInvokeSetting,
	void *PtrToThis, BOOL SecuritySet)
{/** This callback function is called with TreeSetNamedSecurityInfo(). It tells us what happened with this case.
*	We can extract the Object this belongs to in ThisClass (so use it).
*	Input: pObjectName - Name of the file.
*		Status - The Last Error (GetLastError code).
*		pInvokeSetting - We've set this to ProgressInvokeEveryObject.
*		PtrToThis - Dereference and cast PtrToThis to CSecurityObj to get the class back.
*		SecuritySet - if FALSE. We'll need to do something
*	Output: No return value, but we are expected to alter pInvokeSetting.
**/
	CObjSecurity *ThisClass = reinterpret_cast<CObjSecurity *>(PtrToThis);
	if(::IsBadReadPtr(PtrToThis, sizeof(CObjSecurity)))
		throw STD(_T("A pointer was not properly passed to a callback"));
	/* We need the ThisClass pointer regardless of whether security was set. */

	/* Later on, we need to sync ThisClass->pInvokeSetting with the pInvokeSetting set here. */

	if(SecuritySet != TRUE)
	{/* Something occurred. Inform the User. */
		INT_PTR Choice = IDIGNORE;
		std::basic_stringstream<TCHAR> TError;

		TError << _T("Error setting security on:\n") << pObjectName << _T("\nThe error code was ") << Status << _T(": ")
			<< static_cast<const TCHAR *>(::GetLastErrorText(Status)) << _T(". Would you like to continue?");

		if(ThisClass->pInvokeSetting != ProgressInvokeNever)
		{
			/* Put the mouse cursor back to normal. */
			HANDLE hCursor = ::LoadImage(NULL, MAKEINTRESOURCE(OCR_NORMAL), IMAGE_CURSOR, 0, 0, LR_DEFAULTSIZE | LR_SHARED);
			if(hCursor != NULL)/* If we couldn't load the image, just ignore the cursor stuff. */
				SetClassLongPtr(ThisClass->get_currenthWnd(), GCLP_HCURSOR, LONG_PTR2(hCursor));

			Choice = ::DialogBoxParam(g_hInst, MAKEINTRESOURCE(IDD_DIALOG2), ThisClass->get_currenthWnd(),
				TreeErrorFunc, reinterpret_cast<LPARAM>(TError.str().c_str()));

			/* Re-hourglass the mouse after the user responded. */
			hCursor = ::LoadImage(NULL, MAKEINTRESOURCE(OCR_APPSTARTING), IMAGE_CURSOR, 0, 0, LR_DEFAULTSIZE | LR_SHARED);
			if(hCursor != NULL)/* If we couldn't load the image, just ignore the cursor stuff. */
				SetClassLongPtr(ThisClass->get_currenthWnd(), GCLP_HCURSOR, LONG_PTR2(hCursor));
		}

		switch(Choice)
		{/* Choice will be one of: IDCANCEL, IDRETRY, IDIGNORE, IDC_BUTTON1 */
			default:/* By default, cancel. */
			case IDCANCEL:
			{/* Set pInvokeSetting according to result */
				*pInvokeSetting = ProgressCancelOperation;
				ThisClass->pInvokeSetting = *pInvokeSetting;
				break;
			}
			case IDRETRY:
			{/* This function is to be recalled */
				*pInvokeSetting = ProgressRetryOperation;
				break;
			}
			case IDIGNORE:
			{
				*pInvokeSetting = ProgressInvokeEveryObject;
				ThisClass->pInvokeSetting = *pInvokeSetting;
				break;
			}
			case IDC_BUTTON1:
			{/* Ignore All. Never show the error again. */
				*pInvokeSetting = ProgressInvokeEveryObject;
				ThisClass->pInvokeSetting = ProgressInvokeNever;
				break;
			}
		}
	}
	else
	{/* Sanity change pInvokeSetting to default object. TreeResetNamedSecurityInfo may have changed it. */
		*pInvokeSetting = ProgressInvokeEveryObject;
		/* Post a message to the Progress Window. */
	}
	if(*pInvokeSetting != ProgressRetryOperation)
	{/* If we aren't retrying this operation, set the progress bar */
		if(!ThisClass->CProgressObj.IncProgressBar(pObjectName))
		{/* The user wanted to cancel. Cancel. */
			*pInvokeSetting = ProgressCancelOperation;
			ThisClass->pInvokeSetting = *pInvokeSetting;
		}
	}
}





CObjSecurity::CObjSecurity(const std::list< const std::basic_string<TCHAR> > &ObjName,
	SE_OBJECT_TYPE seObjType /*= SE_FILE_OBJECT */, HWND TheirhWnd /* = NULL */) :
	m_cRef(1), KeepExplicit(2), pfnTreeResetNamedSecurityInfo(NULL), hAdvApi32(NULL), ClasshWnd(TheirhWnd),
	CProgressObj(), SeObjectType(seObjType), m_dwSIFlags ( SI_READONLY | SI_ADVANCED | SI_EDIT_ALL |
	SI_EDIT_EFFECTIVE | SI_OWNER_READONLY | SI_RESET), pInvokeSetting(ProgressInvokeEveryObject),
	PrivClassImpl(), ServerName(0)
{/** The Constructor. **/
	hAdvApi32 = ::LoadLibrary(_T("ADVAPI32"));
	pfnTreeResetNamedSecurityInfo = reinterpret_cast<DWORD (WINAPI *)(LPTSTR, SE_OBJECT_TYPE,
		SECURITY_INFORMATION, PSID, PSID, PACL, PACL, BOOL, FN_PROGRESS, PROG_INVOKE_SETTING, PVOID)>
		(::GetProcAddress(hAdvApi32, "TreeResetNamedSecurityInfoW"));

	if(ObjName.size() == 0)
	{
		throw STD(_T("The list of filenames is empty."));
	}

	for(std::list<const std::basic_string<TCHAR> >::const_iterator Iter = ObjName.begin();
		Iter != ObjName.end(); Iter++)
	{/** We are given STL strings in ObjName, but our class only holds sized_arrays, so we need to marshal
	*	a tstring-2-sized_array converter for each object in the collection. That's this loop.
	**/
		m_pszObjectName.push_back(static_cast<sized_array<TCHAR> >(*Iter));
		std::list<sized_array<TCHAR> >::const_iterator Iter2 = m_pszObjectName.end();
		Iter2--;

		if(this->IsContainer(Iter2->get()))
		{/* Set folders as containers. */
			if(!(m_dwSIFlags & SI_CONTAINER) && Iter != ObjName.begin())
			{/* One of these folders is of multiple type. Just use the first one */
				std::list<sized_array<TCHAR> >::iterator Iter3 = this->m_pszObjectName.begin();
				Iter3++;
				this->m_pszObjectName.erase(Iter3, this->m_pszObjectName.end());
				break;
			}
			this->m_dwSIFlags = this->m_dwSIFlags | SI_CONTAINER | SI_RESET_DACL_TREE | SI_RESET_SACL_TREE | SI_OWNER_RECURSE;
		}
	}

#ifdef _DEBUG
	pfnTreeResetNamedSecurityInfo = NULL;
#endif /* _DEBUG. Win2k Emulator. Uncomment the previous line to revert to Win2k behaviour. */

	this->GetServerNameForFile(ObjName.front());

	::InterlockedIncrement(const_cast<volatile LONG *>(reinterpret_cast<LONG *>(&g_RefCount)));
}



CObjSecurity & CObjSecurity::operator=(const CObjSecurity &OldClass)
{/* Custom methods. Equality Operator. */
	if(this == &OldClass) return *this;

	this->m_cRef++;	/* Private for all classes */
	this->m_dwSIFlags = OldClass.m_dwSIFlags;
	this->m_pszObjectName.resize(OldClass.m_pszObjectName.size());
	this->ClasshWnd = OldClass.ClasshWnd;
	this->pfnTreeResetNamedSecurityInfo = OldClass.pfnTreeResetNamedSecurityInfo;
	this->KeepExplicit = OldClass.KeepExplicit;
	this->CProgressObj = OldClass.CProgressObj;
	//if(this->CProgressObj != NULL)
	/* TODO: If you are using _com_ptr_t, do not AddRef() it. */
	//	CProgressObj->AddRef();

	/* Increment our own reference count for AdvApi32 (Handles need to be private to the class) */
	if(this->hAdvApi32 != NULL) ::FreeLibrary(this->hAdvApi32); hAdvApi32 = NULL;
	this->hAdvApi32 = ::LoadLibrary(_T("ADVAPI32"));

	/* Now to copy the old list into the new list */
	std::list<sized_array<TCHAR> >::iterator Iter2 = this->m_pszObjectName.begin();
	for(std::list<sized_array<TCHAR> >::const_iterator Iter = OldClass.m_pszObjectName.begin();
		Iter != OldClass.m_pszObjectName.end(); Iter++, Iter2++)
	{/* Cast the constant old array, into a string to get its size. */
		*Iter2 = *Iter;
	}
	return *this;
}



CObjSecurity::~CObjSecurity()
{/* We need to remove the critical section, free the Advapi32 library, and decrement the reference count. */
	if(hAdvApi32 != NULL)
		::FreeLibrary(hAdvApi32);
	//if(CProgressObj != NULL)
	/* TODO: If you are using _com_ptr_t, do not use this. */
	//	CProgressObj->Release();
	if(g_RefCount > 0)
		::InterlockedDecrement(const_cast<volatile LONG *>(reinterpret_cast<LONG *>(&g_RefCount)));
}



STDMETHODIMP
CObjSecurity::MakeWritable(BOOL bSet)
{/* This method enables/disables the window according to bSet. FALSE ==> ACLUI is read only. TRUE ==> ACLUI is editable. */
	if(bSet==TRUE)
	{
		m_dwSIFlags = m_dwSIFlags &~ (SI_READONLY | SI_OWNER_READONLY);
		m_dwSIFlags = m_dwSIFlags | SI_EDIT_AUDITS | SI_EDIT_PROPERTIES | SI_EDIT_OWNER;
	}
	else
		m_dwSIFlags = m_dwSIFlags | SI_READONLY | SI_OWNER_READONLY;
	return S_OK;
}



STDMETHODIMP
CObjSecurity::DisableSacl(void)
{/* If the user cannot edit the SACL, the Audits page is useless. */
	m_dwSIFlags = m_dwSIFlags &~ (SI_EDIT_AUDITS);
	return S_OK;
}



HWND CObjSecurity::get_currenthWnd(void)
{/* Accessor for hWnd (a vector) */
	HWND OurhWnd = ::GetLastActivePopup(this->ClasshWnd);
	return OurhWnd;
}



DWORD_PTR CObjSecurity::CloseCurrenthWnd(void)
{/* Popper for hWnd. This may block the calling thread for ~2 seconds (because we need to close the 1st window). */
	DWORD_PTR dwMsgResult = static_cast<DWORD_PTR>(NULL);
	::SendMessageTimeout(this->get_currenthWnd(), WM_CLOSE, 0, 0, SMTO_NORMAL, 2000, &dwMsgResult);
	/* Send a close to the window. */

	return dwMsgResult;
}



BOOL CObjSecurity::GetTokenOwner(std::basic_string<TCHAR> &PrimaryUser)
{/** Custom method. This gets the default owner for new objects (contained in process token).
*	returns TRUE for success (PrimaryUser is changed), FALSE for failure (Primary User is unchanged).
*	The resultant string will be suitable for adding to an SDDL.
**/
	DWORD dwLength = 0;
	HANDLE hToken = NULL;
	std::basic_string<TCHAR> Result = _T("O:");
	LPTSTR lpTmp = NULL;
	sized_array<BYTE> TokenInformation;

	/* Get a token to us. */
	if(::OpenProcessToken(::GetCurrentProcess(), TOKEN_QUERY, &hToken) != TRUE) return FALSE;
	::GetTokenInformation(hToken, TokenOwner, TokenInformation.get(), 0, &dwLength);	/* We expect this to fail */
	TokenInformation.reset(dwLength);
	/* Allocate a buffer */
	if(::GetTokenInformation(hToken, TokenOwner, TokenInformation.get(), dwLength, &dwLength) != TRUE)
	{/* We now have the Owner SID of us (in a PSID). */
		::CloseHandle(hToken);
		return FALSE;
	}

	if(::ConvertSidToStringSid(reinterpret_cast<TOKEN_OWNER *>(TokenInformation.get())->Owner, &lpTmp) != TRUE)
	{/* We need this PSID in SDDL form. */
		::CloseHandle(hToken); hToken = NULL;
		return FALSE;
	}
	Result.append(lpTmp);
	::LocalFree(lpTmp); lpTmp = NULL;

	Result.append(_T("G:"));

	/* Repeat all for Group SID. */
	TokenInformation.reset();
	::GetTokenInformation(hToken, TokenPrimaryGroup, TokenInformation.get(), 0, &dwLength);	/* We expect this to fail */
	TokenInformation.reset(dwLength);
	if(::GetTokenInformation(hToken, TokenOwner, TokenInformation.get(), dwLength, &dwLength) != TRUE)
	{/* Group SID */
		::CloseHandle(hToken); hToken = NULL;
		return FALSE;
	}

	if(::ConvertSidToStringSid(reinterpret_cast<TOKEN_PRIMARY_GROUP *>(TokenInformation.get())->PrimaryGroup, &lpTmp) != TRUE)
	{/* Group SID */
		::CloseHandle(hToken); hToken = NULL;
		return FALSE;
	}
	Result.append(lpTmp);
	::LocalFree(lpTmp); lpTmp = NULL;
	::CloseHandle(hToken); hToken = NULL;

	/* After Cleanup, put Result into PrimaryUser. */
	PrimaryUser = Result;
	return TRUE;
}



void
CObjSecurity::GetDefaultSecurity(const std::basic_string<TCHAR> &ObjName, std::basic_string<TCHAR> &PrimaryUser)
{/* Helper Virtual function that gets the default security for the object. */

	/* The default security descriptor is the security descriptor for the parent. */

	if(ObjName.size() > 3)
	{/* Twas a file in a directory. Find the parent owner/group, and apply this security descriptor: S:ARAID:ARAI */
		PSECURITY_DESCRIPTOR pSDRes = NULL;
		try	{/* Get the owner of the parent object. */
			PSID SidOwner = NULL, SidGroup = NULL;
			LPTSTR StringSid = NULL;
			size_t dwLength = ObjName.find_last_of(_T("\\/"));
			sized_array<TCHAR> ParentName (dwLength + 1);

			/* Get the parent name of this object */
			ObjName.copy(ParentName.get(), dwLength, 0);

			if( ::GetNamedSecurityInfo(ParentName.get(), this->SeObjectType, OWNER_SECURITY_INFORMATION |
				GROUP_SECURITY_INFORMATION, &SidOwner, &SidGroup, NULL, NULL, &pSDRes) != ERROR_SUCCESS)
			{/* Use the parent's security descriptor to get its owner. */
				throw STD(_T("Error getting the owner of the parent object"));
			}
			if(::ConvertSidToStringSid(SidOwner, &StringSid) != TRUE)
			{/* In SDDL. */
				::LocalFree(pSDRes); pSDRes = NULL;
				throw STD(_T("Error interpreting owner SID"));
			}
			PrimaryUser = _T("O:");
			/* Put in the found owner into this SD. */
			PrimaryUser.append(StringSid);
			::LocalFree(StringSid); StringSid = NULL;

			if(::ConvertSidToStringSid(SidGroup, &StringSid) != TRUE)
			{/* And The Owner. */
				::LocalFree(pSDRes); pSDRes = NULL;
				throw STD(_T("Error interpreting Group SID"));
			}
			PrimaryUser.append(_T("G:"));
			PrimaryUser.append(StringSid);
			::LocalFree(StringSid); StringSid = NULL;
			::LocalFree(pSDRes); pSDRes = NULL;
		} catch(const std::basic_string<TCHAR> &) {/* If something went wrong, use the SID of Us. */
			if(GetTokenOwner(PrimaryUser) != TRUE) PrimaryUser = _T("O:BAG:BA");
		}

		PrimaryUser += _T("S:ARAID:ARAI");
	}
	else
	{/** 'Twas a root object. We'll need to apply this security descriptor:
	*	O:BAG:BAS:D:(A;OICI;FA;;;BA)(A;OICI;FA;;;SY)(A;OICI;0x1200a9;;;BU)(A;CI;LC;;;BU)(A;;0x1200a9;;;AU)
	*	(if owner shouldn't be BA, then we should use the sid of the owner).
	*	Our SD should be a little more secure than Windows (eg. replace Everyone with AU).
	**/
		if(GetTokenOwner(PrimaryUser) != TRUE) PrimaryUser = _T("O:BAG:BA");
		PrimaryUser += _T("S:D:(A;OICI;FA;;;BA)(A;OICI;FA;;;SY)")
			_T("(A;OICI;0x1200a9;;;BU)(A;CI;LC;;;BU)(A;;0x1200a9;;;AU)");
		/** TODO: The root drive isn't the only folder that needs protection, there are other folders that
		*	need specific acls.
		**/
		::SetLastError(ERROR_SUCCESS);
	}

}



BOOL
CObjSecurity::IsContainer(const std::basic_string<TCHAR> &ObjectName)
{/* Returns true if the object specified is a container. Abstract, because this is a object specific property. */
	DWORD FileAttrib = ::GetFileAttributes(ObjectName.c_str());
	if((FileAttrib & FILE_ATTRIBUTE_DIRECTORY) && !(FileAttrib & FILE_ATTRIBUTE_REPARSE_POINT))
		return TRUE;
	return FALSE;
}




BOOL
CObjSecurity::IsDuplicate(const std::basic_string<TCHAR> &Iter,
	const std::list<const std::basic_string<TCHAR> > &Collection)
{/* Returns true if the object contains */
	if(std::find<std::list<const std::basic_string<TCHAR> >::const_iterator,
		const std::basic_string<TCHAR> >(Collection.begin(), Collection.end(), Iter) == Collection.end())
	{
		return FALSE;
	}
	return TRUE;
}



DWORD
CObjSecurity::RefreshShell(BOOL IsContainer)
{/* Informs the shell that there are new files to handle (if we have updated the security descriptor). */
	if(IsContainer != FALSE)
	{
		std::list<sized_array<TCHAR> >::const_iterator Iter = this->m_pszObjectName.begin();
		::SHChangeNotify(SHCNE_UPDATEDIR, SHCNF_PATH, Iter->get(), NULL);
	}
	::SetLastError(ERROR_SUCCESS);
	DWORD dwErr = ERROR_SUCCESS;
	return dwErr;
}

BOOL
CObjSecurity::GetServerNameForFile(const std::basic_string<TCHAR> &FileNameIn)
{/* Given the specified full filename, returns the computer name of that file in the class member ServerName. */
	if(FileNameIn.size() == 0)
		return FALSE;
	/* Get just the first character of FileNameIn, and append trailing slashes */
	std::basic_string<TCHAR> DriveName = FileNameIn.substr(0, 1);
	DriveName.append(_T(":"));

	/* If we aren't remote, use NULL as the text. */
	UINT drvType = ::GetDriveType(DriveName.c_str());
	if(!(drvType & DRIVE_REMOTE)) return FALSE;

	/* Okay we are remote: use the WNET functions to find out our server name. */
	DWORD dwSize = 0;
	::WNetGetConnection(DriveName.c_str(), NULL, &dwSize);
	/* This is expected to fail with ERROR_INSUFFICIENT_BUFFER */
	sized_array<TCHAR> TargetPath(dwSize);
	/* We need a movable pointer to this block of data */
	if(::WNetGetConnection(DriveName.c_str(), TargetPath.get(), &dwSize) != NO_ERROR)
	{/* in case of failure, use NULL as the server */
		return FALSE;
	}

	/* Copy it again to a basic_string. */
	DriveName = TargetPath.get();
	/* Get the bits between "\\" and "\" */
	const std::basic_string<TCHAR>::size_type start = DriveName.find_first_not_of(_T("\\/:"));
	if(start > DriveName.size())
		return FALSE;
	const std::basic_string<TCHAR>::size_type end = DriveName.find_first_of(_T("\\/"), start);
	DriveName = DriveName.substr(start, end - start);
	/* That's the finished computer name. */

	this->ServerName = DriveName;
	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