I have some code that I wanted to improve. It uses Process.Start and should be able to handle any input any argument, and still work.
I don't think I have covered all the bases. Can anyone suggest a better/more thorough approach?
ToMaybeUri is an extension method that tries to create a Uri.
ToValidMailToArgument is an extension method that adds "attachments="
IsValidEmail is an extension method that does a RegEx on the email address.
public static void RunProcess(string fileName, string Params)
{
var useProcessStart = true;
var validFile = false;
var validDir = false;
var validEmail = false;
var validURL = false;
var proc = new Process();
var info = new ProcessStartInfo(fileName);
info.UseShellExecute = true;
info.Arguments = Params;
//try catches here in case the syntax of the string has invalid characters for dir/file
try
{
var di = new DirectoryInfo(fileName);
validDir = di == null ? false : di.ExistsNow();
}
catch (Exception ex) { }
try
{
var fi = new FileInfo(fileName);
validFile = fi == null ? false : fi.Exists();
}
catch (Exception ex) { }
if (Params == "")
{
if (validFile)
{
if (Path.GetExtension(fileName).ToUpper() == ".CHM")
{
var helpProvider1 = new HelpProvider();
helpProvider1.HelpNamespace = fileName;
Help.ShowHelp(Application.OpenForms[0], helpProvider1.HelpNamespace);
MessageBox.Show(msg);
return;
}
}
else if (validDir)
{
//skip
}
else if (fileName.IsValidEmail())
{
validEmail = true;
info.FileName = "mailto:" + info.FileName;
info.Arguments = "";
}
else if (fileName.IsValidUrl())
{
validURL = true;
info.FileName = fileName.ToMaybeUri().Value.ToString();
info.Arguments = "";
}
else
{
MessageBox.Show(fileName + " does not exist.");
}
}
else
{
//and has params
if (Path.GetExtension(fileName).ToUpper() == ".PDF" && Params.ToLower().StartsWith("p"))
{
int pageNum = 0;
string pageNumString = Grazer.Utilities.Strings.Right(Params, Params.Length - 1);
int.TryParse(pageNumString, out pageNum);
//PDFLocation = "/A \"page=" + pageNum + "=OpenActions\" \"" + ssGlobals.ssStartDir + "\\Example.pdf\""
string app = GrRegistry.GetApplicationFromExtension(".PDF");
if (Path.GetFileNameWithoutExtension(app).ToUpper() == "ACROBAT" || Path.GetFileNameWithoutExtension(app).ToUpper() == "ACRORD32")
{
string PDFLocation = String.Format("/A \"page={0}=OpenActions\" \"{1}\"", pageNum, Path.GetFullPath(fileName));
info = new ProcessStartInfo(app);
info.Arguments = PDFLocation;
}
}
else if (fileName.IsValidEmail())
{
validFile = false;
try
{
var fi = new FileInfo(info.Arguments);
validFile = fi == null ? false : fi.ExistsNow();
}
catch (Exception ex) { }
info.FileName = String.Format("mailto:{0}{1}", fileName, new FileInfo(info.Arguments).ToValidMailToArgument());
info.Arguments = "";
}
}
if (useProcessStart)
{
proc.StartInfo = info;
try
{
if (validURL || validFile || validDir || validEmail)
proc.Start();
}
catch (Exception ex)
{
switch (ex.Message)
{
case "No process is associated with this object.":
break;
default:
MessageBox.Show(ex);
if (info.Arguments.ToEmptyIfNull().Length > 0)
MessageBox.Show(String.Format("{0} could not be opened with parameters: {1}", info.FileName, info.Arguments));
else
MessageBox.Show(String.Format("{0} could not be opened", info.FileName));
break;
}
}
}
}
To start with, you don't want to swallow exceptions. On your catch blocks, make sure you're doing something after catching an exception. Also, find out what specific exceptions can be thrown by methods you're calling in your try blocks and catch those specific exceptions, like so:
try
{
SomeMethod();
}
catch (SpecificExceptionType1)
{
//do something based on what this exception means
}
catch (SpecificExceptionType2)
{
//ditto here
}
catch
{
//handle unexpected exceptions here
}
Also, this smells suspiciously like homework - perhaps implementing a command shell? If so, retag it as homework. If not, just tell me to stuff it.
Related
I hope I can explain my scenario the best I can.
I have code that when the "Load" button is clicked, all file names (if any) located in a predefined network directory path, are loaded to a text-area.
Currently there can be .txt, .xml files.
Contents could look like:
first_file_found.xml
second_file_found.xml
third_file_found.txt
Also, in the code there is another function "isCoValid" that performs an additional validation of the contents of these files, based on return value (true/false) of this function, the "Process" button is enabled:
if (IsFlatFile(fileName) || IsXMLFile(fileName))
{
if (isCoValid(fileName))
{
btnProcess.Enabled = true;
}
else
{
btnProcess.Enabled = false;
break;
}
}
Now I have to add a .csv file type, but this file does not required to perform the isCoValid function.
The text-area contents now look like:
first_file_found.xml
second_file_found.xml
third_file_found.txt
fourht_file_found.csv
My request for help is to ask how can the check to find out if there is a CSV file can be done, and also controlling the enabling of the "Process" button, but still respect the existing check for .txt, and .xml and the validation of contents?
I might have xml and text files, that aren't valid, but I still need to be able to process the .csv. file.
I did change it like this:
if (IsFlatFile(fileName) || IsXMLFile(fileName))
{
if (isCoValid(fileName))
{
btnProcess.Enabled = true;
}
else
{
btnProcess.Enabled = false;
break;
}
}
if (IsCSVFile(fileName))
{
btnProcess.Enabled = true;
}
But I am sure this is not correct and I would like to ask for some help if possible.
I hope I explained my problem with some clarity and straightforwardness, if not, please let me know and I can try to provide more information.
Thank you,
Erasmo
Additional Code Requested
public bool IsFlatFile(string FileName)
bool ReturnValue = false;
if (FileName.ToUpper().Right(4) == ".TXT")
{
if ((FileName.Substring(0, 2).ToUpper() == "MN") ||
(FileName.Substring(0, 2).ToUpper() == "CH"))
{
ReturnValue = true;
}
}
return ReturnValue;
}
public bool IsXMLFile(string FileName)
bool ReturnValue = false;
if (FileName.ToUpper().Right(4) == ".XML")
{
if ((FileName.Substring(0, 2).ToUpper() == "TR") ||
(FileName.Substring(0, 2).ToUpper() == "SK"))
{
ReturnValue = true;
}
}
return ReturnValue;
}
protected bool isCoValid(string fName)
{
bool retCode = false;
Parameters parms;
var reader = new AppSettingsReader();
Application app = new Application();
Package package = null;
try
{
package = app.LoadPackage(packagePath + "ValidateContents.dtsx", null);
parms = package.Parameters;
parms["ID"].Value = "";
parms["ImportFileName"].Value = fName;
parms["UserID"].Value = userName;
DTSExecResult results = package.Execute();
if (results == Microsoft.SqlServer.Dts.Runtime.DTSExecResult.Failure)
{
foreach (Microsoft.SqlServer.Dts.Runtime.DtsError local_DtsError in package.Errors)
{
retCode = false;
resultText = resultText + "DTSX Package Execution results: " + local_DtsError.Description.ToString() + Environment.NewLine;
}
}
else
{
resultText = resultText + "Successful Process Completion." + Environment.NewLine + Environment.NewLine;
string sqlStr = "SELECT TOP 1 * FROM Validation WHERE Type = 'VALCO' AND CAST(CreatedDate AS DATE) = CAST(GETDATE() AS DATE)";
DataTable dt = new DataTable();
dt = GetDataSet(sqlStr);
if (dt.Rows.Count > 0)
{
foreach (DataRow row in dt.Rows)
{
if (row["Status"].ToString() == "Valid")
{
retCode = true;
resultText = "Output: Valid" + Environment.NewLine + "Press the 'Process' button to proceed.";
}
else
{
retCode = false;
resultText = "Output: " + row["Status"].ToString() + Environment.NewLine + "Validation Fail: " + row["Error"].ToString();
}
}
}
else
{
resultText = "Unable to read Validation Table for this file.";
retCode = false;
}
}
}
catch (Exception)
{
throw;
}
return retCode;
}
Your code will look like so:
if (IsFlatFile(fileName) || IsXMLFile(fileName) || IsCSVFile(fileName))
{
btnProcess.Enabled = isCoValid(fileName);
if (!btnProcess.Enabled) break;
}
public static bool IsCSVFile(string FileName) =>
Path.GetExtension(FileName).Equals(".csv", StringComparison.OrdinalIgnoreCase);
when you write your own IsCSVFile() method and update isCoValid() method to fit your needs.
Sorry, but I can't guess what happens inside classes that are used in isCoValid() method.
When I try to run my game in Unity3d it gives me this error in CocoaPodHelper.cs file in the compiler
Process ` :type used in a using statement must be implicitly convertible to system.idisposable
and anything after "using (var process = new Process())" is full of errors.
here is the code of the cocapodhelper.cs
using System;
using System.Diagnostics;
using System.IO;
namespace GoogleMobileAds
{
public class CocoaPodHelper
{
public static string Update(string projDir)
{
if (!Directory.Exists(projDir))
{
throw new Exception("project not found: " + projDir);
}
string podPath = ExecuteCommand("which", "pod", null);
if (podPath.Equals(""))
{
throw new Exception("pod executable not found");
}
return ExecuteCommand(podPath.Trim(), "update", projDir);
}
private static string ExecuteCommand(string command, string argument, string workingDir)
{
using (var process = new Process())
{
if (!process.StartInfo.EnvironmentVariables.ContainsKey("LANG"))
{
process.StartInfo.EnvironmentVariables.Add("LANG", "en_US.UTF-8");
}
string path = process.StartInfo.EnvironmentVariables["PATH"];
if(!path.Contains("/usr/local/bin"))
{
path = path + ":/usr/local/bin";
process.StartInfo.EnvironmentVariables.Remove("PATH");
process.StartInfo.EnvironmentVariables.Add("PATH", path);
}
if (workingDir != null)
{
process.StartInfo.WorkingDirectory = workingDir;
}
process.StartInfo.FileName = command;
process.StartInfo.Arguments = argument;
UnityEngine.Debug.Log("Executing " + command + " argument: " +
process.StartInfo.Arguments);
process.StartInfo.CreateNoWindow = true;
process.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;
process.StartInfo.UseShellExecute = false;
process.StartInfo.RedirectStandardError = true;
process.StartInfo.RedirectStandardOutput = true;
try
{
process.Start();
process.StandardError.ReadToEnd();
var stdOutput = process.StandardOutput.ReadToEnd();
var stdError = process.StandardError.ReadToEnd();
UnityEngine.Debug.Log("command stdout: " + stdOutput);
if (stdError != null && stdError.Length > 0)
{
UnityEngine.Debug.LogError("command stderr: " + stdError);
}
if (!process.WaitForExit(10 * 1000))
{
throw new Exception("command did not exit in a timely fashion");
}
return stdOutput;
}
catch (Exception e)
{
throw new Exception("Encountered unexpected error while running pod", e);
}
}
}
}
}
A using statement allows the object in the statement to be "disposed of" after the end of the using statement - the statement will call a method to get rid of the object. However, System.Diagnostics.Process does not "have" a method in it that allows it to be disposed of; it doesn't implement the IDisposable interface. So, instead of having:
using (var process = new Process()) { //Do stuff here } just do:
var process = new Process(); //Do stuff here and the variable will go out of scope at the end of the method.
When I try to debug my code with debugger with F11 (Step Into), my code produced the expected result. When I try to run the code without debugger(without break point), the looping in my code produced unexpected result; to be specific, the looping part only loop for 1 times and terminated, I am confused here, anyone have any idea about this? Below is the loop I mentioned which produced unexpected result.
public bool CopyFileAndFolder(string sourceFolder, string replacePath)
{
bool result = false;
try
{
foreach (string extractPath in Directory.GetDirectories(sourceFolder, "*", SearchOption.AllDirectories))
{
string destFolder = extractPath.Replace(sourceFolder, replacePath);
if (!Directory.Exists(destFolder))
Directory.CreateDirectory(destFolder);
}
foreach (string extractFile in Directory.GetFiles(sourceFolder, "*.*", SearchOption.AllDirectories))
{
string destFile = extractFile.Replace(sourceFolder, replacePath);
File.Copy(extractFile, destFile, true);
}
result = true;
}
catch (Exception)
{
result = false;
}
return result;
}
The complete code, i called the method above with this method:
private bool StartFileRollBackProcess()
{
bool result = false;
string backupFolder = Path.Combine(ConfigurationManager.AppSettings["BackupPath"], completeVersionNumber);
string destBackUpFolder = Directory.GetParent(iisConf.PhysicalPath).FullName;
try
{
DirectoryInfo folderToBeDelete = new DirectoryInfo(destBackUpFolder);
folderToBeDelete.Delete(true);
if (Directory.Exists(backupFolder))
{
Directory.CreateDirectory(destBackUpFolder);
result = CopyFileAndFolder(backupFolder, destBackUpFolder);
if (result)
{
ErrorMsg = "Copy process Failed,Your File has rolled back to previous version";
IsErrorDetected = true;
}
else
{
ErrorMsg = "copy got error";
IsErrorDetected = true;
}
}
}
catch (Exception)
{
ErrorMsg = "Error during roll up process";
IsErrorDetected = true;
}
return result;
}
try
{
responseFile = (HttpWebResponse)requestFile.GetResponse();
}
catch (WebException exRequestFile)
{
MessageBox.Show(exRequestFile.Message);
closeRequest = false;
}
and
if(closeRequest == true) responseFile.Close();
So now my problem:
If i try to download a file, it does so, closes the response. All good.
However, if I type wrong URL (with 404 returned or smth), I can't close the response because it gives me nullreference error. So I close it only when I get a response.
However, after providing wrong URL and not closing response, further tries to download anything result in timeout (even if URL is correct).
Why is that happening and can I resolve it somehow?
EDIT: Full code of class:
public void DownloadAndReplace(string webFile, string sourceFile)
{
var requestFile = (HttpWebRequest)WebRequest.Create(webFile);
var requestMD5 = (HttpWebRequest)WebRequest.Create(webFile + ".md5");
var requestSHA = (HttpWebRequest)WebRequest.Create(webFile + ".sha");
bool closeRequest = true;
_location = sourceFile + "\\" + OriginalFileName(webFile);
requestFile.Method = "HEAD";
requestFile.Timeout = 2000;
HttpWebResponse responseFile = null;
HttpWebResponse responseMD5 = null;
HttpWebResponse responseSHA = null;
try
{
responseFile = (HttpWebResponse)requestFile.GetResponse();
}
catch (WebException exRequestFile)
{
MessageBox.Show(exRequestFile.Message);
closeRequest = false;
}
if (!File.Exists(_location) || responseFile.LastModified > File.GetLastWriteTime(_location))
{
downloadFile(webFile, _location);
if (_controlsRef.chooseMD5.Checked)
{
try
{
responseMD5 = (HttpWebResponse)requestMD5.GetResponse();
downloadFile(webFile + ".md5", _location);
responseMD5.Close();
}
catch (WebException exRequestFile)
{
MessageBox.Show(exRequestFile.Message + " " + webFile + ".md5");
}
}
else if (_controlsRef.chooseSHA1.Checked)
{
try
{
responseSHA = (HttpWebResponse)requestSHA.GetResponse();
downloadFile(webFile + ".sha", _location);
responseSHA.Close();
}
catch (WebException exRequestFile)
{
MessageBox.Show(exRequestFile.Message + " " + webFile + ".sha");
}
}
}
else MessageBox.Show("Newest version");
if(closeRequest == true) responseFile.Close();
public void downloadFile(string urlAddress, string location)
{
_fileHasher = new HashFile(_controlsRef);
using (var downloadClient = new WebClient())
{
downloadClient.DownloadFileCompleted += new System.ComponentModel.AsyncCompletedEventHandler(Completed);
downloadClient.DownloadProgressChanged += new DownloadProgressChangedEventHandler(ProgressChanged);
Uri URL = new Uri(urlAddress);
if (location == Environment.GetFolderPath(Environment.SpecialFolder.Desktop))
{
location = location + "\\" + OriginalFileName(urlAddress);
this._location = location;
}
else location = _location;
_downloadStopWatch.Start();
if (File.Exists(location))
{
File.Delete(location);
}
if (_isFile == true)
{
try
{
downloadClient.DownloadFileAsync(URL, location);
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
_isFile = false;
}
}
}
}
private string OriginalFileName(string urlAddress)
{
var requestFile = (HttpWebRequest)WebRequest.Create(urlAddress);
requestFile.Method = "HEAD";
HttpWebResponse responseFile = null;
string originalFileName = "";
try
{
responseFile = (HttpWebResponse)requestFile.GetResponse();
}
catch (WebException exResponse)
{
MessageBox.Show(exResponse.Message);
_isFile = false;
}
if (_isFile == true)
{
int segmentLenght = responseFile.ResponseUri.Segments.Length;
originalFileName = responseFile.ResponseUri.Segments[segmentLenght - 1];
responseFile.Close();
}
return originalFileName;
}
To further clarify:
I provide URL for existing file 1. All is ok.
I provide URL for existing file 2. All is ok.
I provide URL for non-exisitng file 3. It throws me an 404 error.
I provide URL for existing file 4. I get program hangup and timeout.
Wrap your HttpWebResponse inside a using statement so that the object can be disposed correctly.
Something like this:
try
{
using(responseFile = (HttpWebResponse)requestFile.GetResponse())
{
...your code...
}
}
catch (WebException exRequestFile)
{
MessageBox.Show(exRequestFile.Message);
closeRequest = false;
}
"using" reference: https://msdn.microsoft.com/en-us/library/yh598w02.aspx
I need to search all connected drives (logical as well as physical) for a specific file type (e.g. mp4). I know that I can write a recursive function to do so. But I am looking for a most efficient way as this may be a time and CPU consuming operation.
Finally I made it work. The code is as below:
static List<string> SearchFiles(string pattern)
{
var result = new List<string>();
foreach (string drive in Directory.GetLogicalDrives())
{
Console.WriteLine("searching " + drive);
var files = FindAccessableFiles(drive, pattern, true);
Console.WriteLine(files.Count().ToString() + " files found.");
result.AddRange(files);
}
return result;
}
private static IEnumerable<String> FindAccessableFiles(string path, string file_pattern, bool recurse)
{
Console.WriteLine(path);
var list = new List<string>();
var required_extension = "mp4";
if (File.Exists(path))
{
yield return path;
yield break;
}
if (!Directory.Exists(path))
{
yield break;
}
if (null == file_pattern)
file_pattern = "*." + required_extension;
var top_directory = new DirectoryInfo(path);
// Enumerate the files just in the top directory.
IEnumerator<FileInfo> files;
try
{
files = top_directory.EnumerateFiles(file_pattern).GetEnumerator();
}
catch (Exception ex)
{
files = null;
}
while (true)
{
FileInfo file = null;
try
{
if (files != null && files.MoveNext())
file = files.Current;
else
break;
}
catch (UnauthorizedAccessException)
{
continue;
}
catch (PathTooLongException)
{
continue;
}
yield return file.FullName;
}
if (!recurse)
yield break;
IEnumerator<DirectoryInfo> dirs;
try
{
dirs = top_directory.EnumerateDirectories("*").GetEnumerator();
}
catch (Exception ex)
{
dirs = null;
}
while (true)
{
DirectoryInfo dir = null;
try
{
if (dirs != null && dirs.MoveNext())
dir = dirs.Current;
else
break;
}
catch (UnauthorizedAccessException)
{
continue;
}
catch (PathTooLongException)
{
continue;
}
foreach (var subpath in FindAccessableFiles(dir.FullName, file_pattern, recurse))
yield return subpath;
}
}
You can utilize the dir command for this, and the let the filesystem do what it's good at.
static string[] SearchFiles(params string[] patterns)
{
var searchPatterns = DriveInfo.GetDrives()
.Where(d => d.IsReady && d.DriveType != DriveType.NoRootDirectory)
.SelectMany(d => patterns.Select(p => d.RootDirectory + p));
using (var process = new Process())
{
process.StartInfo.FileName = Path.Combine(Environment.SystemDirectory, "cmd.exe");
process.StartInfo.Arguments = "/C dir " + String.Join(" ", searchPatterns) + " /s/b";
process.StartInfo.UseShellExecute = false;
process.StartInfo.RedirectStandardOutput = true;
process.StartInfo.CreateNoWindow = true;
process.Start();
string strOutput = process.StandardOutput.ReadToEnd();
process.WaitForExit();
return strOutput.Split(Environment.NewLine.ToArray());
}
}
and use it like:
var files = SearchFiles("*.jpg", "*.mp?", "*.mpeg");
Since this operation might take sometime, you could use a BackgroundWorker for running it in a background thread.
Also, since the output might be quite large, you might consider outputing to a file (e.g. by adding > out.txt after /s/b) and processing the file, instead of returning an array of strings.
EDIT:
You could improve performance by searching drives in parallel.
static List<string> SearchFiles(params string[] patterns)
{
var result = new List<string>();
var drives = DriveInfo.GetDrives();
Parallel.ForEach(drives, drive =>
{
if (!drive.IsReady || drive.DriveType == DriveType.NoRootDirectory)
return;
var searchPatterns = patterns.Select(p => drive.RootDirectory + p);
using (var process = new Process())
{
process.StartInfo.FileName = Path.Combine(Environment.SystemDirectory, "cmd.exe");
process.StartInfo.Arguments = "/C dir " + String.Join(" ", searchPatterns) + " /s/b";
process.StartInfo.UseShellExecute = false;
process.StartInfo.RedirectStandardOutput = true;
process.StartInfo.CreateNoWindow = true;
process.Start();
string strOutput = process.StandardOutput.ReadToEnd();
process.WaitForExit();
result.AddRange(strOutput.Split(Environment.NewLine.ToArray(), StringSplitOptions.RemoveEmptyEntries));
}
});
return result;
}