0

I want to have my C# (Xamarin) program run an EXE or batch (BAT) file. The user will be running my program, and will click on one of several buttons, some of which open Web pages and others of which run external programs. These files will be on the same computer as the one running the main program and don't need greater permissions. The overall program will be in Windows, UWP.

I already have code to pull info from the database saying "the button the user clicked references a program and it's (eg) C:\Tools\MyTool.exe". (Real path more like (C:\Users\Me\source\repos\ProductNameV2\ProductName\ProductName.UWP\Assets\EXE\whatever.exe".) I used a "demo.bat" file containing nothing but echo and pause statements, or references to a built-in Windows program like Notepad or Calc that an ordinary command prompt can recognize without an explicit path (ie. that's part of the recognized system Path). Yes, the real path to the dummy file does exist; I checked. I've also explicitly added files demo.bat and dummy.txt to my C# project.

Here's roughly what I've tried so far to actually run a batch file, or an EXE, or just to try opening a text file. Nothing works.

1)
bool check = await Launcher.CanOpenAsync(@"file:///C:\Tools\demo.bat"); // Returns false.
bool check = await Launcher.CanOpenAsync(@"file:///C:\Tools\dummy.txt"); // Returns true.
await Launcher.OpenAsync(@"file:///C:\Tools\demo.bat") // Seems to do nothing; silently fails.
await Launcher.OpenAsync(@"file:///C:\Tools\dummy.txt") // Same.

2)
Process batchProcess = new Process();
batchProcess.StartInfo.FileName = @"file:///C:\Tools\demo.bat"; // Same result with notepad.exe
batchProcess.StartInfo.WindowStyle = ProcessWindowStyle.Normal;
batchProcess.Start();
batchProcess.WaitForExit();
// Result: "Access is denied" error during Start().

3)
var otherProcessInfo = new ProcessStartInfo(@"file:///C:\Tools\demo.bat")
var otherProcess = Process.Start(otherProcessInfo);
otherProcess.WaitForExit();
otherProcess.Close();
// Result: "The system cannot find the file specified" despite it being the same path as in previous examples.
// Also tried literally using the path C:\Tools\demo.bat, without adding that to the C# project.
// One thing that slightly works is to use:
var otherProcessInfo = new ProcessStartInfo("cmd.exe", "/c echo Hello world!");
// This version opens a window and instantly closes it again. With "/c pause" instead, it opens, saying "press any key to continue".
// Chaining multiple commands with newline or semicolon characters doesn't work as a form of batch file.

So: the only tiny success I've had here is to run cmd.exe, to run a one-line command. I suppose that depending on what the batch file must do, there's some possibility of receiving a string, breaking it into lines, then running cmd.exe using method 3 to call them one at a time. Which is ugly at best.

Is there some better way to do this -- to run a batch file or an EXE from within my program?

EDIT: Yes, I did in fact look at documentation before asking. Why did I use URIs? Because of multiple errors telling me that the simple path strings ("C:\this\that") I was using were in an "Invalid URI format". Using Process.Start("notepad.exe") silently fails, doing nothing. Using a method involving System.Diagnostics.Process (found at How to run external program via a C# program? and yes I saw that before) fails with an error of "Access denied" when using my batch file reference, or silently failing (no window opens) using plain old notepad.exe. I avoided setting Process options that say hide the window.

So to rephrase: Is there a way to make my program run some EXE somewhere on the computer, or to run a batch file that has more than one command in it? What is that way?

Snow
  • 115
  • 6

1 Answers1

0

Using the data you collected, I was able to run a batch file by doing the following:

    var strPathToExeOrBat = System.IO.Path.Combine("C:\\Tools", "demo.bat");
    var otherProcessInfo = new ProcessStartInfo("cmd.exe", $"/c call \"{strPathToExeOrBat\"");
    var otherProcess = Process.Start(otherProcessInfo);
    otherProcess.WaitForExit();
    otherProcess.Close();

I also think it would be helpful to review the capabilities of the cmd.exe application. I found this post to be helpful: https://stackoverflow.com/questions/515309/what-does-cmd-c-mean#:~:text=%2FC%20Carries%20out%20the%20command%20specified%20by%20the%20string%20and,switches%20by%20typing%20cmd%20%2F%3F%20.

In particular the /k option will leave the window open, if you don't want it to close after running a script.

Thank you very much for your question! It really helped me find the answer to this! (at least for my situation of a .NET MAUI windows app, but MAUI is built off of Xamarin.Forms, so you shouldn't have a problem doing the same thing)

EDIT: Updated to use file path from question and string interpolation with System.IO.Path.Combine for slightly greater cross platform capability