/*----------------------------------------------------------------------------- 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. Copyright (C) 1999 - 2001 Microsoft Corporation. All rights reserved. DLEDIT -- Drive Letter Assignment Editor This program demonstrates how to add or remove persistent drive letter assignments in Windows 2000, Windows XP, and Windows Server 2003. These drive letter assignments persist through machine reboots. Platforms: This program requires Windows 2000 or later. Command-line syntax: DLEDIT -- Adds persistent drive letter DLEDIT -t -- Adds temporary drive letter DLEDIT -r -- Removes a drive letter DLEDIT -- Shows drive letter mapping DLEDIT -a -- Shows all drive letter mappings Command-line examples: Say that E: refers to CD-ROM drive, and you want to make F: point to that CD-ROM drive instead. Use the following two commands: DLEDIT -r E:\ DLEDIT F:\ \Device\CdRom0 To display what device a drive letter is mapped to, use the following command: DLEDIT f: ******************************************************************************* WARNING: WARNING: WARNING: WARNING: WARNING: WARNING: WARNING: WARNING: This program really will change drive letter assignments, and the changes persist through reboots. Do not remove drive letters of your hard disks if you don't have this program on a floppy disk or you might not be able to access your hard disks again! ******************************************************************************* -----------------------------------------------------------------------------*/ /* reduce number of headers pulled in by windows.h to improve build time */ #define WIN32_LEAN_AND_MEAN #define _WIN32_WINNT 0x0500 #include #pragma warning(disable:4201) // nonstandard extension used : nameless struct/union #include #include #include #include /*-----------------------------------------------------------------------------*/ #define GPT_BASIC_DATA_ATTRIBUTE_HIDDEN (0x4000000000000000) // from DDK's NTDDDISK.H #define IOCTL_VOLUME_GET_GPT_ATTRIBUTES CTL_CODE(IOCTL_VOLUME_BASE, 14, METHOD_BUFFERED, FILE_ANY_ACCESS) typedef struct _VOLUME_GET_GPT_ATTRIBUTES_INFORMATION { ULONGLONG GptAttributes; } VOLUME_GET_GPT_ATTRIBUTES_INFORMATION, *PVOLUME_GET_GPT_ATTRIBUTES_INFORMATION; /*-----------------------------------------------------------------------------*/ #define HIDDEN_PARTITION_FLAG 0x10 /* Private error codes. Follows guidelines in winerror.h */ const DWORD PRIV_ERROR_DRIVE_LETTER_IN_USE = 0xE0000001; //const DWORD PRIV_ERROR_DRIVE_LETTER_NOT_USED = 0xE0000002; const DWORD PRIV_ERROR_PARTITION_HIDDEN = 0x60000001; const DWORD PRIV_ERROR_PARTITION_NOT_RECOGNIZED = 0x60000002; //const DWORD PRIV_ERROR_NOT_PARTITIONABLE_DEVICE = 0x60000003; BOOL WINAPI AssignPersistentDriveLetter (LPCTSTR pszDriveLetter, LPCTSTR pszDeviceName); BOOL WINAPI RemovePersistentDriveLetter (LPCTSTR pszDriveLetter); BOOL WINAPI AssignTemporaryDriveLetter (LPCTSTR pszDriveLetter, LPCTSTR pszDeviceName); BOOL WINAPI RemoveTemporaryDriveLetter (LPCTSTR pszDriveLetter); BOOL WINAPI IsPartitionHidden (BYTE partitionType, ULONGLONG partitionAttribs); BOOL WINAPI IsDriveLetter (LPCTSTR pszDriveLetter); BOOL WINAPI IsWindowsXP_orLater (void); static void PrintHelp (LPCTSTR pszAppName); /*----------------------------------------------------------------------------- main( IN argc, IN argv ) Parameters argc Count of the command-line arguments argv Array of pointers to the individual command-line arguments This function is the main program. It parses the command-line arguments and performs the work of either removing a drive letter or adding a new one. -----------------------------------------------------------------------------*/ extern "C" void _tmain (int argc, TCHAR **argv) { TCHAR * pszDriveLetter, * pszNTDevice, * pszOptions; /* Command-line parsing. 1) Validate arguments 2) Determine what user wants to do */ if (3 == argc && !lstrcmpi (argv[1], _T("-r")) && IsDriveLetter (argv[2])) { /* User wants to remove the drive letter. Command line should be: dledit -r */ pszOptions = argv[1]; pszDriveLetter = argv[2]; pszNTDevice = NULL; if (!RemovePersistentDriveLetter (pszDriveLetter)) { if (!RemoveTemporaryDriveLetter (pszDriveLetter)) { switch (GetLastError()) { case ERROR_FILE_NOT_FOUND: _tprintf(_T("%s is not in use\n"), pszDriveLetter); break; default: _tprintf(_T("error %lu: couldn't remove %s\n"), GetLastError(), pszDriveLetter); } } } } /* End if remove drive letter mapping */ else if (3 == argc && IsDriveLetter (argv[1])) { /* User wants to add a persistent drive letter. Command line should be: dledit */ pszOptions = NULL; pszDriveLetter = argv[1]; pszNTDevice = argv[2]; /* Try a persistent drive letter; if partition is hidden, the persistent mapping will fail--try a temporary mapping and tell user about it. Note: Hidden partitions can be assigned temporary drive letters. These are nothing more than symbolic links created with DefineDosDevice. Temporary drive letters will be removed when the system is rebooted. */ if (!AssignPersistentDriveLetter (pszDriveLetter, pszNTDevice)) { switch (GetLastError()) { case PRIV_ERROR_PARTITION_HIDDEN: if (AssignTemporaryDriveLetter (pszDriveLetter, pszNTDevice)) _tprintf(_T("%s is hidden; mapped %s temporarily\n"), pszNTDevice, pszDriveLetter); else _tprintf(_T("%s is hidden; couldn't map %s to it\n"), pszNTDevice, pszDriveLetter); break; case PRIV_ERROR_DRIVE_LETTER_IN_USE: _tprintf(_T("%s is in use, can't map it to %s\n"), pszDriveLetter, pszNTDevice); break; case ERROR_FILE_NOT_FOUND: _tprintf(_T("%s doesn't exist or can't be opened\n"), pszNTDevice); break; case ERROR_INVALID_PARAMETER: _tprintf(_T("%s already has a drive letter; can't map %s to it\n"), pszNTDevice, pszDriveLetter); break; default: _tprintf(_T("error %lu: couldn't map %s to %s\n"), GetLastError(), pszDriveLetter, pszNTDevice); } } } /* End if add persistent drive letter mapping */ else if (4 == argc && !lstrcmpi (argv[1], _T("-t")) && IsDriveLetter (argv[2])) { /* User wants to add a temporary drive letter. Command line should be: dledit -t */ pszOptions = argv[1]; pszDriveLetter = argv[2]; pszNTDevice = argv[3]; if (!AssignTemporaryDriveLetter (pszDriveLetter, pszNTDevice)) { switch (GetLastError()) { case ERROR_FILE_NOT_FOUND: _tprintf(_T("%s doesn't exist or can't be opened\n"), pszNTDevice); break; case PRIV_ERROR_DRIVE_LETTER_IN_USE: _tprintf(_T("%s is in use, can't map it to %s\n"), pszDriveLetter, pszNTDevice); break; default: _tprintf(_T("error %lu: couldn't map %s to %s\n"), GetLastError(), pszDriveLetter, pszNTDevice); } } } /* End if add temporary drive letter mapping */ else if (2 == argc && IsDriveLetter (argv[1])) { /* User wants to show what device is connected to the drive letter. Command line should be: dledit */ pszOptions = NULL; pszDriveLetter = argv[1]; pszNTDevice = NULL; TCHAR szNtDeviceName[MAX_PATH]; TCHAR szDriveLetter[3]; /* Command-line argument for the drive letter could be in one of two formats: C:\ or C:. We normalize this to C: for QueryDosDevice. */ szDriveLetter[0] = pszDriveLetter[0]; szDriveLetter[1] = _T(':'); szDriveLetter[2] = _T('\0'); if (QueryDosDevice (szDriveLetter, szNtDeviceName, MAX_PATH)) { _tprintf(_T("%s is mapped to %s\n"), szDriveLetter, szNtDeviceName); } else { switch (GetLastError()) { case ERROR_FILE_NOT_FOUND: _tprintf(_T("%s is not in use\n"), pszDriveLetter); break; default: _tprintf(_T("error %lu: couldn't get mapping for %s\n"), GetLastError(), pszDriveLetter); } } } /* End if show drive letter mapping */ else if (2 == argc && !lstrcmpi (argv[1], _T("-a"))) { /* User wants to show all mappings of drive letters to their respective devices. Command line should be: dledit -a */ pszOptions = argv[1]; pszDriveLetter = NULL; pszNTDevice = NULL; /* Largest possible array of drive strings is 26 drive letters times 4 chars per drive letter plus 1 for the terminating NULL */ const DWORD MAX_DRIVE_STRING_LENGTH = 26 * 4 + 1; TCHAR szDriveStrings[MAX_DRIVE_STRING_LENGTH]; DWORD cchDriveStrings; /* Get a list of all current drive letters, then for each, print the mapping. Drive letters are returned in the form A:\B:\C:\.... */ cchDriveStrings = GetLogicalDriveStrings (MAX_DRIVE_STRING_LENGTH, szDriveStrings); if ( (0 != cchDriveStrings) && (cchDriveStrings <= MAX_DRIVE_STRING_LENGTH) ) { TCHAR * pszCurrentDriveLetter; TCHAR szNtDeviceName[MAX_PATH]; TCHAR szDriveLetter[3]; _tprintf(_T("Drive Device\n") _T("----- ------\n")); pszCurrentDriveLetter = szDriveStrings; while (_T('\0') != *pszCurrentDriveLetter) { /* QueryDosDevice requires drive letters in the format X: */ szDriveLetter[0] = pszCurrentDriveLetter[0]; szDriveLetter[1] = _T(':'); szDriveLetter[2] = _T('\0'); if (QueryDosDevice (szDriveLetter, szNtDeviceName, MAX_PATH)) _tprintf(_T("%-10s%s\n"), szDriveLetter, szNtDeviceName); pszCurrentDriveLetter += 4; /* size of X:\ */ } } else { _tprintf(_T("couldn't list drive letters and their devices\n")); } } /* End if show all drive letter mappings */ else { /* User has selected an invalid operation--display help. */ PrintHelp (argv[0]); } } /*----------------------------------------------------------------------------- PrintHelp( IN pszAppName ) Parameters pszAppName The name of the executable. Used in displaying the help for this app. Prints the command-line usage help. -----------------------------------------------------------------------------*/ static void PrintHelp (LPCTSTR pszAppName) { _tprintf(_T("Adds, removes, queries drive letter assignments\n\n")); _tprintf(_T("usage: %s add a drive letter\n"), pszAppName); _tprintf(_T(" %s -t add a temporary drive letter\n"), pszAppName); _tprintf(_T(" %s -r remove a drive letter\n"), pszAppName); _tprintf(_T(" %s show mapping for drive letter\n"), pszAppName); _tprintf(_T(" %s -a show all mappings\n\n"), pszAppName); _tprintf(_T("example: %s e:\\ \\Device\\CdRom0\n"), pszAppName); _tprintf(_T(" %s -r e:\\\n"), pszAppName); } /*----------------------------------------------------------------------------- AssignPersistentDriveLetter (IN pszDriveLetter, IN pszDeviceName) Description: Creates a persistent drive letter that refers to a specified device. This drive letter will remain even when the system is restarted. Parameters: pszDriveLetter The new drive letter to create. Must be in the form X: or X:\ pszDeviceName The NT device name to which the drive letter will be assigned. Return Value: Returns TRUE if the drive letter was added, or FALSE if it wasn't. -----------------------------------------------------------------------------*/ BOOL WINAPI AssignPersistentDriveLetter ( LPCTSTR pszDriveLetter, LPCTSTR pszDeviceName) { BOOL fResult; TCHAR szUniqueVolumeName[MAX_PATH]; TCHAR szDriveLetterAndSlash[4]; TCHAR szDriveLetter[3]; /* Make sure we are passed a drive letter and a device name. lstrlen is useful because it will return zero if the pointer points to memory that can't be read or a string that causes an invalid page fault before the terminating NULL. */ if (0 == lstrlen(pszDriveLetter) || 0 == lstrlen(pszDeviceName) || !IsDriveLetter (pszDriveLetter)) { SetLastError (ERROR_INVALID_PARAMETER); return FALSE; } /* GetVolumeNameForVolumeMountPoint, SetVolumeMountPoint, and DeleteVolumeMountPoint require drive letters to have a trailing backslash. However, DefineDosDevice and QueryDosDevice require that the trailing backslash be absent. So, we'll set up the following variables: szDriveLetterAndSlash for the mount point APIs szDriveLetter for DefineDosDevice */ szDriveLetterAndSlash[0] = pszDriveLetter[0]; szDriveLetterAndSlash[1] = _T(':'); szDriveLetterAndSlash[2] = _T('\\'); szDriveLetterAndSlash[3] = _T('\0'); szDriveLetter[0] = szDriveLetterAndSlash[0]; szDriveLetter[1] = _T(':'); szDriveLetter[2] = _T('\0'); /* Determine if the drive letter is currently in use. If so, return the error to the caller. NOTE: we temporarily reuse szUniqueVolumeName instead of allocating a large array for this one call. */ fResult = QueryDosDevice (szDriveLetter, szUniqueVolumeName, MAX_PATH); if (fResult) { SetLastError (PRIV_ERROR_DRIVE_LETTER_IN_USE); return FALSE; } /* To map a persistent drive letter, we must make sure that the target device is one of the following: A recognized partition and is not hidden A dynamic volume A non-partitionable device such as CD-ROM Start by using the drive letter as a symbolic link to the device. Then, open the device to gather the information necessary to determine if the drive can have a persistent drive letter. */ fResult = DefineDosDevice (DDD_RAW_TARGET_PATH, szDriveLetter, pszDeviceName); if (fResult) { HANDLE hDevice; DWORD dwBytesReturned; BOOL fResult; TCHAR szDriveName[7]; // holds \\.\X: plus NULL. VOLUME_GET_GPT_ATTRIBUTES_INFORMATION volinfo; PARTITION_INFORMATION partinfo; _tcsncpy_s (szDriveName, _countof(szDriveName), _T("\\\\.\\"), _TRUNCATE); _tcsncat_s (szDriveName, _countof(szDriveName), szDriveLetter, _TRUNCATE); hDevice = CreateFile (szDriveName, GENERIC_READ, FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL); if (INVALID_HANDLE_VALUE != hDevice) { /* See if drive is partitionable and retrieve the partition type. If the device doesn't have a partition, note it by setting the partition type to unused. */ fResult = DeviceIoControl (hDevice, IOCTL_DISK_GET_PARTITION_INFO, NULL, 0, &partinfo, sizeof(partinfo), &dwBytesReturned, NULL); if (!fResult) { partinfo.PartitionType = PARTITION_ENTRY_UNUSED; } /* On Windows XP, partition entries on GUID Partition Table drives have an attribute that determines whether partitions are hidden. Therefore, we must check this bit on the target partition. If we're running on Windows 2000, there are no GPT drives, so set flags to none. This is important for the check for hidden partitions later. */ if (IsWindowsXP_orLater()) { fResult = DeviceIoControl (hDevice, IOCTL_VOLUME_GET_GPT_ATTRIBUTES, NULL, 0, &volinfo, sizeof(volinfo), &dwBytesReturned, NULL); if (!fResult) volinfo.GptAttributes = 0; } else { volinfo.GptAttributes = 0; } CloseHandle (hDevice); } else { /* Remove the drive letter symbolic link we created, let caller know we couldn't open the drive. */ DefineDosDevice (DDD_RAW_TARGET_PATH|DDD_REMOVE_DEFINITION| DDD_EXACT_MATCH_ON_REMOVE, szDriveLetter, pszDeviceName); return (FALSE); } /* Now, make sure drive meets requirements for receiving a persistent drive letter. Note: on Windows XP, partitions that were hidden when the system booted are not assigned a unique volume name. They will not be given one until they are marked as not hidden and the system rebooted. Therefore, we cannot create a mount point hidden partitions. On Windows 2000, hidden partitions are assigned unique volume names and so can have persistent drive letters. However, we will not allow that behavior in this tool as hidden partitions should not be assigned drive letters. */ if (IsPartitionHidden (partinfo.PartitionType, volinfo.GptAttributes)) { // remove the drive letter we created, let caller know what happened. DefineDosDevice (DDD_RAW_TARGET_PATH|DDD_REMOVE_DEFINITION| DDD_EXACT_MATCH_ON_REMOVE, szDriveLetter, pszDeviceName); SetLastError (PRIV_ERROR_PARTITION_HIDDEN); return (FALSE); } /* Verify that the drive letter must refer to a recognized partition, a dynamic volume, or a non-partitionable device such as CD-ROM. */ if (IsRecognizedPartition(partinfo.PartitionType) || PARTITION_LDM == partinfo.PartitionType || PARTITION_ENTRY_UNUSED == partinfo.PartitionType) { /* Now add the drive letter by calling on the volume mount manager. Once we have the unique volume name that the new drive letter will point to, delete the symbolic link because the Mount Manager allows only one reference to a device at a time (the new one to be added). */ fResult = GetVolumeNameForVolumeMountPoint (szDriveLetterAndSlash, szUniqueVolumeName, MAX_PATH); DefineDosDevice (DDD_RAW_TARGET_PATH|DDD_REMOVE_DEFINITION| DDD_EXACT_MATCH_ON_REMOVE, szDriveLetter, pszDeviceName); if (fResult) fResult = SetVolumeMountPoint (szDriveLetterAndSlash, szUniqueVolumeName); return (fResult); } else { /* Device doesn't meet the criteria for persistent drive letter. Remove the drive letter symbolic link we created. */ DefineDosDevice (DDD_RAW_TARGET_PATH|DDD_REMOVE_DEFINITION| DDD_EXACT_MATCH_ON_REMOVE, szDriveLetter, pszDeviceName); SetLastError (PRIV_ERROR_PARTITION_NOT_RECOGNIZED); return (FALSE); } } /* end if DeviceIoControl to map symbolic link*/ else return (FALSE); } /*----------------------------------------------------------------------------- RemovePersistentDriveLetter( IN pszDriveLetter) Description: Removes a drive letter that was created by AssignPersistentDriveLetter(). Parameters: pszDriveLetter The drive letter to remove. Must be in the format X: or X:\ Return Value: Returns TRUE if the drive letter was removed, or FALSE if it wasn't. -----------------------------------------------------------------------------*/ BOOL WINAPI RemovePersistentDriveLetter (LPCTSTR pszDriveLetter) { BOOL fResult; TCHAR szDriveLetterAndSlash[4]; /* Make sure we have a drive letter. */ if (0 == lstrlen(pszDriveLetter) || !IsDriveLetter (pszDriveLetter)) { SetLastError (ERROR_INVALID_PARAMETER); fResult = FALSE; } /* pszDriveLetter could be in the format X: or X:\. DeleteVolumeMountPoint requires X:\, so add a trailing backslash. */ szDriveLetterAndSlash[0] = pszDriveLetter[0]; szDriveLetterAndSlash[1] = _T(':'); szDriveLetterAndSlash[2] = _T('\\'); szDriveLetterAndSlash[3] = _T('\0'); fResult = DeleteVolumeMountPoint (szDriveLetterAndSlash); return (fResult); } /*----------------------------------------------------------------------------- AssignTemporaryDriveLetter( IN pszDriveLetter, IN pszDeviceName) Description: Creates a temporary drive letter that refers to a specified device. This drive letter will exist only until the system is shut down or restarted. Parameters: pszDriveLetter The new drive letter to create. Must be in the form X: or X:\ pszDeviceName The NT device name to which the drive letter will be assigned. Return Value: Returns TRUE if the temporary drive letter was assigned, or FALSE if it could not be. Notes: A temporary drive letter is just a symbolic link. It can be removed at any time by deleting the symbolic link. If it exists when the system is shut down or restarted, it will be removed automatically. AssignTemporaryDriveLetter requires device to be present. -----------------------------------------------------------------------------*/ BOOL WINAPI AssignTemporaryDriveLetter (LPCTSTR pszDriveLetter, LPCTSTR pszDeviceName) { TCHAR szDevice[MAX_PATH]; TCHAR szDriveLetter[3]; BOOL fResult; /* Verify that caller passed a drive letter and device name. */ if (0 == lstrlen(pszDriveLetter) || 0 == lstrlen(pszDeviceName) || !IsDriveLetter (pszDriveLetter)) { SetLastError (ERROR_INVALID_PARAMETER); return (FALSE); } /* Make sure the drive letter isn't already in use. If not in use, create the symbolic link to establish the temporary drive letter. pszDriveLetter could be in the format X: or X:\; QueryDosDevice and DefineDosDevice need X: */ szDriveLetter[0] = pszDriveLetter[0]; szDriveLetter[1] = _T(':'); szDriveLetter[2] = _T('\0'); if (!QueryDosDevice (szDriveLetter, szDevice, MAX_PATH)) { /* If we can create the symbolic link, verify that it points to a real device. If not, remove the link and return an error. CreateFile sets the last error code to ERROR_FILE_NOT_FOUND. */ fResult = DefineDosDevice (DDD_RAW_TARGET_PATH, szDriveLetter, pszDeviceName); if (fResult) { HANDLE hDevice; TCHAR szDriveName[7]; // holds \\.\X: plus NULL. _tcsncpy_s (szDriveName, _countof(szDriveName), _T("\\\\.\\"), _TRUNCATE); _tcsncat_s (szDriveName, _countof(szDriveName), szDriveLetter, _TRUNCATE); hDevice = CreateFile (szDriveName, GENERIC_READ, FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL); if (INVALID_HANDLE_VALUE != hDevice) { fResult = TRUE; CloseHandle (hDevice); } else { DefineDosDevice (DDD_RAW_TARGET_PATH|DDD_REMOVE_DEFINITION| DDD_EXACT_MATCH_ON_REMOVE, szDriveLetter, pszDeviceName); fResult = FALSE; } } } else { SetLastError (PRIV_ERROR_DRIVE_LETTER_IN_USE); fResult = FALSE; } return (fResult); } /*----------------------------------------------------------------------------- RemoveTemporaryDriveLetter( IN pszDriveLetter) Description: Removes a drive letter that was created by AssignTemporaryDriveLetter(). Parameters: pszDriveLetter The drive letter to remove. Must be in the format X: or X:\ Return Value: Returns TRUE if the drive letter was removed, or FALSE if it wasn't. -----------------------------------------------------------------------------*/ BOOL WINAPI RemoveTemporaryDriveLetter (LPCTSTR pszDriveLetter) { BOOL fResult; TCHAR szDriveLetter[3]; TCHAR szDeviceName[MAX_PATH]; /* Verify that caller passed a drive letter and device name. */ if (0 == lstrlen(pszDriveLetter) || !IsDriveLetter (pszDriveLetter)) { SetLastError (ERROR_INVALID_PARAMETER); fResult = FALSE; } /* pszDriveLetter could be in the format X: or X:\; DefineDosDevice needs X: */ szDriveLetter[0] = pszDriveLetter[0]; szDriveLetter[1] = _T(':'); szDriveLetter[2] = _T('\0'); fResult = QueryDosDevice (szDriveLetter, szDeviceName, MAX_PATH); if (fResult) { fResult = DefineDosDevice (DDD_RAW_TARGET_PATH|DDD_REMOVE_DEFINITION| DDD_EXACT_MATCH_ON_REMOVE, szDriveLetter, szDeviceName); } return (fResult); } /*----------------------------------------------------------------------------- IsPartitionHidden( IN partitionType, IN partitionAttribs) Description: Determines if the partition hosting a volume is hidden or not. Parameters: partitionType Specifies the partitition type; this value is obtained from IOCTL_DISK_GET_PARTITION_INFO or IOCTL_DISK_GET_DRIVE_GEOMETRY. A partition type that has bit 0x10 set is hidden. partitionAttribs Specifies the attributes of a GUID Partition Type (GPT)-style partition entry. This value is obtained from IOCTL_VOLUME_GET_GPT_ATTRIBUTES or IOCTL_DISK_GET_DRIVE_GEOMETRY_EX. A GPT partition that has GPT_BASIC_DATA_ATTRIBUTE_HIDDEN set is hidden. Return Value: Returns TRUE if the partition is hidden or FALSE if it is not. Notes: On Windows XP and Windows Server 2003, partitions that were hidden when the system booted are not assigned a unique volume name. They will not be given one until they are marked as not hidden and the system rebooted. Therefore, we cannot create a mount point hidden partitions. On Windows 2000, hidden partitions are assigned unique volume names and so can have persistent drive letters. However, we will not allow that behavior in this tool as hidden partitions should not be assigned drive letters. -----------------------------------------------------------------------------*/ BOOL WINAPI IsPartitionHidden (BYTE partitionType, ULONGLONG partitionAttribs) { if ((partitionAttribs & GPT_BASIC_DATA_ATTRIBUTE_HIDDEN) || ((partitionType & HIDDEN_PARTITION_FLAG) && IsRecognizedPartition (partitionType & ~HIDDEN_PARTITION_FLAG))) { return TRUE; } else { return FALSE; } } /*----------------------------------------------------------------------------- IsDriveLetter( IN pszDriveLetter) Description: Verifies string passed in is of the form X: or X:\ where X is a letter. Parameters: pszDriveLetter A null terminated string. Return Value: TRUE if the string is of the form X: or X:\ where X is a letter. X may be upper-case or lower-case. If the string isn't of this from, returns FALSE. -----------------------------------------------------------------------------*/ BOOL WINAPI IsDriveLetter (LPCTSTR pszDriveLetter) { /* format must be: X: or X:\ where X is a letter. lstrlen will return 0 if inaccessible pointer is supplied, or if reading bytes of string causes an access violation before reaching a terminating NULL. */ if ( (0 < lstrlen (pszDriveLetter)) && (IsCharAlpha (pszDriveLetter[0])) && (_T(':') == pszDriveLetter[1]) && ((_T('\0') == pszDriveLetter[2]) || ((_T('\\') == pszDriveLetter[2]) && (_T('\0') == pszDriveLetter[3]))) ) { return TRUE; } else { return FALSE; } } /*----------------------------------------------------------------------------- IsWindowsXP_orLater() Description: Determines if the currently-running version of Windows is Windows XP or Windows Server 2003 or later. Return Value: Returns TRUE if the currently-running system is Windows XP or Windows 2003 Server or later systems. Returns FALSE otherwise. -----------------------------------------------------------------------------*/ BOOL WINAPI IsWindowsXP_orLater (void) { OSVERSIONINFOEX osvi; ULONGLONG comparisonMask; ZeroMemory (&osvi, sizeof(osvi)); osvi.dwOSVersionInfoSize = sizeof(osvi); osvi.dwMajorVersion = 5; osvi.dwMinorVersion = 1; comparisonMask = 0; comparisonMask = VerSetConditionMask (comparisonMask, VER_MAJORVERSION, VER_GREATER_EQUAL); comparisonMask = VerSetConditionMask (comparisonMask, VER_MINORVERSION, VER_GREATER_EQUAL); return (VerifyVersionInfo (&osvi, VER_MAJORVERSION|VER_MINORVERSION, comparisonMask)); }