4

The PowerShell commandlet Get-Item, if applied on a file, returns a System.IO.FileInfo type:

PS C:\> $item = get-item c:\windows\System32\atl.dll
PS C:\> $item.GetType().FullName
System.IO.FileInfo

Using the tabulator on $item, I find that it has a member/property named Target:

PS C:\> $item.Target
C:\Windows\WinSxS\amd64_microsoft-windows-atl_31bf3856ad364e35_10.0.18362.1_none_7d7dafc1d6eadbc7\atl.dll

However, there is no mention of that member/property in Microsoft's documentation for System.IO.FileInfo and its base class System.IO.FileSystemInfo.

I suspect that the value of Target is some kind of where a junction or symbolic or hard link points to - yet, if I apply Target on the target-file, I get the original file again:

PS C:\> (get-item $item.Target).Target
C:\Windows\System32\atl.dll

So, what is this Target member?

René Nyffenegger
  • 39,402
  • 33
  • 158
  • 293

1 Answers1

2

tl;dr:

  • There is no documentation for ETS (Extended Type System) members such as .Target.

  • c:\windows\System32\atl.dll is one of two so-called hard links that point to the same file data (the other being C:\Windows\WinSxS\amd64_microsoft-windows-atl_31bf3856ad364e35_10.0.18362.1_none_7d7dafc1d6eadbc7\atl.dll).

    • For hard links, .Target reports all the other hard links (paths that point to the same file), which is why the .Target property values of the two items point to the respective other path.
  • Your code no longer works in PowerShell [Core], where support for reporting hard links via .Target was removed - see this GitHub issue.


As Mathias R. Jessen points out, the .Target property is an ETS (Extended Type System) property - a property added by PowerShell to extend the capabilities of a native .NET type.

There is no documentation for such ETS members (which may also include methods) - your best bet is to study their definition, which isn't guaranteed to give you the full story or only with a nontrivial amount of effort:

In a first step, you can use the Get-TypeData cmdlet to inspect the definition of the .Target property (which is added to both System.IO.FileInfo and System.IO.DirectoryInfo instances):

# Windows PowerShell
PS> (Get-TypeData System.IO.FileInfo).Members.Target | foreach GetCodeReference

Name                       : GetTarget
DeclaringType              : Microsoft.PowerShell.Commands.InternalSymbolicLinkLinkCodeMethods
[...]
MemberType                 : Method
ReturnType                 : System.Collections.Generic.IEnumerable`1[System.String]
[...]

(Get-Item $PROFILE | Get-Member Target can also be used, but lacks important information in this case: the .Target property is of type CodeProperty, i.e. a property whose value is determined by a call to a static method of a .NET type, but the output's .Definition property shows the method only as Target{get=GetTarget;}; that is, the full type name is missing).

That is, a call to [Microsoft.PowerShell.Commands.InternalSymbolicLinkLinkCodeMethods]::GetTarget() with the System.IO.DirectoryInfo instance at hand is used to return that instance's .Target property value.

  • In Windows PowerShell, this is the end of the line, given that its source code isn't publicly available.

  • In PowerShell [Core] 6+, you can examine the source code in the GitHub repository, notably in file FileSystemProvider.cs (this link is a permalink, which will become outdated over time; click on the Branch: dropdown list at the top of the page to switch to the current master branch).

The partially inferred behavior of .Target is discussed next.


.Target's purpose and behavior:

  • The .Target property returns a file-system reparse point's (Windows) / symlink's (symbolic link's; Unix) target path(s).[1]

  • If the input path isn't a reparse point or symlink, .Target returns "nothing": specifically, [System.Management.Automation.Internal.AutomationNull]::Value (Windows PowerShell; behaves like $null in most contexts) / $null (PowerShell [Core]).

There's an important difference in the behavior of .Target between Windows PowerShell and PowerShell [Core] 6+ (as of PowerShell 7.0):

  • In Windows PowerShell, .Target returns an enumeration of target paths, meaning that multiple paths may be returned, which only applies if the input path is one of several hard links to the same file (data).

    • Specifically, a hard link's .Target property reports all other hard links that exist to that file (i.e., not including the input path).

    • This explains why, given a file with two hard links, their .Target properties point to the respective other path, as is the case with the file pointed to by hard link C:\Windows\System32\atl.dll, for instance.

  • In PowerShell [Core], .Target only every returns a single path, because support for hard links was removed - see this GitHub issue.

    • As an aside: Unlike Windows, Unix-like platforms have no system-level support for enumerating hard links, so finding all hard links to a given file is both cumbersome and slow.

[1] Apart from hard links, Unix-like platforms have only symbolic links, whereas on Windows the category of reparse points comprises not only symbolic links, but also junctions, volume mount points, and, in recent Windows versions, AppX reparse points (app execution aliases for Microsoft Store applications).

mklement0
  • 382,024
  • 64
  • 607
  • 775