Runtime: Process Start fails with URL on .NET Core 3

Created on 27 Nov 2018  路  34Comments  路  Source: dotnet/runtime

The following code works on .NET Framework but throws on .NET Core 3:

Process.Start("https://github.com");

System.ComponentModel.Win32Exception
  HResult=0x80004005
  Message=The system cannot find the file specified.
  Source=System.Diagnostics.Process
  StackTrace:
   at System.Diagnostics.Process.StartWithCreateProcess(ProcessStartInfo startInfo)
   at System.Diagnostics.Process.Start()
   at System.Diagnostics.Process.Start(ProcessStartInfo startInfo)
   at System.Diagnostics.Process.Start(String fileName)
   at NetCoreProcessStartBug.MainWindow.ButtonBase_OnClick(Object sender, RoutedEventArgs e) in c:\dev\NetCoreProcessStartBug\NetCoreProcessStartBug\MainWindow.xaml.cs:line 31
   at System.Windows.RoutedEventHandlerInfo.InvokeHandler(Object target, RoutedEventArgs routedEventArgs)
   at System.Windows.RouteItem.InvokeHandler(RoutedEventArgs routedEventArgs)
   at System.Windows.EventRoute.InvokeHandlersImpl(Object source, RoutedEventArgs args, Boolean reRaised)
   at System.Windows.EventRoute.InvokeHandlers(Object source, RoutedEventArgs args)
   at System.Windows.UIElement.RaiseEventImpl(DependencyObject sender, RoutedEventArgs args)
   at System.Windows.UIElement.RaiseEvent(RoutedEventArgs e)
   at System.Windows.Controls.Primitives.ButtonBase.OnClick()
   at System.Windows.Controls.Button.OnClick()
   at System.Windows.Controls.Primitives.ButtonBase.OnMouseLeftButtonUp(MouseButtonEventArgs e)
   at System.Windows.UIElement.OnMouseLeftButtonUpThunk(Object sender, MouseButtonEventArgs e)
   at System.Windows.Input.MouseButtonEventArgs.InvokeEventHandler(Delegate genericHandler, Object genericTarget)
   at System.Windows.RoutedEventArgs.InvokeHandler(Delegate handler, Object target)
   at System.Windows.RoutedEventHandlerInfo.InvokeHandler(Object target, RoutedEventArgs routedEventArgs)
   at System.Windows.RouteItem.InvokeHandler(RoutedEventArgs routedEventArgs)
   at System.Windows.EventRoute.InvokeHandlersImpl(Object source, RoutedEventArgs args, Boolean reRaised)
   at System.Windows.EventRoute.ReInvokeHandlers(Object source, RoutedEventArgs args)
   at System.Windows.UIElement.ReRaiseEventAs(DependencyObject sender, RoutedEventArgs args, RoutedEvent newEvent)
   at System.Windows.UIElement.CrackMouseButtonEventAndReRaiseEvent(DependencyObject sender, MouseButtonEventArgs e)
   at System.Windows.UIElement.OnMouseUpThunk(Object sender, MouseButtonEventArgs e)
   at System.Windows.Input.MouseButtonEventArgs.InvokeEventHandler(Delegate genericHandler, Object genericTarget)
   at System.Windows.RoutedEventArgs.InvokeHandler(Delegate handler, Object target)
   at System.Windows.RoutedEventHandlerInfo.InvokeHandler(Object target, RoutedEventArgs routedEventArgs)
   at System.Windows.RouteItem.InvokeHandler(RoutedEventArgs routedEventArgs)
   at System.Windows.EventRoute.InvokeHandlersImpl(Object source, RoutedEventArgs args, Boolean reRaised)
   at System.Windows.EventRoute.InvokeHandlers(Object source, RoutedEventArgs args)
   at System.Windows.UIElement.RaiseEventImpl(DependencyObject sender, RoutedEventArgs args)
   at System.Windows.UIElement.RaiseTrustedEvent(RoutedEventArgs args)
   at System.Windows.UIElement.RaiseEvent(RoutedEventArgs args, Boolean trusted)
   at System.Windows.Input.InputManager.ProcessStagingArea()
   at System.Windows.Input.InputManager.ProcessInput(InputEventArgs input)
   at System.Windows.Input.InputProviderSite.ReportInput(InputReport inputReport)
   at System.Windows.Interop.HwndMouseInputProvider.ReportInput(IntPtr hwnd, InputMode mode, Int32 timestamp, RawMouseActions actions, Int32 x, Int32 y, Int32 wheel)
   at System.Windows.Interop.HwndMouseInputProvider.FilterMessage(IntPtr hwnd, WindowMessage msg, IntPtr wParam, IntPtr lParam, Boolean& handled)
   at System.Windows.Interop.HwndSource.InputFilterMessage(IntPtr hwnd, Int32 msg, IntPtr wParam, IntPtr lParam, Boolean& handled)
   at MS.Win32.HwndWrapper.WndProc(IntPtr hwnd, Int32 msg, IntPtr wParam, IntPtr lParam, Boolean& handled)
   at MS.Win32.HwndSubclass.DispatcherCallbackOperation(Object o)
   at System.Windows.Threading.ExceptionWrapper.InternalRealCall(Delegate callback, Object args, Int32 numArgs)
   at System.Windows.Threading.ExceptionWrapper.TryCatchWhen(Object source, Delegate callback, Object args, Int32 numArgs, Delegate catchHandler)
   at System.Windows.Threading.Dispatcher.WrappedInvoke(Delegate callback, Object args, Int32 numArgs, Delegate catchHandler)
   at System.Windows.Threading.Dispatcher.LegacyInvokeImpl(DispatcherPriority priority, TimeSpan timeout, Delegate method, Object args, Int32 numArgs)
   at System.Windows.Threading.Dispatcher.Invoke(DispatcherPriority priority, Delegate method, Object arg)
   at MS.Win32.HwndSubclass.SubclassWndProc(IntPtr hwnd, Int32 msg, IntPtr wParam, IntPtr lParam)
   at MS.Win32.UnsafeNativeMethods.DispatchMessage(MSG& msg)
   at System.Windows.Threading.Dispatcher.TranslateAndDispatchMessage(MSG& msg)
   at System.Windows.Threading.Dispatcher.PushFrameImpl(DispatcherFrame frame)
   at System.Windows.Threading.Dispatcher.PushFrame(DispatcherFrame frame)
   at System.Windows.Threading.Dispatcher.Run()
   at System.Windows.Application.RunDispatcher(Object ignore)
   at System.Windows.Application.RunInternal(Window window)
   at System.Windows.Application.Run(Window window)
   at System.Windows.Application.Run()
   at NetCoreProcessStartBug.App.Main()

GitHub repro: https://github.com/onovotny/NetCoreProcessStartBug

area-System.Diagnostics.Process

Most helpful comment

I believe the currently recommended workaround is to set UseShellExecute to true:

c# ProcessStartInfo psi = new ProcessStartInfo { FileName = url, UseShellExecute = true }; Process.Start (psi);

All 34 comments

I believe this is a known breaking change on .NETCore, see https://github.com/dotnet/corefx/issues/10361.

/cc @danmosemsft

Can that be revisited? I suspect a lot of desktop apps use Process.Start to launch a web page and will break people.

I believe the currently recommended workaround is to set UseShellExecute to true:

c# ProcessStartInfo psi = new ProcessStartInfo { FileName = url, UseShellExecute = true }; Process.Start (psi);

@dsplaisted I tried that and it does not work.

You need this instead

ProcessStartInfo psi = new ProcessStartInfo
{
    FileName = "cmd",
    Arguments = "/c start https://www.baidu.com/s?wd=beijing%20time"
};
Process.Start(psi);

Which is ugly...and more likely to bite people, I think.

Actually it's worse than that, here's what you need to do it right so no cmd window appears:

var psi = new ProcessStartInfo
{
    FileName = "cmd",
    WindowStyle = ProcessWindowStyle.Hidden,
    UseShellExecute = false,
    CreateNoWindow = true,
    Arguments = $"/c start {link.AbsoluteUri}"
};
Process.Start(psi);

Yes, that's a bad workaorund. Use the UseShellExecute approach that @dsplaisted suggested. It should work. If it doesn't lets investigate why that doesn't work. I just tried it in a 3.0 console and winforms app and it worked for me. Including with using that specific URL you provided. Can you elaborate on what didn't work when you tried UseShellExecute?

Weird鈥 tried again and it worked. Maybe hit an up-to-date fluke when I tried before and it ran old code?

Anyway, I would still urge a reconsideration given that it鈥檚 another papercut people will have to sort through.

This was a consciously made breaking change. We can leave this open for feedback I guess. In theory we could look for something that looks like an http(s) URL in the case where start fails.

@danmosemsft I believe there should be a consolidated list of breaking changes or known behavioral differences that's targeted towards migration scenarios. Another example is the overload of SignedCms.ComputeSignature(), where .NET Framework ignored the silent context switch, but it's fixed on Core.

Basically, a guide/wiki/something that I can point people to for reviewing common gotchas.

I thought there was one actually. @karelz?

Or @ericstj msybe knows where it is

@mairaw would know. I don't think we were that good at keeping track of each bug fix against .NET Framework though.
We need IMO list of higher-level issues ...

cc @terrajobst

The only wiki I know of is the following and is not complete AFAIK:
https://github.com/dotnet/corefx/wiki/ApiCompat

.NET Framework has a rigorous process for app-compat stuff that we use to generate docs like this that @rpetrusha knows well:
https://docs.microsoft.com/en-us/dotnet/framework/migration-guide/runtime/

I think we need to come up with a system for these things for Core too. We're not usually aware of breaking changes introduced for Core.

Ran into this today porting a WPF 4.7.1 app to .NET Core 3, @onovotny's work around worked though.

To clarify for others arriving here, the change is that in .NET Framework, UseShellExecute = true is the default, and in .NET Core UseShellExecute = false is the default. The reasoning is that UseShellExecute is very Windows-specific and significantly slower.

To clarify for others arriving here, the change is that in .NET Framework, UseShellExecute = true is the default, and in .NET Core UseShellExecute = false is the default. The reasoning is that UseShellExecute is very Windows-specific and significantly slower.

Which is a very valid reason, thanks for clarifying.

The method suggested by @dsplaisted works fine.
Why does Microsoft change so many things in .net core?
We have to unlearn stuff that we've been doing for years
Querystring has now become Request.Query and so on....

The method suggested by @dsplaisted works fine.
Why does Microsoft change so many things in .net core?
We have to unlearn stuff that we've been doing for years
Querystring has now become Request.Query and so on....

I see it as more of keeping things from being stagnate. I'm all for the innovation and break changes if it means I can do more. Keeping up with the changes and continually learning is part of the fun being an engineer (imho).

@mcp111 in this case the reasoning for this change is higher up - performance.

We've tried to maintain a breaking change list at https://github.com/dotnet/corefx/wiki/ApiCompat

It's not just performance; it's also consistency for library authors.

ShellExecute vs. CreateProcess have different feature sets, for example, when you want to redirect input/output. We feel it's more important to have consistency between all operating systems than to remain behavior compatible with .NET Framework.

AFAIK @danmosemsft https://github.com/dotnet/corefx/wiki/ApiCompat is outdated.

@PriyaPurkayastha has now created a new process that allow us to better document these breaking changes at https://docs.microsoft.com/en-us/dotnet/core/compatibility/breaking-changes.

Could anybody confirm if this code works on all platforms?

ProcessStartInfo psi = new ProcessStartInfo
{
    FileName = url,
    UseShellExecute = true
};
Process.Start (psi);

Thanks.-

Could anybody confirm if this code works on all platforms?

We'd like it to, but what happens on Unix is that if it doesn't successfully launch directly, we have to find a program to attempt to use:

https://github.com/dotnet/runtime/blob/c50eae5725623c6d7b6de0d54e1010eb6e2c17fa/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/Process.Unix.cs#L414-L441

So that program needs to be installed

https://github.com/dotnet/runtime/blob/c50eae5725623c6d7b6de0d54e1010eb6e2c17fa/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/Process.Linux.cs#L109-L120

and on macOS and other Unix currently we don't look for one.
https://github.com/dotnet/runtime/blob/c50eae5725623c6d7b6de0d54e1010eb6e2c17fa/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/Process.UnknownUnix.cs#L85-L89

So it's "best effort". If you have suggestions for doing better, please share as it would be good to do better.

Could anybody confirm if this code works on all platforms?

ProcessStartInfo psi = new ProcessStartInfo
{
    FileName = url,
    UseShellExecute = true
};
Process.Start (psi);

Thanks.-

On my Xamarin.Forms project, I'm getting a system.platformnotsupportedexception: 'useshellexecute is not supported on this platform.' .

I tried the following codes on 3.1:

var psi = new ProcessStartInfo
{
    FileName = _https,
    UseShellExecute = true,
    Verb = "open",
};
using (var proc = Process.Start(psi))
{
}

And:

using (var proc = new Process())
{
    proc.StartInfo.FileName = _https;
    proc.StartInfo.UseShellExecute = true;
    proc.StartInfo.Verb = "open";
    proc.Start();
}

(I still prefer the last one though.) Also where _https is https://testing.us.sysb.ai

However both results in:

Win32Exception: No application is associated with the specified file for this operation.   at System.Diagnostics.Process.StartWithShellExecuteEx(ProcessStartInfo startInfo)
   at System.Diagnostics.Process.Start()
   at System.Diagnostics.Process.Start(ProcessStartInfo startInfo)
   at WebApi1.Ssh.SshHandler.Url() in C:\Users\User\Desktop\github\WebApi1\Ssh\SshHandler.cs:line 87
   at WebApi1.Ssh.SshHandler.Start() in C:\Users\User\Desktop\github\WebApi1\Ssh\SshHandler.cs:line 45
   at WebApi1.Program.Main(String[] args) in C:\Users\User\Desktop\github\WebApi1\Program.cs:line 46

However this being Windows 10 on the latest fast ring insider build It should at least open up edge as the default browser or firefox depending on what the user set it to if I am correct still.

Who else wants to test this on net core 3.1?

edit: It seems somehow despite firefox being set as my default browser nothing could obtain the settings and when I tried to view the settings the settings program would time out and close itself for some odd reason until I forced rebooted windows itself. It even went as far as not letting me open the start menu; all because of something where if you try to right click a file and go to > open with windows explorer would hang and restart itself with some taskbar pins not showing there icon ever again until reboot. Something is not right here.

@AraHaan I tested on .NET Core 3.1 on Windows 10 and worked fine (i.e. opened my default browser (Chrome) without any errors)

C:\>dotnet --info
.NET Core SDK (reflecting any global.json):
 Version:   3.1.101
 Commit:    b377529961

Runtime Environment:
 OS Name:     Windows
 OS Version:  10.0.18363
 OS Platform: Windows
 RID:         win10-x64
 Base Path:   C:\Program Files\dotnet\sdk\3.1.101\

Host (useful for support):
  Version: 3.1.1
  Commit:  a1388f194c

I found in a post from 2016 that this code supposedly worked in other platforms:

if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
{
    Process.Start("xdg-open", url);
}
else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
{
    Process.Start("open", url);
}

Could anybody confirm if this code works on all platforms?

ProcessStartInfo psi = new ProcessStartInfo
{
    FileName = url,
    UseShellExecute = true
};
Process.Start (psi);

Thanks.-

This is working on Windows 10. I'm happy with that since I'm targeting WPF which is windows only anyway.

@soul4soul yes, it should -- assuming we can find xdg-open or similar app as shown above
https://github.com/dotnet/runtime/issues/28005#issuecomment-558716791

With the passing of time, I do not think we plan to change this behavior. There does not seem evidence of lots of existing code that cannot be changed to add UseShellExecute=true and that launches URL's.

Hello, and Thank you for providing the workaround, first of all. It can be done in one line:

           string my_web_link = "https://dynamic-applications.org/downloads/startup-product-manager/";

    // start the external weblink in a system process (as an executable):
    Process.Start( new ProcessStartInfo { FileName = my_web_link, UseShellExecute = true } );

As i have found out today, there seems to be a side-issue with installing .NET 5.0 Desktop Environment. I have managed to provide a Setup.exe (.vdproj) where the correct .NET 5.0 Desktop (x86) Installer download will be triggered from.

However, when i wanted to provide an Appx for the Windows 10 Store, the Tester always reported it will not run.

It seems to be so that a .NET executable contains a small, automatic stub that will check the environment at the beginning. Then it pops up a small requester allowing you to download the required version, just in case. However, with .NET 5.0 you can click "yes" or "no" in that built-in exe .NET installer, and nothing happens.

Without having any insight, i would guess this strange behavior could find its reason right here.
So the .NET Stub calls Process.Start() and that one fails silently. Nobody thought of porting that command to the next version.

In effect, the self-check for all standard .NET 5.0 Desktop Applications (.exe), will fail completely. So if the proper .NET Framework or Desktop Environment is missing, they'll fail with a useless message when clicking the .exe, nothing happens, and they give no hint what to install. This may affect very many users naturally (Windows Forms and all traditional Desktop Apps).

Maybe someone could fix this for the next release? -
thank you so much

best regards

Martin Bernhardt
[email protected]

Hi @dynamicons, I'm not sure I understand the scenario you're describing as broken?

What exactly is failing? Separately, there should be no reason for a setup.exe to install the Desktop Runtime on end-user machines; self-contained apps are recommended here. I know NuGet Package Explorer is distributed in the Store as a self-contained app and has no trouble with using Process.Start to launch a URL with UseShellExecute set to true.

Hello Claire,

there seems to be a small self-check or self-repair component that's built-in to every .NET program.exe.

With .NET 4.0 Framework, whenever the .NET Framework is missing (e.g. uninstalled by user on System clean-up), the application will show up a small dialog that will allow you to download .NET Framework again. So it's really like a built-in self-repair function.

Here is a Screenshot of the .NET self-repair dialog that shows up from the .exe itself on missing installation.

https://dynamions.files.wordpress.com/2020/11/iotdeveloper.exe-built-in-.net-5.0-detector.png

This functionality seems defunct with .NET 5.0 Desktop Environment (or compiling for .NET 5.0 target in VS 2019.8 with .NET 5.0). So the new functionality is that the self-repair of a .exe shows up the same popup but then it won't start your browser and download the .NET installer anymore.

So that's why i thought it could be the same reason - the old Process.Start() call failing silently here.

The reason that i prefer the traditional Setup Installer is that this way you have to provide and deploy .NET 5.0 Desktop Environment only once. So the Setup.exe and the Appx package for Windows 10 Store are really, really small (4-5 MByte).

The self-contained all-in-one Exe generator is an interesting idea, of course. But it leads to 100 MByte IoTdeveloper.exe in my case. So that's way out of bounds of what i would find acceptable (double size with 10 MByte would be ok). The whole .NET Desktop Environment Setup.exe is only 50 MBytes, by the way. So the self-contained exe seems not be well compressed.

I have tried with Trimming and ReadyToRun=false but still it's pretty much 100 MBytes, minimum.
With a DSL upload connection of 1 MBit/s here, it would take all day to upload my Setups to the Windows 10 Store.

Also the user experience is slow and there is hundreds of files created that don't really have a purpose or justification to exist in parallel to each other, per program installation, like that.

That's why i really prefer to provide a small Setup.exe and then only once download and install .NET 5.0 Desktop, separately.

@dynamicons could you please open a new issue -- probably in dotnet/sdk -- proposing this "missing installation" experience? Note that Windows has special knowledge of .NET Framework (via mscoree.dll etc), but it does not of .NET Core.

By opening a new issue we can get it routed to the right place - this issue is closed.

Hello Dan,

thank you - yes, that seems a good idea.
I have filed in Issue 45542 against .NET Core development.

https://github.com/dotnet/runtime/issues/45542

It has already been reproduced, however, Process.Start() seems not to be the issue here.
The Error message itself was misleading, instead it hides other internal messages, and the feature itself also won't work.

However, the idea to fix this seems to be good for everyone.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

sahithreddyk picture sahithreddyk  路  3Comments

v0l picture v0l  路  3Comments

chunseoklee picture chunseoklee  路  3Comments

bencz picture bencz  路  3Comments

GitAntoinee picture GitAntoinee  路  3Comments