I'm attempting to build a java process to execute the docusign retrieve product via the command line. I've written the process to execute based on a given property file.
buildRoot = isWindowsOs() ? "C:" + "\\Program Files (x86)\\DocuSign, Inc\\Retrieve 3.2" : "\\Program Files (x86)\\DocuSign, Inc\\Retrieve 3.2" ;
String[] command = new String [2];
command[0] = "\""+buildRoot+ "\\" + docuSignAppName+"\"";
logger.info(command[0].toString());
//ADDED FOR EXPLANATION - "C:\Program Files (x86)\DocuSign, Inc\Retrieve 3.2\DocuSignRetrieve.exe"
command[1] = arguments;
logger.info(command[1].toString());
ProcessBuilder processBuilder = new ProcessBuilder(command);
logger.info("ProcessBuilder starting directory" +processBuilder.directory());
processBuilder.redirectErrorStream(true);
p = processBuilder.start();
InputStream is = p.getInputStream();
InputStreamReader isr = new InputStreamReader(is);
stdout = new BufferedReader(isr);
Once I pass in the built out string of parameters the executed code looks like the sample provided but always results in the error back to the screen "Missing "accountid" parameter".
The parameter list looks like the following.
/endpoint "Demo"
/userid "REMOVED"
/password "REMOVED"
/accountid "REMOVED"
/span "-1"
/spanfilter "Completed"
/statusfilter "Completed"
/fieldlog "LIST OF FIELDS"
/nstyle "EnvelopeID"
/save "MergedPdfWithoutCert"
/dir "D:\DocuSignStore"
/includeheaders "true"
Any help or assistance would be appreciated.
The solution was found in a StackOverflow discussion regarding common issues with the ProcessBuilder.
My problem was that I expected by changing the putting in the full path, that I could run the executable. For the reason I'm not sure right now, that wasn't working as expected. The solution was to run the CMD command which exists on the %PATH% on any windows OS.
String[] command = new String [2];
command[0] = "\""+buildRoot+ "\\" + docuSignAppName+"\"";
logger.info(command[0].toString());
//ADDED FOR EXPLANATION - "C:\Program Files (x86)\DocuSign, Inc\Retrieve 3.2\DocuSignRetrieve.exe"
command[1] = arguments;
logger.info(command[1].toString());
//This starts a new command prompt
ProcessBuilder processBuilder = new ProcessBuilder("cmd","/c","DocusignRetreive.exe);
//This sets the directory to run the command prompt from
File newLoc = new File("C:/Program Files (x86)/DocuSign, Inc/Retrieve 3.2");
processBuilder.directory(newLoc);
logger.info("ProcessBuilder starting directory" +processBuilder.directory());
processBuilder.redirectErrorStream(true);
/*When the process builder starts the prompt looks like
*C:\Program Files (x86)\DocuSign, Inc\Retrieve 3.2
*Now DocusignRetrieve.exe is an executable in the directory to be run
*/
p = processBuilder.start();
Related
When I try to update Windows features; When I update UseShellExecute to "true"; "The Process object must have the UseShellExecute property set to false in order to redirect IO streams." I get an error. When I set it to False; Unable to update. How can I do it ? Do you have any other suggestions?
static void InstallIISSetupFeature()
{
var featureNames = new List<string>() {
"IIS-WebServerRole",
"IIS-WebServer",
"IIS-CommonHttpFeatures",
"IIS-HttpErrors",
"IIS-HttpRedirect",
"IIS-ApplicationDevelopment",
"IIS-Security",
"IIS-RequestFiltering",
"IIS-NetFxExtensibility",
"IIS-NetFxExtensibility45",
"IIS-HealthAndDiagnostics",
"IIS-HttpLogging",
"IIS-LoggingLibraries",
"IIS-RequestMonitor",
"IIS-HttpTracing",
"IIS-URLAuthorization",
"IIS-IPSecurity",
"IIS-Performance",
"IIS-HttpCompressionDynamic",
"IIS-WebServerManagementTools",
"IIS-ManagementScriptingTools",
"IIS-IIS6ManagementCompatibility",
"IIS-Metabase",
"IIS-HostableWebCore","IIS-StaticContent",
"IIS-DefaultDocument",
"IIS-DirectoryBrowsing",
"IIS-WebDAV",
"IIS-WebSockets",
"IIS-ApplicationInit",
"IIS-ASPNET",
"IIS-ASPNET45",
"IIS-ASP",
"IIS-CGI",
"IIS-ISAPIExtensions",
"IIS-ISAPIFilter",
"IIS-ServerSideIncludes",
"IIS-CustomLogging",
"IIS-BasicAuthentication",
"IIS-HttpCompressionStatic",
"IIS-ManagementConsole",
"IIS-ManagementService",
"IIS-WMICompatibility",
"IIS-LegacyScripts",
"IIS-LegacySnapIn",
"IIS-FTPServer",
"IIS-FTPSvc",
"IIS-FTPExtensibility",
"IIS-CertProvider",
"IIS-WindowsAuthentication",
"IIS-DigestAuthentication",
"IIS-ClientCertificateMappingAuthentication",
"IIS-IISCertificateMappingAuthentication",
"IIS-ODBCLogging",
"NetFx4-AdvSrvs",
"NetFx4Extended-ASPNET45",
"NetFx3",
"WAS-WindowsActivationService",
"WCF-HTTP-Activation",
"WCF-HTTP-Activation45",
"WCF-MSMQ-Activation45",
"WCF-NonHTTP-Activation",
"WCF-Pipe-Activation45",
"WCF-TCP-Activation45",
"WCF-TCP-PortSharing45",
"WCF-Services45",
};
ManagementObjectSearcher obj = new ManagementObjectSearcher("select * from Win32_OperatingSystem");
foreach (ManagementObject wmi in obj.Get())
{
string Name = wmi.GetPropertyValue("Caption").ToString();
Name = Regex.Replace(Name.ToString(), "[^A-Za-z0-9 ]", "");
if (Name.Contains("Server 2008 R2") || Name.Contains("Windows 7"))
{
featureNames.Add("IIS-ASPNET");
featureNames.Add("IIS-NetFxExtensibility");
featureNames.Add("WCF-HTTP-Activation");
featureNames.Add("WCF-MSMQ-Activation");
featureNames.Add("WCF-Pipe-Activation");
featureNames.Add("WCF-TCP-Activation");
featureNames.Add("WCF-TCP-Activation");
}
string Version = (string)wmi["Version"];
string Architecture = (string)wmi["OSArchitecture"];
}
foreach (var featureName in featureNames)
{
Run(string.Format("dism/online/Enable-Feature:{0}", featureName));
}
}
static void Run(string arguments)
{
try
{
string systemPath = Path.Combine(Environment.ExpandEnvironmentVariables("%windir%"), "system32");
var dism = new Process();
dism.StartInfo.WorkingDirectory = systemPath;
dism.StartInfo.Arguments = arguments;
dism.StartInfo.FileName = "dism.exe";
dism.StartInfo.Verb = "runas";
dism.StartInfo.UseShellExecute = true;
dism.StartInfo.RedirectStandardOutput = true;
dism.Start();
var result = dism.StandardOutput.ReadToEnd();
dism.WaitForExit();
}
catch (Exception ex)
{
}
}`
I tried to update the feature with dism.exe and cmd.exe, when it gave an authorization error, I used the Verb property
`
Since the use of .Verb = "RunAs" requires .UseShellExecute = true, and since the latter cannot be combined with RedirectStandardOutput = true, you cannot directly capture the elevated process' output in memory.
It seems that the system itself, by security-minded design, prevents a non-elevated process from directly capturing an elevated process' output.
The workaround is to launch the target executable (dism.exe, in your case) indirectly, via a shell, and then use the latter's redirection feature (>) to capture the target executable's output (invariably) in a file, as shown below.
string systemPath = Path.Combine(Environment.ExpandEnvironmentVariables("%windir%"), "system32");
// Create a temp. file to capture the elevated process' output in.
string tempOutFile = Path.GetTempFileName();
var dism = new Process();
dism.StartInfo.WorkingDirectory = systemPath;
// Use cmd.exe as the executable, and pass it a command line via /c
dism.StartInfo.FileName = "cmd.exe" ;
// Use a ">" redirection to capture the elevated process' output.
// Use "2> ..." to also capture *stderr* output.
// Append "2>&1" to capture *both* stdout and stderr in the file targeted with ">"
dism.StartInfo.Arguments =
String.Format(
"/c {0} {1} > \"{2}\"",
"dism.exe", arguments, tempOutFile
);
dism.StartInfo.Verb = "RunAs";
dism.StartInfo.UseShellExecute = true;
dism.Start();
dism.WaitForExit();
// Read the temp. file in which the output was captured...
var result = File.ReadAllText(tempOutFile);
// ... and delete it.
File.Delete(tempOutFile);
First, you can use WindowsPrincipal::IsInRole() to check if you're running elevated.
See Microsoft Learn for details.
Second, this may be one of those cases where using native PS is easier than the cmdlet approach (admittedly, still not great).
If the script is supposed to run on clients as well as server operating systems: use Get-WmiObject or Get-CimInstance to get a reference to what you're running on. ActiveDirectory also has that information (in operatingSystem attribute).
For servers use Get-WindowsFeature in ServerManager module.
For clients use Get-WindowsOptionalFeature with switch -Online in DISM module which, if you indeed need to support OSes older than 6.3.xxxx, can be copied over from a machine that has it and added to $Env:Path before C:\Windows and C:\Windows\System32.
For either platform just pass the list of features to configure.
If in a (binary) cmdlet you have to call external tools then the advantage of them is mostly gone. It may be possible to access Windows CBS using a managed API to avoid this but even then the script based approach gets more results faster, especially since you can just just put together a quick wrapper around dism.exe .
We have an MVC web app that allows downloading dynamically generated PDF reports. I am trying to allow viewing the report in the browser, and because of browser compatibility issues, we can't use a JS PDF viewer, so am working on a controller action that generated the PDF using existing code, then converts it to HTML using a third party program and returns the HTML.
The third party program, pdf2htmlEX, is used via a command line interface, but when I try to invoke the program to convert the PDF to HTML nothing happens. I do not receive an error, but no HTML file is generated.
I first tried just a single line to start the conversion Process.Start("commands here"), but when that didn't work I tried a more advanced way to start the process and allow capturing the StdOut found on this answer: How To: Execute command line in C#, get STD OUT results, but I don't seem to be getting any output either. I am not familiar with invoking command line programs using c#, so I am not sure if I am making a simple mistake. My current controller action looks like this:
public ActionResult GetReportPdfAsHtml(int userId, ReportType reportType, int page = 1)
{
// get pdf
var pdfService = new PdfServiceClient();
var getPdfResponse = pdfService.GetPdfForUser(new GetPdfForUserRequest {
UserId = userId,
ReportType = reportType,
BaseUri = Request.Url.Host
});
pdfService.Close();
// save pdf to temp location
var folderRoot = Server.MapPath("~");
var location = Path.Combine(folderRoot, "pdfTemp");
var outputDir = $"{location}\\output";
var fileName = $"{userId}_{reportType}";
Directory.CreateDirectory(outputDir);
var file = $"{location}\\{fileName}.pdf";
//IOFile is alias of system.IO.File to avoid collision with the 'File' Method already on the controller
IOFile.WriteAllBytes(file, getPdfResponse.Pdf);
//********************************************************************
//***** Works fine up to here, PDF is successfully generated and saved
//********************************************************************
// Convert pdf above to html
var arguments = $"{file} --dest-dir {outputDir} -f {page} -l {page}";
// Start the child process.
var p = new Process {
StartInfo = {
UseShellExecute = false,
RedirectStandardOutput = true,
FileName = Server.MapPath("~\\pdf2htmlEX.exe"),
Arguments = arguments
}
};
p.Start();
// Read the output stream first and then wait.
var output = p.StandardOutput.ReadToEnd();
p.WaitForExit();
// Function continues and returns fine, but MVC then errors because the
// file isn't created so the path below doesn't exist
return File($"{outputDir}\\{fileName}.html", "text/html");
}
Update: I have tried running the command in a cmd console and it works fine. However when I try and run it via the Process.Start() method, i get following output from Pdf2htmlEX:
>temporary dir: [Redacted]\pdfTemp\temp/pdf2htmlEX-a46244
>Preprocessing: 0/1
>Preprocessing: 1/1
>Add new temporary file: [Redacted]\pdfTemp\temp/pdf2htmlEX-a46244/__css
>Add new temporary file: [Redacted]\pdfTemp\temp/pdf2htmlEX-a46244/__outline
>Add new temporary file: [Redacted]\pdfTemp\temp/pdf2htmlEX-a46244/__pages
>Working: 0/1
>Install font 1: (14 0) SUBSET+LatoLightItalic
>Add new temporary file: [Redacted]\pdfTemp\temp/pdf2htmlEX-a46244/f1.ttf
>Embed font: [Redacted]\pdfTemp\temp/pdf2htmlEX-a46244/f1.ttf 1
>Add new temporary file: [Redacted]\pdfTemp\temp/pdf2htmlEX-a46244/__raw_font_1.ttf
>em size: 2000
>Add new temporary file: [Redacted]\pdfTemp\temp/pdf2htmlEX-a46244/f1.map
>Missing space width in font 1: set to 0.5
>space width: 0.5
>Add new temporary file: [Redacted]\pdfTemp\temp/pdf2htmlEX-a46244/__tmp_font1.ttf
>Add new temporary file: [Redacted]\pdfTemp\temp/pdf2htmlEX-a46244/__tmp_font2.ttf
>Internal Error: Attempt to output 2147483647 into a 16-bit field. It will be truncated and the file may not be useful.
>Internal Error: File Offset wrong for ttf table (name-data), -1 expected 150
>Save Failed
>Cannot save font to [Redacted]\pdfTemp\temp/pdf2htmlEX-a46244/__tmp_font1.ttf
I have windows form app with the following part of code when the Form Loads
public MonitorMail()
{
InitializeComponent();
pathfile = Directory.GetCurrentDirectory();
pathfile = pathfile + #"\Log\Configuration.txt";
var Lista = LoadConfigFile.LoadConfig(pathfile);
if (Lista.Count > 0)
{
SwithMailText.Text = Lista[0];
Excel_Textbox.Text = Lista[1];
LogFileText.Text = Lista[2];
MailServerText.Text = Lista[3];
FromText.Text = Lista[4];
SslText.Text = Lista[5];
UserText.Text = Lista[6];
}
}
As you can see in this code i declare a List named as "Lista" which List takes the records of the Configuration file and fill some textboxes with the data of that Configuration file.
My problem is the following: when I run my program inside in Visual Studio, it loads the records correctly in those textboxes.
When I run my program runs outside of Visual Studio, it also loads the records correctly
BUT
When I try run my program from the command prompt (because this how it should be run) like MonitorMail.exe the program runs but does not show the data in the textboxes.
After trying to understand why is this happening I noticed that is has something to do with
pathfile = Directory.GetCurrentDirectory();
I concluded to that because I changed the pathfile to pathfile="complete path of the Configuration.txt" so when I hit it from cmd works as it should be.
Any idea why Directory.GetCurrentDirectory(); affects cmd? Or is something am I missing?
You wrote in the comments: "i need for every PC to get current directory that my .exe is", but that is not what Directory.GetCurrentDirectory() does...
You need
string myPath = System.Reflection.Assembly.GetEntryAssembly().Location;
instead. That gives you the full path including the file name. You can take the Location's Directory if that is what you need.
I have written the c# console application. when i lauch the application (not using cmd). i can see it is listed in process list in task manager ,now i need to write another application in which i need to find whether previous application is running or not ,i know the application name and path so i have written management object searcher query to get the list of process and also i am using the path to compare it with its executable path ,code is given below
var name="test.exe"
var path="D:\test"
var processCollection = new ManagementObjectSearcher("SELECT * " + "FROM Win32_Process " + "WHERE Name='" + name + "'").Get();
if (processCollection.Count > 0)
{
foreach(var process in processCollection)
{
var executablePath=process["ExecutablePath"];
if(executablePath.equals(path))
{
return true;
}
}
}
but executable path is always null.
how to get its executable path?.
I can not only use process name because i am using common name like startserver and stopserver to my application. so i need to ensure it executable path.
there is a easier way, import System.Diagnostics then write following code,
public bool ProcessIsRun()
{
Process[] proc = Process.GetProcesses();
Return proc.Any(m => m.ProcessName.Contains("Your process name") && m.Modules[0].FileName == "Your File Path" );
}
This is solution for your problem:
System.Reflection.Assembly.GetEntryAssembly()
This will return complete folder path for executable current application.
You can use System.Reflection.Assembly.GetEntryAssembly().Location to find exact application executable with full path.
Right what im trying to accomplish is a program that basically sets the active partition in 1 click, saving the effort time and skill of using cmd prompt etc.
I have looked into the System.Management name space but couldn't work out how to use it :(
So i have resorted to using CMD, i have got a module application written in C# and basically i want to run "DISKPART" which then starts the diskpart in the cmd window, then i want to ask it to "Select disk 0" followed by "select partition 1" finally followed by "active".
Doing this in CMD yourself works fine but with an application its proved to be awkward :( What ive managed to get it to do is run DiskPart fine in one window with Process.Start, then get it to open a new window and run the next piece of code but because the new window hasnt ran the diskpart cmd it doesnt work >:(
Any suggestions?
Thanks!
Ash
As long as you aren't making decisions on the output, you could build a batch file in your C# app and start that via Process.Start(...).
You'll need to generate two files.
First runDiskPart.bat:
diskpart /s myScript.dp
Second myScript.dp:
...some commands...
exit
Obviously the names are completely arbitrary but the /s directive needs to reference the name of your second file.
After some searching, I think you can do what you want with a script file. Read this.
You can therefore run diskpart /s script.txt with Process.Start after creating a script.txt file with your necessary commands.
This may be a bit of a read so im sorry in advance. And this is my tried and tested way of doing this, there may be a simpler way but this is from me throwing code at a wall and seeing what stuck
TLDR code for this question in particular
Ok sorry This one is actually not tested. this one IN THEORY works
public static void ChangeMe()
{
string docPath =
Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
string path1 = docPath + "\\Test.txt";
string path2 = docPath + "\\Test.bat";
string[] lines =
{
"select disk 0",
"clean",
"convert gpt",
"create partition primary size=300",
"format quick fs=ntfs label=Windows RE tools",
"assign letter=T"
};
using (StreamWriter outputFile = new StreamWriter(Path.Combine(docPath, "test.txt")))
{
foreach (string line in lines)
outputFile.WriteLine(line);
}
string[] lines =
{
"diskpart /s test.txt"
};
using (StreamWriter outputFile = new StreamWriter(Path.Combine(docPath, "test.bat")))
{
foreach (string line in lines)
outputFile.WriteLine(line);
}
System.Diagnostics.Process.Start(path2);
}
If what you want to do be can be done in batch file, then the maybe over complicated work around is have c# write a .bat file and run it. If you want user input you could place the input into a variable and have c# write it into the file. it will take trial and error with this way because its like controlling a puppet with another puppet. And with Diskpart its a little more complicated because you have to make 2 files one that is a .bat and one that is a txt.
here is an example for just a batch file, In this case the function is for a push button in windows forum app that clears the print queue.
using System.IO;
using System;
public static void ClearPrintQueue()
{
//this is the path the document or in our case batch file will be placed
string docPath =
Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
//this is the path process.start usues
string path1 = docPath + "\\Test.bat";
// these are the batch commands
// remember its "", the comma separates the lines
string[] lines =
{
"#echo off",
"net stop spooler",
"del %systemroot%\\System32\\spool\\Printers\\* /Q",
"net start spooler",
//this deletes the file
"del \"%~f0\"" //do not put a comma on the last line
};
//this writes the string to the file
using (StreamWriter outputFile = new StreamWriter(Path.Combine(docPath, "test.bat")))
{
//This writes the file line by line
foreach (string line in lines)
outputFile.WriteLine(line);
}
System.Diagnostics.Process.Start(path1);
}
IF you want user input then you could try something like this.
This is for setting the computer IP as static but asking the user what the IP, gateway, and dns server is.
you will need this for it to work
public static void SetIPStatic()
{
//These open pop up boxes which ask for user input
string STATIC = Microsoft.VisualBasic.Interaction.InputBox("Whats the static IP?", "", "", 100, 100);
string SUBNET = Microsoft.VisualBasic.Interaction.InputBox("Whats the Subnet?(Press enter for default)", "255.255.255.0", "", 100, 100);
string DEFAULTGATEWAY = Microsoft.VisualBasic.Interaction.InputBox("Whats the Default gateway?", "", "", 100, 100);
string DNS = Microsoft.VisualBasic.Interaction.InputBox("Whats the DNS server IP?(Input required, 8.8.4.4 has already been set as secondary)", "", "", 100, 100);
//this is the path the document or in our case batch file will be placed
string docPath =
Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
//this is the path process.start usues
string path1 = docPath + "\\Test.bat";
// these are the batch commands
// remember its "", the comma separates the lines
string[] lines =
{
"SETLOCAL EnableDelayedExpansion",
"SET adapterName=",
"FOR /F \"tokens=* delims=:\" %%a IN ('IPCONFIG ^| FIND /I \"ETHERNET ADAPTER\"') DO (",
"SET adapterName=%%a",
"REM Removes \"Ethernet adapter\" from the front of the adapter name",
"SET adapterName=!adapterName:~17!",
"REM Removes the colon from the end of the adapter name",
"SET adapterName=!adapterName:~0,-1!",
//the variables that were set before are used here
"netsh interface ipv4 set address name=\"!adapterName!\" static " + STATIC + " " + STATIC + " " + DEFAULTGATEWAY,
"netsh interface ipv4 set dns name=\"!adapterName!\" static " + DNS + " primary",
"netsh interface ipv4 add dns name=\"!adapterName!\" 8.8.4.4 index=2",
")",
"ipconfig /flushdns",
"ipconfig /registerdns",
":EOF",
"DEL \"%~f0\"",
""
};
//this writes the string to the file
using (StreamWriter outputFile = new StreamWriter(Path.Combine(docPath, "test.bat")))
{
//This writes the file line by line
foreach (string line in lines)
outputFile.WriteLine(line);
}
System.Diagnostics.Process.Start(path1);
}
Like I said. It may be a little overcomplicated but it never fails unless I write the batch commands wrong.
This is the code for diskpart. You have to understand the command prompt in order to get these to work. With diskpart you cannot just write a script like
diskpart
select disk 0
clean
convert gpt
create partition primary size=300
format quick fs=ntfs label=Windows RE tools
assign letter=T
This is because diskpart opens its own window and the rest of the commands just throw errors in the command prompt window
so you have to get c# to first write a text file with the commands. Then a batch file with the diskpart command to call the text file that you just wrote.
As I said at first This one is actually not tested. this one IN THEORY works
public static void ChangeMe()
{
string docPath =
Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
string path1 = docPath + "\\Test.txt";
string path2 = docPath + "\\Test.bat";
string[] lines =
{
"select disk 0",
"clean",
"convert gpt",
"create partition primary size=300",
"format quick fs=ntfs label=Windows RE tools",
"assign letter=T"
};
using (StreamWriter outputFile = new StreamWriter(Path.Combine(docPath, "test.txt")))
{
foreach (string line in lines)
outputFile.WriteLine(line);
}
string[] lines =
{
"diskpart /s test.txt"
};
using (StreamWriter outputFile = new StreamWriter(Path.Combine(docPath, "test.bat")))
{
foreach (string line in lines)
outputFile.WriteLine(line);
}
System.Diagnostics.Process.Start(path2);
}
What about introducing a delay, such as Thread.Sleep(1000), so that the other process has time to complete the first command?
What you really want to do is wait for the program to exit and then move onto the next invocation. Take a look at this question.