ShortcutLib 1.0.6

dotnet add package ShortcutLib --version 1.0.6
                    
NuGet\Install-Package ShortcutLib -Version 1.0.6
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="ShortcutLib" Version="1.0.6" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="ShortcutLib" Version="1.0.6" />
                    
Directory.Packages.props
<PackageReference Include="ShortcutLib" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add ShortcutLib --version 1.0.6
                    
#r "nuget: ShortcutLib, 1.0.6"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package ShortcutLib@1.0.6
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=ShortcutLib&version=1.0.6
                    
Install as a Cake Addin
#tool nuget:?package=ShortcutLib&version=1.0.6
                    
Install as a Cake Tool

ShortcutLib

A zero-dependency .NET library for creating, opening, and editing Windows Shell Link (.lnk) shortcut files in memory.

Features

  • Create shortcuts to local files, folders, network shares, and printers
  • Open and parse existing .lnk files back into structured options
  • Edit existing shortcuts by modifying properties and re-serializing
  • Environment variable support (e.g. %windir%\notepad.exe)
  • Custom icon, arguments, working directory, and description
  • Window style control (normal, maximized, minimized)
  • Run as Administrator support
  • Hotkey assignment (key + modifier combinations)
  • Argument padding to hide command-line arguments in shortcut properties
  • Unicode string data support
  • Custom file timestamps (creation, access, write)
  • Target file size metadata
  • Relative path support
  • LinkInfo structure (local volume info and network share info)
  • Extra data blocks: Console, ConsoleFE, Darwin (MSI), Shim, IconEnvironment, KnownFolder, Tracker, PropertyStore, SpecialFolder, VistaAndAboveIDList
  • Unknown extra data block preservation (round-trips unrecognized blocks)
  • All 27 LinkFlags from the MS-SHLLINK spec (AllowLinkToLink, ForceNoLinkTrack, etc.)
  • FileAttributes enum (ReadOnly, Hidden, System, Encrypted, etc.)
  • Network path enhancements (DeviceName, NetworkProviderType)
  • LinkInfo Unicode support (0x24 header) for non-ASCII paths, volume labels, and share names
  • PropertyStoreBuilder for typed AppUserModelID, ToastActivatorCLSID, PreventPinning, and 30+ additional properties
  • PropertyStoreReader for parsing/deserializing property store binary data back into typed entries
  • 10 VT types: VT_LPWSTR, VT_BOOL, VT_UI4, VT_CLSID, VT_I2, VT_I4, VT_UI2, VT_I8, VT_UI8, VT_FILETIME, VT_BLOB, VT_LPSTR
  • Arbitrary named property support in PropertyStoreBuilder (string, bool, int, uint, DateTime, blob, etc.)
  • WinXHasher for computing WinX Power User Menu hashes
  • DarwinDescriptor for decoding MSI advertised shortcut descriptors (product/component GUIDs, feature ID)
  • TrackerData forensic extraction (MAC address, timestamp from Version 1 UUIDs)
  • Named constants: DriveTypes, CsidlFolderIds, VirtualKeys, ConsoleFillAttributes, ConsoleFontFamilies, ShimLayerNames
  • ShortcutSanitizer for stripping privacy-sensitive metadata (machine name, MAC address, unknown blocks, etc.)
  • Overlay data (post-terminal block data) preservation
  • Returns raw byte[] — no COM interop or Windows Shell dependency
  • Targets .NET 10

Installation

Add a reference to the ShortcutLib project or install the NuGet package:

dotnet add package ShortcutLib

Usage

Creating Shortcuts

using ShortcutLib;

// Unicode strings with timestamps and file metadata
byte[] lnk = Shortcut.Create(new ShortcutOptions
{
    Target = @"C:\Windows\notepad.exe",
    Arguments = "readme.txt",
    Description = "Notepad shortcut",
    WorkingDirectory = @"C:\Windows",
    UseUnicode = true,
    CreationTime = new DateTime(2024, 1, 15, 12, 0, 0, DateTimeKind.Utc),
    AccessTime = DateTime.UtcNow,
    WriteTime = DateTime.UtcNow,
    FileSize = 193536
});

// Shortcut with relative path
byte[] lnk = Shortcut.Create(new ShortcutOptions
{
    Target = @"C:\Projects\MyApp\bin\app.exe",
    RelativePath = @".\bin\app.exe"
});

// Shortcut with LinkInfo (local volume)
byte[] lnk = Shortcut.Create(new ShortcutOptions
{
    Target = @"C:\Windows\notepad.exe",
    LinkInfo = new LinkInfo
    {
        Local = new LocalPathInfo
        {
            BasePath = @"C:\Windows\notepad.exe",
            DriveType = DriveTypes.Fixed,
            DriveSerialNumber = 0x1234ABCD,
            VolumeLabel = "Windows"
        }
    }
});

// Shortcut with LinkInfo (network share)
byte[] lnk = Shortcut.Create(new ShortcutOptions
{
    Target = @"\\server\share\docs\report.docx",
    LinkInfo = new LinkInfo
    {
        Network = new NetworkPathInfo
        {
            ShareName = @"\\server\share",
            CommonPathSuffix = @"docs\report.docx"
        }
    }
});

// Shortcut with KnownFolder data block
byte[] lnk = Shortcut.Create(new ShortcutOptions
{
    Target = @"C:\Windows\notepad.exe",
    KnownFolder = new KnownFolderData
    {
        FolderId = KnownFolderIds.Windows
    }
});

// Shortcut with distributed link tracker
byte[] lnk = Shortcut.Create(new ShortcutOptions
{
    Target = @"C:\Windows\notepad.exe",
    Tracker = new TrackerData
    {
        MachineId = "WORKSTATION01",
        VolumeId = Guid.Parse("a1b2c3d4-e5f6-7890-abcd-ef1234567890"),
        ObjectId = Guid.Parse("12345678-9abc-def0-1234-567890abcdef")
    }
});

// Icon with environment variable path
byte[] lnk = Shortcut.Create(new ShortcutOptions
{
    Target = @"C:\Windows\notepad.exe",
    IconEnvironmentPath = @"%SystemRoot%\system32\shell32.dll"
});

// Special folder data block
byte[] lnk = Shortcut.Create(new ShortcutOptions
{
    Target = @"C:\Windows\notepad.exe",
    SpecialFolder = new SpecialFolderData
    {
        FolderId = CsidlFolderIds.Windows
    }
});

// Console shortcut with display settings
byte[] lnk = Shortcut.Create(new ShortcutOptions
{
    Target = @"C:\Windows\System32\cmd.exe",
    Console = new ConsoleData
    {
        ScreenBufferSizeX = 120,
        ScreenBufferSizeY = 9001,
        WindowSizeX = 120,
        WindowSizeY = 30,
        FaceName = "Consolas",
        QuickEdit = true
    },
    ConsoleCodePage = 65001   // UTF-8
});

// MSI advertised shortcut (Darwin data block)
byte[] lnk = Shortcut.Create(new ShortcutOptions
{
    Target = @"C:\Windows\notepad.exe",
    DarwinData = "[ProductCode]>Feature>Component"
});

// App compatibility shim
byte[] lnk = Shortcut.Create(new ShortcutOptions
{
    Target = @"C:\OldApp\setup.exe",
    ShimLayerName = ShimLayerNames.WinXPSP3
});

// Explicit file attributes
byte[] lnk = Shortcut.Create(new ShortcutOptions
{
    Target = @"C:\Windows\notepad.exe",
    FileAttributes = FileAttributes.Hidden | FileAttributes.System | FileAttributes.Archive
});

// Network share with mapped drive and provider
byte[] lnk = Shortcut.Create(new ShortcutOptions
{
    Target = @"\\server\share\file.txt",
    LinkInfo = new LinkInfo
    {
        Network = new NetworkPathInfo
        {
            ShareName = @"\\server\share",
            CommonPathSuffix = "file.txt",
            DeviceName = "Z:",
            NetworkProviderType = NetworkProviderTypes.Lanman
        }
    }
});

// Typed property store (AppUserModelId for taskbar grouping)
var builder = new PropertyStoreBuilder
{
    AppUserModelId = "MyCompany.MyApp",
    PreventPinning = true
};
byte[] lnk = Shortcut.Create(new ShortcutOptions
{
    Target = @"C:\MyApp\app.exe",
    PropertyStoreData = builder.Build()
});

// PropertyStore with System.Link and named properties
var builder = new PropertyStoreBuilder
{
    AppUserModelId = "MyCompany.MyApp",
    TargetParsingPath = @"C:\MyApp\app.exe",
    ItemTypeText = "Application"
};
builder.AddNamedStringProperty("CustomTag", "MyValue");
byte[] lnk = Shortcut.Create(new ShortcutOptions
{
    Target = @"C:\MyApp\app.exe",
    PropertyStoreData = builder.Build()
});

// Unicode LinkInfo for non-ASCII paths (auto-detects when needed)
byte[] lnk = Shortcut.Create(new ShortcutOptions
{
    Target = @"C:\Users\ユーザー\Documents\レポート.docx",
    LinkInfo = new LinkInfo
    {
        Local = new LocalPathInfo
        {
            BasePath = @"C:\Users\ユーザー\Documents\レポート.docx",
            VolumeLabel = "データ"
        }
    }
});

// Force Unicode LinkInfo explicitly
byte[] lnk = Shortcut.Create(new ShortcutOptions
{
    Target = @"C:\test.exe",
    UseUnicodeLinkInfo = true,
    LinkInfo = new LinkInfo
    {
        Local = new LocalPathInfo { BasePath = @"C:\test.exe" }
    }
});

// WinX Power User Menu shortcut (Win+X menu)
byte[] propStore = WinXHasher.BuildPropertyStore(@"C:\Windows\System32\cmd.exe");
byte[] lnk = Shortcut.Create(new ShortcutOptions
{
    Target = @"C:\Windows\System32\cmd.exe",
    PropertyStoreData = propStore
});

// Parse property store data from an existing shortcut
ShortcutOptions opts = Shortcut.Open(File.ReadAllBytes("shortcut.lnk"));
if (opts.PropertyStoreData != null)
{
    var entries = PropertyStoreReader.Parse(opts.PropertyStoreData);
    foreach (var entry in entries)
        Console.WriteLine($"{entry.FormatId} PID={entry.PropertyId} Name={entry.Name} Value={entry.Value}");
}

// Decode a Darwin (MSI) descriptor
var darwin = DarwinDescriptor.TryDecode(opts.DarwinData);
if (darwin != null)
{
    Console.WriteLine($"Product: {darwin.ProductCode}");
    Console.WriteLine($"Feature: {darwin.FeatureId}");
    Console.WriteLine($"Component: {darwin.ComponentCode}");
}

// Forensic extraction from TrackerData
if (opts.Tracker != null)
{
    Console.WriteLine($"MAC: {opts.Tracker.ExtractMacAddressString()}");
    Console.WriteLine($"Timestamp: {opts.Tracker.ExtractTimestamp()}");
    Console.WriteLine($"Birth MAC: {opts.Tracker.ExtractBirthMacAddressString()}");
}

// Preserve unknown extra data blocks during round-trip
var parsed = Shortcut.Open(File.ReadAllBytes("shortcut.lnk"));
// Any unrecognized extra data blocks are preserved in UnknownExtraDataBlocks
byte[] roundTripped = Shortcut.Create(parsed);

// Strip privacy-sensitive metadata (machine name, MAC address, unknown blocks, etc.)
byte[] original = File.ReadAllBytes("shortcut.lnk");
byte[] sanitized = ShortcutSanitizer.SanitizeBytes(original);
File.WriteAllBytes("clean.lnk", sanitized);

// Allow shortcut to link to another .lnk file
byte[] lnk = Shortcut.Create(new ShortcutOptions
{
    Target = @"C:\Users\Desktop\other.lnk",
    AllowLinkToLink = true
});

// All features combined
byte[] lnk = Shortcut.Create(new ShortcutOptions
{
    Target = @"C:\Windows\notepad.exe",
    Arguments = "test.txt",
    Description = "Full-featured shortcut",
    WorkingDirectory = @"C:\Windows",
    IconLocation = @"C:\Windows\notepad.exe",
    WindowStyle = ShortcutWindowStyle.Maximized,
    RunAsAdmin = true,
    HotkeyKey = VirtualKeys.T,
    HotkeyModifiers = HotkeyModifiers.Control | HotkeyModifiers.Alt,
    UseUnicode = true,
    CreationTime = DateTime.UtcNow,
    FileSize = 193536,
    RelativePath = @".\notepad.exe",
    LinkInfo = new LinkInfo
    {
        Local = new LocalPathInfo
        {
            BasePath = @"C:\Windows\notepad.exe",
            VolumeLabel = "C"
        }
    },
    KnownFolder = new KnownFolderData { FolderId = KnownFolderIds.Windows },
    Tracker = new TrackerData
    {
        MachineId = "MYPC",
        VolumeId = Guid.NewGuid(),
        ObjectId = Guid.NewGuid()
    }
});

Opening Shortcuts

Parse an existing .lnk file to inspect or reuse its properties:

using ShortcutLib;

// Read an existing shortcut
byte[] data = File.ReadAllBytes("Notepad.lnk");
ShortcutOptions options = Shortcut.Open(data);

Console.WriteLine(options.Target);           // C:\Windows\notepad.exe
Console.WriteLine(options.Description);      // My Notepad Shortcut
Console.WriteLine(options.WindowStyle);      // Normal
Console.WriteLine(options.RunAsAdmin);       // False

Editing Shortcuts

Modify specific properties of an existing shortcut without rebuilding from scratch:

using ShortcutLib;

byte[] original = File.ReadAllBytes("Notepad.lnk");

// Change target and add arguments
byte[] modified = Shortcut.Edit(original, options =>
{
    options.Target = @"C:\Windows\System32\cmd.exe";
    options.Arguments = "/k echo Hello";
    options.RunAsAdmin = true;
});

File.WriteAllBytes("Admin-CMD.lnk", modified);

// Change window style
byte[] maximized = Shortcut.Edit(original, options =>
{
    options.WindowStyle = ShortcutWindowStyle.Maximized;
});

// Add a hotkey
byte[] withHotkey = Shortcut.Edit(original, options =>
{
    options.HotkeyKey = VirtualKeys.T;
    options.HotkeyModifiers = HotkeyModifiers.Control | HotkeyModifiers.Alt;
});

API

Shortcut.Open

public static ShortcutOptions Shortcut.Open(byte[] data)

Parses a .lnk file's binary content and returns a ShortcutOptions object with all recognized properties populated. Throws ArgumentException if data is too short and FormatException if the header is invalid.

Shortcut.Edit

public static byte[] Shortcut.Edit(byte[] data, Action<ShortcutOptions> modify)

Opens an existing .lnk file, applies the modification callback, and returns the re-serialized result as a new byte array. Unmodified properties are preserved.

Shortcut.Create

public static byte[] Shortcut.Create(ShortcutOptions options)

ShortcutOptions

Property Type Default Description
Target string (required) Target path
Arguments string? null Command-line arguments
PadArguments bool false Pad arguments to 31 KB buffer
IconLocation string? null Icon file path
IconIndex int 0 Icon index within file
Description string? null Shortcut description
WorkingDirectory string? null Working directory
IsPrinterLink bool false Treat target as a printer
WindowStyle ShortcutWindowStyle Normal Initial window state
RunAsAdmin bool false Run target as administrator
HotkeyKey byte 0 Virtual key code (0 = none)
HotkeyModifiers HotkeyModifiers None Hotkey modifier keys
UseUnicode bool false Write string data as Unicode (UTF-16LE)
CreationTime DateTime? null Target file creation time
AccessTime DateTime? null Target file last access time
WriteTime DateTime? null Target file last write time
FileSize uint 0 Target file size in bytes
RelativePath string? null Relative path to target from .lnk file
LinkInfo LinkInfo? null Target location info (volume/network)
IconEnvironmentPath string? null Icon path with environment variables
KnownFolder KnownFolderData? null Known folder data block
Tracker TrackerData? null Distributed link tracker data
PropertyStoreData byte[]? null Raw serialized property store bytes
SpecialFolder SpecialFolderData? null Special folder data block
Console ConsoleData? null Console display settings
ConsoleCodePage uint? null Far East console code page
DarwinData string? null MSI advertised shortcut descriptor
ShimLayerName string? null App compatibility layer (e.g. "WINXP")
VistaIdListData byte[]? null Vista+ alternative IDList
OverlayData byte[]? null Data after terminal block
UnknownExtraDataBlocks List<RawExtraDataBlock>? null Preserved unrecognized extra data blocks
UseUnicodeLinkInfo bool? null Force Unicode (0x24) LinkInfo header; null = auto-detect
FileAttributes FileAttributes? null Explicit file attributes (auto-detected when null)
ForceNoLinkInfo bool false Ignore LinkInfo during resolution
RunInSeparateProcess bool false 16-bit target in separate VDM
NoPidlAlias bool false No shell namespace alias
ForceNoLinkTrack bool false Ignore TrackerDataBlock
EnableTargetMetadata bool false Populate PropertyStore on target set
DisableLinkPathTracking bool false Ignore EnvironmentVariableDataBlock
DisableKnownFolderTracking bool false Ignore KnownFolder/SpecialFolder
DisableKnownFolderAlias bool false Use unaliased known folder IDList
AllowLinkToLink bool false Allow shortcut to another .lnk
UnaliasOnSave bool false Unalias target IDList on save
KeepLocalIDListForUNCTarget bool false Store local IDList for UNC targets

ShortcutWindowStyle

Value Description
Normal Normal window (default)
Maximized Start maximized
Minimized Start minimized

HotkeyModifiers

Combinable flags for hotkey modifier keys:

Flag Description
None No modifier
Shift Shift key
Control Control key
Alt Alt key

Use VirtualKeys constants for HotkeyKey: VirtualKeys.A–VirtualKeys.Z, VirtualKeys.D0–VirtualKeys.D9, VirtualKeys.F1–VirtualKeys.F24, VirtualKeys.NumLock, VirtualKeys.ScrollLock.

LinkInfo

Describes the target's location per [MS-SHLLINK] 2.3. Provide Local, Network, or both.

LocalPathInfo:

Property Type Default Description
BasePath string (required) Full local path to target
DriveType uint DriveTypes.Fixed Drive type (see DriveTypes)
DriveSerialNumber uint 0 Volume serial number
VolumeLabel string "" Volume label

NetworkPathInfo:

Property Type Default Description
ShareName string (required) UNC share name
CommonPathSuffix string "" Path suffix after share
DeviceName string? null Mapped drive letter (e.g. "Z:")
NetworkProviderType uint? null Network provider (see NetworkProviderTypes)

KnownFolderData

Property Type Description
FolderId Guid Known folder GUID (use KnownFolderIds constants)
Offset uint Offset into the IDList

KnownFolderIds

Predefined GUIDs for common known folders (55 constants):

User folders: Desktop, Documents, Downloads, Music, Pictures, Videos, Profile, SavedGames, Contacts, Searches, Favorites, Links, Templates

System: ProgramFiles, ProgramFilesX86, ProgramFilesCommon, ProgramFilesCommonX86, System, SystemX86, Windows, Fonts

Application data: AppData, LocalAppData, LocalAppDataLow, ProgramData

Start menu: StartMenu, Programs, Startup, AdminTools

Common (all-users): CommonStartMenu, CommonPrograms, CommonStartup, CommonDesktopDir, CommonTemplates, CommonAdminTools

Public: UserProfiles, Public, PublicDesktop, PublicDocuments, PublicDownloads, PublicMusic, PublicPictures, PublicVideos

Shell locations: RecycleBin, QuickLaunch, SendTo, Recent, PrintHood, NetHood, Cookies, History, InternetCache, UserProgramFiles

TrackerData

Distributed link tracking service data per [MS-SHLLINK] 2.5.10.

Property Type Description
MachineId string NetBIOS machine name (max 15 chars)
VolumeId Guid Volume GUID
ObjectId Guid Object GUID
BirthVolumeId Guid? Birth volume GUID (defaults to VolumeId)
BirthObjectId Guid? Birth object GUID (defaults to ObjectId)

SpecialFolderData

Property Type Description
FolderId uint CSIDL value identifying the special folder
Offset uint Offset into the IDList

FileAttributes

[Flags] enum for explicit target file attributes:

ReadOnly, Hidden, System, Directory, Archive, Normal, Temporary, SparseFile, ReparsePoint, Compressed, Offline, NotContentIndexed, Encrypted

When ShortcutOptions.FileAttributes is null, attributes are auto-detected from the target path.

ConsoleData

Console display settings for ConsoleDataBlock (signature 0xA0000002, 204 bytes).

Property Type Default Description
FillAttributes ushort 0 Text foreground/background color
PopupFillAttributes ushort 0 Popup color
ScreenBufferSizeX short 80 Buffer width (columns)
ScreenBufferSizeY short 300 Buffer height (rows)
WindowSizeX short 80 Window width (columns)
WindowSizeY short 25 Window height (rows)
WindowOriginX/Y short 0 Window position
FontSize uint 0 Font height (high) + width (low)
FontFamily uint 0 Font family + pitch flags
FontWeight uint 0 400=normal, 700=bold
FaceName string "Consolas" Font name (max 31 chars)
CursorSize uint 25 Cursor size % (1-100)
FullScreen bool false Full screen mode
QuickEdit bool false Mouse selection
InsertMode bool false Insert mode
AutoPosition bool true Auto-position window
HistoryBufferSize uint 50 History buffer size
NumberOfHistoryBuffers uint 4 History buffer count
HistoryNoDup bool false Remove history duplicates
ColorTable uint[16] (classic palette) 16 RGB colors (0x00BBGGRR)

NetworkProviderTypes

Well-known WNNC_NET_* constants (45 values): Lanman, Netware, SunPcNfs, Vines, Avid, Docuspace, Mangosoft, Sernet, Riverfront1, Riverfront2, Decorb, Protstor, FjRedir, Distinct, Twins, Rdr2Sample, Csc, ThreeIn1, ExtendNet, Stac, Foxbat, Yahoo, Exifs, Dav, Knoware, ObjectDire, Masfax, HobNfs, Shiva, Ibmal, Lock, TerminalServices, Srt, Quincy, OpenAfs, Avid1, Dfs, Kwnp, Zenworks, DriveOnWeb, VMware, Rsfx, Mfiles, MsNfs, Google

PropertyStoreBuilder

Builds typed MS-PROPSTORE data for ShortcutOptions.PropertyStoreData:

var builder = new PropertyStoreBuilder
{
    AppUserModelId = "MyCompany.MyApp",
    PreventPinning = true,
    ToastActivatorCLSID = Guid.Parse("..."),
    RelaunchCommand = "myapp.exe --relaunch"
};
byte[] lnk = Shortcut.Create(new ShortcutOptions
{
    Target = @"C:\MyApp\app.exe",
    PropertyStoreData = builder.Build()
});

AppUserModel properties (Format ID: 9F4C2855-9CDB-4D7B-82BF-440971C8D266):

Property Type Description
AppUserModelId string? Taskbar grouping / jump list ID (PID 5)
ToastActivatorCLSID Guid? Toast notification COM CLSID (PID 26)
PreventPinning bool? Prevent taskbar/Start pinning (PID 9)
RelaunchCommand string? Taskbar relaunch command (PID 2)
RelaunchDisplayNameResource string? Relaunch display name (PID 4)
RelaunchIconResource string? Relaunch icon (PID 3)
ExcludeFromShowInNewInstall bool? Hide from "New programs" list (PID 8)
IsDestListSeparator bool? Jump list separator (PID 6)
IsDestListLink bool? Destination list link (PID 7)
BestShortcut bool? Best shortcut flag (PID 10)
IsDualMode bool? Dual mode app flag (PID 11)
StartPinOption uint? Start pin option (PID 12)
PackageRelativeApplicationID string? Package-relative app ID (PID 13)
HostEnvironment uint? Host environment (PID 14)
PackageFamilyName string? Package family name (PID 15)
PackageFullName string? Package full name (PID 16)
PackageInstallPath string? Package install path (PID 17)
InstalledBy string? Installed by (PID 18)
RecordState uint? Record state (PID 19)
ParentID string? Parent ID (PID 20)
Relevance uint? Relevance (PID 21)
DestListProvidedTitle string? Destination list title (PID 22)
DestListProvidedDescription string? Destination list description (PID 23)
DestListProvidedGroupName string? Destination list group name (PID 24)
DestListLogoUri string? Destination list logo URI (PID 25)
RunFlags uint? Run flags (PID 27)
ActivationContext string? Activation context (PID 28)
VisualElementsManifestHintPath string? Visual elements manifest hint path (PID 29)
ExcludedFromLauncher bool? Excluded from launcher (PID 30)
FeatureOnDemand bool? Feature on demand (PID 31)
TileUniqueId string? Tile unique ID (PID 32)

System.Link properties (Format ID: B9B4B3FC-2B51-4A42-B5D8-324146AFCF25):

Property Type Description
TargetParsingPath string? Canonical target path (PID 2)
LinkComment string? Link comment (PID 3)
DateVisited DateTime? Date visited (PID 4, VT_FILETIME)
FeedUrl string? Feed URL (PID 5)
LinkStatus int? Link status (PID 6, VT_I4)
TargetSFGAOFlags uint? Shell attributes (PID 8)

Other storage properties:

Property Type Description
ItemTypeText string? System.ItemTypeText
MimeType string? System.MIMEType
TargetUrl string? System.Link.TargetUrl
TargetExtension string? System.Link.TargetExtension
WinXHash uint? WinX Power User Menu hash

Named properties (arbitrary string-keyed name/value pairs):

var builder = new PropertyStoreBuilder();
builder.AddNamedStringProperty("CustomKey", "CustomValue")
       .AddNamedUInt32Property("Count", 42)
       .AddNamedBoolProperty("Enabled", true)
       .AddNamedInt32Property("Status", -1)
       .AddNamedFileTimeProperty("Timestamp", DateTime.UtcNow);
Method Description
AddNamedStringProperty(name, value) Named string (VT_LPWSTR)
AddNamedUInt32Property(name, value) Named uint32 (VT_UI4)
AddNamedBoolProperty(name, value) Named bool (VT_BOOL)
AddNamedInt32Property(name, value) Named int32 (VT_I4)
AddNamedInt16Property(name, value) Named int16 (VT_I2)
AddNamedUInt16Property(name, value) Named uint16 (VT_UI2)
AddNamedInt64Property(name, value) Named int64 (VT_I8)
AddNamedUInt64Property(name, value) Named uint64 (VT_UI8)
AddNamedFileTimeProperty(name, value) Named DateTime (VT_FILETIME)
AddNamedAnsiStringProperty(name, value) Named ANSI string (VT_LPSTR)
AddNamedBlobProperty(name, value) Named byte[] (VT_BLOB)

ShortcutSanitizer

Strips privacy-sensitive metadata from shortcut files. LNK files may contain forensic artifacts: machine name and MAC address (TrackerData), file owner and computer name (PropertyStoreData), and unstructured data after the terminal block (OverlayData).

// Sanitize in-place
ShortcutSanitizer.Sanitize(options);

// Or sanitize a raw byte array
byte[] clean = ShortcutSanitizer.SanitizeBytes(File.ReadAllBytes("shortcut.lnk"));

PropertyStoreReader

Parses serialized MS-PROPSTORE binary data back into typed entries:

var entries = PropertyStoreReader.Parse(options.PropertyStoreData);
foreach (var entry in entries)
{
    Console.WriteLine($"FormatId={entry.FormatId} PID={entry.PropertyId} Name={entry.Name}");
    Console.WriteLine($"  VtType={entry.VtType} Value={entry.Value}");
}

Returns a List<PropertyStoreEntry> with each entry containing:

Property Type Description
FormatId Guid Property storage format ID
PropertyId uint? Numeric property ID (null for named properties)
Name string? Property name (null for PID-based properties)
VtType ushort VARIANT type code
Value object? Deserialized value (string, bool, uint, int, Guid, DateTime, etc.)

Supported VT types: VT_LPWSTR (31), VT_BOOL (11), VT_UI4 (19), VT_I4 (3), VT_CLSID (72), VT_FILETIME (64), VT_I2 (2), VT_UI2 (18), VT_I8 (20), VT_UI8 (21), VT_BLOB (65), VT_LPSTR (30). Unknown types return the raw bytes as byte[].

WinXHasher

Computes the hash required for Windows 10+ Power User Menu (Win+X) shortcuts:

// Compute hash only
uint hash = WinXHasher.ComputeHash(@"C:\Windows\System32\cmd.exe");

// With arguments
uint hash = WinXHasher.ComputeHash(@"C:\Windows\System32\cmd.exe", "/k echo test");

// Build a complete property store with the hash embedded
byte[] propStore = WinXHasher.BuildPropertyStore(@"C:\Windows\System32\cmd.exe");
byte[] lnk = Shortcut.Create(new ShortcutOptions
{
    Target = @"C:\Windows\System32\cmd.exe",
    PropertyStoreData = propStore
});

// Or use PropertyStoreBuilder for more control
var builder = new PropertyStoreBuilder
{
    WinXHash = WinXHasher.ComputeHash(@"C:\test.exe"),
    AppUserModelId = "MyApp"
};

DarwinDescriptor

Decodes MSI (Windows Installer) Darwin descriptor strings from DarwinDataBlock:

var darwin = DarwinDescriptor.TryDecode(options.DarwinData);
if (darwin != null)
{
    Console.WriteLine($"Product: {darwin.ProductCode}");
    Console.WriteLine($"Feature: {darwin.FeatureId}");
    Console.WriteLine($"Component: {darwin.ComponentCode}");
}

// Encode a GUID to MSI compressed format
string packed = DarwinDescriptor.EncodeCompressedGuid(someGuid);
Property Type Description
ProductCode Guid MSI product code
FeatureId string Feature identifier
ComponentCode Guid MSI component code

TrackerData Forensic Methods

Version 1 UUIDs in TrackerData contain embedded MAC addresses and timestamps. These methods extract that forensic information:

var tracker = options.Tracker;
if (tracker != null && tracker.IsObjectIdVersion1())
{
    byte[] mac = tracker.ExtractMacAddress();       // [0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF]
    string macStr = tracker.ExtractMacAddressString(); // "AA:BB:CC:DD:EE:FF"
    DateTime? ts = tracker.ExtractTimestamp();       // UTC timestamp from UUID

    // Birth IDs (original machine that created the file)
    string birthMac = tracker.ExtractBirthMacAddressString();
    DateTime? birthTs = tracker.ExtractBirthTimestamp();
}
Method Returns Description
IsObjectIdVersion1() bool True if ObjectId is a Version 1 UUID
IsBirthObjectIdVersion1() bool True if BirthObjectId is a Version 1 UUID
ExtractMacAddress() byte[]? 6-byte MAC from ObjectId (null if not V1)
ExtractBirthMacAddress() byte[]? 6-byte MAC from BirthObjectId
ExtractMacAddressString() string? MAC as "AA:BB:CC:DD:EE:FF"
ExtractBirthMacAddressString() string? Birth MAC as "AA:BB:CC:DD:EE:FF"
ExtractTimestamp() DateTime? UTC timestamp from ObjectId
ExtractBirthTimestamp() DateTime? UTC timestamp from BirthObjectId

RawExtraDataBlock

Unrecognized extra data blocks are preserved during round-trips:

Property Type Description
Signature uint Block signature identifier
Data byte[] Raw block payload

DriveTypes

Drive type constants for LocalPathInfo.DriveType:

Unknown (0), NoRootDir (1), Removable (2), Fixed (3), Remote (4), CDRom (5), RamDisk (6)

CsidlFolderIds

CSIDL folder ID constants for SpecialFolderData.FolderId (45 values):

Desktop, Internet, Programs, Controls, Printers, Personal, Favorites, Startup, Recent, SendTo, RecycleBin, StartMenu, MyMusic, MyVideo, DesktopDirectory, Drives, Network, NetHood, Fonts, Templates, CommonStartMenu, CommonPrograms, CommonStartup, CommonDesktopDirectory, AppData, PrintHood, LocalAppData, CommonAppData, Windows, System, ProgramFiles, MyPictures, Profile, SystemX86, ProgramFilesX86, CommonFiles, CommonFilesX86, CommonTemplates, CommonDocuments, AdminTools, CommonAdminTools, Cookies, History, InternetCache

VirtualKeys

Virtual key code constants for ShortcutOptions.HotkeyKey:

Letters: A–Z (0x41–0x5A) | Digits: D0–D9 (0x30–0x39) | Function keys: F1–F24 (0x70–0x87) | Toggle: NumLock, ScrollLock

ConsoleFillAttributes

Console color flag constants for ConsoleData.FillAttributes and PopupFillAttributes:

ForegroundBlue, ForegroundGreen, ForegroundRed, ForegroundIntensity, BackgroundBlue, BackgroundGreen, BackgroundRed, BackgroundIntensity

ConsoleFontFamilies

Font family and pitch constants for ConsoleData.FontFamily:

Families: DontCare, Roman, Swiss, Modern, Script, Decorative | Pitch: FixedPitch, Vector, TrueType, Device

ShimLayerNames

Common compatibility shim layer names for ShortcutOptions.ShimLayerName:

OS compatibility: Win95, Win98, WinNT4SP5, Win2000, Win2000SP3, WinXPSP1, WinXPSP2, WinXPSP3, WinVistaSP1, WinVistaSP2, Win7RTM, Win8RTM, Win81RTM, Win10RTM

Display: Color256, Resolution640x480, ReducedColorMode, DisableDWM, GdiScalingOff, GdiDpiScaling, HighDpiAware, DpiUnaware, PerProcessSystemDpiForceOn, PerMonitorV2

Behavior: DisableNXShowUI, DisableThemes, RunAsAdmin, ForceDirectDrawEmulation, ElevateCreateProcess, DisableUserCallbackException

Argument Padding

When PadArguments is true, the arguments string is placed at the end of a 31 KB buffer filled with whitespace and control characters (CR, Tab, LF, file/group/record/unit separators, Space). This pushes the actual arguments beyond what the Windows shortcut properties dialog displays, effectively hiding them from casual inspection.

File vs. Directory Detection

The library classifies the target as a file or directory based on its extension length:

  • File (FILE_ATTRIBUTE_ARCHIVE): Extension is 1–3 characters (e.g. .exe, .txt, .a)
  • Directory (FILE_ATTRIBUTE_DIRECTORY): No extension, or extension longer than 3 characters

Path Handling

  • Local paths are split on the first \. The root portion gets a trailing \ (e.g. C:\), the remainder becomes the leaf item.
  • Network paths (starting with \\) are split on the last \. The server+share portion is the root, the final component is the leaf.
  • Printer links treat the entire target as the root (no leaf item).

Building

dotnet build

Testing

dotnet test

License

MIT

Product Compatible and additional computed target framework versions.
.NET net10.0 is compatible.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.
  • net10.0

    • No dependencies.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
1.0.6 148 8/2/2026
1.0.5-alpha.3 358 2/23/2026
1.0.5-alpha.2 79 2/23/2026