how to find out the path of the program using c# - c#

I found a snippet of code explaining how to use System.Diagnostics.Process.Start to run an external program in C#. The snippet shows running cmd.exe, which is in the path.
Let's assume that there is some external program (Beyond Compare for example). I don't know if it is installed on the PC. How can I check if this program is installed using C#? If the program is installed, I would like to find the path so that I can launch it.

I found this question, which directed me to this this article.
I've modified the source for readability, and to solve your problem specifically (note that I've guessed the description and executable name of Beyond Compare.)
You can call it like this, from your main:
string path = FindAppPath("Beyond Compare");
if (path == null)
{
Console.WriteLine("Failed to find program path.");
return;
}
path += "BeyondCompare.exe";
if (File.Exists(path))
{
Process beyondCompare = new Process()
{
StartInfo = new ProcessStartInfo()
{
FileName = path + "BeyondCompare.exe",
Arguments = string.Empty // You may need to specify args.
}
};
beyondCompare.Start();
}
The source for FindAppPath follows:
static string FindAppPath(string appName)
{
// If you don't use contracts, check this and throw ArgumentException
Contract.Requires(!string.IsNullOrEmpty(appName));
const string keyPath =
#"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall";
using (RegistryKey key = Registry.LocalMachine.OpenSubKey(keyPath))
{
var installed =
(from skName in key.GetSubKeyNames()
let subkey = key.OpenSubKey(skName)
select new
{
name = subkey.GetValue("DisplayName") as string,
path = subkey.GetValue("InstallLocation") as string
}).ToList();
var desired = installed.FindAll(
program => program.name != null &&
program.name.Contains(appName) &&
!String.IsNullOrEmpty(program.path));
return (desired.Count > 0) ? desired[0].path : null;
}
}
Keep in mind that this method returns the first matching path, so don't feed it an appName argument that's too generic (eg. "Microsoft") or you probably won't get what you're looking for.

Well, if you're trying to see if a program exists where you're looking for it (like BeyondCompare.exe), you can just use a call to:
System.IO.File.Exists("path_to_program.exe");
If it returns true, then you know your program exists and you can run it with your process runner code. If it returns false, then you know it's not there and you shouldn't launch your process.
If I'm misunderstanding your question, please let me know and I'll update my answer accordingly.
Thanks. Hope this helps!

Simple logic to do for this.
string filepath = "c:\windows\test.exe";
bool bOk = false;
try
{
bOk = System.IO.File.Exists(filepath);
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
if (!bOk)
{
Console.WriteLine("Error: Invalid Path");
}
else
{
Process p = new Process();
p.StartInfo.FileName = filepath;
p.StartInfo.Arguments = "/c dir *.cs";
p.StartInfo.UseShellExecute = false;
p.StartInfo.RedirectStandardOutput = true;
p.Start();
string output = p.StandardOutput.ReadToEnd();
p.WaitForExit();
Console.WriteLine("Output:");
Console.WriteLine(output);
}

Are you sure you don't need to make that check. Simply start the program without path (only the filename) and set ProcessStartInfo.UseShellExecute = true.
Windows will look for the app in its list of installed app. If it doesn't find it, Process.Start()will fail. The interesting thing is that you never had to care about where the app is stored.

Related

Getting "File already in Use by other process" when using File.Move, why/how can I fix this?

I want to move several files that names are saved in an ObservableCollection<String> _collection with this method:
string firstFolderThatContainsEveryFile = "...\Folder\Files";
string secondFolderArchiv = "...\Folder\Files\Archiv";
foreach (var item in _collection)
{
string firstFolder = System.IO.Path.Combine(firstFolderThatContainsEveryFile, item);
string secondFolder = System.IO.Path.Combine(secondFolderArchiv, item);
File.Move(firstFolder, secondFolder);
}
This works at the first time, but if i load new files into firstFolderThatContainsEveryFile and try to use my move method i get an exception:
File already in Use by other process
This are the steps:
I open the programm -> use the move method -> success -> close the programm -> fill the folder with new files -> open the programm -> use the move method -> exception!
How can i get the processname or processID to close the process before i use my move method, or is there even a better way to get around this?
To figure out which process are using your file, with the solution proposed by this, you can use a tool Handle of Microsoft and this code C# to invoke the tool.
public void ViewProcess(string filePath)
{
Process tool = new Process();
tool.StartInfo.FileName = "handle.exe";
tool.StartInfo.Arguments = filePath + " /accepteula";
tool.StartInfo.UseShellExecute = false;
tool.StartInfo.RedirectStandardOutput = true;
tool.Start();
tool.WaitForExit();
string outputTool = tool.StandardOutput.ReadToEnd();
string matchPattern = #"(?<=\s+pid:\s+)\b(\d+)\b(?=\s+)";
foreach (Match match in Regex.Matches(outputTool, matchPattern))
{
try{
Console.WriteLine(match.Value); // this is the process ID using the file
}
catch(Exception ex)
{
}
}
}
If the file is using by the others program, you should figure why they use it, if by your program, so recheck your code to understand why.

Rename computer name with .NET

I am trying to rename a computer name from a C# application.
public class ComputerSystem : IComputerSystem
{
private readonly ManagementObject computerSystemObject;
public ComputerSystem()
{
var computerPath = string.Format("Win32_ComputerSystem.Name='{0}'", Environment.MachineName);
computerSystemObject = new ManagementObject(new ManagementPath(computerPath));
}
public bool Rename(string newComputerName)
{
var result = false;
var renameParameters = computerSystemObject.GetMethodParameters("Rename");
renameParameters["Name"] = newComputerName;
var output = computerSystemObject.InvokeMethod("Rename", renameParameters, null);
if (output != null)
{
var returnValue = (uint)Convert.ChangeType(output.Properties["ReturnValue"].Value, typeof(uint));
result = returnValue == 0;
}
return result;
}
}
The WMI call returns error code 1355.
MSDN doesn't mention much about error codes, what does it mean and how can I fix it?
Error code 1355 means ERROR_NO_SUCH_DOMAIN: "The specified domain either does not exist or could not be contacted.".
The documentation for the Rename method states that the name must contain the domain name. For a non-domain-joined machine, try .\NewName instead of just NewName.
It's very difficult to update the PC name using any external methods due to protection of the system. The best way to do so is to use the Windows own utility of WMIC.exe to rename the PC. Just launch the wmic.exe from C# and pass rename command as argument.
exit code 0
>
public void SetMachineName(string newName)
{
// Create a new process
ProcessStartInfo process = new ProcessStartInfo();
// set name of process to "WMIC.exe"
process.FileName = "WMIC.exe";
// pass rename PC command as argument
process.Arguments = "computersystem where caption='" + System.Environment.MachineName + "' rename " + newName;
// Run the external process & wait for it to finish
using (Process proc = Process.Start(process))
{
proc.WaitForExit();
// print the status of command
Console.WriteLine("Exit code = " + proc.ExitCode);
}
}

How to redirect output from the NASM command line assembler in C#

Brief Summary
I am creating a lightweight IDE for NASM development in C# (I know kind of an irony). Kinda of like Notepad++ but simpler but with features that make it more than source editor. Since Notepad++ is really just a fancy source editor. I have already implemented features like Project creation (using a project format similar to how Visual Studio organizes projects). Project extension .nasmproj. I am also in the works of hosting it in an open-source place (Codeplex). Although the program is far from finish, and definitely cannot be used in a production environment without proper protection and equipment. In addition, I am working alone with it at this moment, more like a spare time project since I just finished my last Summer final taking Calculus I.
Problem
Right now I am facing a problem, I can build the project but no output from NASM is being fed into the IDE. I have succesfully built a project, and I was able to produce object files. I even tried producing a syntax error to see if I finally see something come up but none and I check the bin folder of the test project I created and I see no object file creating. So definitely NASM is doing its magic. Is it because NASM doesn't want me to see its output. Is there a solution? Any advice would be great. Here is the code which I think is giving Trouble.
Things to Note
I have already checked if events have been invoked. An yes they have but they return empty strings
I have also checked error data and same effect.
Code
public static bool Build(string arguments, out Process nasmP)
{
try
{
ProcessStartInfo nasm = new ProcessStartInfo("nasm", arguments);
nasm.CreateNoWindow = true;
nasm.RedirectStandardError = true;
nasm.RedirectStandardInput = true;
nasm.RedirectStandardOutput = true;
nasm.UseShellExecute = false;
nasmP = new Process();
nasmP.EnableRaisingEvents = true;
nasmP.StartInfo = nasm;
bool predicate = nasmP.Start();
nasmP.BeginOutputReadLine();
return true;
}
catch
{
nasmP = null;
return false;
}
}
//Hasn't been tested nor used
public static bool Clean(string binPath)
{
if (binPath == null || !Directory.Exists(binPath))
{
throw new ArgumentException("Either path is null or it does not exist!");
}
else
{
try
{
DirectoryInfo binInfo = new DirectoryInfo(binPath);
FileInfo[] filesInfo = binInfo.GetFiles();
for (int index = 0; index < filesInfo.Length; index++)
{
try
{
filesInfo[index].Delete();
filesInfo[index] = null;
}
catch
{
break;
}
}
GC.Collect();
return true;
}
catch
{
return false;
}
}
}
}
using (BuildDialog dlg = new BuildDialog(currentSolution))
{
DialogResult result = dlg.ShowDialog();
dlg.onOutputRecieved += new BuildDialog.OnOutputRecievedHandler(delegate(Process _sender, string output)
{
if (result == System.Windows.Forms.DialogResult.OK)
{
outputWindow.Invoke(new InvokeDelegate(delegate(string o)
{
Console.WriteLine("Data:" + o);
outputWindow.Text = o;
}), output);
}
});
}
Edits
I have tried doing synchronously instead of asynchronously but still the same result (and empty string "" is returned) actually by debugging the stream is already at the end. So looks like nothing has been written into the stream.
This is what I tried:
string readToEnd = nasmP.StandardOutput.ReadToEnd();
nasmP.WaitForExit();
Console.WriteLine(readToEnd);
And another interesting thing I have tried was I copied the arguments from the debugger and pasted it in the command line shell and I can see NASM compiling and giving the error that I wanted to see all along. So definitely not a NASM problem. Could it be a problem with my code or the .Net framework.
Here is a nice snapshot of the shell window (although not technically proof; this is what the output should look like in my IDE):
Alan made a very good point, check the sub processes or threads. Is sub process and thread synonymous? But here is the problem. Almost all the properties except a select few and output/error streams are throwing an invalid operation. Here is the debugger information as an image (I wish Visual Studio would allow you to copy the entire information in click):
Okay I finally was able to do it. I just found this control that redirect output from a process and I just looked at the source code of it and got what I needed to do. Here is the the modified code:
public static bool Build(string command, out StringBuilder buildOutput)
{
try
{
buildOutput = new StringBuilder();
ProcessStartInfo startInfo = new ProcessStartInfo("cmd.exe");
startInfo.Arguments = "/C " + " nasm " + command;
startInfo.RedirectStandardError = true;
startInfo.RedirectStandardOutput = true;
startInfo.UseShellExecute = false;
startInfo.CreateNoWindow = true;
Process p = Process.Start(startInfo);
string output = p.StandardOutput.ReadToEnd();
string error = p.StandardError.ReadToEnd();
p.WaitForExit();
if (output.Length != 0)
buildOutput.Append(output);
else if (error.Length != 0)
buildOutput.Append(error);
else
buildOutput.Append("\n");
return true;
}
catch
{
buildOutput = null;
return false;
}
}
Here is how the output is formatted like:
I also wanted to thank Alan for helping me debug my code, although he didn't physically had my code. But he really was helpful and I thank him for it.

How can I launch a URL in the users default browser from my application?

How can I have a button in my desktop application that causes the user's default browser to launch and display a URL supplied by the application's logic.
Process.Start("http://www.google.com");
Process.Start([your url]) is indeed the answer, in all but extremely niche cases. For completeness, however, I will mention that we ran into such a niche case a while back: if you're trying to open a "file:\" url (in our case, to show the local installed copy of our webhelp), in launching from the shell, the parameters to the url were thrown out.
Our rather hackish solution, which I don't recommend unless you encounter a problem with the "correct" solution, looked something like this:
In the click handler for the button:
string browserPath = GetBrowserPath();
if (browserPath == string.Empty)
browserPath = "iexplore";
Process process = new Process();
process.StartInfo = new ProcessStartInfo(browserPath);
process.StartInfo.Arguments = "\"" + [whatever url you're trying to open] + "\"";
process.Start();
The ugly function that you shouldn't use unless Process.Start([your url]) doesn't do what you expect it's going to:
private static string GetBrowserPath()
{
string browser = string.Empty;
RegistryKey key = null;
try
{
// try location of default browser path in XP
key = Registry.ClassesRoot.OpenSubKey(#"HTTP\shell\open\command", false);
// try location of default browser path in Vista
if (key == null)
{
key = Registry.CurrentUser.OpenSubKey(#"Software\Microsoft\Windows\Shell\Associations\UrlAssociations\http", false); ;
}
if (key != null)
{
//trim off quotes
browser = key.GetValue(null).ToString().ToLower().Replace("\"", "");
if (!browser.EndsWith("exe"))
{
//get rid of everything after the ".exe"
browser = browser.Substring(0, browser.LastIndexOf(".exe") + 4);
}
key.Close();
}
}
catch
{
return string.Empty;
}
return browser;
}

Automatically starting a java-web-start application from C# and .NET, .WaitForExit(); doesn't work as desired

I'm using a C# application to launch a java web application. I'd like my C# application to know when the user closes out of the java app. I'm using this code:
var javaws = File.Exists(#"C:\Program Files\Java\jre6\bin\javaws.exe") ? #"C:\Program Files\Java\jre6\bin\javaws.exe" : #"C:\Program Files (x86)\Java\jre6\bin\javaws.exe";
var psi = new ProcessStartInfo(javaws, String.Format("http://{0}:/appstart.jnlp", hostAddress));
Process.Start(psi).WaitForExit();
This code successfully launches the java program, however, WaitForExit() is called and immediately returns. I believe this is because "javaws" simply starts another process called "javaw" and then "javaws" closes. Is there any way to A) wait for all child processes to finish too, or B) wait for a specific child process?
--
I've discovered this related post: Are javaws exit codes really broken?, though the answer there suggests that 1.6.0_23 has fixed it, I am seeing the exact same behavior on my development machine with Java 1.6.0_23. What I am looking for now is a workaround to make the above code work as expected. I need to start this jnlp file, wait for its execution to complete, and then do some additional code in C# program. The Java application is out of my control, so I can't add the functionality there.
--
For anyone wondering, the final solution looks like this:
var javaws = File.Exists(#"C:\Program Files\Java\jre6\bin\javaws.exe") ? #"C:\Program Files\Java\jre6\bin\javaws.exe" : #"C:\Program Files (x86)\Java\jre6\bin\javaws.exe";
var psi = new ProcessStartInfo(javaws, String.Format("http://{0}:/appstart.jnlp", hostAddress));
Process.Start(psi).WaitForExit();
var javaw = Process.GetProcessesByName("javaw");
javaw.Single(ja => ja.StartTime.Equals(javaw.Max(j => j.StartTime))).WaitForExit();
This provides the added benefit of only waiting for the most recently started javaw process, in the event there are other java web apps running on the machine.
You can get desired process from list of running processes and then wait it for exit:
var javaws = File.Exists(#"C:\Program Files\Java\jre6\bin\javaws.exe") ? #"C:\Program Files\Java\jre6\bin\javaws.exe" : #"C:\Program Files (x86)\Java\jre6\bin\javaws.exe";
var psi = new ProcessStartInfo(javaws, String.Format("http://{0}:/appstart.jnlp", hostAddress));
// make sure child process is already started
Process.Start(psi).WaitForExit();
foreach (Process p in Process.GetProcessesByName("javaw"))
{
p.WaitForExit();
}
To kill not only javaw, but all of child processes you need to get process ID of javaws and compare it to parent ID of all running processes. Here is complete code:
(extension method comes from here)
class Program
{
static void Main(string[] args)
{
var javaws = File.Exists(#"C:\Program Files\Java\jre6\bin\javaws.exe") ? #"C:\Program Files\Java\jre6\bin\javaws.exe" : #"C:\Program Files (x86)\Java\jre6\bin\javaws.exe";
var psi = new ProcessStartInfo(javaws, String.Format("http://{0}:/appstart.jnlp", hostAddress));
var parentProc = Process.Start(psi);
parentProc.WaitForExit();
foreach (Process p in Process.GetProcesses())
{
if (p.Parent().Id == parentProc.Id)
p.WaitForExit();
}
}
}
public static class ProcessExtensions
{
private static string FindIndexedProcessName(int pid)
{
var processName = Process.GetProcessById(pid).ProcessName;
var processesByName = Process.GetProcessesByName(processName);
string processIndexdName = null;
for (var index = 0; index < processesByName.Length; index++)
{
processIndexdName = index == 0 ? processName : processName + "#" + index;
var processId = new PerformanceCounter("Process", "ID Process", processIndexdName);
if ((int)processId.NextValue() == pid)
{
return processIndexdName;
}
}
return processIndexdName;
}
private static Process FindPidFromIndexedProcessName(string indexedProcessName)
{
var parentId = new PerformanceCounter("Process", "Creating Process ID", indexedProcessName);
return Process.GetProcessById((int)parentId.NextValue());
}
public static Process Parent(this Process process)
{
return FindPidFromIndexedProcessName(FindIndexedProcessName(process.Id));
}
}
Looking at some documentation, it seems you can pass '-wait' as a parameter to javaws to make it wait until the application exits.
http://download.oracle.com/javase/1.5.0/docs/guide/javaws/developersguide/javaws.html

Categories

Resources