# Delphi function to canonicalize the capitalization in a file path

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). I also got a suggestion to do a double call to avoid using 64K for FilePath but I don't see how I could optimize memory usage for that part.

function GetCaseSensitivePath(const APath: String): String;

const
EXT_MAX_PATH = 32767;
UNC_PREFIX = '\\';
UNIFIED_PREFIX = '\\?\';
UNIFIED_UNC_PREFIX = UNIFIED_PREFIX + 'UNC\';

var
UnifiedPath: PWideChar;
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));

try
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

As many Windows API functions work since 90's - you can call a function with zero size buffer first - function will fail, but will return needed buffer size, then you create your buffer with required size and call the function again with real buffer. In your case - you can make FilePath dynamic array and resize it between two calls of GetFinalPathNameByHandle.