Skip to main content
1 of 3
AlexV
  • 141
  • 5

Is there anything wrong with my use of GetFinalPathNameByHandle (Windows API) in my Delphi program?

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;
AlexV
  • 141
  • 5