I have some code which retrieves the 128bit NTFS Ids from files at specific paths. Then I attempted to retrieve the file path using this ID. The code works as long as when retrieving the paths I run as admin. This is not going to be possible in production. Unfortunately I am unable to call Marshal.GetLastWin32Error() because the System.AccessViolationException causes the application to completely crash. Below is the code to retrieve the paths.
public const int NO_PERMISSION = 0;
[DllImportAttribute("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
public static extern SafeFileHandle CreateFile(
string lpFileName,
uint dwDesiredAccess,
uint dwShareMode,
[InAttribute()] System.IntPtr lpSecurityAttributes,
uint dwCreationDisposition,
uint dwFlagsAndAttributes,
[InAttribute()] System.IntPtr hTemplateFile
);
[DllImportAttribute("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
public static extern SafeFileHandle OpenFileById(
IntPtr hVolumeHint,
FILE_ID_DESCRIPTOR lpFileId,
uint dwDesiredAccess,
uint dwShareMode,
[InAttribute()] System.IntPtr lpSecurityAttributes,
uint dwFlagsAndAttributes
);
public enum _FILE_ID_TYPE
{
FileIdType = 0,
ObjectIdType,
ExtendedFileIdType,
MaximumFileIdType
}
[StructLayout(LayoutKind.Explicit)]
public struct FILE_ID_128
{
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)]
[FieldOffset(0)]
public byte[] Identifier;
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct FILE_ID_DESCRIPTOR
{
public uint dwSize;
public _FILE_ID_TYPE Type;
public FILE_ID_128 ExtendedFileId;
}
public static string GetObjectPathFromId(string pathToSection, string hexId)
{
// We need a file handle to the drive we are looking in
using (SafeFileHandle handle = Methods.CreateFile(
pathToSection,
Constants.NO_PERMISSION,
Constants.NO_PERMISSION,
IntPtr.Zero,
Constants.OPEN_EXISTING,
0x02000000 | 0x00000080,
IntPtr.Zero))
{
// Build descriptor
FILE_ID_DESCRIPTOR descriptor = new FILE_ID_DESCRIPTOR();
descriptor.dwSize = (uint)Marshal.SizeOf(descriptor);
descriptor.Type = _FILE_ID_TYPE.ExtendedFileIdType;
descriptor.ExtendedFileId.Identifier = StringToByteArrayFastest(hexId);
using (SafeFileHandle actualFile = OpenFileById(handle.DangerousGetHandle(), descriptor,
Constants.NO_PERMISSION, Constants.NO_PERMISSION,
IntPtr.Zero, 0))
{
if (actualFile.IsInvalid)
return "";
// Buffer for the path, this should be way big enough
int sizeOfBuffer = 1024;
// Allocate a buffer
IntPtr pointer = Marshal.AllocHGlobal(sizeOfBuffer);
uint size = (uint)sizeOfBuffer;
uint returnValue = GetFinalPathNameByHandleW(actualFile.DangerousGetHandle(), pointer, size, 0);
// Copy it into a managed array
byte[] outPut = new byte[sizeOfBuffer];
Marshal.Copy(pointer, outPut, 0, (int)returnValue);
// Decode it
var str = Encoding.Unicode.GetString(outPut);
// Will be an empty string if the call fails
return str;
}
}
}
Again I want to specify - this code works perfectly when running as admin. The files are owned by the user, the user is able to delete, rename and move the files without any additional permissions.
Any help would be greatly appreciated thanks!
Edit1:
I implemented the answer found here How to handle AccessViolationException to successfully catch the exception. However even after doing this Marshal.GetLastWin32Error() returns 0. If anyone has any idea of how I can debug this type of issue please let me know.
Also it's still functioning when I run as admin, just not as a user.
Edit2:
Not sure if it's relevant - library with this code is building for .NET Standard 2.0 - Application using this library code is building for .NET Framework 4.6.2
User contributions licensed under CC BY-SA 3.0