I have the following directory structure:
D:\LinkTest
├─Links
│ ├─LinkToTargetDirectory.lnk
│ └─LinkToTargetFile.lnk
└─Targets
├─TargetFile.txt
└─TargetDirectory
└─FileInTargetDirectory.txt
.lnk
files are symbolic links.
They show up in Windows Explorer without the .lnk
extension, with the respective target's icon, and double-clicking LinkToTargetDirectory
shows the contents of D:\LinkTest\Targets\TargetDirectory
, and double-clicking LinkToTargetFile
opens the file D:\LinkTest\Targets\TargetFile.txt
in notepad.
.txt
files are text files.
Everything else are directories.
I would like to use the following code taken from this answer to get the path of the file/directory pointed to:
public static class NativeMethods
{
private static readonly IntPtr INVALID_HANDLE_VALUE = new IntPtr(-1);
private const uint FILE_READ_EA = 0x0008;
private const uint FILE_FLAG_BACKUP_SEMANTICS = 0x2000000;
[DllImport("Kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)]
static extern uint GetFinalPathNameByHandle(IntPtr hFile, [MarshalAs(UnmanagedType.LPTStr)] StringBuilder lpszFilePath, uint cchFilePath, uint dwFlags);
[DllImport("kernel32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool CloseHandle(IntPtr hObject);
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern IntPtr CreateFile(
[MarshalAs(UnmanagedType.LPTStr)] string filename,
[MarshalAs(UnmanagedType.U4)] uint access,
[MarshalAs(UnmanagedType.U4)] FileShare share,
IntPtr securityAttributes, // optional SECURITY_ATTRIBUTES struct or IntPtr.Zero
[MarshalAs(UnmanagedType.U4)] FileMode creationDisposition,
[MarshalAs(UnmanagedType.U4)] uint flagsAndAttributes,
IntPtr templateFile);
public static string GetFinalPathName(string path)
{
var h = CreateFile(path,
FILE_READ_EA,
FileShare.ReadWrite | FileShare.Delete,
IntPtr.Zero,
FileMode.Open,
FILE_FLAG_BACKUP_SEMANTICS,
IntPtr.Zero);
if (h == INVALID_HANDLE_VALUE)
throw new Win32Exception();
try
{
var sb = new StringBuilder(1024);
var res = GetFinalPathNameByHandle(h, sb, 1024, 0);
if (res == 0)
throw new Win32Exception();
return sb.ToString();
}
finally
{
CloseHandle(h);
}
}
}
What should happen:
Console.WriteLine(NativeMethods.GetFinalPathName(@"D:\LinkTest\Links\LinkToTargetDirectory.lnk"));
should print \\?\D:\LinkTest\Targets\TargetDirectory
Console.WriteLine(NativeMethods.GetFinalPathName(@"D:\LinkTest\Links\LinkToTargetFile.lnk"));
should print \\?\D:\LinkTest\Targets\TargetFile.txt
What actually happens:
Console.WriteLine(NativeMethods.GetFinalPathName(@"D:\LinkTest\Links\LinkToTargetDirectory.lnk"));
prints \\?\D:\LinkTest\Links\LinkToTargetDirectory.lnk
Console.WriteLine(NativeMethods.GetFinalPathName(@"D:\LinkTest\Links\LinkToTargetFile.lnk"));
prints \\?\D:\LinkTest\Links\LinkToTargetFile.lnk
So instead of getting the target's path, it just returns the path of the link file.
What is wrong with this code and how can I fix it?
I already found this: https://social.msdn.microsoft.com/forums/windowsdesktop/en-US/d8a26a7f-6661-48ce-b183-8e476dfffecd/am-i-using-getfinalpathnamebyhandle-incorrectly?forum=windowsgeneraldevelopmentissues
Passing 8 instead of 0 as the last argument to GetFinalPathNameByHandle
seems to help that user, but it does not change the behavior for me.