.net core can't open file from python script - c#

Someone i know has made a python script that checks validity of some files. Right now that only works local, but he wants it to work cloud based. He gave me the .exe file with a few other files that i can use. I'm using .net core 2 for my backend. I can start the .exe file just fine. But when the python code tries to open the config file he made, the program returns it cannot find the file.
try:
json_data=open('config.txt').read()
except FileNotFoundError:
sys.exit('FATAL ERROR: config.txt not found!') <--returns this
except Exception as e:
sys.exit('FATAL ERROR: {}'.format(e))
The file is in the same folder as the .exe. Whenever i just run it straight from windows it works perfect and it can find the file. But whenever i run the program from within my .net core webapp it gives back that error. I'm using visual studio 2017 for development and debugging.
Here is my c# code i got from a post on stackoverflow to run the exe:
public IActionResult Index(string cmd, string args)
{
try
{
ProcessStartInfo start = new ProcessStartInfo();
start.FileName = "Python\\Windows 64-bit\\mdqt.exe";
start.Arguments = string.Format("\"{0}\" \"{1}\"", cmd, args);
start.UseShellExecute = false;
start.CreateNoWindow = false;
start.RedirectStandardOutput = true;
start.RedirectStandardError = true;
using (Process process = Process.Start(start))
{
string result = "";
while (!process.HasExited)
{
while (!process.StandardOutput.EndOfStream)
{
result += process.StandardOutput.ReadLine();
}
while (!process.StandardError.EndOfStream)
{
result += process.StandardError.ReadLine();
}
}
return Ok(result);
}
}
catch (Exception e)
{
return Ok(e);
}
}
Could it be some file permissions issue? If so how can i fix it?
EDIT:
I have set the working directory using the following code:
start.WorkingDirectory = Path.Combine(hostingEnvironment.ContentRootPath.ToString(), "Python\\Windows 64-bit");
It now gives me an error it cannot find the folder. Yet when I put the link into the filemanager(and ofcourse delete 1 backslash everywhere), it directs me to the right folder. Could this be a permissions issue? The folder I'm linking to is within the project folder, so IIS should be able to reach that folder as it has permission to interact with the parent folders. What am I overlooking?

Related

Launching C# program from another C# program

Due to me having knowledge of launching apps I am aware that you have multiple ways of launching an application in C# .NET, but I'm running into a issue that occurs when attempting to launch a SDL2 application.
I have attempted the following using the Process class to:
Start the .exe file of the build.
Start the application using "cmd.exe /K" or "cmd.exe /c" followed by "exec" or "call" or "start" followed by "{path to file}" or "{path to batch file to launch the application}". Launching the application via a batch file and CMD works fine. But, whenever I attempt to even launch the application (even in a new instance of Command-Prompt launched from cmd.exe /? start cmd.exe ?params) it will yield no result.
What I can observe is that the application tries to open. It takes forever to launch into the Window mode (starting the 3D environment). After a timeout it will either, render a couple of frames of a blank window before closing or close immediately after opening the window.
So my question is, does anyone have succesfully made a launcher application for a SDL app written in C# .NET? Or knows a way to debug this behaviour? Because unfortunately, the app does not send out a error message and since SDL safely closes the application I can't observe a crash either.
Edit #1
I'm not doing anything fancy with parameters as there shouldn't be any. I already have another one functioning that launches a normal C# application as my launcher requires to open 2 programs. 1 SLD application, 1 COM:VBA controlling application.
Given:
string audioSpectrumProgram = "AudioSpectrum.exe";
string audioSpectrumBatchProgram = "AudioSpectrum.bat";
private void BtnLaunchPPTApp_OnClick()
{
//Powerpoint controlling application
pVBAApp = Process.Start(presenterProgram, $"\"{this.path}\" {this.audioFormatParams[0]} {((this.ckboxGenerate.Checked) ? "--create" : "")} lang={this.languageCodesParams[this.cboxLanguage.SelectedIndex]}");
}
Method 1:
private void BtnLaunchSDLApp_OnClick()
{
pVizualizer = Process.Start(audioSpectrumProgram); //file launched from local path (is correct)
}
Method 2:
pVizualizer = Process.Start(audioSpectrumBatchProgram); //file launched from local path (is correct)
Method 3:
ProcessStartInfo info = new ProcessStartInfo("cmd.exe");
FileInfo spectrumFileInfo = new FileInfo(audioSpectrumProgram);
if (spectrumFileInfo.Exists)
info.Arguments = $"/c \"{spectrumFileInfo.FullName}\"";
pVizualizer = Process.Start(info);
Method 4:
based on senario of method 3. You don't have to parse arguments using ProcessStartInfo.
pVizualizer = Process.Start($"cmd.exe /K call \"{spectrumFileInfo.FullName}\"") //to observe what happens to the application
Edit #2
Not affected by changing the UseShellExecute to true or false
private void btnOpenVisualizer_Click(object sender, EventArgs e)
{
FileInfo spectrumFileInfo = new FileInfo(audioSpectrumProgram);
ProcessStartInfo info = new ProcessStartInfo(spectrumFileInfo.FullName);
info.UseShellExecute = true;
pVizualizer = new Process();
pVizualizer.StartInfo = info;
pVizualizer.EnableRaisingEvents = true;
pVizualizer.Exited += new EventHandler(myProcess_Exited);
pVizualizer.Start();
}
private void myProcess_Exited(object sender, System.EventArgs e)
{
Console.WriteLine(
$"Exit time : {pVizualizer.ExitTime}\n" +
$"Exit code : {pVizualizer.ExitCode}\n"
);
}
A general way of analyzing startup issues is to use SysInternals Process Monitor.
Record the application that is not starting up properly. Use a filter for your application. Then go through all items which don't have SUCCESS in the result column. Typically you want to do that bottom-up, since the last error is the one stopping your application from loading.
Like this you'll find common startup issues like:
missing DLLs or other dependencies
old DLLs or DLLs loaded from wrong location (e.g. registered COM components)
wrong working directory, e.g. access to non-existent config files
Ok For Future reference:
Pathing to the files can be correct and everything might be in order but if you are using DLLs for imports. Change the process's working directory.
The project will run, libs can "sometimes" be found but can cause a weird unknown bug like this one. So the most optimal way of running another C# instance with SDL or any other kind of library:
private void RunSDLProgram()
{
FileInfo spectrumFileInfo = new FileInfo("pathToFile.exe");
ProcessStartInfo info = new ProcessStartInfo(spectrumFileInfo.FullName);
info.RedirectStandardOutput = true;
info.RedirectStandardError = true;
info.UseShellExecute = false;
info.WorkingDirectory = spectrumFileInfo.DirectoryName;
pVizualizer = new Process();
pVizualizer.StartInfo = info;
pVizualizer.EnableRaisingEvents = true;
pVizualizer.Exited += new EventHandler(myProcess_Exited);
pVizualizer.Start();
}
private void myProcess_Exited(object sender, System.EventArgs e)
{
Console.WriteLine(
$"Exit time : {pVizualizer.ExitTime}\n" +
$"Exit code : {pVizualizer.ExitCode}\n" +
$"output : {pVizualizer.StandardOutput}\n" +
$"err : {pVizualizer.StandardError}\n"
);
}
Running a batch file will look at it's own directory and makes all references local, but it won't alter the working directory. (already had my suspicions about changing the work directory but I didn't see a way to call 2 opperations in process.start("cmd.exe");)

File copy started from aspnet webforms and implemented using batch, pstools and robocopy doesn't work

I have 4 independent servers (not in domain):
IIS, SQL1, SQL2, SQL3
I want to copy a database backup from SQL1 to SQL2 or SQL3 (depending on parameters) by button click on webpage hosted on IIS
I wrote a button click method for that, which is calling batch file located in inetpub folder on IIS
Batch is using pstools to run robocopy on SQL1 which should copy required file to destination server (SQL2 or SQL3)
This solution works if I execute batch directly on IIS (cmd as Administrator) or when I debug it on my local machine, but it doesn't if it is called from the running site.
It even doesn't spend any time between the following lines:
batchProcess.Start();
batchProcess.WaitForExit();
Here is my copy method:
private bool ProcessCopy(string file, string destinationIp)
{
SecureString password = ConvertToSecureString("myPassword");
try
{
string batchPath = Server.MapPath(".") + "\\CopyFile.bat";
string cmd = #"c:\Windows\System32\cmd.exe";
ProcessStartInfo processInfo = new ProcessStartInfo
{
FileName = cmd,
UseShellExecute = false
};
Process batchProcess = new Process {StartInfo = processInfo};
batchProcess.StartInfo.Arguments = $"/C {batchPath} {file} {destinationIp}";
batchProcess.StartInfo.Domain = "";
batchProcess.StartInfo.UserName = "Administrator";
batchProcess.StartInfo.Password = password;
batchProcess.StartInfo.RedirectStandardOutput = true;
batchProcess.StartInfo.RedirectStandardError = true;
batchProcess.StartInfo.CreateNoWindow = true;
batchProcess.Start();
batchProcess.WaitForExit();
string response = batchProcess.StandardOutput.ReadToEnd();
response += batchProcess.StandardError.ReadToEnd();
statusStringAppend($"response: {response}");
return true;
}
catch (Exception ex)
{
statusStringAppend($"Failed: {ex.Message}. {ex.StackTrace}");
}
return false;
}
Batch body is:
#echo off
c:\qa\tools\pstools\psexec64.exe -accepteula -u Administrator -p myPassword \\SourceIP robocopy \\SourceIP\qa\db_backup\ \\%2\qa\db_backup\ %1 /is
My questions are:
1. Why the file was not copied?
2. Is there any better way to get it copied?
CODE UPDATED ACCORDING TO SUGGESTIONS BELOW
My guess is that you never executed pstools as the user that your IIS service is running as before, so the EULA dialog is blocking your execution.
If you remember, you always got a window and needed to press the accept button when running any sysinternals tool like pstools the first time.
I guess this should work for you:
c:\qa\tools\pstools\psexec64.exe -accepteula -u Administrator -p myPassword \\SourceIP robocopy \\SourceIP\qa\db_backup\ \\%2\qa\db_backup\ %1 /is
[EDIT]
You would most likely have hit this problem later on, anyway it did not work for you, so i have to list what else could be wrong with your code:
starting a .bat file needs cmd.exe as mother process, you cannot just start a .bat file as process directly. Instead you can for example use another method than ProcessStartInfo that spawns the system default script interpreter automatically: Executing Batch File in C#
the process for executing batch files is "cmd.exe", first parameter "/C", second parameter the batch file you are executing
when executing typical commandline tools, you might consider reading the SDTOUT (standard output) of the process you are executing, like this: Capturing console output from a .NET application (C#)

What are relative paths relative to?

Consider the following statement in a C# console application:
Process.Start("3rdParty/SomeTool.exe");
This statement starts SomeTool.exe in the 3rdParty folder relative to... what, exactly? The folder where the application's .exe resides? The current working directory (which can be changed during the application's lifetime)? Something else?
It is relative to the current working directory of your process.
You can determine your current working directory using Directory.GetCurrentDirectory() and change it using Directory.SetCurrentDirectory().
Well, why don't we find out?
Let's create a simple console application and have some fun with it:
namespace ConsoleApplication1
{
class Program
{
public static void Main()
{
Directory.CreateDirectory("Test");
Console.WriteLine($"Absolute path is: { Path.GetFullPath("Test")}");
Console.ReadLine();
}
}
}
Build it in release mode (we are deploying it after all) and put it in some accesible location. Now double click on it and see what output you get.
Absolute path is: {SomeAccesibleLocationPath}\Test
Hmmm, it seems like the relative path is relative to the directory where the executable was launched. Is this always so?
Let's build another app, we'll call it ConsoleApplication2, and play some more:
class Program
{
public static void Main()
{
Console.WriteLine($"TEST #1: {LaunchProcessAndGetAbsolutePath()}");
Console.WriteLine($"TEST #2: {LaunchProcessAndGetAbsolutePath(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments))}");
Console.ReadLine();
}
private static string LaunchProcessAndGetAbsolutePath(string workingDirectory = null)
{
var startInfo = new ProcessStartInfo(#"{SomeAccesibleLocationPath}\ConsoleApplication1.exe");
startInfo.RedirectStandardOutput = true;
startInfo.RedirectStandardInput = true;
startInfo.CreateNoWindow = true;
startInfo.UseShellExecute = false;
if (workingDirectory != null)
{
startInfo.WorkingDirectory = workingDirectory;
}
using (var p = Process.Start(startInfo))
{
var ret = p.StandardOutput.ReadLine();
p.StandardInput.WriteLine();
p.WaitForExit();
return ret;
}
}
}
If we run this, you'll see that the output is the following:
TEST #1: {MyConsoleApplication2ExecutableDirectory}\Test
TEST #2: {MyDocumentsPath}\Test
Important facts to consider:
The relative paths are always relative to the working directory.
When starting a process from another process, the default working directory is the working directory of the "parent" process, its not the directory of the launched "child" process.
When launching a process by double clicking on the executable, the working directory is set to the executable's directory.
In general, the working directory need not be the executable's directory. Your program's correctness should not rely on this condition ever.
As mentioned, it is relative to the current working directory. The value can be changed and determined via Directory.SetCurrentDirectory(), resp. Directory.GetCurrentDirectory()
Some notes:
The current working directory CAN be the folder where the executable resides; this is the default working directory if none is specified. But it can also be set to a different folder when a process is started or while running. On start either by setting the property ProcessStartInfo.WorkingDirectory when launching a process, or also by using the working directory field in the symbolic link dialog in e.g. the start menu. The latter can be done by a user. The current working directory CAN also be changed by any 3rd party library that is being loaded into the process.
Therefore relative paths in an application without checking the working directory are unreliable and usually cause unexpected behavior.

C# Run command prompt from resources

I have a file that i need to run by creating a command prompt.
Basically this is how it goes:
The file's name is UArtCMD.exe, i use it by opening a command prompt in it's directory and run commands that are programmed into it, for example "uartcmd a". What i'm trying to do is add a gui to this software externally, and when, let's say, a button is pressed on the gui, it will run a command using the command prompt.
I've managed to make it run off my computer without any hitches, but the problems occur occur when i install the software on a different computer. Because the UArtCMD is an external software, it wont be on every computer, so what i tried doing is adding it to the resources of the project and run it from there, the problem is i have no idea how to do so.
This is my code for running the command prompt on the computer which has the UArtCMD
public static string executeLine(string command)
{
string result;
try
{
Process process = Process.Start(new ProcessStartInfo("cmd.exe", "/c " + command)
{
CreateNoWindow = true,
UseShellExecute = false,
WorkingDirectory = "C:\\UArtCMD",
RedirectStandardError = true,
RedirectStandardOutput = true
});
process.WaitForExit();
string text = process.StandardOutput.ReadToEnd();
process.Close();
MainWindow.log(text);
result = text;
}
catch (Exception ex)
{
result = ex.StackTrace;
}
return result;
}
Thanks in advance!
If you need me to add any info or expand more on the question do let me know i will gladly do so.
Include a reference to the file UArtCMD.exe in your solution (can be anywhere in the solution structure).
Set its "Copy to Output Directory" property to "Always".
Set your process' "WorkingDirectory" property to
"Environment.CurrentDirectory".
Now UArtCMD.exe should always find it's way into the bin directory (even if it's built by a build agent into a strange output directory) and the code will know to look for it in the same directory as the code is running from.
Edit: The comment about removing the "WorkingDirectory" property from your process object is valid, you can do that instead of setting it to "Environment.CurrentDirectory", they both do the same thing.

Overwrite restricted application file?

I'm trying to manually patch my application. The application makes use of a Service which i make sure to stop and uninstall prior to attempting any overwriting of the application dll's.
The issue is that i can't overwrite, or even delete some of the dll files which are the core of the application, these dll files are used by the service i uninstalled first.
I use the following method to pass in the new file-path in order to replace the old DLL which is located inside the root directory of the application in C:\Program Files\AppName\
public static bool CopyFile(string newFile, string oldFile)
{
var newfile = new FileInfo(newFile);
var oldfile = new FileInfo(oldFile);
var f2 = new FileIOPermission(FileIOPermissionAccess.AllAccess, oldFile);
f2.AddPathList(FileIOPermissionAccess.Write | FileIOPermissionAccess.Read, newFile);
try
{
f2.Demand();
}
catch (SecurityException s)
{
Console.WriteLine(s.Message);
}
for (int x = 0; x < 100; x++)
{
try
{
File.Delete(oldfile.FullName);
newfile.CopyTo(oldfile.FullName, true);
return true;
}
catch
{
Thread.Sleep(200);
}
}
return false;
}
I just wish to provide a new file and remove the old one, replace it, overwrite it.... The application
Note: The application i run to do the patching runs as administrator.
Any idea?
I was able to fix this issue by making use of a "middle man" in other words, another application which downloads another executable and passes command line arguments to it.
Originally, my service would download an executable (call it Installer.exe). Installer.exe would then attempt to stop the service and patch the content, this did not work.
I now have the service running, it downloads "Installer.exe".
Installer.exe will load up and download PatchPayload.exe.
PatchPayload.exe runs and kills off the Service, uninstalls it and then download all required patch content from a centralized server and patch the service core files individually then install the service and run it again.

Categories

Resources