-
Notifications
You must be signed in to change notification settings - Fork 998
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
fix: Application.ExecutablePath
returns dll instead of exe
#2801
Conversation
In .NET artifacts are DLLs even for executable projects. With some automagic they get bundled into executables. However `Assembly.GetEntryAssembly()` always returns the dll instead of the exe. Following the guidance from the Runtime team retrieve the path to the executable via `GetModuleFileNameW` call. Resolves dotnet#1143
@merriemcgaw I think we should consider this fix for servicing release/3.1 |
@dotnet/dotnet-winforms I'd appreciate your thoughts on whether we should check some specific use cases. @JeremyKuhne @hughbe @weltkante any thoughts on how we better test this change? |
Codecov Report
@@ Coverage Diff @@
## master #2801 +/- ##
===================================================
- Coverage 59.31806% 59.31307% -0.00499%
===================================================
Files 1243 1243
Lines 430508 430492 -16
Branches 38972 38970 -2
===================================================
- Hits 255369 255338 -31
- Misses 169742 169769 +27
+ Partials 5397 5385 -12
|
I want to point at the fact that you'll want this API to work for all deployment modes, so you'll probably also want to test them in different deployment modes. You can run a WinForms application via I'm not great with tests so can't really offer suggestions here. Since you need to test applications in different deployment modes you can't really use xunit here (because that would mess up what the entry point assembly is), I have no idea about which other options you have available, sorry. Though considering you are basically just wrapping the win32 API for getting the executable, it is probably more important to test the features dependent on this functionality, rather than getting the executable path itself (to make sure everyone plays well with the fact that it now basically always is an unmanaged application, where previously this was a rare corner case - callers may need to be switched to call [edit] I checked the callers and they should be fine, there are only two callers:
|
Uri codeBase = new Uri(cb); | ||
if (codeBase.IsFile) | ||
{ | ||
s_executablePath = codeBase.LocalPath + Uri.UnescapeDataString(codeBase.Fragment); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Seems like after the change, file path is not formatted, instead, according to docs, we return what was specified when the module was loaded.
The string returned will use the same format that was specified when the module was loaded. Therefore, the path can be a long or short file name, and can use the prefix "\?". For more information, see Naming a File.
Also if the app was started using only only the file name and GetModuleFileName
returns that short name, then Path.GetFullPath
would resolve the full path based on the current working directory, which might not be the same as the app was started from. See remarks here.
[EDIT] fix formatting
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Are you sure GetModuleFileName
can return a relative file name? I was under the impression that only the format (long/short/escaped) could differ, but that it always is an absolute path. (Doc says "Retrieves the fully qualified path ..." as its first line and then notes later that the format can differ, but I don't think it retracts the fact that its fully qualified.)
Can you add the output of the scripts you ran? |
I've tested all of these scenarios, I failed to mentioned that in the PR message, my bad. . . . I've run apps in a number of use-cases, you think I've missed some please let me know. Breaking the results into multiple posts for ease of navigation |
Single project app
PS C:\Development\winforms> C:\Development\!Tests\DynamicApps\SingleApp\app\bin\Debug\net472\app.exe
C:\Development\!Tests\DynamicApps\SingleApp\app\bin\Debug\net472\app.exe
PS C:\Development\winforms> ..\!Tests\DynamicApps\SingleApp\app\bin\Debug\net472\app.exe
C:\Development\!Tests\DynamicApps\SingleApp\app\bin\Debug\net472\app.exe
PS C:\Development\winforms> & '\\SCREAMERMS\c$\Development\!Tests\DynamicApps\SingleApp\app\bin\Debug\net472\app.exe'
\\screamerms\c$\Development\!Tests\DynamicApps\SingleApp\app\bin\Debug\net472\app.exe
PS C:\Development\winforms> \\SCREAMERMS\c$\Development\!Tests\DynamicApps\SingleApp\app\bin\Debug\net472\app.exe
\\screamerms\c$\Development\!Tests\DynamicApps\SingleApp\app\bin\Debug\net472\app.exe
PS C:\Development\winforms> \\.\C:\Development\!Tests\DynamicApps\SingleApp\app\bin\Debug\net472\app.exe
Unhandled Exception: System.IO.FileNotFoundException: Could not load file or assembly 'app.exe' or one of its dependencies. The network path was not found.
PS C:\Development\winforms> \\?\C:\Development\!Tests\DynamicApps\SingleApp\app\bin\Debug\net472\app.exe
Unhandled Exception: System.UriFormatException: Invalid URI: The format of the URI could not be determined.
at System.Uri.CreateThis(String uri, Boolean dontEscape, UriKind uriKind)
at System.Windows.Forms.Application.get_ExecutablePath()
at app.Program.Main() in C:\Development\!Tests\DynamicApps\vjyp220f.rar\app\Program.cs:line 20
PS C:\Development\winforms> \\127.0.0.1\C$\Development\!Tests\DynamicApps\SingleApp\app\bin\Debug\net472\app.exe
\\127.0.0.1\C$\Development\!Tests\DynamicApps\SingleApp\app\bin\Debug\net472\app.exe
PS C:\Development\winforms> C:\Development\winforms\artifacts\bin\SingleApp\Debug\netcoreapp5.0\SingleApp.exe
C:\Development\winforms\artifacts\bin\SingleApp\Debug\netcoreapp5.0\SingleApp.exe
PS C:\Development\winforms> .\artifacts\bin\SingleApp\Debug\netcoreapp5.0\SingleApp.exe
C:\Development\winforms\artifacts\bin\SingleApp\Debug\netcoreapp5.0\SingleApp.exe
PS C:\Development\winforms> & '\\SCREAMERMS\c$\Development\winforms\artifacts\bin\SingleApp\Debug\netcoreapp5.0\SingleApp.exe'
\\SCREAMERMS\c$\Development\winforms\artifacts\bin\SingleApp\Debug\netcoreapp5.0\SingleApp.exe
PS C:\Development\winforms> \\SCREAMERMS\c$\Development\winforms\artifacts\bin\SingleApp\Debug\netcoreapp5.0\SingleApp.exe
\\SCREAMERMS\c$\Development\winforms\artifacts\bin\SingleApp\Debug\netcoreapp5.0\SingleApp.exe
PS C:\Development\winforms> \\.\C:\Development\winforms\artifacts\bin\SingleApp\Debug\netcoreapp5.0\SingleApp.exe
\\.\C:\Development\winforms\artifacts\bin\SingleApp\Debug\netcoreapp5.0\SingleApp.exe
PS C:\Development\winforms> \\?\C:\Development\winforms\artifacts\bin\SingleApp\Debug\netcoreapp5.0\SingleApp.exe
\\?\C:\Development\winforms\artifacts\bin\SingleApp\Debug\netcoreapp5.0\SingleApp.exe
PS C:\Development\winforms> \\127.0.0.1\C$\Development\winforms\artifacts\bin\SingleApp\Debug\netcoreapp5.0\SingleApp.exe
\\127.0.0.1\C$\Development\winforms\artifacts\bin\SingleApp\Debug\netcoreapp5.0\SingleApp.exe
PS C:\Development\winforms\artifacts\bin\SingleApp\Release\netcoreapp5.0\win-x64\publish> .\SingleApp.exe
C:\Development\winforms\artifacts\bin\SingleApp\Release\netcoreapp5.0\win-x64\publish\SingleApp.exe
PS C:\Development\winforms> C:\Development\winforms\artifacts\bin\SingleApp\Release\netcoreapp5.0\win-x64\publish\SingleApp.exe
C:\Development\winforms\artifacts\bin\SingleApp\Release\netcoreapp5.0\win-x64\publish\SingleApp.exe
PS C:\Development\winforms> .\artifacts\bin\SingleApp\Release\netcoreapp5.0\win-x64\publish\SingleApp.exe
C:\Development\winforms\artifacts\bin\SingleApp\Release\netcoreapp5.0\win-x64\publish\SingleApp.exe
PS C:\Development\winforms> & '\\SCREAMERMS\c$\Development\winforms\artifacts\bin\SingleApp\Release\netcoreapp5.0\win-x64\publish\SingleApp.exe'
\\SCREAMERMS\c$\Development\winforms\artifacts\bin\SingleApp\Release\netcoreapp5.0\win-x64\publish\SingleApp.exe
PS C:\Development\winforms> \\SCREAMERMS\c$\Development\winforms\artifacts\bin\SingleApp\Release\netcoreapp5.0\win-x64\publish\SingleApp.exe
\\SCREAMERMS\c$\Development\winforms\artifacts\bin\SingleApp\Release\netcoreapp5.0\win-x64\publish\SingleApp.exe
PS C:\Development\winforms> \\.\C:\Development\winforms\artifacts\bin\SingleApp\Release\netcoreapp5.0\win-x64\publish\SingleApp.exe
\\.\C:\Development\winforms\artifacts\bin\SingleApp\Release\netcoreapp5.0\win-x64\publish\SingleApp.exe
PS C:\Development\winforms> \\?\C:\Development\winforms\artifacts\bin\SingleApp\Release\netcoreapp5.0\win-x64\publish\SingleApp.exe
\\?\C:\Development\winforms\artifacts\bin\SingleApp\Release\netcoreapp5.0\win-x64\publish\SingleApp.exe
PS C:\Development\winforms> \\127.0.0.1\C$\Development\winforms\artifacts\bin\SingleApp\Release\netcoreapp5.0\win-x64\publish\SingleApp.exe
\\127.0.0.1\C$\Development\winforms\artifacts\bin\SingleApp\Release\netcoreapp5.0\win-x64\publish\SingleApp.exe |
Multi project app
PS C:\Development\winforms> C:\Development\!Tests\DynamicApps\MultiApp\app\bin\Debug\net472\app.exe
C:\Development\!Tests\DynamicApps\MultiApp\app\bin\Debug\net472\app.exe
PS C:\Development\winforms> ..\!Tests\DynamicApps\MultiApp\app\bin\Debug\net472\app.exe
C:\Development\!Tests\DynamicApps\MultiApp\app\bin\Debug\net472\app.exe
PS C:\Development\winforms> & '\\SCREAMERMS\c$\Development\!Tests\DynamicApps\mULTIApp\app\bin\Debug\net472\app.exe'
\\screamerms\c$\Development\!Tests\DynamicApps\mULTIApp\app\bin\Debug\net472\app.exe
PS C:\Development\winforms> \\SCREAMERMS\c$\Development\!Tests\DynamicApps\mULTIApp\app\bin\Debug\net472\app.exe
\\screamerms\c$\Development\!Tests\DynamicApps\mULTIApp\app\bin\Debug\net472\app.exe
PS C:\Development\winforms> \\.\C:\Development\!Tests\DynamicApps\MultiApp\app\bin\Debug\net472\app.exe
Unhandled Exception: System.IO.FileNotFoundException: Could not load file or assembly 'app.exe' or one of its dependencies. The network path was not found.
PS C:\Development\winforms> \\?\C:\Development\!Tests\DynamicApps\MultiApp\app\bin\Debug\net472\app.exe
Unhandled Exception: System.IO.FileNotFoundException: Could not load file or assembly 'lib, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null' or one of its dependencies. The system cannot find the file specified.
at app.Program.Main()
PS C:\Development\winforms> \\127.0.0.1\C$\Development\!Tests\DynamicApps\MultiApp\app\bin\Debug\net472\app.exe
\\127.0.0.1\C$\Development\!Tests\DynamicApps\MultiApp\app\bin\Debug\net472\app.exe
PS C:\Development\winforms> \\127.0.0.1\C$\DEVELO~1\!Tests\DynamicApps\MultiApp\app\bin\Debug\net472\app.exe
\\127.0.0.1\C$\DEVELO~1\!Tests\DynamicApps\MultiApp\app\bin\Debug\net472\app.exe
PS C:\Development\winforms> C:\Development\winforms\artifacts\bin\MultiApp\Debug\netcoreapp5.0\MultiApp.exe
C:\Development\winforms\artifacts\bin\MultiApp\Debug\netcoreapp5.0\MultiApp.exe
PS C:\Development\winforms> .\artifacts\bin\MultiApp\Debug\netcoreapp5.0\MultiApp.exe
C:\Development\winforms\artifacts\bin\MultiApp\Debug\netcoreapp5.0\MultiApp.exe
PS C:\Development\winforms> & '\\SCREAMERMS\c$\Development\winforms\artifacts\bin\MultiApp\Debug\netcoreapp5.0\MultiApp.exe'
\\SCREAMERMS\c$\Development\winforms\artifacts\bin\MultiApp\Debug\netcoreapp5.0\MultiApp.exe
PS C:\Development\winforms> \\SCREAMERMS\c$\Development\winforms\artifacts\bin\MultiApp\Debug\netcoreapp5.0\MultiApp.exe
\\SCREAMERMS\c$\Development\winforms\artifacts\bin\MultiApp\Debug\netcoreapp5.0\MultiApp.exe
PS C:\Development\winforms> \\.\C:\Development\winforms\artifacts\bin\MultiApp\Debug\netcoreapp5.0\MultiApp.exe
\\.\C:\Development\winforms\artifacts\bin\MultiApp\Debug\netcoreapp5.0\MultiApp.exe
PS C:\Development\winforms> \\?\C:\Development\winforms\artifacts\bin\MultiApp\Debug\netcoreapp5.0\MultiApp.exe
\\?\C:\Development\winforms\artifacts\bin\MultiApp\Debug\netcoreapp5.0\MultiApp.exe
PS C:\Development\winforms> \\127.0.0.1\C$\Development\winforms\artifacts\bin\MultiApp\Debug\netcoreapp5.0\MultiApp.exe
\\127.0.0.1\C$\Development\winforms\artifacts\bin\MultiApp\Debug\netcoreapp5.0\MultiApp.exe
PS C:\Development\winforms> \\127.0.0.1\C$\DEVELO~1\winforms\artifacts\bin\MultiApp\Release\netcoreapp5.0\win-x64\publish\MultiApp.exe
\\127.0.0.1\C$\Development\winforms\artifacts\bin\MultiApp\Release\netcoreapp5.0\win-x64\publish\MultiApp.exe
PS C:\Development\winforms\artifacts\bin\MultiApp\Release\netcoreapp5.0\win-x64\publish> .\MultiApp.exe
C:\Development\winforms\artifacts\bin\MultiApp\Release\netcoreapp5.0\win-x64\publish\MultiApp.exe
PS C:\Development\winforms> C:\Development\winforms\artifacts\bin\MultiApp\Release\netcoreapp5.0\win-x64\publish\MultiApp.exe
C:\Development\winforms\artifacts\bin\MultiApp\Release\netcoreapp5.0\win-x64\publish\MultiApp.exe
PS C:\Development\winforms> .\artifacts\bin\MultiApp\Release\netcoreapp5.0\win-x64\publish\MultiApp.exe
C:\Development\winforms\artifacts\bin\MultiApp\Release\netcoreapp5.0\win-x64\publish\MultiApp.exe
PS C:\Development\winforms> & '\\SCREAMERMS\c$\Development\winforms\artifacts\bin\MultiApp\Release\netcoreapp5.0\win-x64\publish\MultiApp.exe'
\\SCREAMERMS\c$\Development\winforms\artifacts\bin\MultiApp\Release\netcoreapp5.0\win-x64\publish\MultiApp.exe
PS C:\Development\winforms> \\SCREAMERMS\c$\Development\winforms\artifacts\bin\MultiApp\Release\netcoreapp5.0\win-x64\publish\MultiApp.exe
\\SCREAMERMS\c$\Development\winforms\artifacts\bin\MultiApp\Release\netcoreapp5.0\win-x64\publish\MultiApp.exe
PS C:\Development\winforms> \\.\C:\Development\winforms\artifacts\bin\MultiApp\Release\netcoreapp5.0\win-x64\publish\MultiApp.exe
\\.\C:\Development\winforms\artifacts\bin\MultiApp\Release\netcoreapp5.0\win-x64\publish\MultiApp.exe
PS C:\Development\winforms> \\?\C:\Development\winforms\artifacts\bin\MultiApp\Release\netcoreapp5.0\win-x64\publish\MultiApp.exe
\\?\C:\Development\winforms\artifacts\bin\MultiApp\Release\netcoreapp5.0\win-x64\publish\MultiApp.exe
PS C:\Development\winforms> \\127.0.0.1\C$\Development\winforms\artifacts\bin\MultiApp\Release\netcoreapp5.0\win-x64\publish\MultiApp.exe
\\127.0.0.1\C$\Development\winforms\artifacts\bin\MultiApp\Release\netcoreapp5.0\win-x64\publish\MultiApp.exe
PS C:\Development\winforms> \\127.0.0.1\C$\DEVELO~1\winforms\artifacts\bin\MultiApp\Release\netcoreapp5.0\win-x64\publish\MultiApp.exe
\\127.0.0.1\C$\Development\winforms\artifacts\bin\MultiApp\Release\netcoreapp5.0\win-x64\publish\MultiApp.exe |
Overall the behaviour is comparable with the following notable differencces:
\\?\C:\Development\!Tests\DynamicApps\MultiApp\app\bin\Debug\net472\app.exe
\\.\C:\Development\!Tests\DynamicApps\MultiApp\app\bin\Debug\net472\app.exe
PS C:\Development\winforms> \\127.0.0.1\C$\DEVELO~1\!Tests\Dynami~1\MultiApp\app\bin\Debug\net472\app.exe
\\127.0.0.1\C$\DEVELO~1\!Tests\Dynami~1\MultiApp\app\bin\Debug\net472\app.exe
PS C:\Development\winforms> \\127.0.0.1\C$\DEVELO~1\winforms\artifa~1\bin\MultiApp\Release\netcor~1.0\win-x64\publish\MultiApp.exe
\\127.0.0.1\C$\Development\winforms\artifacts\bin\MultiApp\Release\netcoreapp5.0\win-x64\publish\MultiApp.exe I'm not sure how significant this one is... 🤔 |
@dreddy-work will this have any impact on ClickOnce scenarios? The docs
How can we test this? |
|
What i see from doc is, in a scenario where application is deployed using clickonce, user launch their application either by clicking "*.Application" file or running setup.exe that is available in publish folder. In these cases, i see Application.ExecutablePath returning the actual winforms application path as"%appdata%\local\2.0......exe". This is the actual location of winforms application from where clickonce is running the app. However, this is inline with current core behavior ( returning the dll path instead of exe that invoked it). |
With this change, what do you see when you run the core winforms app using dotnet.exe ? dotnet.exe path or winforms.dll path? |
And I think this is correct, since the executable is |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM - also checked the callers:
Application.Restart
always wants theexe
Application.GetAppFileVersionInfo
already has code looking for the managed entry point first (Assembly.GetEntryAssembly
) and only usesApplication.ExecutablePath
as a fallback. If you have no managed entrypoint you are in a custom host scenario and returning the version of the hosting unmanaged exe is the correct thing to do (Desktop did the same)
Agreed, this is a good servicing candidate. Please add the servicing-consider label when this is ready to go to shiproom. |
…2801) In .NET artifacts are DLLs even for executable projects. With some automagic they get bundled into executables. However `Assembly.GetEntryAssembly()` always returns the dll instead of the exe. Following the guidance from the Runtime team retrieve the path to the executable via `GetModuleFileNameW` call. Resolves dotnet#1143 (cherry picked from commit 2af3af9)
I ran further tests to verify attribute-based information, such as I added the following attributes and published the app: [assembly: AssemblyTitle("Test app title")]
[assembly: AssemblyCompany("Test app company")]
[assembly: AssemblyProduct("Test app product")]
[assembly: AssemblyDescription("Test app description")]
[assembly: AssemblyCopyright("Copyright © 2020 Microsoft")]
[assembly: AssemblyVersion("1.2.3")]
[assembly: AssemblyFileVersion("1.2.3.4")]
[assembly: AssemblyInformationalVersion("2.3.4.5")]
|
Resolves #1143 following the advice from @swaroop-sridhar in dotnet/runtime#11201 (comment)
Proposed changes
In .NET artifacts are DLLs even for executable projects. With some automagic they get bundled into executables.
However
Assembly.GetEntryAssembly()
always returns the dll instead of the exe.Following the guidance from the Runtime team retrieve the path to the executable via
GetModuleFileNameW
call.Customer Impact
Regression?
Risk
Test methodology
Application.ExecutablePath
in the following scenarios:Application.ExecutablePath
is called from the library projectTest results
Application.ExecutablePath
returns dll instead of exe #2801 (comment)Application.ExecutablePath
returns dll instead of exe #2801 (comment)Microsoft Reviewers: Open in CodeFlow