Created
February 15, 2023 18:43
-
-
Save glopesdev/47e4d87e0eea7d8154e806713a6d975c to your computer and use it in GitHub Desktop.
Resolve UWP package reparse point metadata
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| using System; | |
| using System.Collections.Generic; | |
| using System.Runtime.InteropServices; | |
| using Microsoft.Win32.SafeHandles; | |
| using System.IO; | |
| /// Resolve UWP package reparse point metadata (IO_REPARSE_TAG_APPEXECLINK) | |
| /// inspired by: https://stackoverflow.com/a/65583702/628228 | |
| internal static class AppExecLinkHelper | |
| { | |
| const string Kernel32 = "kernel32.dll"; | |
| const int MAX_REPARSE_SIZE = 0x4000; | |
| const uint FILE_GENERIC_READ = 0x80000000; | |
| const uint FILE_FLAG_BACKUP_SEMANTICS = 0x02000000; | |
| const uint FILE_FLAG_OPEN_REPARSE_POINT = 0x00200000; | |
| const uint IO_REPARSE_TAG_APPEXECLINK = 0x8000001B; | |
| const int FSCTL_GET_REPARSE_POINT = 0x900A8; | |
| public static string GetReparsePointTarget(string path) | |
| { | |
| using (var handle = OpenReparsePoint(path)) | |
| { | |
| var buffer = GetReparsePointData(handle); | |
| var reparseTag = BitConverter.ToUInt32(buffer, 0); | |
| if (reparseTag == IO_REPARSE_TAG_APPEXECLINK) | |
| { | |
| // from REPARSE_GUID_DATA_BUFFER (winnt.h) | |
| // (0) DWORD ReparseTag; | |
| // (4) WORD ReparseDataLength; | |
| // (6) WORD Reserved; | |
| // (8) ULONG ReparseGuid; | |
| // (12) WCHAR DataBuffer; | |
| var reparseDataLength = BitConverter.ToUInt16(buffer, 4) - 4; | |
| var reparseDataOffset = buffer.Length - reparseDataLength; | |
| var reparseData = GetStringListFromBuffer(buffer, reparseDataOffset, reparseDataLength); | |
| if (reparseData.Count > 2 && path.Contains(reparseData[0])) | |
| { | |
| return reparseData[2]; | |
| } | |
| } | |
| return path; | |
| } | |
| } | |
| static byte[] GetReparsePointData(SafeFileHandle hReparsePoint) | |
| { | |
| var buffer = new byte[MAX_REPARSE_SIZE]; | |
| if (!DeviceIoControl( | |
| hReparsePoint, | |
| FSCTL_GET_REPARSE_POINT, | |
| null, 0, | |
| buffer, MAX_REPARSE_SIZE, | |
| out uint bytesReturned, | |
| IntPtr.Zero)) | |
| { | |
| var errorCode = Marshal.GetHRForLastWin32Error(); | |
| Marshal.ThrowExceptionForHR(errorCode); | |
| } | |
| Array.Resize(ref buffer, (int)bytesReturned); | |
| return buffer; | |
| } | |
| static SafeFileHandle OpenReparsePoint(string path) | |
| { | |
| var handle = CreateFile( | |
| path, | |
| FILE_GENERIC_READ, | |
| FileShare.Read, | |
| IntPtr.Zero, | |
| FileMode.Open, | |
| FILE_FLAG_BACKUP_SEMANTICS | | |
| FILE_FLAG_OPEN_REPARSE_POINT, | |
| IntPtr.Zero); | |
| if (handle.IsInvalid) | |
| { | |
| var errorCode = Marshal.GetHRForLastWin32Error(); | |
| Marshal.ThrowExceptionForHR(errorCode); | |
| } | |
| return handle; | |
| } | |
| private static List<string> GetStringListFromBuffer(byte[] buffer, int offset, int length) | |
| { | |
| var list = new List<string>(); | |
| char[] chars = new char[length]; | |
| Buffer.BlockCopy(buffer, offset, chars, 0, length); | |
| offset = 0; | |
| var len = 0; | |
| for (int i = 0; i < length; i++, len++) | |
| { | |
| if (chars[i] == '\0') | |
| { | |
| if (--len == 0) break; | |
| list.Add(new string(chars, offset, len)); | |
| offset = i + 1; | |
| len = 0; | |
| } | |
| } | |
| return list; | |
| } | |
| [DllImport(Kernel32, SetLastError = true, CharSet = CharSet.Unicode)] | |
| static extern SafeFileHandle CreateFile( | |
| string lpFileName, | |
| uint dwDesiredAccess, | |
| [MarshalAs(UnmanagedType.U4)] FileShare dwShareMode, | |
| IntPtr lpSecurityAttributes, | |
| [MarshalAs(UnmanagedType.U4)] FileMode dwCreationDisposition, | |
| uint dwFlagsAndAttributes, | |
| IntPtr hTemplateFile); | |
| [DllImport(Kernel32, SetLastError = true, CharSet = CharSet.Unicode)] | |
| static extern bool DeviceIoControl( | |
| SafeHandle hDevice, | |
| uint dwIoControlCode, | |
| [In] byte[] lpInBuffer, | |
| uint nInBufferSize, | |
| [Out] byte[] lpOutBuffer, | |
| uint nOutBufferSize, | |
| [Out] out uint lpBytesReturned, | |
| [In] IntPtr lpOverlapped); | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment