Handle DLLs for Symlinked executables
caiohamamura opened this issue · 17 comments
Description of the new feature / enhancement
With current zip
support, the only types of installer allowed are:
- Portable standalone
exe
files - Compressed installers
But what about supporting regular compressed binary packages, such as those having exe
along with dynamic dll
libraries?
Proposed technical implementation details
To use the dynamic dll
libraries, they must either be:
- in
PATH
environment variable - linked by specifying
Files -> RelativeFilePath
within the installer manifest
It is not possible to link dll
files to make it work using the 2
approach, because the links are forced to have .exe
extension.
This could be fixed either by:
- Adding the
dll
s folder toPATH
environment variable - Allowing to link
dll
files specifying something likeNestedInstallerType: library
. Then it would also be useful to provide pattern matchingRelativeFilePath
, such as*.dll
- Instead of making a symlink inside
WinGet\Links
, make an executablebat
with the command@call "path\to\exe" %*
, then the working directory will be the same where theexe
actually is (this would the easiest path in my opinion)
iirc, the entire Zip folder is extracted and moved to the default install location. Take the Paint.Net Pull Request for example. If you comment out everything except for the zip-portable installer entry, it still installs and runs correctly, meaning that all the DLLs needed to run it are referenced. The DLLs aren’t needed in the installer files because they aren’t installers
iirc, the entire Zip folder is extracted and moved to the default install location. Take the Paint.Net Pull Request for example. If you comment out everything except for the zip-portable installer entry, it still installs and runs correctly, meaning that all the DLLs needed to run it are referenced. The DLLs aren’t needed in the installer files because they aren’t installers
Maybe I wasnt' clear enough. By binary files I mean plain binary, not an exe
installer, that's the case for Paint.Net. What I mean is plain binary packages, which is just a bunch of exe
files along with dll
files, many development tools are deployed this way such as nginx
, php
, etc. And they won't run unless:
- They are launched from the same directory where they were extracted, which a symlink will break
- They are added to
PATH
- They are launched through a
.bat
file as I proposed: this strategy is used throughout many different packages.
If the community agrees with the bat
implementation I would gladly like to contribute. I think it is much better than polluting the PATH
or forcing the manifest
to declare a whole bunch of DLL files which could also drive other side effects such as DLL
versions conflicts.
iirc, the entire Zip folder is extracted and moved to the default install location. Take the Paint.Net Pull Request for example. If you comment out everything except for the zip-portable installer entry, it still installs and runs correctly, meaning that all the DLLs needed to run it are referenced. The DLLs aren’t needed in the installer files because they aren’t installers
Maybe I wasnt' clear enough. By binary files I mean plain binary, not an
exe
installer, that's the case for https://github.com/microsoft/winget-pkgs/pull/88065/files. What I mean is plain binary packages, which is just a bunch ofexe
files along withdll
files, many development tools are deployed this way such asnginx
,php
, etc. And they won't run unless:
- They are launched from the same directory where they were extracted, which a symlink will break
- They are added to
PATH
- They are launched through a
.bat
file as I proposed: this strategy is used throughout many different packages.If the community agrees with the
bat
implementation I would gladly like to contribute. I think it is much better than polluting thePATH
or forcing themanifest
to declare a whole bunch of DLL files which could also drive other side effects such asDLL
versions conflicts.
I think you should look at the Paint.Net manifest and zip file more closely. Specifically, this entry -
- Architecture: x64
NestedInstallerType: portable
NestedInstallerFiles:
- RelativeFilePath: paintdotnet.exe
PortableCommandAlias: paint.net
InstallerUrl: https://github.com/paintdotnet/release/releases/download/v4.3.12/paint.net.4.3.12.portable.x64.zip
InstallerSha256: AF58C12B92BC759F8E38C8623E356C8B8A2534809C44058AB76055DFA15DE4F8
That specific entry is exactly what you described - a loose .exe file with a bunch of DLLs and other assets included.
If you comment out all the other entries in the manifest, and then use winget to install this specific entry, it will still run perfectly fine
@Trenly, it does work if you install it and launch it through run dialog (Win + R
) paint.net.exe
, but that's because it looks like it follows the symlink working directory somehow, but if you try to launch it through cmd
or powershell
it won't work at all!
Although this is fine for Desktop Apps, it is not the expected behavior for development tools and won't work for CLI apps.
But even for Desktop Apps you need to specify paint.net.exe
which is not what I would expect to type in the run dialog, I usually just type paint.net
, code
, etc. I do not type the .exe
, I think this is another issue with the SymLinking strategy.
@Trenly, it does work if you install it and launch it through run dialog (
Win + R
)paint.net.exe
, but that's because somehow it looks like it follows the symlink working directory somehow, but if you try to launch it throughcmd
orpowershell
it won't work at all!Although this is fine for Desktop Apps, it is not the expected behavior for development tools and won't work for CLI apps.
But even for Desktop Apps you need to specify
paint.net.exe
which is not what I would expect to type in the run dialog, I usually just typepaint.net
,code
, etc. I do not type the.exe
, I think this is another issue with the SymLinking strategy.
It works fine for me from powershell when I use start paint.net
That's my point, start
shouldn't be needed at all, you won't be running development tools using start
by default, besides you probably won't be launching these development tools yourself but they will be launched by other development tools which you don't have control over.
Ahhh, I see. Thank you for your patience and the clarification!
The issue title should be better renamed to something like "Handle DLLs for symlinked executables."
This could be fixed either by:
- Adding the
dll
s folder toPATH
environment variable- Allowing to link
dll
files specifying something likeNestedInstallerType: library
. Then it would also be useful to provide pattern matchingRelativeFilePath
, such as*.dll
- Instead of making a symlink inside
WinGet\Links
, make an executablebat
with the command@call "path\to\exe" %*
, then the working directory will be the same where theexe
actually is (this would the most easier path in my opinion)
Another solution would be to use shims, like Scoop or Chocolatey:
iirc, the entire Zip folder is extracted and moved to the default install location. Take the Paint.Net Pull Request for example. If you comment out everything except for the zip-portable installer entry, it still installs and runs correctly, meaning that all the DLLs needed to run it are referenced. The DLLs aren’t needed in the installer files because they aren’t installers
Maybe I wasnt' clear enough. By binary files I mean plain binary, not an
exe
installer, that's the case for Paint.Net. What I mean is plain binary packages, which is just a bunch ofexe
files along withdll
files, many development tools are deployed this way such asnginx
,php
, etc. And they won't run unless:
- They are launched from the same directory where they were extracted, which a symlink will break
- They are added to
PATH
- They are launched through a
.bat
file as I proposed: this strategy is used throughout many different packages.If the community agrees with the
bat
implementation I would gladly like to contribute. I think it is much better than polluting thePATH
or forcing themanifest
to declare a whole bunch of DLL files which could also drive other side effects such asDLL
versions conflicts.
Use bat might cause problem while execute it in another bat script.
iirc, the entire Zip folder is extracted and moved to the default install location. Take the Paint.Net Pull Request for example. If you comment out everything except for the zip-portable installer entry, it still installs and runs correctly, meaning that all the DLLs needed to run it are referenced. The DLLs aren’t needed in the installer files because they aren’t installers
Maybe I wasnt' clear enough. By binary files I mean plain binary, not an
exe
installer, that's the case for Paint.Net. What I mean is plain binary packages, which is just a bunch ofexe
files along withdll
files, many development tools are deployed this way such asnginx
,php
, etc. And they won't run unless:
- They are launched from the same directory where they were extracted, which a symlink will break
- They are added to
PATH
- They are launched through a
.bat
file as I proposed: this strategy is used throughout many different packages.If the community agrees with the
bat
implementation I would gladly like to contribute. I think it is much better than polluting thePATH
or forcing themanifest
to declare a whole bunch of DLL files which could also drive other side effects such asDLL
versions conflicts.Use bat might cause problem while execute it in another bat script.
I really don't know, I'm not that versed in BAT scripts. But AFAIK @call
does not cause any side effects inside other bat files, it won't change the running environment as a regular call and as the content is just a single call to an exe and won't change any variable, it will be just like calling the exe itself which could also break/stop the bat by itself.
I suggested this solution because it is used by many development tools such as Anaconda, python tools, msys, osgeo4w, this is not a clever hack that came out of my mind.
iirc, the entire Zip folder is extracted and moved to the default install location. Take the Paint.Net Pull Request for example. If you comment out everything except for the zip-portable installer entry, it still installs and runs correctly, meaning that all the DLLs needed to run it are referenced. The DLLs aren’t needed in the installer files because they aren’t installers
Maybe I wasnt' clear enough. By binary files I mean plain binary, not an
exe
installer, that's the case for Paint.Net. What I mean is plain binary packages, which is just a bunch ofexe
files along withdll
files, many development tools are deployed this way such asnginx
,php
, etc. And they won't run unless:
- They are launched from the same directory where they were extracted, which a symlink will break
- They are added to
PATH
- They are launched through a
.bat
file as I proposed: this strategy is used throughout many different packages.If the community agrees with the
bat
implementation I would gladly like to contribute. I think it is much better than polluting thePATH
or forcing themanifest
to declare a whole bunch of DLL files which could also drive other side effects such asDLL
versions conflicts.Use bat might cause problem while execute it in another bat script.
I really don't know, I'm not that versed in BAT scripts. But AFAIK
@call
does not cause any side effects inside other bat files, it won't change the running environment as a regular call and as the content is just a single call to an exe and won't change any variable, it will be just like calling the exe itself which could also break/stop the bat by itself.I suggested this solution because it is used by many development tools such as Anaconda, python tools, msys, osgeo4w, this is not a clever hack that came out of my mind.
If you run the second bat script directly in the first bat script, the remaining commands in the first script will not be executed after the second script is executed(even if you use the @call
command in the second script). Unless the call
command is also used when invoking the second bat script in the first bat script. But bat scripts from the network and the average users doesn't know this, because the difference doesn't occur if a exe file is invoked. In addition, Microsoft documentation shows that the use of the @call
command is not recommended with redirection and pipes, so I'm not sure if this will affect the behavior of using redirection and pipes when running a bat script containing the call command as a command.
+1
I've got a .NET Core 7.0 app
but winget isn't capable of handling its XCOPY-Install scenario. Trying it as a 'portable' app
Installers:
- InstallerUrl: https://github.com/DrusTheAxe/AppData/releases/download/2.0.0/AppData-2.0.0.zip
Architecture: x64
InstallerType: zip
NestedInstallerType: portable
NestedInstallerFiles:
- RelativeFilePath: appdata.exe
InstallerSha256: 7717bd116f15e6debfc581e6bde36ec8854e60eced1c9cb5c5496a7f640af0f3
Looking forward to a solution
I found out in microsoft/winget-pkgs#109711 that this impacts non-DLL files as well. That is somewhat concerning as it means there could be other programs that validate normally but do not work properly due to symlinking/expected reachable files. Not all self-contained portable applications store the info they within the executable so I'm not sure how this would be validated against until implementation.
Since no one seems to have posted it here, there is a workaround, kindly described by @mdanish-kh here:
"A not so pretty workaround (as it has the tendency to quickly fill up your PATH variable limit) is to install the application in a non-admin shell with developer mode disabled in Windows. In this case, WinGet cannot create a symlink and will resort to putting the entire package path (i.e., %LOCALAPPDATA%\Microsoft\Winget\Packages) into the PATH variable. That way all the required libraries, .dlls will be referenced properly."
It look like another problem with the winget while during install, https://download.wsusoffline.net/wsusoffline120.zip
Well the PATH issue can be mitigated by having separate variable for winget package path eg WINGET
so you can add stuff to path like %WINGET%
or another option is to hold all winget path variables in WINGET variable and just add that to path. You can extend path by just chaining variables togather.https://stackoverflow.com/questions/34491244/environment-variable-is-too-large-on-windows-10
Any progress on this issue? Seems to be blocked many, many packages.