Skip to content
David Kittell
David Kittell

Application & System: Development / Integration / Orchestration

  • Services
    • Application Development
    • Online Application Integration
  • Code
  • Online Tools
  • Tech Support
David Kittell

Application & System: Development / Integration / Orchestration

Get Windows Shortcut (.lnk) Details

Posted on March 17, 2016 By David Kittell

The below will help you get the full path of a Windows shortcut (.lnk) in your program. The below examples specifically create a command-line executable file but you could use the function in your program just as well.

View the full code in GitHub at https://github.com/dkittell/lnk-parser

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.IO;

namespace lnk_parser_cSharp
{

	public class Functions
	{
		public static string GetShortcutTarget(string file)
		{
			try
			{
				if (System.IO.Path.GetExtension(file).ToLower() != ".lnk")
				{
					throw new Exception("Supplied file must be a .LNK file");
				}

				FileStream fileStream = File.Open(file, FileMode.Open, FileAccess.Read);
				using (System.IO.BinaryReader fileReader = new BinaryReader(fileStream))
				{
					fileStream.Seek(0x14, SeekOrigin.Begin);     // Seek to flags
					uint flags = fileReader.ReadUInt32();        // Read flags
					if ((flags & 1) == 1)
					{                      // Bit 1 set means we have to
										   // skip the shell item ID list
						fileStream.Seek(0x4c, SeekOrigin.Begin); // Seek to the end of the header
						uint offset = fileReader.ReadUInt16();   // Read the length of the Shell item ID list
						fileStream.Seek(offset, SeekOrigin.Current); // Seek past it (to the file locator info)
					}

					long fileInfoStartsAt = fileStream.Position; // Store the offset where the file info
																 // structure begins
					uint totalStructLength = fileReader.ReadUInt32(); // read the length of the whole struct
					fileStream.Seek(0xc, SeekOrigin.Current); // seek to offset to base pathname
					uint fileOffset = fileReader.ReadUInt32(); // read offset to base pathname
															   // the offset is from the beginning of the file info struct (fileInfoStartsAt)
					fileStream.Seek((fileInfoStartsAt + fileOffset), SeekOrigin.Begin); // Seek to beginning of
																						// base pathname (target)
					long pathLength = (totalStructLength + fileInfoStartsAt) - fileStream.Position - 2; // read
																										// the base pathname. I don't need the 2 terminating nulls.
					char[] linkTarget = fileReader.ReadChars((int)pathLength); // should be unicode safe
					var link = new string(linkTarget);

					int begin = link.IndexOf("\0\0");
					if (begin > -1)
					{
						int end = link.IndexOf("\\\\", begin + 2) + 2;
						end = link.IndexOf('\0', end) + 1;

						string firstPart = link.Substring(0, begin);
						string secondPart = link.Substring(end);

						return firstPart + secondPart;
					}
					else {
						return link;
					}
				}
			}
			catch
			{
				return "";
			}
		}
	}

	class Program
	{
		static void Main(string[] args)
		{
			if (args.Length == 0)
			{
				System.Console.WriteLine("Please try again with a file path");
			}
			else
			{
				System.Console.WriteLine("LNK File: " + args[0]);
				System.Console.WriteLine("LNK Path: " + Functions.GetShortcutTarget(args[0]));
			}
		}
	}
}
C:\temp>lnk_parser_cSharp.exe "C:\Virtual Machine Images.lnk"
LNK File: C:\Virtual Machine Images.lnk
LNK Path: C:\Virtual Machines

VB.Net example converted from C# example above.

Imports System.Collections.Generic
Imports System.Linq
Imports System.Text
Imports System.Threading.Tasks
Imports System.IO

Public Class Functions
	Public Shared Function GetShortcutTarget(file__1 As String) As String
		Try
			If System.IO.Path.GetExtension(file__1).ToLower() <> ".lnk" Then
				Throw New Exception("Supplied file must be a .LNK file")
			End If

			Dim fileStream As FileStream = File.Open(file__1, FileMode.Open, FileAccess.Read)
			Using fileReader As System.IO.BinaryReader = New BinaryReader(fileStream)
				fileStream.Seek(&H14, SeekOrigin.Begin)
				' Seek to flags
				Dim flags As UInteger = fileReader.ReadUInt32()
				' Read flags
				If (flags And 1) = 1 Then
					' Bit 1 set means we have to
					' skip the shell item ID list
					fileStream.Seek(&H4C, SeekOrigin.Begin)
					' Seek to the end of the header
					Dim offset As UInteger = fileReader.ReadUInt16()
					' Read the length of the Shell item ID list
					' Seek past it (to the file locator info)
					fileStream.Seek(offset, SeekOrigin.Current)
				End If

				Dim fileInfoStartsAt As Long = fileStream.Position
				' Store the offset where the file info
				' structure begins
				Dim totalStructLength As UInteger = fileReader.ReadUInt32()
				' read the length of the whole struct
				fileStream.Seek(&HC, SeekOrigin.Current)
				' seek to offset to base pathname
				Dim fileOffset As UInteger = fileReader.ReadUInt32()
				' read offset to base pathname
				' the offset is from the beginning of the file info struct (fileInfoStartsAt)
				fileStream.Seek((fileInfoStartsAt + fileOffset), SeekOrigin.Begin)
				' Seek to beginning of
				' base pathname (target)
				Dim pathLength As Long = (totalStructLength + fileInfoStartsAt) - fileStream.Position - 2
				' read
				' the base pathname. I don't need the 2 terminating nulls.
				Dim linkTarget As Char() = fileReader.ReadChars(CInt(pathLength))
				' should be unicode safe
				Dim link = New String(linkTarget)

				Dim begin As Integer = link.IndexOf(vbNullChar & vbNullChar)
				If begin > -1 Then
					Dim [end] As Integer = link.IndexOf("\\", begin + 2) + 2
					[end] = link.IndexOf(ControlChars.NullChar, [end]) + 1

					Dim firstPart As String = link.Substring(0, begin)
					Dim secondPart As String = link.Substring([end])

					Return firstPart & secondPart
				Else
					Return link
				End If
			End Using
		Catch
			Return ""
		End Try
	End Function
End Class


Module Module1
	Sub Main(args As String())
		If args.Length = 0 Then
			System.Console.WriteLine("Please try again with a file path")
		Else
			System.Console.WriteLine("LNK File: " + args(0))
			System.Console.WriteLine(Convert.ToString("LNK Path: ") & Functions.GetShortcutTarget(args(0)))
		End If
	End Sub
End Module
C:\temp>lnk_parser_vbNet.exe "C:\Virtual Machine Images.lnk"
LNK File: C:\Virtual Machine Images.lnk
LNK Path: C:\Virtual Machines

References: C# – https://blez.wordpress.com/2013/02/18/get-file-shortcuts-target-with-c/

All information on this site is shared with the intention to help. Before any source code or program is ran on a production (non-development) system it is suggested you test it and fully understand what it is doing not just what it appears it is doing. I accept no responsibility for any damage you may do with this code.

Related

C++ Code CSharp VB.NET c#CSharplnkLNK ParserShortcut DetailsVB.NET

Post navigation

Previous post
Next post

Related Posts

PowerShell Get All Environment Variables

Posted on November 6, 2015

If you need to know some of the system variables that are already set you can run this script to get the values Get-Childitem env: gci env: | sort name Once you know what variable you want you can do something like this Write-Host "Computer:`t" $env:COMPUTERNAME Write-Host "Domain:`t" $env:USERDOMAIN All…

Read More

PHP WordPress – Use Radius Server Login

Posted on October 27, 2015November 19, 2015

Securing WordPress with Radius Server login. This project was created in mind to help secure WordPress but allow users an easier way to securely login without having to have a really long password to remember. While I do not go into creating the Radius server you can go to http://www.radiusdesk.com/getting_started/install_ubuntu_nginx…

Read More

Ubuntu Supported Distros

Posted on November 5, 2015

cp /etc/apt/sources.list /etc/apt/sources.list-orig echo ‘deb http://us.archive.ubuntu.com/ubuntu/ trusty main restricted’ > /etc/apt/sources.list echo ‘deb-src http://us.archive.ubuntu.com/ubuntu/ trusty main restricted’ >> /etc/apt/sources.list echo ‘deb http://us.archive.ubuntu.com/ubuntu/ trusty-updates main restricted’ >> /etc/apt/sources.list echo ‘deb-src http://us.archive.ubuntu.com/ubuntu/ trusty-updates main restricted’ >> /etc/apt/sources.list echo ‘deb http://security.ubuntu.com/ubuntu trusty-security main restricted’ >> /etc/apt/sources.list echo ‘deb-src http://security.ubuntu.com/ubuntu trusty-security main restricted’ >>…

Read More

Code

Top Posts & Pages

  • PowerShell - Rename Pictures to Image Taken
  • Front Page
  • C# - Start/Stop/Restart Services
  • MacPorts / HomeBrew - Rip CD tracks from terminal
  • PowerShell - Show File Extensions

Recent Posts

  • Javascript – Digital Clock with Style
  • BASH – Web Ping Log
  • BASH – Picture / Video File Name Manipulation
  • Mac OSX Terminal – Create SSH Key
  • Bash – Rename Picture

Top Posts

  • PowerShell - Rename Pictures to Image Taken
  • C# - Start/Stop/Restart Services
  • MacPorts / HomeBrew - Rip CD tracks from terminal
  • PowerShell - Show File Extensions
  • Open On Screen Keyboard (OSK)
  • SQLite - Auto-Increment / Auto Generate GUID
©2025 David Kittell | WordPress Theme by SuperbThemes