Saturday, August 24, 2019

Converting .NET Framework to .NET Core - UseShellExecute Has a Different Default Value

I came across a difference between .NET Framework and .NET Core that caused a bit of frustration when I was converting a project.
Process.StartInfo.UseShellExecute defaults to "true" in .NET Framework, but it defaults to "false" in .NET Core.
If you have code using Process, this may come up when converting your projects over to .NET Core.

Discovery and Pain
.NET Core is the path forward. And I have been slowly converting my code from .NET Framework to .NET Core. This is especially interesting because I can now move my WPF projects over to .NET Core 3 (which will be released in about a month).

Yesterday, I was converting my maze generation program (GitHub: jeremybytes/mazes-for-programmers) and ran into an issue. This program generates a maze using a selected algorithm and outputs it as a text maze (using ASCII art) as well as a graphical maze (using a bitmap). The bitmap gets saved to the file system as a .png file.

The application itself is a console application, and I want to show the graphical maze after it is saved to the file system. So, I do a Process.Start to have Windows open up the file using the default bitmap viewer. Here is the code for that (code can be found in Program.cs of the DrawMaze project):

This works fine with .NET Framework. But when I ran it in .NET Core, I got a runtime exception.

"System.ComponentModel.Win32Exception; 'The specified executable is not a valid application for this OS platform.'"

This left me scratching my head.

There was something wrong with the Process.Start. This is not something I do regularly, but I do know that "StartInfo" has a lot of properties. So I started fumbling around looking for answers.

Google & StackOverflow were not very helpful -- probably because I didn't know what to search for.

One thing I tried was changing the file name to ".\maze.png". You need to use a ".\" in PowerShell to execute something, so I thought maybe I needed it here. That didn't work.

Another thing I tried was changing the "FileName" property to "cmd.exe" and adding an "Argument" property that had "maze.png". That didn't work.

Then I started looking through the docs for Process and more specifically StartInfo.

Eventually, I ended up at the "UseShellExecute" property (docs) and found this little gem:

"The default is true on .NET Framework apps and false on .NET Core apps."


As soon as I saw this, I knew it was my problem. This property determines whether to use the operating system shell to start a process.

When it is "true", it's basically the same as typing "maze.png" from a command prompt. If you do this from a Windows command prompt, it will will try to open the file using the default image viewer.

But if it is "false", then we're saying "execute this file", and a bitmap is not an executable.

Fixing the Code
The code fix was easy after that. I just had to set the UseShellExecute property to "true":

Then the program worked. On my machine it opens the "Photos" app, which is my default viewer.

(And in case you're curious, here's the full image of the maze that was generated -- be sure to zoom in to see all the interesting detail)

Cross-Platform Solution?
I find the cross-platform features of .NET Core to be particularly interesting. So I've been trying a bunch of my .NET Core projects on MacOS and on Linux.

I have not figured out how to do something like this in a cross-platform-friendly way. The text maze generation in a console/terminal window works just fine. The bitmap is created and saved to the file system just fine. But I'm not sure how to display the bitmap file automatically. I'm really relying on the default Windows behavior in the current application.

One potential solution is to put in some OS-specific code. If it is Windows, do a shell execute. If it is MacOS, open up the "Preview" application with the bitmap file. But there's really no equivalent in the Linux world. What I'd like to do is "open this bitmap with the default bitmap viewer". That doesn't exist on Linux.

Another idea is to open the file in a browser. But there's no guarantee that a particular browser is available on a particular platform.

If you have any ideas, be sure to leave a comment on this article.

Wrap Up
I really like .NET Core, and I'm excited for the Core 3 release so I can start using it with my WPF application on Windows.

.NET Core is the path forward. So far, moving code over to new projects has been fairly painless. But I have run into a few issues like this one that are a bit frustrating. This is a breaking change between platforms.

I did not run this application through the .NET Portability Analyzer. This application is small and straight forward -- a console application and a couple of libraries that aren't doing anything exciting; they don't even use any external NuGet packages. I should probably run it through the analyzer to see if it finds this change between the platforms.

I'll be sure to post any other strangeness I find when doing my conversions. And I'm hoping to do a walkthrough of an entire application conversion sometime soon.

Happy Coding!