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.
Related
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..
I am working on a solution where I want to get parameters from a Builddefinition per Code. When I hit it, I get an error message "No build definition was found for
team project ToyStory with name Spass-mit-Flaggen."
The used code is written below:
var tfsCreds = new TfsClientCredentials(new WindowsCredential(), false);
var tpc = new TfsTeamProjectCollection(new Uri(options.CollectionUri), tfsCreds);
var buildServer = (IBuildServer)tpc.GetService(typeof(IBuildServer));
var buildDetail = buildServer.GetBuild(new Uri(options.BuildUri));
var buildDefinition = buildServer.GetBuildDefinition(
buildDetail.TeamProject,
options.BuildDefinition);
The options object contains all program parameters. In this case they are the following strings:
options.CollectionUri == "http://tfs-test:8080/tfs/Test/"
options.BuildUri == "vstfs:///Build/Build/85"
options.BuildDefiniton == "Spass-mit-Flaggen"
Has someone an idea what's going wrong here?
Thanks in advance
You're using the old SOAP API for accessing builds. The new build system introduced in TFS 2015 doesn't use SOAP messaging, it has a totally separate REST API. You'll need to use the REST API, available in easily-consumable object model form on NuGet.
Is there any way to get the information about Launching identity of DCOM application programmatically. See the picture attached to understand what i mean.
I tried to use WMI
ManagementObjectSearcher s = new ManagementObjectSearcher(new ManagementScope(#"\\.\root\cimv2"), new ObjectQuery(
"select * from Win32_DCOMApplicationSetting where AppID='{048EB43E-2059-422F-95E0-557DA96038AF}'"))
ManagementObjectCollection dcomSett = s.Get();
var value = dcomSett.Cast<ManagementObject>().ToArray()
[0].Properties["RunAsUser"].Value;
but "RunAsUser" property was empty.
Also tried Interop.COMAdmin
COMAdmin.COMAdminCatalogClass catalog = (COMAdmin.COMAdminCatalogClass)new COMAdmin.COMAdminCatalog();
(COMAdmin.COMAdminCatalogCollection)catalog.GetCollection("Applications")
in this way i managed to get applications which are listed under the "COM+ Applications" node in the "Component Services" snap-in of MMC:
I'm new in COM, DCOM, COM+ stuff and sure that i missed something important.
After a while i found out why i used to get NULL in the first approach (ManagementObject).
You will receive:
NULL if identity is currently set to The launching user
"Interactive User" in case of "The interactive user"
some string with username in case of third option (see the first picture)
But still i need a way to change identity for items like Microsoft PowerPoint Slide under DCOM Config node in MMC.
In the DCOM config, if you are using a specific user for the identity and you want to update the password via code, you need to update it in the Local Security Authority (LSA). This is possible with Windows API calls. MS has some sample code for a utility called dcomperm that does it, and you can see how they implemented in C++. You could make the same calls in C#. See the SetRunAsPassword method here. They are using the method LsaOpenPolicy to get a handle to the policy and calling LsaStorePrivateData to update the password. Then they are adding "login as a batch job" access to the account (but that shouldn't be necessary if you are only changing the password).
This sample code on pinvoke.net looks like it is making the requisite calls, except for the optional part about granting the login as a batch job permission. Note the "key" in the LSA is in the format SCM:{GUID-of-DCOM-object} Example: SCM:{00000000-0000-0000-0000-000000000000}
Oh, and I should mention as an aside that if you wanted to change the RunAs user itself (i.e. the username), you'd need to also update that in the windows registry directly (AFAIK that's the only way to do it). DCOM entries are stored under HKLM\SOFTWARE\Classes\AppID. You can do that with WMI or just use the Registry classes in .NET.
This is very simple , you can get APPId from
Computer\HKEY_LOCAL_MACHINE\SOFTWARE\Classes\AppID\{048EB43E-2059-422F-95E0-557DA96038AF}
using
(RegistryKey dcomPPTIdentity = Registry.LocalMachine.OpenSubKey("Software\\Classes\\AppID\\{048EB43E-2059-422F-95E0-557DA96038AF}"))
{
if (dcomPPTIdentity != null)
{
Registry.SetValue(dcomPPTIdentity.ToString(), "RunAs", "userName");
}
}
I am using COMAdmin DLL successfully. Try something like this:
COMAdminCatalog catalog = new COMAdminCatalog();
COMAdminCatalogCollection applications = catalog.GetCollection("Applications");
applications.Populate();
for (int i = 0; i < applications.Count; i++)
{
COMAdminCatalogObject application = COMAppCollectionInUse.Item[i];
if (application.Name == "Your COM+ application name")
{
application.Value["Identity"] = "nt authority\\localservice"; // for example
}
}
This works for me on my development server. Keep in mind, it is run against the server directly on the server
using COMAdmin;
using System;
namespace ComComponents
{
class Program
{
static void Main(string[] args)
{
COMAdminCatalog catalog = new COMAdminCatalog();
COMAdminCatalogCollection applications = catalog.GetCollection("Applications");
applications.Populate();
for (int i = 0; i < applications.Count; i++)
{
COMAdminCatalogObject application = applications.Item[i];
Console.WriteLine(application.Name);
Console.WriteLine(application.Value["Identity"]);
}
Console.WriteLine("Press any key to continue...");
Console.ReadKey();
}
}
}
I'm writing a Windows service that relies on other services, how should I wait for the other services to start?
Thanks
In addition to what other answers have alredy pointed out, if one of those services is SQL Server you will need to ensure that the specific database is available as well as the SQL Server service itself.
I use a function similar to the following:
public class DbStatus
{
public static bool DbOnline()
{
const int MaxRetries = 10;
int count = 0;
while (count < MaxRetries)
{
try
{
// Just access the database. any cheap query is ok since we don't care about the result.
return true;
}
catch (Exception ex)
{
Thread.Sleep(30000);
count++;
}
}
return false;
}
}
I think you shoud this line
installer.ServicesDependedOn = new string [] { "DependenceService" };
like this:
using (ServiceProcessInstaller processInstaller = new ServiceProcessInstaller())
{
processInstaller.Account = ServiceAccount.LocalSystem;
processInstaller.Username = null;
processInstaller.Password = null;
using (ServiceInstaller installer = new ServiceInstaller())
{
installer.DisplayName = "yourservice.";
installer.StartType = ServiceStartMode.Automatic;
installer.ServiceName = "YourService";
installer.ServicesDependedOn = new string [] { "DependenceService" };
this.Installers.Add(processInstaller);
this.Installers.Add(installer);
}
}
good luck
You need to specify the dependencies. You can do this in your Installer class.
Further clarification
You should be using an Installer class as a Custom Action in your setup project to install your service. If you aren't, post a comment and I'll update this answer with steps on how to do that.
Inside the designer for your Installer class you should see two components: a serviceInstaller and a serviceProcessInstaller. I don't remember which off of the top of my head, but one of these has a property that allows you to specify a multiline string that lists the service names of your service's dependencies.
As others here said, you should use the ServiceInstaller Class, but you don't need a full blowned setup project.
You can do a quick instalation using InstallUtil.exe, a command-line utility that comes with the .NET Framework.
Do you have control over the other services?
If yes have them start you, if not I guess you have to start anyway and monitor yourself what is going on.
It is possible to register yourself with WMI to get notifyied when other processes are started - there is a question about it.
In your service project, add a project installer as described here. One of the properties of your ProjectInstaller will be ServicesDependedOn. When you add services to that array of strings (you can do that through the IDE), they will be required to be started before your service will start. If they are not started the SCM will try and start them.
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.