Run bat file with UWP C# - c#

I'm trying to build UWP app and I'm struggling with an issue:
I have noticed there is now way to launch external executable, but I can open file with its default application using LaunchFile (Like mentioned here https://learn.microsoft.com/en-us/uwp/api/Windows.System.Launcher#Windows_System_Launcher_LaunchFileAsync_Windows_Storage_IStorageFile_).
Should it work for a .bat file too? Where should i place the .bat file if it is.? Because i tried to give a path to a .bat file i wrote and I got an exception.
Thanks!!

Its not going to work. If you can get some 3rd party application to open and handle bat files by default then maybe, but otherwise nothing the OS handles like cmd or vbscript will run. Not sure what your goal is, but a 3rd party scripting app like Autohotkey does work.

From .NET 4 there is and Process and ProcessStartInfo
classes under the System.Diagnostics namespace which allow you
to Win32 applications from C#.
public static Task<bool> ExecuteBatchFile(string BatchFilePath, string BatchFileDirectory)
{
try
{
Task<bool> executeBatchFileTask = Task.Run<bool>(() =>
{
bool hasProcessExited = false;
ProcessStartInfo startInfo = new ProcessStartInfo()
{
FileName = BatchFilePath,
CreateNoWindow = false,
UseShellExecute = true,
WindowStyle = ProcessWindowStyle.Normal,
WorkingDirectory = BatchFileDirectory
};
// Start the process with the info we specified.
// Call WaitForExit and then the using-statement will close.
using (System.Diagnostics.Process exeProcess = System.Diagnostics.Process.Start(startInfo))
{
while (!exeProcess.HasExited)
{
//Do nothing
}
hasProcessExited = true;
}
return hasProcessExited;
});
return executeBatchFileTask;
}
catch (Exception ex)
{
throw;
}
}
And call this method in your code by:
bool hasBatchFileExecuted = await ExecuteBatchFile(BatchFilePath, BatchFilePathDirectory);
Create your own batch file like:
1) Create a text file in notepad and save it as run.bat under save as category and all files.
2) Write the following code to execute a C# program/However you can write your own commands also:
csc someRandomCode.cs
pause
3) Save and quit.

Related

Trying to automatically rebuild and restart .NET console application on linux

I'm making a small webserver .NET console application on my windows machine that will run on a linux server (amazon os), and I'd like to make it automatically update itself when I push new code to github.
Automatically pulling from github already works by waiting for a webhook and then creating a Process which, as I understand it, practically lets me run a command line from the code.
using System.Diagnostics;
public static class GitApi
{
public static void Pull(string path)
{
Process process = new Process()
{
StartInfo = new ProcessStartInfo()
{
FileName = "git",
Arguments = "pull",
UseShellExecute = false,
RedirectStandardOutput = true,
WorkingDirectory = path
}
};
process.Start();
}
}
What I'd then like to do is to automatically rebuild and restart after pulling from git, by using the dotnet run command in a similar way.
using System.Diagnostics;
public static class Restarter
{
public static void Restart(string path)
{
Process process = new Process()
{
StartInfo = new ProcessStartInfo()
{
FileName = "dotnet",
Arguments = "run",
UseShellExecute = false,
WorkingDirectory = path
}
};
process.Start();
Environment.Exit(0);
}
}
While this seems to work fine on windows, when I try on linux it seems to exit the main process and then start the new one in the background somehow. It still writes in the same screen, but I can't kill it with Ctrl + C. Instead I have to find the process by checking the port that it's using and then use the kill command.
Could I fix this somehow or is there another simple way to rebuild and restart a .NET console application?

System.Diagnostics.Process locks the files when Process.UseShellExecute=false used

I have found a lock file issue with System.Diagnostics.Process when it is running several threads from the console application.
For example, I run two command-line applications (App1, App2) using the code below and pass two different files for each app (App1 -> File1, App2 -> File2)
Sometimes, the App1 -> File1 finish processing the file, but for an unknown reason, File1 is locked by the App2 app.
It is not possible to read the File1 file until App2 exits, through File1 was executed with App1 app in the first System.Diagnostics.Process instance.
This problem only happens when Process.StartInfo.UseShellExecute is set to False. I need to disable shell execute to be able to read Process.StandardError property if execution fails.
Any idea what is going on? Is the code below thread-safe?
using (var p = new Process
{
StartInfo =
{
FileName = "c:\commandLine.exe",
Arguments = "c:\file.jpg",
UseShellExecute = false,
RedirectStandardError = true,
RedirectStandardOutput = true
}
})
{
try
{
p.Start();
p.PriorityClass = ProcessPriorityClass.Normal;
p.WaitForExit();
var output = await p.StandardOutput.ReadToEndAsync();
var error = await p.StandardError.ReadToEndAsync();
var exitCode = p.ExitCode;
return .....;
}
finally
{
try
{
if (!p.HasExited)
p.Kill();
}
catch (InvalidOperationException)
{
//The process is not found, probably executable was not fount
}
p.Close();
}
}

How do i execute an external exe using a specific user

Im making an application which needs to monitor the filesystem using FileSystemWatcher, to detect how an installation affects the filesystem.
To get rid of noise i want to filter the events that are created by their creating user, and that code is working with the //BUILTIN //Administrator user, which is used by default when doing an installation. But still there are quite a bit of noise. Then i got the idea of creating a specific user that i can use for running the installation file, and filter on that specific user, and thereby getting rid of allmost all the noise.
this is my code for the process creation and start
private void executeInnoInstaller(string path, string fileName)
{
// Use ProcessStartInfo class
ProcessStartInfo installerProces = new ProcessStartInfo();
installerProces.CreateNoWindow = true;
installerProces.UseShellExecute = false;
installerProces.FileName = path + "\"" + fileName + "\"";
installerProces.WindowStyle = ProcessWindowStyle.Normal;
installerProces.UserName = "test";
System.Security.SecureString encPassword = new System.Security.SecureString();
foreach (System.Char c in "test")
{
encPassword.AppendChar(c);
}
encPassword.MakeReadOnly();
installerProces.Password = encPassword;
try
{
// Start the process with the info we specified.
// Call WaitForExit and then the using statement will close.
using (Process exeProcess = Process.Start(installerProces))
{
exeProcess.WaitForExit();
//int exitCode = exeProcess.ExitCode;
}
}
catch (Exception e)
{
Console.WriteLine(e);
}
}
this code exits with a access denied.
OS=Windows
Ive already tried to run the installer.exe from the OS filehandler with SHIFT - Rightclick using the specified user, and it works.
VisualStudio is run as administrator.
Ive tried to run the build project exe file as administrator, but it does not work.
Without the user credentials, the code works and uses the //BUILTIN //Administrator account
Does anybody have any idea ?
Thank you beforehand for your time and effort.
This code works if i turn down the UAC securitylevel to the lowest.

How do I call a python script from a network drive using c#

I'm calling a python script from a C# tool. I based my code on this post. I use ProcessStartInfo to start python and pass a .py file and some argument to it. The code runs fine when the .py, CreateAssessorMap.py, file is on the c drive but not when it is on a mapped network drive. No error is thrown but no python code is executed as far as I can see. If I manually do the same operation from the command line it runs fine.
The code below the first procStartInfo.Arguments will fail as CreateAssessorMap.py is on a network drive. The commented out line below it would work as the script is on the C drive.
private void btnPrint_Click(object sender, EventArgs e)
{
try
{
ProcessStartInfo procStartInfo = new ProcessStartInfo();
procStartInfo.FileName = "python";
procStartInfo.Arguments = string.Format("{0} {1} {2}", #"D:\Map_Generate_Scripts\CreateAssessorMap.py", this.txtSheet.Text, txtDestinationFolder.Text);
//procStartInfo.Arguments = string.Format("{0} {1} {2} ", #"C:\Projects\Map_Generate_Scripts\CreateAssessorMap.py", this.txtSheet.Text, txtDestinationFolder.Text);
procStartInfo.UserName = null;
procStartInfo.UseShellExecute = false;
procStartInfo.RedirectStandardOutput = true;
// Do not create the black window.
procStartInfo.CreateNoWindow = true;
// Now you create a process, assign its ProcessStartInfo, and start it.
using (Process process = Process.Start(procStartInfo))
{
using (System.IO.StreamReader reader = process.StandardOutput)
{
string result = reader.ReadToEnd();
Console.Write(result);
}
}
}
catch (Exception ecpt)
{
Console.WriteLine(ecpt.Message);
}
this.Parent.Hide();
}
Edit
I added some error handling and the python code is failing with the message that the .py file cannot be found.
python: can't open file 'D:\Map_Generate_Scripts\CreateAssessorMapCreateAssessorMap.py': [Errno 2] No such file or directory
I know it exists since I can run the file from the command line with the same arguments. So it seems that when I run from C# the python process can't find the d drive.
I assume that when it runs from a network drive it takes longer and your program does not wait for Python script's completion. I suggest adding proces.Wait() before reading the output stream.
As stated in the comments, the solution for Dowlers was to use the full path instead of the mapped network drive.
Change
#"D:\Map_Generate_Scripts\CreateAssessorMap.py"
To
#"\\[network path]\Map_Generate_Scripts\CreateAssessorMap.py"

Powerpoint Process works in standalone console project, but not integrated project

I'm trying to run a local process using C# to call Powerpoint and convert a .pdf to a .ppt.
I made a standalone console app, in hopes of reproducing and isolating the problem.
Unfortunately, it works for the standalone version, but not with the integrated version.
When it doesn't work, it throws no exceptions. It just silently fails to create the .pdf file.
NEW:
I'm getting an error on the event log:
Microsoft PowerPoint
PowerPoint can't do this because a dialog box is open. Please close the dialog box to continue.
P1: 400205
P2: 15.0.4420.1017
I don't see any sort of dialog box when running the console commands, the standalone console application, or running the integrated web project on my local machine.
The /pt command is supposed to be silent, as per the official documentation.
I can set the Identity of the ApplicationPool the project is running under to the user that I log in as, and I no longer get the above error in the event log. However, I get no other errors (that I can tell are related) from the event log.
It still doesn't work, however. Powerpoint or PDFCreator still crashes, and doesn't create the .pdf.
I also tried to run my working console app by calling it as a Process from my integrated issue, and that didn't work either.
The working console application:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System;
using System.Diagnostics;
using System.Text;
namespace ConsoleApplication3
{
class Program
{
static void Main(string[] args)
{
var psi = new ProcessStartInfo();
psi.FileName = "\"C:\\Program Files\\Microsoft Office\\Office15\\POWERPNT.exe\"";
psi.Arguments = "/pt \"PDFCreator\" \"\" \"\" dd0e03ff-f386-4e65-b89d-72c7f1ee502d.pptx";
psi.WorkingDirectory = "C:\\Temp";
psi.CreateNoWindow = true;
psi.ErrorDialog = true;
psi.UseShellExecute = false;
psi.WindowStyle = ProcessWindowStyle.Hidden;
psi.RedirectStandardOutput = true;
psi.RedirectStandardInput = false;
psi.RedirectStandardError = true;
try
{
using (Process exeProcess = Process.Start(psi))
{
exeProcess.PriorityClass = ProcessPriorityClass.High;
var outString = new StringBuilder(100);
exeProcess.OutputDataReceived += (s, e) => outString.AppendLine(e.Data);
exeProcess.BeginOutputReadLine();
var errString = exeProcess.StandardError.ReadToEnd();
if (!string.IsNullOrEmpty(errString))
{
Console.WriteLine("errors reported 1");
}
}
}
catch (Exception ex)
{
ex.ToString();
Console.WriteLine("Errors reported 2 ");
Console.WriteLine(ex.ToString());
}
Console.WriteLine(psi.FileName);
Console.WriteLine(psi.Arguments);
Console.WriteLine(psi.WorkingDirectory);
}
}
}
It prints
"C:\Program Files\Microsoft Office\Office15\POWERPNT.exe"
/pt "PDFCreator" "" "" dd0e03ff-f386-4e65-b89d-72c7f1ee502d.pptx
C:\Temp
The not working integrated application:
using System;
using System.Diagnostics;
using System.Text;
namespace CT.Services.Helper
{
public static class ExecutableRunner
{
public static ExecutableResult RunExeNamed(string exeFilename, string commandLineArgs)
{
return RunExeNamed(exeFilename, commandLineArgs, null);
}
public static ExecutableResult RunExeNamed(string exeFilename, string commandLineArgs, string workingDirectory)
{
var result = new ExecutableResult { WasSuccessful = true };
var psi = new ProcessStartInfo();
psi.FileName = "\""+exeFilename+"\"";
psi.Arguments = commandLineArgs;
if(!string.IsNullOrEmpty(workingDirectory))
{
psi.WorkingDirectory = workingDirectory;
}
psi.CreateNoWindow = false;
psi.ErrorDialog = true;
psi.UseShellExecute = false;
psi.WindowStyle = ProcessWindowStyle.Hidden;
psi.RedirectStandardOutput = true;
psi.RedirectStandardInput = false;
psi.RedirectStandardError = true;
try
{
// Start the process with the info we specified.
// Call WaitForExit and then the using statement will close.
using (Process exeProcess = Process.Start(psi))
{
exeProcess.PriorityClass = ProcessPriorityClass.High;
var outString = new StringBuilder(100);
// use ansynchronous reading for at least one of the streams
// to avoid deadlock
exeProcess.OutputDataReceived += (s, e) => outString.AppendLine(e.Data);
exeProcess.BeginOutputReadLine();
// now read the StandardError stream to the end
// this will cause our main thread to wait for the
// stream to close (which is when ffmpeg quits)
var errString = exeProcess.StandardError.ReadToEnd();
if (!string.IsNullOrEmpty(errString))
{
result.WasSuccessful = false;
result.ErrorMessage = errString;
}
}
}
catch (Exception ex)
{
result.WasSuccessful = false;
result.ErrorMessage = ex.ToString();
}
Debug.WriteLine(psi.FileName);
Debug.WriteLine(psi.Arguments);
Debug.WriteLine(psi.WorkingDirectory);
return result;
}
}
public class ExecutableResult
{
public bool WasSuccessful { get; set; }
public string ErrorMessage { get; set; }
}
}
it prints
"C:\Program Files\Microsoft Office\Office15\POWERPNT.exe"
/pt "PDFCreator" "" "" dd0e03ff-f386-4e65-b89d-72c7f1ee502d.pptx
C:\Temp
I would think that one of the strings I'm using as the file path or console command would be wrong, so I had them printed to the console, but you can see that those aren't the issue.
Last try ;-)
You could use Interop, open the file and afterwards save it as pdf.
For this little example to work you need to reference the Microsoft.Office.Interop.PowerPoint assembly and the Microsoft.Office.Core namespace (found in the "Microsoft Office 14.0 Object Library" assembly which is located under the COM assemblies).
Little Example:
/// <summary>
/// Converts the specified source file and saves it as pdf with the
/// specified destination filename.
/// </summary>
/// <param name="sourceFilename">The filename of the file to be converted into a pdf.</param>
/// <param name="destinationFilename">The filename of the pdf.</param>
/// <exception cref="System.IO.FileNotFoundException">Is thrown if the specified source file does not exist.</exception>
/// <exception cref="System.Exception">Is thrown if something went wrong during the convertation process.</exception>
public static void SaveAsPDF(string sourceFilename, string destinationFilename)
{
if (!File.Exists(sourceFilename))
{
throw new FileNotFoundException(string.Format("The specified file {0} does not exist.", sourceFilename), sourceFilename);
}
try
{
Microsoft.Office.Interop.PowerPoint.Application app = new Microsoft.Office.Interop.PowerPoint.Application();
app.Presentations.Open(sourceFilename).SaveAs(destinationFilename, Microsoft.Office.Interop.PowerPoint.PpSaveAsFileType.ppSaveAsPDF);
app.Quit();
}
catch (Exception e)
{
throw new Exception(string.Format("Unable to convert {0} to {1}", sourceFilename, destinationFilename), e);
}
}
Could it be a problem with the permissions of the application pool? Maybe it does not have permissions to perform all the actions you want to - you could check that by executing your console application under the same context as the application pool is running.
Just another idea (not sure if it is a stupid one or not). As far as I remember powerpoint (at least since 2010) is able to save directly to pdf. For 2007 you can use a microsoft plugin called "Save as PDF" which can be found here 2007 Microsoft Office Add-in: Microsoft Save as PDF or XPS.
Using the provided Solution from Todd Main you could (theoretically) execute a macro at startup which could store the file directly as PDF (without printing and hopefully without dialog box).
Update:
I've tried it right now - problem is you have to store the macro in the presentation, therfore you have to use the pptm file format. Next you have to open the file once and activate the macro execution (is shown on the top of the editing screen). After that you can simply execute it from the command line like
POWERPNT.EXE /M Filename.pptm SaveAsPDF
The macro looks like this:
Sub SaveAsPDF()
Dim ap As Presentation: Set ap = ActivePresentation
ap.SaveAs ap.Path & "\" & ap.Name, ppSaveAsPDF
PowerPoint.Application.Quit
End Sub
The integrated version of the code is part of a web application
Provide access to the current working directory where this file is being created for the app pool user and verify it has Read/Write.
Check the Temp directory on the server for the app-pool process user and verify that the process has read/write access to the temp directory.
Edit Upon seeing the event log.
Asp.net should not be using any application which is a user interactive process. You need to find a different application to create these powerpoint slides that is asp.net friendly or determine what Powerpoint is trying to do when it opens a dialog.
Odds are you are seeing this problem because you've never actually opened up Powerpoint under the user that is running the application in the environment where it's failing.
This will be the user that the application pool is running under and, as I see you've mentioned to OmegaMan, that user is LocalSystem.
When LocalSystem tries to open Powerpoint, then Powerpoint will be doing that thing that all Office applications do where they complete their install the first time a new user runs them.
You are not seeing this installation box in your console app or on your local system because you're running those under your own account and you've previously opened PowerPoint.
As OmegaMan rightly says Powerpoint is meant to be an interactive process so it is probably going to cause trouble even if you're very very careful. You'll end up with instances of Powerpoint staying open after they're supposed to have closed and loads of instances building up in the
That said, if you really want to get this running then you either need to get the application pool running as a user that has opened Powerpoint interactively, or change your code to use impersonation at the point where you need to run Powerpoint and then impersonate a user that has used it interactively to do what you need.

Categories

Resources