How do I create a shortcut (.lnk) with a relative target?
Asked Answered
D

5

15

I have an executable on my disk-on-key in dir\program\prog.exe I'd like to have a shortcut to the executable on the DoK's root directory, that is, prog.lnk would refer to dir\program\prog.exe.

However, it seems that prog.lnk can't have a relative target. This is a problem when the DoK will have different drive letters assigned to it, depending on which PC it's connected to.

Any suggestions, aside from the obvious one of putting prog.exe in the root dir?

(ultimately, I'd like to do this at install time using nsis)

Thanks,

Rony

Deem answered 5/8, 2010 at 19:42 Comment(0)
S
9

While it is possible for shortcuts to contain a relative path to the target (.lnk files have a flag called SLDF_HAS_RELPATH) NSIS does not support creating anything other than "normal" shortcuts so you need to write the binary data directly (The .lnk format is pretty stable and has been documented by MS)

!macro FileWriteHexBytes h b
push ${h}
push ${b}
call FileWriteHexBytes
!macroend
Function FileWriteHexBytes
exch $9
exch
exch $0
push $1
push $2
loop:
    StrCpy $2 $9 2
    StrLen $1 $2
    IntCmp $1 2 0 end
    FileWriteByte $0 "0x$2"
    StrCpy $9 $9 "" 2
    goto loop
end:
pop $2
pop $1
pop $0
pop $9
FunctionEnd


Function CreateRelativeLnk
exch $9
exch
exch $0
push $1
FileOpen $0 "$0" w
StrCmp $0 "" clean
!insertmacro FileWriteHexBytes $0 "4C0000000114020000000000C000000000000046"
!insertmacro FileWriteHexBytes $0 48010400 ;flags
!insertmacro FileWriteHexBytes $0 00000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000

StrLen $1 $9 ;must be < 255!
FileWriteByte $0 $1
FileWriteByte $0 0
FileWrite $0 "$9" ;relative target path

!if 0
;The icon is problematic, does not seem like it works with relative paths (but you can use system icons...)
StrCpy $9 "explorer.exe"
StrLen $1 $9
FileWriteByte $0 $1
FileWriteByte $0 0
FileWrite $0 "$9"
!else
!insertmacro FileWriteHexBytes $0 05003e2e657865 ;fake default .exe icon
!endif

clean:
FileClose $0
pop $1
pop $0
pop $9
FunctionEnd

Call it like this:

push "$temp\testlink.lnk"
push "testdir\testapp.exe" ;full path to this is $temp\testdir\testapp.exe
call CreateRelativeLnk

While the generated .lnk seems to work, I'm not sure if I would use this in production code

A much better solution is to create a little NSIS app like Oleg suggests (NSIS applications can contain embedded data at the end of the .exe that it can read from itself at runtime etc..)

Seaman answered 6/8, 2010 at 12:54 Comment(1)
.lnk file format documentation: msdn.microsoft.com/en-us/library/dd871305.aspxBarbiturate
S
12

If we assume cmd.exe would be on the same absolute path for all windows installations (probable, but not fool-proof) you can make out the .lnk file to start cmd like this

cmd.exe /c start /d. your command here

/d sets the directory to the directory of the .lnk file

There might be other useful options for the start command (e.g. /b)

Shamanism answered 24/8, 2010 at 12:53 Comment(0)
S
9

While it is possible for shortcuts to contain a relative path to the target (.lnk files have a flag called SLDF_HAS_RELPATH) NSIS does not support creating anything other than "normal" shortcuts so you need to write the binary data directly (The .lnk format is pretty stable and has been documented by MS)

!macro FileWriteHexBytes h b
push ${h}
push ${b}
call FileWriteHexBytes
!macroend
Function FileWriteHexBytes
exch $9
exch
exch $0
push $1
push $2
loop:
    StrCpy $2 $9 2
    StrLen $1 $2
    IntCmp $1 2 0 end
    FileWriteByte $0 "0x$2"
    StrCpy $9 $9 "" 2
    goto loop
end:
pop $2
pop $1
pop $0
pop $9
FunctionEnd


Function CreateRelativeLnk
exch $9
exch
exch $0
push $1
FileOpen $0 "$0" w
StrCmp $0 "" clean
!insertmacro FileWriteHexBytes $0 "4C0000000114020000000000C000000000000046"
!insertmacro FileWriteHexBytes $0 48010400 ;flags
!insertmacro FileWriteHexBytes $0 00000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000

StrLen $1 $9 ;must be < 255!
FileWriteByte $0 $1
FileWriteByte $0 0
FileWrite $0 "$9" ;relative target path

!if 0
;The icon is problematic, does not seem like it works with relative paths (but you can use system icons...)
StrCpy $9 "explorer.exe"
StrLen $1 $9
FileWriteByte $0 $1
FileWriteByte $0 0
FileWrite $0 "$9"
!else
!insertmacro FileWriteHexBytes $0 05003e2e657865 ;fake default .exe icon
!endif

clean:
FileClose $0
pop $1
pop $0
pop $9
FunctionEnd

Call it like this:

push "$temp\testlink.lnk"
push "testdir\testapp.exe" ;full path to this is $temp\testdir\testapp.exe
call CreateRelativeLnk

While the generated .lnk seems to work, I'm not sure if I would use this in production code

A much better solution is to create a little NSIS app like Oleg suggests (NSIS applications can contain embedded data at the end of the .exe that it can read from itself at runtime etc..)

Seaman answered 6/8, 2010 at 12:54 Comment(1)
.lnk file format documentation: msdn.microsoft.com/en-us/library/dd871305.aspxBarbiturate
D
4

Install "Relative"

I'm using a bit of a hack. The approach is shown in this screenshot:

It starts explorer.exe and then passes a relative path like so:

%windir%\explorer.exe path\to\your\files\youFileName.example

I'm using a small tool called "Relative" for this. After you install it, you can right-click a file and then select Create relative shortcut.... It will then pop up a Save as... box. This is not quite as comfortable as simply dragging and dropping but it helps. (It also uses its own special icon for the links it creates. So you no longer have the original icon in the link. See above. You may or may not like this.)

Destefano answered 4/9, 2013 at 11:58 Comment(0)
F
1

You are right. You can not use shortcuts (.lnk) on a removable media, because like you wrote yourself the removable media can have different drive letters in different situations.

If you need to have something in a root directory of the drive one use a CMD, VBS or JS instead of a shortcut. In some situation one use also a standard EXE which use a config file like INI file to start another program from the other subdirectory. It is a typical practice for CD/DVD to implement starting of a setup program after inserting of the disk. Probably this way will be OK in for your requirements also?

Ferretti answered 5/8, 2010 at 22:33 Comment(0)
M
0

I believe that the NT 4 Resource Kit command SHORTCUT.exe would create relative linked shortcuts. I wish that Microsoft would create a new supported utility or Powershell Cmdlet to facilitate the creation of relative .lnk files or make NTFS links work more like Linux symbolic links. Until then, I use .cmd/.bat and .ps1 files for this purpose.

Program.cmd

@"%~dp0Relative\Path\Program.exe" %*
  • @ = Suppresses the command echo.
  • %~dp0 = expands to the script's directory.
  • Relative\Path = could include .. to backup a directory.
  • %* = passes any parameters received by the script on to Program.exe.

Program.ps1

Unfortunately, though .cmd/.bat files will run from any context (the run dialog, a CMD prompt, in Powershell, double clicking in File Explorer, etc), they cannot call things stored on a UNC path. For exposing things in a UNC path, in Powershell (I do this for tools like Git and Mercurial), I will create a Powershell version of the above script.

&"$PSScriptRoot\Relative\Path\Program.exe" @args
  • & = puts Powershell into command mode, so that the string in quotes gets ran.
  • "" = contains a string, expanding any variables.
  • $PSScriptRoot = expands to the script's directory.
  • Relative\Path = could include .. to backup a directory.
  • @args = passes any parameters received by the script on to Program.exe.
Maleficence answered 29/5, 2016 at 0:46 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.