Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Opening a shortcut on Windows #14783

Open
straight-shoota opened this issue Jul 4, 2024 · 0 comments
Open

Opening a shortcut on Windows #14783

straight-shoota opened this issue Jul 4, 2024 · 0 comments

Comments

@straight-shoota
Copy link
Member

straight-shoota commented Jul 4, 2024

This has come up in Gitter chat.

Windows has shortcut files for launching programs or opening files. They have the extension .lnk and contain a reference to the target as well as some other properties (such as icon path, working directory etc.). A similar concept in Unix land are Desktop Entries (.desktop).

It's very common to use shortcuts on Windows (e.g. the Desktop is usually full of them) and users expect that they can open a shortcut to execute whatever it points to.

Shortcut files are not directly executable with CreateProcessW, so you cannot open a shortcut with Process.run (not even with shell: true, which doesn't really do anything on Windows; ref #9030).

Process.run("shortcut.lnk") # raises File::BadExecutableError
Process.run("shortcut.lnk", shell: true) # raises File::BadExecutableError

They can be opened in cmd or powershell

Process.run("cmd /c shortcut.lnk")

So going though cmd /c provides a solution, but it feels a bit hacky.

The Win32 API has a native way to open files, which supports shortcuts: ShellExecuteA & co. These functions also provide other operations.

The following example shows how the ShellExecuteExW function can be used to open a file (which may be a shortcut):

def open_file(path)
	shell_exec_info = LibC::SHELLEXECUTEINFOW.new(
	  cbSize: sizeof(LibC::SHELLEXECUTEINFOW),
	  fMask: 0,
	  hwnd: nil,
	  lpVerb: nil,
	  lpFile: Crystal::System.to_wstr(path),
	  lpParameters: nil,
	  lpDirectory: nil,
	  nShow: LibC::SW_NORMAL,
	  hInstApp: nil
	)
	LibC.ShellExecuteExW(pointerof(shell_exec_info))
end

lib LibC
  struct SHELLEXECUTEINFOW
    cbSize: DWORD
    fMask: ULong
    hwnd: Void*
    lpVerb: LPWSTR
    lpFile: LPWSTR
    lpParameters: LPWSTR
    lpDirectory: LPWSTR
    nShow: Int
    hInstApp: Void*
    lpIDList: Void*
    lpClass: LPWSTR
    hkeyClass: HKEY
    dwHotKey: DWORD
    hIconOrMonitor : HANDLE
    hProcess: HANDLE
  end

  fun ShellExecuteExW(
    pExecInfo : SHELLEXECUTEINFOW*
  ) : BOOL

  SW_NORMAL = 1
end

I'm not sure what we could make out of this. Whether there can be a good and simple solution, or a one better than prefixing the file path with cmd /c.
Maybe this issue can draw some ideas on the matter.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
Status: No status
Development

No branches or pull requests

1 participant