Runtime: Calling .NET Core-compiled custom executables via symlinks on Windows should be supported

Created on 2 Oct 2017  路  11Comments  路  Source: dotnet/runtime

_From @mklement0 on September 30, 2017 21:27_

Trying to call a .NET Core 2.0 *.exe file via a symbolic link doesn't work as-is, resulting in the following error message:

The managed DLL bound to this executable: '<original-name>.dll', did not match own 
name '<symlink-name>.dll'.
A fatal error was encountered. This executable was not bound to load a managed DLL.

The idea is to use a symlink to provide an efficient, shell-agnostic way to invoke an executable by a shorter name (without having to resort to shell-specific aliases or cumbersome wrapper batch files) - just like on Unix platforms, where this scenario is already supported.

This came up while discussing a shell-agnostic way to invoke PowerShell Core with a shorter name.


Steps to reproduce

Run the following from an _elevated PowerShell_ prompt on (64-bit) _Windows_:

Create a hello-world project with executable foo.exe in %TEMP%\foo:

PS> Set-Location (mkdir $env:Temp/foo)
PS> dotnet new console
PS> dotnet publish -c Release -r win-x64

Invoke the resulting executable, which works fine:

PS> .\bin\Release\netcoreapp2.0\win-x64\foo.exe
Hello World!

Now create a symlink named fooL.exe that points to the executable and try to invoke it:

PS> cmd /c mklink fooL.exe .\bin\Release\netcoreapp2.0\win-x64\foo.exe
PS> .\fooL.exe  # !! Fails as of .NET Core 2.0

This results in the error message cited at the top.

Note that creating the symlink without extension (fooL instead of fooL.exe) _half_ works:

  • From cmd.exe, you can't call this symlink as an executable at all.
  • PowerShell _does_ allow invocation and even succeeds in calling the target executable with the arguments specified, but invariably runs it in a _new_ console window.

Use Set-Location ~; Remove-Item -Recurse $env:TEMP/foo to remove the temp. directory afterwards.

_Copied from original issue: dotnet/cli#7739_

area-Host enhancement

Most helpful comment

Updated the title. This is probably the right place for the issue. This repo is where the "host" code lives which is what hosts the runtime and launches the application. The application exe is the host and so is dotnet.exe. Once the application is compiled, the CLI is out of the picture.

All 11 comments

@livarcocc Thanks for moving the issue, but it isn't really related to .NET Core's own installation / executables - it is about calling _custom_ executables _compiled with_ .NET Core via symlinks (I didn't do a a great job of making that clear).

@livarcocc:

Based on my later clarification, could you please either explain why this repo is, after all, the right place for this issue or move it back to the cli repo or whichever one is more appropriate?

I'd be happy to update the original post with my clarification, but since the post is now owned by you, I cannot do so.

A quick improvement would be to change the title to:

Calling .NET Core-compiled custom executables via symlinks on Windows should be supported

Updated the title. This is probably the right place for the issue. This repo is where the "host" code lives which is what hosts the runtime and launches the application. The application exe is the host and so is dotnet.exe. Once the application is compiled, the CLI is out of the picture.

Thank you for updating the title and clarifying, @Petermarcu, I appreciate it.

We need to document and discuss the original reason(s) for not allowing the exe to be renamed.

The corresponding .dll name is embedded into the exe which currently must match the .exe name

If we remove that check, the code would probably work. I believe it is just a sanity check to make sure the tooling did the right thing. I don't think there is a security or TwC hijacking-type issue either as anyone can rename a given .exe.

However, is making the change worth the cost and potential risk if the main goal of this is to support easier tooling infrastructure which can be worked around?

if the main goal of this is to support easier tooling infrastructure

I don't see this limited to tooling; to reiterate what I said in the original post:

It should be possible use a symlink as an efficient, shell-agnostic way to invoke an executable by a shorter - or different - name.

On Unix platforms, this is already common practice and some utilities even use the specific name of the symlink / executable they were invoked with to adjust their behavior.

Even in the Windows world this would be handy, now that symlinks are becoming more prevalent there.

I believe this will be addressed with https://github.com/dotnet/core-setup/issues/3720 because there is an implementation detail that removes the exe:dll name check

This is not going to be addressed by dotnet/core-setup#3720 because although the exe:dll name check is removed, there are other conventions including that a self-contained app have all files next to it, contain a runtimeconfig.json, etc.

When using a soft or hard link, it appears Windows treats the link as the actual executable, so when we ask for the currently running executable, Windows returns the symbolic link file, not the target file. Thus the conventions (must have files next to it, etc) fail.

I'm going to close this since I don't see how a symbolic link would ever work, unless there is a way for a symbol link to truly behave like the destination executable. Please re-open if necessary.

dotnet/core-setup#3720 will provide a way to have a renamed exe (apphost) plus point to another location that contains the application files. However it does require a local startupconfig.json file to tell it where the app files are located. So the exe and a simple json file are similar in functionality to the symbolic link original requirements and this new functionality will be leveraged by global tools.

Thanks for the heads-up, @steveharter.

If I understand you correctly, you're saying that argv[0] reports the _symlink's_ path.

If so, wouldn't a call to GetFinalPathNameByHandle resolve this issue?

Perhaps as a fallback mechanism so that the cost doesn't have to be incurred every time?

Was this page helpful?
0 / 5 - 0 ratings