I'm building a small Delphi application where a user can input a file or directory manually by typing it or by selecting it from a dialog . Since it can be input manually, I want to ensure that I save that path as it is on the file system. For example: the user input c:\program files
, the path is updated to C:\Program Files
since it's how it's stored in the file system.
Here are the my requirements for the function:
- The input can be a regular path, an UNC path or a
\\?\
path. - If an invalid (or non-existent) path is provided, the function returns an empty string.
- The function supports Unicode paths.
- The function supports paths longer than
MAX_PATH
(yes it's possible). - The function returns "regular" paths (non
\\?\
paths).
I'm coming back to Windows development under Delphi after a decade long pause (I've been working on Web services for a while now) so I'm out of touch with the Windows API and Delphi a bit.
Here is my current (working) function. Do you see anything wrong with it? Especially with GetFinalPathNameByHandle (lpszFilePath
, cchFilePath
and dwFlags
).
function GetCaseSensitivePath(const APath: String): String;
const
EXT_MAX_PATH = 32767;
UNC_PREFIX = '\\';
UNIFIED_PREFIX = '\\?\';
UNIFIED_UNC_PREFIX = UNIFIED_PREFIX + 'UNC\';
var
UnifiedPath: PWideChar;
LinkHandle: THandle;
FilePath: array [0..EXT_MAX_PATH] of WideChar;
begin
if APath.StartsWith(UNIFIED_PREFIX) then
UnifiedPath := PWideChar(APath)
else
UnifiedPath := PWideChar(IfThen(APath.StartsWith(UNC_PREFIX), UNIFIED_UNC_PREFIX + APath.Substring(Length(UNC_PREFIX)), UNIFIED_PREFIX + APath));
LinkHandle := CreateFile(UnifiedPath, 0, FILE_SHARE_READ, nil, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0);
try
Win32Check(LinkHandle <> INVALID_HANDLE_VALUE);
try
if GetFinalPathNameByHandle(LinkHandle, FilePath, Length(FilePath), VOLUME_NAME_DOS) > 0 then
begin
Result := FilePath;
if UpperCase(Result.Substring(0, Length(UNIFIED_UNC_PREFIX))) = UNIFIED_UNC_PREFIX then
Result := UNC_PREFIX + Result.Substring(Length(UNIFIED_UNC_PREFIX))
else if Result.StartsWith(UNIFIED_PREFIX) then
Result := Result.Substring(Length(UNIFIED_PREFIX));
end
else
RaiseLastOSError;
finally
CloseHandle(LinkHandle);
end;
except
on EOSError do
Result := '';
end;
end;