Very new to C# here, and I'm learning a lot as I go along.
I'm creating a Winforms app that installs patches remotely. Basically, you give it a file (.msi or .exe) and a computer list, and it goes down the list until all of the patches have been installed. It seems to be working for what I want it to do. Just a click and go thing for vuln/patch management. For the record, I use PSexec and powershell to do the same task, and they're wonderful. Just fiddling around with my own, hoping to do some learning in the process.
I want to create an accurate progress bar for the application, so the admin can have a general idea of what processes are being handled at the time. There is a very wide spread in the amount of systems that may need to be patched, anywhere from 10 systems to 1K+.
I have looked at tutorials of progress bar implementation, but most are based on the code writer estimating the time of task completion for specific jobs. Being that every patch size, install time, and amount of computers are different, that doesn't seem to help me much.
private void Start_Click(object sender, EventArgs e)
{
string complist = openFileDialog2.FileName;
string patch = textBox2.Text;
string fileName = patch;
string patchfile = Path.GetFileName(fileName);
foreach (string line in File.ReadLines(complist))
{
//Checks if C:\PatchUpdates exists on remote machine. If not, a folder is created.
if (!Directory.Exists(#"\\" + line + #"\C$\PatchUpdates"))
{
Directory.CreateDirectory(#"\\" + line + #"\C$\PatchUpdates");
}
//XCOPY patch to every computer
System.Diagnostics.Process processCopy = new System.Diagnostics.Process();
ProcessStartInfo StartInfo = new ProcessStartInfo();
StartInfo.WindowStyle = System.Diagnostics.ProcessWindowStyle.Hidden;
StartInfo.FileName = "cmd";
StartInfo.Arguments = string.Format("/c xcopy " + patch + " " + #"\\{0}\C$\PatchUpdates /K /Y", line);
processCopy.StartInfo = StartInfo;
processCopy.Start();
processCopy.WaitForExit();
//Checks filename and installs according to file type.
if (patch.Contains(".msi"))
{
//Uses WMI to execute a remote command to install using msiexec.
ConnectionOptions options = new ConnectionOptions();
options.Impersonation = System.Management.ImpersonationLevel.Impersonate;
ManagementScope WMIscope = new ManagementScope(
string.Format("\\\\{0}\\root\\cimv2", line));
WMIscope.Connect();
ManagementClass WMIprocess = new ManagementClass(
WMIscope, new ManagementPath("Win32_Process"), new ObjectGetOptions());
object[] processmsi = { #"cmd.exe /c msiexec /qn /i " + #"C:\PatchUpdates\" + patchfile + #" /norestart" };
object result = WMIprocess.InvokeMethod("Create", processmsi);
}
else if (patch.Contains(".exe"))
{
//Uses WMI to execute a remote command to install using commandline.
ConnectionOptions options = new ConnectionOptions();
options.Impersonation = System.Management.ImpersonationLevel.Impersonate;
ManagementScope WMIscope = new ManagementScope(
string.Format("\\\\{0}\\root\\cimv2", line));
WMIscope.Connect();
ManagementClass WMIprocess = new ManagementClass(
WMIscope, new ManagementPath("Win32_Process"), new ObjectGetOptions());
object[] processexe = { #"cmd.exe /c C:\PatchUpdates\" + patchfile + #" /silent /norestart" };
object result = WMIprocess.InvokeMethod("Create", processexe);
}
else if (patch.Contains(".msu"))
{
//Uses WMI to execute a remote command to install using WUSA.
ConnectionOptions options = new ConnectionOptions();
options.Impersonation = System.Management.ImpersonationLevel.Impersonate;
ManagementScope WMIscope = new ManagementScope(
string.Format("\\\\{0}\\root\\cimv2", line));
WMIscope.Connect();
ManagementClass WMIprocess = new ManagementClass(
WMIscope, new ManagementPath("Win32_Process"), new ObjectGetOptions());
object[] processmsu = { #"wusa " + patchfile + " /quiet /norestart" };
object result = WMIprocess.InvokeMethod("Create", processmsu);
}
}
}
The code above is where most of the work is done. When the user clicks "Start", the patch is copied to C:\PatchUpdates on every machine and is installed using WMI.
How could I make a progress bar that is based on the calculation of time taken to do each task, and finishes at 100% when the last computers are being patched?
I'm assuming a lot of work is needed to do this.
Any help is appreciated.
You need to first get the amount of lines (you systems count).
If you are using .Net 4.0 or later you can use
var lineCount = File.ReadLines(#"C:\file.txt").Count();
else you can do first
var lineCount = 0;
using (var reader = File.OpenText(#"C:\file.txt"))
{
while (reader.ReadLine() != null)
{
lineCount++;
}
}
After that you set the progressbars minimum to 0 and the Maximum to this count of systems. In the foreach you need only to do a progressbar.PerformStep.
public void loadFiles()
{
// Sets the progress bar's minimum value to a number representing
// no operations complete -- in this case, no files read.
progressBar1.Minimum = 0;
// Sets the progress bar's maximum value to a number representing
// all operations complete -- in this case, all five files read.
progressBar1.Maximum = Convert.ToInt(lineCount); // in our case to the number of systems
// Sets the Step property to amount to increase with each iteration.
// In this case, it will increase by one with every file read.
progressBar1.Step = 1;
// Uses a for loop to iterate through the operations to be
// completed. In this case, five files are to be copied into memory,
// so the loop will execute 5 times.
for (int i = 0; i <= 4; i++)
{
// Inserts code to copy a file
progressBar1.PerformStep();
// Updates the label to show that a file was read.
label1.Text = "# of Files Read = " + progressBar1.Value.ToString();
}
}
Related
i connected over WMI on a remote Machine.
username and password is correctly set.
var options = new ConnectionOptions();
servicePath = "\\\\Testserver\\root\\cimv2";
options.EnablePrivileges = true;
options.Username = username;
options.Password = pwd;
serviceScope = new ManagementScope(servicePath, options);
serviceScope.Connect();
this is the code sequence i want to run on the remote machine
Process process = new Process();
process.StartInfo.FileName = "diskpart.exe";
process.StartInfo.UseShellExecute = false;
process.StartInfo.CreateNoWindow = true;
process.StartInfo.RedirectStandardInput = true;
process.StartInfo.RedirectStandardOutput = true;
process.Start();
process.StandardInput.WriteLine("list volume");
process.StandardInput.WriteLine("exit");
string output = process.StandardOutput.ReadToEnd();
process.WaitForExit();
// extract information from output
string table = output.Split(new string[] { "DISKPART>" }, StringSplitOptions.None)[1];
var rows = table.Split(new string[] { "\n" }, StringSplitOptions.None);
for (int i = 3; i < rows.Length; i++)
{
if (rows[i].Contains("Volume"))
{
int index = Int32.Parse(rows[i].Split(new string[] { " " }, StringSplitOptions.None)[3]);
string label = rows[i].Split(new string[] { " " }, StringSplitOptions.None)[8];
Console.WriteLine($#"Volume {index} {label}:\");
}
}
if i am going to call the process over wmi like this...
object[] theProcessToRun = { "diskpart" };
using (var managementClass = new ManagementClass(serviceScope, new ManagementPath("Win32_Process"), new ObjectGetOptions()))
{
managementClass.InvokeMethod("Create", theProcessToRun);
}
i can call the process, but havent the possibilites to hand over the commands i would like to execute in this process...
How to solve this ?
You can use a script to be passed in as part of the command line arguments that will contain your command.
Reference: https://www.computerhope.com/diskpart.htm
Also you should redirect your output to a file using cmd as you should get your output directly either.
For script, make sure you use a unc that other machine/user has access to. For output file, make sure you use a unc that other machine/user can write to and you can read from via the program.
I want to generate a couple of hundreds .txt scripts in a C# app, launch GnuPlot and generate .png graphs for each of the scripts I have.
I can launch GnuPlot from C# with the following code:
Process gnuPlotProcess = new Process();
gnuPlotProcess.StartInfo = new ProcessStartInfo(#"C:\Program Files\gnuplot\bin\wgnuplot.exe");
gnuPlotProcess.Start();
The first problem appears when I'm trying to change the current directory, adding this line of code before starting the process:
gnuPlotProcess.StartInfo.Arguments = "cd '" + scriptsPath + "'";
Now GnuPlot doesn't start.
The second problem, which I was able to test working on GnuPlot's default current directory, is when attempting to pass the "load 'script_xxx.txt'" command. Here is the complete code (inspiration from Plotting graph from Gunplot in C#):
Process gnuPlotProcess = new Process();
gnuPlotProcess.StartInfo = new ProcessStartInfo(#"C:\Program Files\gnuplot\bin\wgnuplot.exe");
gnuPlotProcess.StartInfo.RedirectStandardInput = true;
gnuPlotProcess.StartInfo.UseShellExecute = false;
theProcess.Start();
StreamWriter sw = gnuPlotProcess.StandardInput;
sw.WriteLine("load '" + pathToScript + "'");
sw.Flush();
The script that is found on pathToScript should create a .png file, and it works when being launched directly from gnuPlot. But from code, nothing happens.
Any help would be appreciated.
I hope this code will help you:
int N = 1000;
string dataFile = "data.txt"; // one data file
string gnuplotScript = "gnuplotScript.plt"; // gnuplot script
string pngFile = "trajectory.png"; // output png file
// init values
double x = 0, y = 0;
// random values to plot
Random rnd = new Random();
StreamWriter sw = new StreamWriter(dataFile);
// US output standard
Thread.CurrentThread.CurrentCulture = CultureInfo.GetCultureInfo("en-US");
// generate data for visualisation
for (int i = 0; i < N; i++)
{
x += rnd.NextDouble() - 0.5;
y += rnd.NextDouble() - 0.5;
sw.WriteLine(x.ToString("F3") + "\t" + y.ToString("F3"));
}
sw.Close();
// you can download it from file
string gnuplot_script = "set encoding utf8\n" +
"set title \"Random trajectory\"\n" +
"set xlabel \"Coordinate X\"\n" +
"set ylabel \"Coordinate Y\"\n" +
"set term pngcairo size 1024,768 font \"Arial,14\"\n" +
"set output \"pngFile\"\n" +
"plot 'dataFile' w l notitle\n" +
"end";
// change filenames in script
gnuplot_script = gnuplot_script.Replace("dataFile", dataFile);
gnuplot_script = gnuplot_script.Replace("pngFile", pngFile);
// write sccript to file
sw = new StreamWriter(gnuplotScript, false, new System.Text.UTF8Encoding(false));
sw.WriteLine(gnuplot_script);
sw.Close();
// launch script
ProcessStartInfo PSI = new ProcessStartInfo();
PSI.FileName = gnuplotScript;
string dir = Directory.GetCurrentDirectory();
PSI.WorkingDirectory = dir;
using (Process exeProcess = Process.Start(PSI))
{
exeProcess.WaitForExit();
}
// OPTION: launch deafault program to see file
PSI.FileName = pngFile;
using (Process exeProcess = Process.Start(PSI))
{
}
You can repeat this example with your own data as many times as you want to make many png files
I am developing a software that will list all the software install
in Computer
now i want to Uninstall it using my Program In C# by
calling the Uninstall Key of that software in
Registry Key
My Program Is
Like That But the Process Is Not Working
var UninstallDir = "MsiExec.exe /I{F98C2FAC-6DFB-43AB-8B99-8F6907589021}";
string _path = "";
string _args = "";
Process _Process = new Process();
if (UninstallDir != null && UninstallDir != "")
{
if (UninstallDir.StartsWith("rundll32.exe"))
{
_args = ConstructPath(UninstallDir);
_Process.StartInfo.FileName = Environment.SystemDirectory.ToString() + "\\explorer.exe";
_Process.StartInfo.Arguments = Environment.SystemDirectory.ToString() + "\\" + UninstallDir;
_Process.Start();
}
else if (UninstallDir.StartsWith("MsiExec.exe"))
{
_args = ConstructPath(UninstallDir);
_Process.StartInfo.FileName = Environment.SystemDirectory.ToString() + "\\cmd.exe";
_Process.StartInfo.Arguments = Environment.SystemDirectory.ToString() + "\\" + UninstallDir;
_Process.Start();
}
else
{
//string Path = ConstructPath(UninstallDir);
_path = ConstructPath(UninstallDir);
if (_path.Length > 0)
{
_Process.StartInfo.FileName = _path;
_Process.StartInfo.UseShellExecute = false;
_Process.Start();
}
}
Try this approach:
Process p = new Process();
p.StartInfo.FileName = "msiexec.exe";
p.StartInfo.Arguments = "/x {F98C2FAC-6DFB-43AB-8B99-8F6907589021}/qn";
p.Start();
Refer to this link: http://www.microsoft.com/resources/documentation/windows/xp/all/proddocs/en-us/msiexec.mspx?mfr=true
HTH.
The problem with your misexec.exe code is that running cmd.exe someprogram.exe doesn't start the program because cmd.exe doesn't execute arguments passed to it. But, you can tell it to by using the /C switch as seen here. In your case this should work:
_Process.StartInfo.FileName = Environment.SystemDirectory.ToString() + "\\cmd.exe";
_Process.StartInfo.Arguments = "/C " + Environment.SystemDirectory.ToString() + "\\" + UninstallDir;
Where all I did was add /C (with a space after) to the beginning of the arguments. I don't know how to get your rundll32.exe code to work, however.
Your solution looks good, but keep a space before \qn:
p.StartInfo.Arguments = "/x {F98C2FAC-6DFB-43AB-8B99-8F6907589021} /qn";
Otherwise it wont work in silent mode.
I run a Web Role on Windows Azure to receive AAC audio files (uploaded by base64 string) and store them into blob. it works fine by now.
Next, I also have to convert them into MP3 and store the MP3s into blob too. I decided to use something like ffmpeg.exe -i path.aac path.mp3.
The problems are that:
How to call external ffmpeg.exe inside a web service of a web role?
what would be the path?
Please help me if you know. Thank you in advance.
I suggest that you use a Local Storage Resource for your webrole where you can download the AAC files from the blob storage, and have them converted to MP3. Then upload back to blob storage.
Side note is that you can also use the Path.GetTempFileName() to get a temporary file name for your AAC / MP3 files, but I don't encourage to do so (even if I've done it before).
As for the actuall ffmpeg running, you might want to browse the code for AzureVideoConv, which I've built some time ago. You will find a lot of useful code there.
Here is a sample of the actual ffmpeg call (note that I download the exe from a blob storage, to avoid bloating my azure package with external exe files, and to easily update the ffmpeg.exe when required):
internal void ConvertFile(string inputFileName, Guid taskID)
{
string tmpName = string.Format(
"{0}\\{1}.flv",
Path.GetTempPath(), inputFileName.Substring(inputFileName.LastIndexOf("\\")+1));
ProcessStartInfo psi = new ProcessStartInfo();
psi.FileName = this._processorExecutable;
psi.Arguments = string.Format(#"-i ""{0}"" -y ""{1}""", inputFileName, tmpName);
psi.CreateNoWindow = true;
psi.ErrorDialog = false;
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;
string outString = string.Empty;
// use ansynchronous reading for at least one of the streams
// to avoid deadlock
exeProcess.OutputDataReceived += (s, e) => {
outString += 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)
string errString = exeProcess.StandardError.ReadToEnd();
Trace.WriteLine(outString);
Trace.TraceError(errString);
byte[] fileBytes = File.ReadAllBytes(tmpName);
if (fileBytes.Length > 0)
{
this._sSystem.SaveOutputFile(
fileBytes,
tmpName.Substring(tmpName.LastIndexOf("\\")+1),
taskID
);
}
}
}
catch (Exception e)
{
Trace.TraceError(e.Message);
}
}
NOTE the last check in of the project is using Windows Azure SDK 1.3
Thank you a lot #astaykov. You did a good job. Though It's not specific for my case(I need a specific piece of code instead of a whole large project), but it really inspired me. For specifying into my case, I am going to answer this question by my own - note that I did this based on #astaykov's code with somewhere directly copy&paste.
Firstly, configure the role with a Local Storage Resource. Then get its path by these code:
LocalResource converter_path =
RoleEnvironment.GetLocalResource("AudioConvertSpace");
string rootPathName = converter_path.RootPath;
get the path of ffmpeg.exe, xxx.aac and xxx.mp3 in the local storage:
string aac_path = rootPathName + "\\" + "fmwa-" + guidguid + ".aac";
string mp3_path = rootPathName + "\\" + "fmwa-" + guidguid + ".mp3";
string exe_path = rootPathName + "\\" + "ffmpeg.exe";
write the .aac file to local storage:
System.IO.File.WriteAllBytes(aac_path, decoded_audio_byte_array);
keep in mind that the local storage is not guaranteed to be stable or durable, so check the existence of the ffmpeg.exe -- if it doesn't exist, download it from blob.
if (System.IO.File.Exists(exe_path) == false)
{
var exeblob = _BlobContainer.GetBlobReference("ffmpeg.exe");
exeblob.DownloadToFile(exe_path, null);
}
initial and run the ffmpeg.exe process:
ProcessStartInfo psi = new ProcessStartInfo();
psi.FileName = exe_path;
psi.Arguments = string.Format(#"-i ""{0}"" -y ""{1}""",
aac_path, mp3_path);
psi.CreateNoWindow = true;
psi.ErrorDialog = false;
psi.UseShellExecute = false;
psi.WindowStyle = ProcessWindowStyle.Hidden;
psi.RedirectStandardOutput = true;
psi.RedirectStandardInput = false;
psi.RedirectStandardError = true;
Process exeProcess = Process.Start(psi);
exeProcess.PriorityClass = ProcessPriorityClass.High;
string outString = string.Empty;
exeProcess.OutputDataReceived += (s, e) => {
outString += e.Data;
};
exeProcess.BeginOutputReadLine();
string errString = exeProcess.StandardError.ReadToEnd();
Trace.WriteLine(outString);
Trace.TraceError(errString);
exeProcess.WaitForExit();
upload the output of ffmpeg.exe into the blob storage:
byte[] mp3_audio_byte_array = System.IO.File.ReadAllBytes(mp3_path);
var mp3blob = _BlobContainer.GetBlobReference("fmwa-"+guidguid+".mp3");
mp3blob.Properties.ContentType = "audio/mp3";
mp3blob.UploadByteArray(mp3_audio_byte_array);
clean the temp files:
System.IO.File.Delete(aac_path);
System.IO.File.Delete(mp3_path);
I would to be able to stop a typeperf process i started in c# for remote machines.
What i got so far is:
ProcessStartInfo proc = new ProcessStartInfo();
public void startLogs()
{
for (int i = 0; i < remote_machines_num; i++)
{
string line = " -s " + machines[i]
+ " -si 5 \"\\Processor(_Total)\\% Processor Time\" -o C:\\logs\\"
+ machines[i] + ".csv";
proc.FileName = #"C:\WINDOWS\SYSTEM32\typeperf.exe";
proc.Arguments = line;
Process.Start(proc);
}
}
this really starts all monitors - but i would like to write a function that will stop monitoring and close all windows - how can i do that? thanks!
Get a reference to the started process and Kill() it.
var theRunningProc = Process.Start(proc);
theRunningProc.Kill();
Are you looking for this
http://alperguc.blogspot.com/2008/11/c-process-processgetprocessesbyname.html