I want to convert a pdf file into an html file, so that I can extract the values in a table.
pdftohtml.exe can do this.
If I call the following on a command prompt I get an html page with the content from the pdf file:
pdftohtml.exe test.pdf test.html
This works as expected. Now I want to invoke this exe via C#.
I did the following:
string filename = #"C:\Temp\pdftohtml.exe";
Process proc = Process.Start(filename, "test.pdf test.html");
Unfortunately this does not work. I suspect that somehow the parameters are not past to the exe correctly.
When I call this exe via the command line with -c before the parameters I get an error:
pdftohtml.exe -c test.pdf test.html
leads to an error (rangecheck in .putdeviceprops).
Does someone know how to correctly invoke this program?
You can use the following stuff,
using System.Diagnostics;
// Prepare the process to run
ProcessStartInfo start = new ProcessStartInfo();
// Enter in the command line arguments, everything you would enter after the executable name itself
start.Arguments = arguments;
// Enter the executable to run, including the complete path
start.FileName = ExeName;
// Do you want to show a console window?
start.WindowStyle = ProcessWindowStyle.Hidden;
start.CreateNoWindow = true;
// Run the external process & wait for it to finish
using (Process proc = Process.Start(start))
{
proc.WaitForExit();
// Retrieve the app's exit code
exitCode = proc.ExitCode;
}
Usually /C will be used to execute the command and then terminate. In the above code, do modifications as required.
Related
I'm trying to run a curl command from a C# program. My code is below. When I run the code below, I get an exception that the file is not found. I want to be able to do this but I do not want to use a batch file as a parameter for the filename. That is because the arguments for my curl command are variable based upon other conditions in the C# code. My variable strCmdText has the arguments for the curl command (the source and destination files). There are other examples of this on Stackoverflow, but they all use a batch file which I'm trying to avoid.
Process p = new Process();
p.StartInfo.UseShellExecute = false;
p.StartInfo.FileName = "C:\\Windows\\System32\\curl.exe";
p.StartInfo.Arguments = strCmdText;
p.StartInfo.UseShellExecute = false;
p.Start();
p.WaitForExit();
I changed my code to the following:
System.Diagnostics.ProcessStartInfo p = new
System.Diagnostics.ProcessStartInfo();
p.UseShellExecute = true;
p.WorkingDirectory = "C:\\Windows\\System32\\";
p.FileName = "curl.exe";
p.ErrorDialog = true;
p.CreateNoWindow = true;
System.Diagnostics.Process.Start(p);
From a DOS prompt, curl does exist in this directory. But I still get the curl not found message.
Something has to be strange with the path here. When I put a break point in though, and view the Environment class, System32 is in the path.
Curl is available at the location: C:\Windows\System32\curl.exe
That only leaves the source file to be the culprit of a "File not found" issue.
As you're launching curl through a process, ensure that your paths are escaped properly in your startup arguments.
Alternatively, you could launch curl through cmd (through a process), you can try with the following, changing the command-line arguments from --help to suit your desired action.
string script = $"\"C:\\Windows\\System32\\curl.exe\" --help";
Process process = new Process()
{
StartInfo = new ProcessStartInfo()
{
FileName = "cmd",
Arguments = script
}
};
process.Start();
Please note that this is in principle, essentially using a batch file as it's just throwing some commands into a cmd.
I had the exact same problem. Just delete curl.exe from System32 and place it on another folder (dont't forget the dependences, dlls, etc.).
Then in the line
p.StartInfo.FileName = "C:\\Windows\\System32\\curl.exe";
Overwrite "C:\\Windows\\System32\\curl.exe" to "C:\\NEW PATH\\curl.exe".
Note: You MUST delete it from System32. If you just copy to the new location it will still don't work.
I want to pass an image object from my c# project to my python script however from my understanding whatever there is in the arguments it is considered as string and also when I try type(passedImage) in python it identifies it as a string even if I try to put a number instead of the image variable.
ProcessStartInfo start = new ProcessStartInfo();
start.FileName = #"C:\Python\Python36\python.exe";
start.Arguments = string.Format("{0} {1}", #"C:\OCRonImage2.py", image );
start.UseShellExecute = false;
start.RedirectStandardOutput = true;
start.CreateNoWindow = true;
using (Process process = Process.Start(start))
{
}
When executing OCRonImage2.py manually, is it an image file location that you would pass as an argument? I would be surprise if you would pass in a stream from the command line. It is no surprise that attempting to put the entire image's bytes into an argument would create a string too long. But with the error you reported, I would also believe that the python script was expecting a file path to the image. However, if you look at that python code, I wouldn't be surprised if you find it using the filepath argument to open the file, probably using Image.open(filepath,mode=r). Mode is optional, r is the default.
You are in luck however, Image.open also takes a stream. If you are willing to modify the python code there are two options:
Try converting the argument to a stream object, since the argument is a string maybe use io.StringIO()
Use input() instead of the argument passed, then you could redirect the input of the process and stream the file into your python.
ProcessStartInfo start = new ProcessStartInfo();
start.FileName = #"C:\Python\Python36\python.exe";
start.Arguments = string.Format("{0}", #"C:\OCRonImage2.py");
start.UseShellExecute = false;
start.RedirectStandardOutput = true;
start.RedirectStandardInput = true;
start.CreateNoWindow = true;
using (Process process = Process.Start(start))
{
StreamWriter streamWriter = process.StandardInput;
streamWriter.Write({imageString});
// ...
}
Be sure the way you encode imageString the same as the decoding is performed in the python script.
Hopefully one of these solutions will work for you.
As I work with the Anaconda distribution of Python, in my tests on an isolated conda environment, the OCR is successful with pytesseract through a Python script, on a test image.
Prerequisites to test:
install Anaconda and create an env called py3.7.4: conda create --name py3.7.4
activate the env with conda activate py3.7.4
install pytesseract with conda install -c conda-forge pytesseract
create a folder called Test and place a jpg file called ocr.jpg with the following sample image:
in the same Test folder also place a Python script called ocr_test.py with the following code:
import pytesseract
from PIL import Image
import argparse
parser = argparse.ArgumentParser(
description='perform OCR on image')
parser.add_argument("--path", "-p", help="path for image")
args = parser.parse_args()
print(pytesseract.image_to_string(Image.open(args.path)))
print("done")
The above snippet accepts the image path as a command line argument. The --path flag must be specified in order to pass the image path as an arg.
Now, in the C# code snippet below, we will:
launch the cmd shell
navigate to the workingDirectory Test folder by specifying the WorkingDirectory arg for the process.start() method.
activate Anaconda with the anaconda.bat file(replace the file path as per its location on your computer)
activate the above conda environment
call the Python script passing the imageFileName as an arg.
C# snippet:
using System.Diagnostics;
using System.Threading;
namespace PyTest
{
class Program
{
static void Main(string[] args)
{
string workingDirectory = #"C:\Test";
string imageFileName = "ocr.JPG";
var process = new Process
{
StartInfo = new ProcessStartInfo
{
FileName = "cmd.exe",
RedirectStandardInput = true,
UseShellExecute = false,
RedirectStandardOutput = false,
WorkingDirectory = workingDirectory
}
};
process.Start();
using (var sw = process.StandardInput)
{
if (sw.BaseStream.CanWrite)
{
// Vital to activate Anaconda
sw.WriteLine(#"C:\Users\xxxxxxx\Anaconda3\Scripts\activate.bat");
Thread.Sleep(500);
// Activate your environment
sw.WriteLine("conda activate py3.7.4");
Thread.Sleep(500);
sw.WriteLine($"python ocr_test.py --path {imageFileName}");
Thread.Sleep(50000);
}
}
}
}
}
If you have followed the above steps, you should receive the following output on executing the C# snippet in Visual Studio:
Output:
Microsoft Windows [Version 10.0.18362.535]
(c) 2019 Microsoft Corporation. All rights reserved.
C:\xxxxxxx\Projects\Scripts>C:\Users\xxxxx\Anaconda3\Scripts\activate.bat
(base) C:\xxxxxx\Projects\Scripts>conda activate py3.7.4
(py3.7.4) C:\xxxxxxx\Projects\Scripts>python ocr_test.py --path ocr.JPG
Introduction
This is a test to see accuracy of Tesseract OCR
Test 1
Test 2
done
Note: I am unable to test with a standalone Python distro but I believe it should work just fine with that too. The key is to pass the image file path as an argument to the Python script too. That way, the image file path passed as argument from C# is treated similarly by Python too. Also, using Image.open() does the following(from the docs):
Opens and identifies the given image file. This is a lazy operation;
this function identifies the file, but the file remains open and the
actual image data is not read from the file until you try to process
the data
You can save the image as a file somewhere on your local machine and give the python program the path to read it.
That's the easiest way I think you can do.
Edited: You can use a temporary file to make sure the file can be deleted in the future
http://www.java2s.com/Tutorial/CSharp/0300__File-Directory-Stream/Createatempfileanddeleteit.htm
http://www.vcskicks.com/temporary-file.php
I think this will be bad to pass IMAGE as Argument.
Good options to go with:
Stdin on your python example, and RedirectStandardInput on your c#.
TCP Communication. Using TCP Services (No Internet Needed)
Sharing Memory. (More Info, ReadyLib)
I am trying to execute a command line prompt in unity using C#, I first need to change the directory to C://Users//HP//Documents then execute a command that should be under Documents, this is what I have been doing:
ProcessStartInfo proc = new ProcessStartInfo ("cmd.exe");
proc.UseShellExecute = false;
proc.RedirectStandardOutput = true;
proc.CreateNoWindow = true;
proc.RedirectStandardInput = true;
var process = Process.Start (proc);
process.StandardInput.WriteLine(#"cd C://Users//HP//Documents");
process.StandardInput.WriteLine ("MyCommandLine");
The MyCommandLine should create a text file under Documents, but instead Unity blocks every time I execute the function.
Can anyone help me with this please.
Your cmd.exe process never exits after running "MyCommandLine". Add an "exit" command like this
process.StandardInput.WriteLine("exit");
I am trying to execute a OS command through C#. I have the following code taken from this webpage:
//Execute command on file
ProcessStartInfo procStart =
new ProcessStartInfo(#"C:\Users\Me\Desktop\Test\System_Instructions.txt",
"mkdir testDir");
//Redirects output
procStart.RedirectStandardOutput = true;
procStart.UseShellExecute = false;
//No black window
procStart.CreateNoWindow = true;
//Creates a process
System.Diagnostics.Process proc = new System.Diagnostics.Process();
//Set start info
proc.StartInfo = procStart;
//Start
proc.Start();
but when I attempt to run the code I get the following error:
{"The specified executable is not a valid application for this OS platform."}
What am I doing wrong? I have tried this example as well but got the same issue.
The overload of the ProcessStartInfo constructor you are using expects an executable file name and parameters to pass to it - a .txt file is not executable by itself.
It sounds more like you want to execute a batch file with commands within the file. For that check this SO thread: How do I use ProcessStartInfo to run a batch file?
Try setting the USESHELLEXECUTE member to TRUE instead of FALSE.
It worked for me - but I think this has reprocussions for certain users after publishing.
You are trying to execute a TXT file. That's why you get
{"The specified executable is not a valid application for this OS platform."}
Because, well, the specified executable (TXT) is not a valid application for this OS platform.
You would target an executable or other file that has a specified opening application. You're targeting a text file; what you should do is target Notepad, and then supply the path to your text file as an argument:
ProcessStartInfo info = new ProcessStartInfo
{
FileName = "C:\\Windows\System32\\notepad.exe",
Arguments = "C:\\Users\\Me\\Desktop\\Test\\System_Instructions.txt"
}
new Process.Start(info);
Alternatively, if you mean for your text file to be executed, it needs to be made a .bat file.
You are trying to execute this:
C:\Users\Me\Desktop\Test\System_Instructions.txt mkdir testDir
The shell has no clue how to "execute" a text file so the command fails.
If you want to execute this text file as a batch file, change file extension to .bat so the system understands it's a batch file, and then set UseShellExecute so it does the default action for it (= runs it, in case of a batch file).
If you want to open up the file in Notepad, use:
ProcessStartInfo procStart =
new ProcessStartInfo("notepad.exe", #"C:\Users\Me\Desktop\Test\System_Instructions.txt");
If you want to write into the file :
//In case the directory doesn't exist
Directory.CreateDirectory(#"C:\Users\Me\Desktop\Test\);
using (var file = File.CreateText(#"C:\Users\Me\Desktop\Test\System_Instructions.txt"))
{
file.WriteLine("mkdir testDir");
}
If you have commands in the text file that you want to execute, just rename it to .bat and it should work (and presumably the contents do something with "mkdir testDir" as a parameter?)
What are you trying to accomplish?
Create a directory? Use the "System.IO.Directory.CreateDirectory" method.
Open a .txt file with associated program? Use ProcessStartInfo(#".\filename.txt") with UseShellExecute set to true. This will cause the associated program for that file type to be executed, which might not be notepad.txt.
I have a service that sometimes calls a batch file. The batch file takes 5-10 seconds to execute:
System.Diagnostics.Process proc = new System.Diagnostics.Process(); // Declare New Process
proc.StartInfo.FileName = fileName;
proc.StartInfo.WindowStyle = System.Diagnostics.ProcessWindowStyle.Hidden;
proc.StartInfo.CreateNoWindow = true;
proc.Start();
proc.WaitForExit();
The file does exist and the code works when I run the same code in-console. However when it runs inside the service, it hangs up at WaitForExit(). I have to kill the batch file from the Process in order to continue. (I am certain the file exists, as I can see it in the processes list.)
How can I fix this hang-up?
Update #1:
Kevin's code allows me to get output. One of my batch files is still hanging.
"C:\EnterpriseDB\Postgres\8.3\bin\pg_dump.exe" -i -h localhost -p 5432 -U postgres -F p -a -D -v -f "c:\backupcasecocher\backupdateevent2008.sql" -t "\"public\".\"dateevent\"" "DbTest"
The other batch file is:
"C:\EnterpriseDB\Postgres\8.3\bin\vacuumdb.exe" -U postgres -d DbTest
I have checked the path and the postgresql path is fine. The output directory does exist and still works outside the service. Any ideas?
Update #2:
Instead of the path of the batch file, I wrote the "C:\EnterpriseDB\Postgres\8.3\bin\pg_dump.exe" for the proc.StartInfo.FileName and added all parameters to proc.StartInfo.Arguments. The results are unchanged, but I see the pg_dump.exe in the process window. Again this only happens inside the service.
Update #3:
I have run the service with a user in the administrator group, to no avail. I restored null for the service's username and password
Update #4:
I created a simple service to write a trace in the event log and execute a batch file that contains "dir" in it. It will now hang at proc.Start(); - I tried changing the Account from LocalSystem to User and I set the admnistrator user and password, still nothing.
Here is what i use to execute batch files:
proc.StartInfo.FileName = target;
proc.StartInfo.RedirectStandardError = true;
proc.StartInfo.RedirectStandardOutput = true;
proc.StartInfo.UseShellExecute = false;
proc.Start();
proc.WaitForExit
(
(timeout <= 0)
? int.MaxValue : timeout * NO_MILLISECONDS_IN_A_SECOND *
NO_SECONDS_IN_A_MINUTE
);
errorMessage = proc.StandardError.ReadToEnd();
proc.WaitForExit();
outputMessage = proc.StandardOutput.ReadToEnd();
proc.WaitForExit();
I don't know if that will do the trick for you, but I don't have the problem of it hanging.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics;
namespace VG
{
class VGe
{
[STAThread]
static void Main(string[] args)
{
Process proc = null;
try
{
string targetDir = string.Format(#"D:\adapters\setup");//this is where mybatch.bat lies
proc = new Process();
proc.StartInfo.WorkingDirectory = targetDir;
proc.StartInfo.FileName = "mybatch.bat";
proc.StartInfo.Arguments = string.Format("10");//this is argument
proc.StartInfo.CreateNoWindow = false;
proc.Start();
proc.WaitForExit();
}
catch (Exception ex)
{
Console.WriteLine("Exception Occurred :{0},{1}", ex.Message,ex.StackTrace.ToString());
}
}
}
}
string targetDir = string.Format(#"D:\");//PATH
proc = new Process();
proc.StartInfo.WorkingDirectory = targetDir;
proc.StartInfo.FileName = "GetFiles.bat";
proc.StartInfo.Arguments = string.Format("10");//argument
proc.StartInfo.CreateNoWindow = false;
proc.Start();
proc.WaitForExit();
Tested,works clear.
What does the batch file do? Are you certain the process is getting launched with enough privs to execute the batch file? Services can be limited in what they are allowed to do.
Also make sure if you are doing something like usin the copy command to overwrite a file that you do something like:
echo Y | copy foo.log c:\backup\
Also, make sure you are using full paths for the batch commands, etc. If the batch file is launching a GUI app in some sort of "Console" mode, that may be an issue too. Remember, services don't have a "Desktop" (unless you enable the "interact with desktop") to draw any kind of windows or message boxes to. In your program, you might want to open the stdout and stderr pipes and read from them during execution in case you are getting any error messages or anything.
WebServices are probably executing as the IUSR account, or the anonymous account, which ever, so that might be an issue for you. If it works when you run it in console, that's just the first step. :)
I don't recall if System.Diagnostics. are available only in debug or not. Probably not, but some of them might be. I'll have to check up on that for ya.
Hope this gives you some ideas.
Larry
pg_dump.exe is probably prompting for user input. Does this database require authentication? Are you relying on any ENVIRONMENT variables that won't be present for the service? I don't know pg_dump but what are the other possible reasons it would prompt for input?
The next step I would take is to fire up the debugger, and see if you can tell what the program is waiting on. If you are expierenced at debugging in assembly, you may be able to get an IDEA of what's happening using tools like ProcExp, FileMon, etc.
Being a windows SERVICE, and not a web service, makes quite a bit of difference. Anyways, have you tried my suggestion of setting the "Allow Service to interact with desktop"?
If you are desperate, you might try launching cmd.exe instead of your batch file. Then, using the cmd.exe's cmd line parameters, you can have IT start the batch file. This would probably give you a cmd prompt window to view the actual output, if you turn on the interact with desktop.
For complete help on cmd.exe, just type cmd /? at any command prompt.
Larry
Here is the solution. The solution is not clear because I have changed so many time the code and now it's working!
I have tried to use a Account of User, and it's not what worked. Use LocalSystem. Here is the code that execute, mostly what Kevin gave me.
System.Diagnostics.Process proc = new System.Diagnostics.Process();
proc.StartInfo.FileName = fileName;
proc.StartInfo.RedirectStandardError = true;
proc.StartInfo.RedirectStandardOutput = true;
proc.StartInfo.UseShellExecute = false;
proc.Start();
proc.WaitForExit();
output1 = proc.StandardError.ReadToEnd();
proc.WaitForExit();
output2 = proc.StandardOutput.ReadToEnd();
proc.WaitForExit();
Thank you all, I'll up-vote everybody and accept Kevin since he helps me since the beginning. Very weird because it works now...
Daok, it looks as if the only thing you changed was the timeout period on the initial WaitForExit(). You need to be VERY careful of that. If something DOES hang your service, it will NEVER return (and well, pretty much work like it has been for you thus far.. heh), but it won't be good for the end users...
Now, perhaps that you know what's causing this to hang, you can debug it further and find the full solution...
That, or spin this off in some thread that you can monitor, and kill if it hangs too long.
Just my 2 cents worth, which usually isn't a whole lot. ;)