I am writing an app to check to see if certain software is installed. One of my cases im looking for a service. I know the full path of the service. i.e. "c:\some folder\MyService.exe" I want to check to see if the service is installed and running. I have tried process.GetProcessbyName, but running into issues with 64 bit vs 32 bit processes. I've also tried ManagementObject but i keep getting invalid object path. Is it possible to get a service knowing only the path to the executable?
I know only the name and path of the executable. There may be more than one version of the executable as well, each with a different service name, which i do not have.
Here is how you can check if the service is installed or not , also get the status of the service
public static string CheckService(string ServiceName)
{
//check service
var services = ServiceController.GetServices();
string serviceStatu = string.Empty;
bool isServiceExist = false;
foreach (var s in services)
{
if (s.ServiceName == ServiceName)
{
serviceStatu = "Service installed , current status: " + s.Status;
isServiceExist = true;
}
}
if (!isServiceExist)
{
serviceStatu= "Service is not installed";
}
return serviceStatu;
}
Console.WriteLine(CheckService("Service name"));
you need to add System.ServiceProcess to the project reference
Try looking into the ServiceController / Management object for the executable path. Then based the executable path determine whether the service is running.
How to get executable path : [1] [2] [3]
Borrowed from an answer above
ManagementClass mc = new ManagementClass("Win32_Service");
foreach(ManagementObject mo in mc.GetInstances())
{
if(mo.GetPropertyValue("PathName").ToString().Trim('"') == "<your executable path>")
{
return mo.GetPropertyValue("Name").ToString(); // or return true;
}
}
I haven't tested this, and a comment suggested PathName may return command line arguments as well, so you may need to write another method to separate the path from the arguments (I'm assuming it'll just be a split on the string), and pass PathName to it in If statement..
Related
I am trying to open a web page using the default browser when someone hits an API endpoint.
I have this working on my local test machine:
[HttpGet("Http/{classId}")]
public void OpenWebLink(Guid classId)
{
string target = "http://astrolab.meeting.trl.edu/class/details.aspx?classId=" + classId;
System.Diagnostics.Process.Start("C:\\Program Files\\Mozilla Firefox\\firefox.exe", target);
}
But when I publish to a server that has IIS, it can't find firefox.exe
The problem is, I had to put the full path to firefox just to get it to work on my machine. If I didn't include the path like that I'd get this error:
System.Diagnostics.Process.Start Win32Exception: 'The system cannot find the file specified.'
I also tried this:
[HttpGet("Http")]
public void OpenWebLink(Guid classId)
{
try
{
var ps = new ProcessStartInfo("http://astrolab.meeting.trl.edu/class/details.aspx?classId=" + classId;)
{
Verb = "open"
};
Process.Start(ps);
}
catch (Win32Exception w32Ex)
{
throw w32Ex;
}
}
But it still fails when I hit the endpoint on the IIS server with this:
System.ComponentModel.Win32Exception (2): The system cannot find the file specified.
Is there a way to set it up so that it will find the default browser on any machine?
Thanks!
It's as if dotnet developers are unaware of other operating systems besides Windows...
#Blindy's answer only works on Windows, but nowhere did they indicate this. And even if #SkyeBoniwell's question mentions IIS which implies Windows, neither the title of the question, nor the body, nor the tags explicitly mention Windows. As such, keeping in mind stackoverflow answers are meant to be used by everyone and not only the OP of a question, a correct answer to this thread should theoretically be os-agnostic. In practice, it should take into account as many operating systems as possible, i.e. at the very least the main three ones.
Here is a solution that works for 99% of end-user systems:
public static void OpenBrowser(string url)
{
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
Process.Start(new ProcessStartInfo(url) { UseShellExecute = true });
}
else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
{
Process.Start("xdg-open", url);
}
else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
{
Process.Start("open", url);
}
else
{
// throw
}
}
taken from here
Either use ShellExecute to launch your url (the more correct way), or pipe it through explorer (the lazier, less portable way).
Process.Start(new()
{
UseShellExecute = true,
FileName = "http://google.ca",
});
Process.Start("explorer.exe", "http://google.ca");
I am making a software in C# and MATLAB that calls another software (CMG) to do some processing. My problem is that the address of the software I have put in my program is only correct on my personal computer and not on the customers' computers (I don't know what would be the path to CMG software on their computer).
How can I provide a general form of the address in order to make it work on every computer?
The following is the path I call from my MATLAB software:
C:\Program Files (x86)\CMG\STARS\2011.10\Win_x64\EXE\st201110.exe
As you see it is in drive C and the version is 2011.10. So if customer's version is something else and it is installed on other drives, this path makes no sense.
Method 1
The registry keys SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall provides a list of where most applications are installed:
Note: It doesn't list all EXE applications on the PC as some dont require installation.
In your case I am pretty sure that CMG STARS will be listed and you will be able to search for it by iterating over all subkeys looking at the DisplayName value and fetching the InstallLocation.
Also note that this Uninstall registry key exists in 3 places in the registry:
1. SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall inside CurrentUser
2. SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall inside LocalMachine
3. SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall in LocalMachine
Here is an class that returns the installed location of an application:
using Microsoft.Win32;
public static class InstalledApplications
{
public static string GetApplictionInstallPath(string nameOfAppToFind)
{
string installedPath;
string keyName;
// search in: CurrentUser
keyName = #"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall";
installedPath = ExistsInSubKey(Registry.CurrentUser, keyName, "DisplayName", nameOfAppToFind);
if (!string.IsNullOrEmpty(installedPath))
{
return installedPath;
}
// search in: LocalMachine_32
keyName = #"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall";
installedPath = ExistsInSubKey(Registry.LocalMachine, keyName, "DisplayName", nameOfAppToFind);
if (!string.IsNullOrEmpty(installedPath))
{
return installedPath;
}
// search in: LocalMachine_64
keyName = #"SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall";
installedPath = ExistsInSubKey(Registry.LocalMachine, keyName, "DisplayName", nameOfAppToFind);
if (!string.IsNullOrEmpty(installedPath))
{
return installedPath;
}
return string.Empty;
}
private static string ExistsInSubKey(RegistryKey root, string subKeyName, string attributeName, string nameOfAppToFind)
{
RegistryKey subkey;
string displayName;
using (RegistryKey key = root.OpenSubKey(subKeyName))
{
if (key != null)
{
foreach (string kn in key.GetSubKeyNames())
{
using (subkey = key.OpenSubKey(kn))
{
displayName = subkey.GetValue(attributeName) as string;
if (nameOfAppToFind.Equals(displayName, StringComparison.OrdinalIgnoreCase) == true)
{
return subkey.GetValue("InstallLocation") as string;
}
}
}
}
}
return string.Empty;
}
}
Here is how you call it:
string installPath = InstalledApplications.GetApplictionInstallPath(nameOfAppToFind);
To get the nameOfAppToFind you'll need to look in the registry at the DisplayName:
REF: I modified the above code from here to return the install path.
Method 2
You can also use the System Management .Net DLL to get the InstallLocation although it is heaps slower and creates "Windows Installer reconfigured the product" event log messages for every installed product on your system.
using System.Management;
ManagementObjectSearcher mos = new ManagementObjectSearcher("SELECT * FROM Win32_Product");
foreach (ManagementObject mo in mos.Get())
{
Debug.Print(mo["Name"].ToString() + "," + mo["InstallLocation"].ToString() + Environment.NewLine);
}
Getting the EXE's name
Neither of the above methods tell you the name of the executable, however it is quite easy to work out by iterating over all the files in the install path and using a technique I discuss here to look at file properties to detect the EXE with the correct File Description, eg:
private string GetFileExeNameByFileDescription(string fileDescriptionToFind, string installPath)
{
string exeName = string.Empty;
foreach (string filePath in Directory.GetFiles(installPath, "*.exe"))
{
string fileDescription = GetSpecificFileProperties(filePath, 34).Replace(Environment.NewLine, string.Empty);
if (fileDescription == fileDescriptionToFind)
{
exeName = GetSpecificFileProperties(filePath, 0).Replace(Environment.NewLine, string.Empty);
break;
}
}
return exeName;
}
Either method (1 or 2) you use I recommend that you save the location of exe name so you only do this operation once. In my opinion its better to use Method 1 as its faster and doesn't create all the "Windows Installer reconfigured the product." event logs.
Alternate Method using an Installer
If your application is being installed you could find out where CMG STARS is located during installation Using Windows Installer to Inventory Products and Patches:
Enumerating Products
Use the MsiEnumProductsEx function to enumerate Windows Installer applications that are installed in the
system. This function can find all the per-machine installations and
per-user installations of applications (managed and unmanaged) for the
current user and other users in the system. Use the dwContext
parameter to specify the installation context to be found. You can
specify any one or any combination of the possible installation
contexts. Use the szUserSid parameter to specify the user context of
applications to be found.
During installation you would find the exe path to CMG STARS and save a registry key with the value.
I discuss using this approach of saving an EXE's install path in the registry for updating applications here.
Tip
As mentioned in the comments, it is worthwhile you do a search in the registry for the EXE's name st201110.exe and see if the authors of the CMG STAR application already provide this information in a registry key you can access directly.
Plan B
If all else fails present the user with a FileOpenDialog and get them to specify the exe's path manually.
What if the 3rd party application is uninstalled or upgraded?
I mentioned to store the install path and exe name in the registry (or database, config file, etc) and you should always check the exe file exists before making any external calls to it, eg:
if (!File.Exists(installPath + exeName))
{
//Run through the process to establish where the 3rd party application is installed
}
I'm looking for a C# version of the following command:
sc config "someServiceName" start=auto
I've found a lot of information about configuring a service to start automatically as you install it, but I'm having trouble finding out how to do the same for an existing service.
Right now, I've resorted to shelling it out, but if there's a way to use .NET APIs, I'd much rather do that.
This should do the trick:
var serviceName = "<your service name here>";
string objPath = string.Format("Win32_Service.Name='{0}'", serviceName);
using (var service = new ManagementObject(new ManagementPath(objPath)))
{
var result = (int)service.InvokeMethod("ChangeStartMode", new object[] {"Automatic"});
}
You'll need to add a reference to the System.Management assembly, as well as import the namespace System.Management.
Note that your program must elevated (running as an Administrator) for this to work, and there is no way around that. For other possible values for ChangeStartMode, you can refer to MSDN.
The result variable will be a numeric value that indicates the result. For example, 0 for Success. Refer the the previously linked MSDN article for all of the possible return values.
var serviceName = "<your service name here>";
string objPath = string.Format("Win32_Service.Name='{0}'", serviceName);
using (var service = new ManagementObject(new ManagementPath(objPath)))
{
service.InvokeMethod("ChangeStartMode", new object[] {"Automatic"});
}
You'll need to add a reference to the System.Management assembly, as well as import the namespace System.Management.
I'm writing a Web Service (WCF) for my work and I'm looking for a way to
run script on demand on other machine.
We got machines that we connect from RDC, and I want to run a script on it
from another C# program.
Also, I can't seem to find a way to run an executable file on another machine from C#.
The reason why you can't find a part of the .Net framework that lets you run executables on another machine is because there isn't one.
If you want a straightfoward way of running an executable on a remote machine then you may be interested in PsExec (a Sysinternals tool released by Micrososft).
this is possible using WMI via C# (see http://www.codeproject.com/KB/cs/Remote_Process_using_WMI_.aspx) or via commandline using http://technet.microsoft.com/en-us/sysinternals/bb897553.aspx ... but it is something you usually should NOT do - it creates several security issues to deal with...
EDIT - WMI with User/PW:
connectionoptions gives you the possibility to supply a UserName + Password - see http://msdn.microsoft.com/en-us/library/system.management.connectionoptions.aspx
I know this is an old post, but I would like to add that it is certainly possible to run a remote executable without PsExec which many virus software flags as problematic. Also most sys admins don't want PsExec on their web servers. And, running a remote executable via mapped drive or UNC does not mean that you have the license installed, so it may fail (or run a demo version) depending on the software.
The key is to wrap the System.Diagnostics.Process steps in a WCF service. Here is a partial example...
{
// Define a service contract.
[ServiceContract(Namespace = "http://MyDataServices.foo.com")]
public interface IDataService
{
[OperationContract]
bool ProcessStatTransfer(MemoryStream inputCommands, string inputName);
}
// Implement the IDataService service contract in a service class.
public class DataService : IDataService
{
// Implement the IDataService methods.
public bool ProcessStatTransfer(MemoryStream inputCommands, string inputName)
{
try
{
// StatTransferLocation is C:\Program Files\StatTransfer12-64\st.exe
// on the machine that hosts the service
string m_stattransfer_loc = ConfigurationManager.AppSettings["StatTransferLocation"].ToString();
string m_stattransfer_file = ConfigurationManager.AppSettings["CommandFiles"].ToString() + inputName;
using (FileStream m_fsfile = new FileStream(m_stattransfer_file, FileMode.Create, FileAccess.Write))
{
inputCommands.WriteTo(m_fsfile);
}
ProcessStartInfo processInfo = new ProcessStartInfo("\"" + m_stattransfer_loc + "\"");
processInfo.Arguments = "\"" + m_stattransfer_file + "\"";
processInfo.UseShellExecute = false;
processInfo.ErrorDialog = false;
processInfo.CreateNoWindow = true;
Process batchProcess = new Process();
batchProcess.StartInfo = processInfo;
batchProcess.Start();
return true;
}
catch
{
return false;
}
}
.....
Then you add a service reference and invoke the method. No mappings, PsExec, or WMI. It is a pure C# solution.
If I right-click and choose Properties on a service (like, say, Plug and Play) in the Services dialog, I get several pieces of information, including "Path to executable". For Plug and Play (in Vista) this is:
C:\Windows\system32\svchost.exe -k DcomLaunch
Is there some way I can get this same piece of information using .NET code if I know the service name (and/or the display name)?
(I can't use GetExecutingAssembly() because I'm not running the service from my project.)
Another option, without the interop, would be a WMI lookup (or registry - bit hacky!).
Here's a quick example, based on this code:
private static string GetServiceImagePathWMI(string serviceDisplayName)
{
string query = string.Format("SELECT PathName FROM Win32_Service WHERE DisplayName = '{0}'", serviceDisplayName);
using (ManagementObjectSearcher search = new ManagementObjectSearcher(query))
{
foreach(ManagementObject service in search.Get())
{
return service["PathName"].ToString();
}
}
return string.Empty;
}
This information is in the QUERY_SERVICE_CONFIG structure. You will need to use P/Invoke to get it out.
The basic process is:
Call OpenSCManager to get a handle to the services managed.
Call OpenService to get a handle to the service.
Call QueryServiceConfig to get the QUERY_SERVICE_CONFIG structure.
There's always the WMI class Win32_Service as described here, specifically the PathName.
This works:
ManagementClass mc = new ManagementClass("Win32_Service");
foreach(ManagementObject mo in mc.GetInstances())
{
if(mo.GetPropertyValue("Name").ToString() == "<Short name of your service>")
{
return mo.GetPropertyValue("PathName").ToString().Trim('"');
}
}
If you have any issue related to Reference then add a reference of System.Management in your project.