Problem:
I have a windows Service which I want to start in interactive mode.
Some of our customers are not experienced with services and our service needs to be configured in the way that it can interact with the desktop.
From the commandline I would configure it like this:
C:\Windows\system32>sc config myservice obj= LocalSystem type=
interact type= own
Since the program is started from a configuration GUI I want to set the values in C#:
ServiceController[] mySc = ServiceController.GetServices();
foreach (ServiceController sc in mySc)
{
if (sc.DisplayName == "myservice")
{
if (sc.Status == ServiceControllerStatus.Stopped)
{
//sc.ServiceType <-- readonly so i can't set it
sc.Start();
}
break;
}
}
The only way I have found which could work is to use a process object
var process = new Process();
var processStartInfo = new ProcessStartInfo(startpath);
arg += "all my arguments...";
processStartInfo.Arguments = arg;
processStartInfo.CreateNoWindow = true;
processStartInfo.WindowStyle = ProcessWindowStyle.Hidden;
process.StartInfo = processStartInfo;
process.Start();
process.WaitForExit();
My question:
Is there a way to configure the service via the ServiceController ? I saw that you can start the service via sc.Start(args[]) but I haven't found the arguments that can be passed.
With the help of an article which Michael(See the comments above) provided I created a class which enables me to configurate the service.
My code is mostly a copy of this blog post with some minor changes: Changing Start Mode of a Windows Service
Here is my Class:
public static class C_ServiceControllerExtension
{
[DllImport("advapi32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
public static extern Boolean ChangeServiceConfig(
IntPtr hService,
UInt32 nServiceType,
UInt32 nStartType,
UInt32 nErrorControl,
String lpBinaryPathName,
String lpLoadOrderGroup,
IntPtr lpdwTagId,
[In] char[] lpDependencies,
String lpServiceStartName,
String lpPassword,
String lpDisplayName);
[DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Auto)]
static extern IntPtr OpenService(
IntPtr hSCManager, string lpServiceName, uint dwDesiredAccess);
[DllImport("advapi32.dll", EntryPoint = "OpenSCManagerW", ExactSpelling = true, CharSet = CharSet.Unicode, SetLastError = true)]
public static extern IntPtr OpenSCManager(
string machineName, string databaseName, uint dwAccess);
[DllImport("advapi32.dll", EntryPoint = "CloseServiceHandle")]
public static extern int CloseServiceHandle(IntPtr hSCObject);
private const uint SERVICE_NO_CHANGE = 0xFFFFFFFF;
private const uint SERVICE_QUERY_CONFIG = 0x00000001;
private const uint SERVICE_CHANGE_CONFIG = 0x00000002;
private const uint SC_MANAGER_ALL_ACCESS = 0x000F003F;
/// <summary>
/// Changes the configuration of the service
/// </summary>
/// <param name="svc">Service controller</param>
/// <param name="mode">ServiceStartMode || 0</param>
/// <param name="type">ServiceType || 0</param>
public static bool ChangeServiceConfiguration(this ServiceController svc, ServiceStartMode mode, ServiceType type)
{
uint uMode = SERVICE_NO_CHANGE;
uint uType = SERVICE_NO_CHANGE;
if (mode > 0)
{
uMode = (uint)mode;
}
if (type > 0)
{
uType = (uint)type;
}
var scManagerHandle = OpenSCManager(null, null, SC_MANAGER_ALL_ACCESS);
if (scManagerHandle == IntPtr.Zero)
{
throw new ExternalException("Open Service Manager Error");
}
var serviceHandle = OpenService(
scManagerHandle,
svc.ServiceName,
SERVICE_QUERY_CONFIG | SERVICE_CHANGE_CONFIG);
if (serviceHandle == IntPtr.Zero)
{
throw new ExternalException("Open Service Error");
}
var result = ChangeServiceConfig(
serviceHandle,
uType,
uMode,
SERVICE_NO_CHANGE,
null,
null,
IntPtr.Zero,
null,
null,
null,
null);
if (result == false)
{
int nError = Marshal.GetLastWin32Error();
var win32Exception = new Win32Exception(nError);
return false;
//throw new ExternalException("Could not change service start type: " + win32Exception.Message);
}
CloseServiceHandle(serviceHandle);
CloseServiceHandle(scManagerHandle);
return true;
}
}
The ChangeServiceConfiguration method is an extension method so you can call the method directly on your ServiceController.
I call the method as follows:
ServiceController[] mySc = ServiceController.GetServices();
bool startedServiceCorrect = false;
foreach (ServiceController sc in mySc)
{
if (sc.DisplayName == "myservice")
{
if (sc.Status == ServiceControllerStatus.Stopped)
{
if (sc.ServiceType != (ServiceType.InteractiveProcess | ServiceType.Win32OwnProcess))
{
startedServiceCorrect = sc.ChangeServiceConfiguration(0, (ServiceType.InteractiveProcess | ServiceType.Win32OwnProcess));
}
try
{
sc.Start();
}
catch
{
startedServiceCorrect = false;
}
}
break;
}
}
If you are using .Net 3.0 and above the extension methods should work out of the box but if you are using .Net2.0 then you have to add this little namespace so the extension method will work:
Extension Method C# 2.0
namespace System.Runtime.CompilerServices
{
[AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Class
| AttributeTargets.Method)]
public sealed class ExtensionAttribute : Attribute { }
}
Related
Trying to create the windows service programmatically. Found out the reference from stackoverflow to do it. However the parameter filename in InstallAndStart method expects a .exe
In my case, my WCF project which i want to host as windows service is a Service library project. Hence the output is in .dll
How can i use the below code to achieve installing WCF as Windows Service?
The purpose is, I have many WCF projects and don't want to create multiple Windows Service projects again for each WCF project.
Reference from How to install a windows service programmatically in C#?
public static class ServiceManager
{
private const int STANDARD_RIGHTS_REQUIRED = 0xF0000;
private const int SERVICE_WIN32_OWN_PROCESS = 0x00000010;
[StructLayout(LayoutKind.Sequential)]
private class SERVICE_STATUS
{
public int dwServiceType = 0;
public ServiceState dwCurrentState = 0;
public int dwControlsAccepted = 0;
public int dwWin32ExitCode = 0;
public int dwServiceSpecificExitCode = 0;
public int dwCheckPoint = 0;
public int dwWaitHint = 0;
}
#region OpenSCManager
[DllImport("advapi32.dll", EntryPoint = "OpenSCManagerW", ExactSpelling = true, CharSet = CharSet.Unicode, SetLastError = true)]
static extern IntPtr OpenSCManager(string machineName, string databaseName, ScmAccessRights dwDesiredAccess);
#endregion
#region OpenService
[DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Auto)]
static extern IntPtr OpenService(IntPtr hSCManager, string lpServiceName, ServiceAccessRights dwDesiredAccess);
#endregion
#region CreateService
[DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Auto)]
private static extern IntPtr CreateService(IntPtr hSCManager, string lpServiceName, string lpDisplayName, ServiceAccessRights dwDesiredAccess, int dwServiceType, ServiceBootFlag dwStartType, ServiceError dwErrorControl, string lpBinaryPathName, string lpLoadOrderGroup, IntPtr lpdwTagId, string lpDependencies, string lp, string lpPassword);
#endregion
#region CloseServiceHandle
[DllImport("advapi32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool CloseServiceHandle(IntPtr hSCObject);
#endregion
#region QueryServiceStatus
[DllImport("advapi32.dll")]
private static extern int QueryServiceStatus(IntPtr hService, SERVICE_STATUS lpServiceStatus);
#endregion
#region DeleteService
[DllImport("advapi32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool DeleteService(IntPtr hService);
#endregion
#region ControlService
[DllImport("advapi32.dll")]
private static extern int ControlService(IntPtr hService, ServiceControl dwControl, SERVICE_STATUS lpServiceStatus);
#endregion
#region StartService
[DllImport("advapi32.dll", SetLastError = true)]
private static extern int StartService(IntPtr hService, int dwNumServiceArgs, int lpServiceArgVectors);
#endregion
public static void Uninstall(string serviceName)
{
IntPtr scm = OpenSCManager(ScmAccessRights.AllAccess);
try
{
IntPtr service = OpenService(scm, serviceName, ServiceAccessRights.AllAccess);
if (service == IntPtr.Zero)
throw new ApplicationException("Service not installed.");
try
{
StopService(service);
if (!DeleteService(service))
throw new ApplicationException("Could not delete service " + Marshal.GetLastWin32Error());
}
finally
{
CloseServiceHandle(service);
}
}
finally
{
CloseServiceHandle(scm);
}
}
public static bool ServiceIsInstalled(string serviceName)
{
IntPtr scm = OpenSCManager(ScmAccessRights.Connect);
try
{
IntPtr service = OpenService(scm, serviceName, ServiceAccessRights.QueryStatus);
if (service == IntPtr.Zero)
return false;
CloseServiceHandle(service);
return true;
}
finally
{
CloseServiceHandle(scm);
}
}
public static void InstallAndStart(string serviceName, string displayName, string fileName)
{
IntPtr scm = OpenSCManager(ScmAccessRights.AllAccess);
try
{
IntPtr service = OpenService(scm, serviceName, ServiceAccessRights.AllAccess);
if (service == IntPtr.Zero)
service = CreateService(scm, serviceName, displayName, ServiceAccessRights.AllAccess, SERVICE_WIN32_OWN_PROCESS, ServiceBootFlag.AutoStart, ServiceError.Normal, fileName, null, IntPtr.Zero, null, null, null);
if (service == IntPtr.Zero)
throw new ApplicationException("Failed to install service.");
try
{
StartService(service);
}
finally
{
CloseServiceHandle(service);
}
}
finally
{
CloseServiceHandle(scm);
}
}
public static void StartService(string serviceName)
{
IntPtr scm = OpenSCManager(ScmAccessRights.Connect);
try
{
IntPtr service = OpenService(scm, serviceName, ServiceAccessRights.QueryStatus | ServiceAccessRights.Start);
if (service == IntPtr.Zero)
throw new ApplicationException("Could not open service.");
try
{
StartService(service);
}
finally
{
CloseServiceHandle(service);
}
}
finally
{
CloseServiceHandle(scm);
}
}
public static void StopService(string serviceName)
{
IntPtr scm = OpenSCManager(ScmAccessRights.Connect);
try
{
IntPtr service = OpenService(scm, serviceName, ServiceAccessRights.QueryStatus | ServiceAccessRights.Stop);
if (service == IntPtr.Zero)
throw new ApplicationException("Could not open service.");
try
{
StopService(service);
}
finally
{
CloseServiceHandle(service);
}
}
finally
{
CloseServiceHandle(scm);
}
}
private static void StartService(IntPtr service)
{
SERVICE_STATUS status = new SERVICE_STATUS();
StartService(service, 0, 0);
var changedStatus = WaitForServiceStatus(service, ServiceState.StartPending, ServiceState.Running);
if (!changedStatus)
throw new ApplicationException("Unable to start service");
}
private static void StopService(IntPtr service)
{
SERVICE_STATUS status = new SERVICE_STATUS();
ControlService(service, ServiceControl.Stop, status);
var changedStatus = WaitForServiceStatus(service, ServiceState.StopPending, ServiceState.Stopped);
if (!changedStatus)
throw new ApplicationException("Unable to stop service");
}
public static ServiceState GetServiceStatus(string serviceName)
{
IntPtr scm = OpenSCManager(ScmAccessRights.Connect);
try
{
IntPtr service = OpenService(scm, serviceName, ServiceAccessRights.QueryStatus);
if (service == IntPtr.Zero)
return ServiceState.NotFound;
try
{
return GetServiceStatus(service);
}
finally
{
CloseServiceHandle(service);
}
}
finally
{
CloseServiceHandle(scm);
}
}
private static ServiceState GetServiceStatus(IntPtr service)
{
SERVICE_STATUS status = new SERVICE_STATUS();
if (QueryServiceStatus(service, status) == 0)
throw new ApplicationException("Failed to query service status.");
return status.dwCurrentState;
}
private static bool WaitForServiceStatus(IntPtr service, ServiceState waitStatus, ServiceState desiredStatus)
{
SERVICE_STATUS status = new SERVICE_STATUS();
QueryServiceStatus(service, status);
if (status.dwCurrentState == desiredStatus) return true;
int dwStartTickCount = Environment.TickCount;
int dwOldCheckPoint = status.dwCheckPoint;
while (status.dwCurrentState == waitStatus)
{
// Do not wait longer than the wait hint. A good interval is
// one tenth the wait hint, but no less than 1 second and no
// more than 10 seconds.
int dwWaitTime = status.dwWaitHint / 10;
if (dwWaitTime < 1000) dwWaitTime = 1000;
else if (dwWaitTime > 10000) dwWaitTime = 10000;
Thread.Sleep(dwWaitTime);
// Check the status again.
if (QueryServiceStatus(service, status) == 0) break;
if (status.dwCheckPoint > dwOldCheckPoint)
{
// The service is making progress.
dwStartTickCount = Environment.TickCount;
dwOldCheckPoint = status.dwCheckPoint;
}
else
{
if (Environment.TickCount - dwStartTickCount > status.dwWaitHint)
{
// No progress made within the wait hint
break;
}
}
}
return (status.dwCurrentState == desiredStatus);
}
private static IntPtr OpenSCManager(ScmAccessRights rights)
{
IntPtr scm = OpenSCManager(null, null, rights);
if (scm == IntPtr.Zero)
throw new ApplicationException("Could not connect to service control manager.");
return scm;
}
}
public enum ServiceState
{
Unknown = -1, // The state cannot be (has not been) retrieved.
NotFound = 0, // The service is not known on the host server.
Stopped = 1,
StartPending = 2,
StopPending = 3,
Running = 4,
ContinuePending = 5,
PausePending = 6,
Paused = 7
}
[Flags]
public enum ScmAccessRights
{
Connect = 0x0001,
CreateService = 0x0002,
EnumerateService = 0x0004,
Lock = 0x0008,
QueryLockStatus = 0x0010,
ModifyBootConfig = 0x0020,
StandardRightsRequired = 0xF0000,
AllAccess = (StandardRightsRequired | Connect | CreateService |
EnumerateService | Lock | QueryLockStatus | ModifyBootConfig)
}
[Flags]
public enum ServiceAccessRights
{
QueryConfig = 0x1,
ChangeConfig = 0x2,
QueryStatus = 0x4,
EnumerateDependants = 0x8,
Start = 0x10,
Stop = 0x20,
PauseContinue = 0x40,
Interrogate = 0x80,
UserDefinedControl = 0x100,
Delete = 0x00010000,
StandardRightsRequired = 0xF0000,
AllAccess = (StandardRightsRequired | QueryConfig | ChangeConfig |
QueryStatus | EnumerateDependants | Start | Stop | PauseContinue |
Interrogate | UserDefinedControl)
}
public enum ServiceBootFlag
{
Start = 0x00000000,
SystemStart = 0x00000001,
AutoStart = 0x00000002,
DemandStart = 0x00000003,
Disabled = 0x00000004
}
public enum ServiceControl
{
Stop = 0x00000001,
Pause = 0x00000002,
Continue = 0x00000003,
Interrogate = 0x00000004,
Shutdown = 0x00000005,
ParamChange = 0x00000006,
NetBindAdd = 0x00000007,
NetBindRemove = 0x00000008,
NetBindEnable = 0x00000009,
NetBindDisable = 0x0000000A
}
public enum ServiceError
{
Ignore = 0x00000000,
Normal = 0x00000001,
Severe = 0x00000002,
Critical = 0x00000003
}
I want users to be able to change the display name of an existing Windows service using our configuration tool. In code, given an instance of the corresponding ServiceController object, setting its DisplayName property seems to have effect. Tried calling Refresh, which seemed to have no effect. The MSDN doc is a little unclear on what Refresh does - does it reread the current service settings or does it write my changes to the service? Code is simple:
ServiceController sc = GetServiceController(CurrentInterfaceData.ServiceName);
sc.DisplayName = "MyService";
sc.Refresh();
Use WMI. For that you have to add a reference to System.Management assembly. Here is excerpt of a working code that I had used for a generic installer:
class Program
{
static void Main(string[] args)
{
UpdateService("ipsecd");
}
private const int LOGON32_LOGON_NEW_CREDENTIALS = 9;
private const int LOGON32_PROVIDER_WINNT50 = 3;
private const int SERVICE_ALL_ACCESS = 0xF01FF;
private const int SC_MANAGER_ALL_ACCESS = 0xF003F;
private const uint SERVICE_NO_CHANGE = 0xFFFFFFFF;
private const uint SERVICE_WIN32_OWN_PROCESS = 0x00000010;
private const uint SERVICE_INTERACTIVE_PROCESS = 0x00000100;
// Win32 function to connect to remote machine
[DllImport("advapi32.dll", SetLastError = true)]
private static extern Boolean LogonUser(string lpUsername, string lpDomain, string lpPassword, int dwLogonType,
int dwLogonProvider, out IntPtr phToken);
// Win32 function to connect to impersonate user
[DllImport("advapi32.dll", SetLastError = true)]
static extern int ImpersonateLoggedOnUser(IntPtr hToken);
// Win32 function to open the service control manager
[DllImport("advapi32.dll")]
private static extern IntPtr OpenSCManager(string lpMachineName, string lpDatabaseName, int dwDesiredAccess);
// Win32 function to open a service instance
[DllImport("advapi32.dll")]
private static extern IntPtr OpenService(IntPtr hSCManager, string lpServiceName, int dwDesiredAccess);
// Win32 function to change the service config
[DllImport("advapi32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
public static extern Boolean ChangeServiceConfig(
IntPtr hService,
UInt32 nServiceType,
UInt32 nStartType,
UInt32 nErrorControl,
String lpBinaryPathName,
String lpLoadOrderGroup,
IntPtr lpdwTagId,
[In] char[] lpDependencies,
String lpServiceStartName,
String lpPassword,
String lpDisplayName);
public static void UpdateService(string serviceName)
{
ManagementScope scope = null;
ObjectQuery filter = new ObjectQuery(string.Format("SELECT * FROM Win32_Service WHERE Name = '{0}'", serviceName));
ManagementObjectSearcher query = new ManagementObjectSearcher(scope, filter);
try
{
ManagementObjectCollection services = query.Get();
// No match = failed condition
if (services.Count == 0)
return;
foreach (ManagementObject service in services)
{
SetServiceDisplayName(serviceName, "new disp name");
service.Dispose();
}
}
catch (Exception ex)
{
//Could not set property
}
}
private static void SetServiceDisplayName(string name, string dispName)
{
IntPtr svcHndl = OpenService("", "", name);
// Call the ChangeServiceFailureActions() abstraction of ChangeServiceConfig2()
bool rslt = ChangeServiceConfig(svcHndl, SERVICE_NO_CHANGE,
SERVICE_NO_CHANGE, SERVICE_NO_CHANGE, null, null, IntPtr.Zero, null, null, null, dispName);
}
private static IntPtr OpenService(string userName, string password, string serviceName)
{
IntPtr scmHndl = IntPtr.Zero;
IntPtr svcHndl = IntPtr.Zero;
// Open the service control manager
scmHndl = OpenSCManager("", null, SC_MANAGER_ALL_ACCESS);
if (scmHndl.ToInt32() <= 0)
return IntPtr.Zero;
// Open the service
svcHndl = OpenService(scmHndl, serviceName, SERVICE_ALL_ACCESS);
if (svcHndl.ToInt32() <= 0)
return IntPtr.Zero;
return svcHndl;
}
}
Try it. It should work.
I believe the name of the Windows Service is set when the service is installed. I don't think that changing it later will have any effect.
I want to use a system wide hotkey in my powershell GUI application. I found this C# code for registering a hotkey and integrated it in my script.
The trouble is, because I can't fully understand the C# code, I don't know how to properly add the parameters for the registerhotkey method.
The goal is: on key press SHIFT+ALT+Z execute the code in $hotkeyExecCode.
[void][System.Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms")
[System.Windows.Forms.Application]::EnableVisualStyles()
$form1 = New-Object 'System.Windows.Forms.Form'
$form1.ClientSize = '200, 150'
$cCode = #"
using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Threading;
/// <summary> This class allows you to manage a hotkey </summary>
public class GlobalHotkeys : IDisposable
{
[DllImport( "user32", SetLastError = true )]
[return: MarshalAs( UnmanagedType.Bool )]
public static extern bool RegisterHotKey (IntPtr hwnd, int id, uint fsModifiers, uint vk);
[DllImport( "user32", SetLastError = true )]
public static extern int UnregisterHotKey (IntPtr hwnd, int id);
[DllImport( "kernel32", SetLastError = true )]
public static extern short GlobalAddAtom (string lpString);
[DllImport( "kernel32", SetLastError = true )]
public static extern short GlobalDeleteAtom (short nAtom);
public const int MOD_ALT = 1;
public const int MOD_CONTROL = 2;
public const int MOD_SHIFT = 4;
public const int MOD_WIN = 8;
public const int WM_HOTKEY = 0x312;
public GlobalHotkeys()
{
this.Handle = Process.GetCurrentProcess().Handle;
}
/// <summary>Handle of the current process</summary>
public IntPtr Handle;
/// <summary>The ID for the hotkey</summary>
public short HotkeyID { get; private set; }
/// <summary>Register the hotkey</summary>
public void RegisterGlobalHotKey(int hotkey, int modifiers, IntPtr handle)
{
UnregisterGlobalHotKey();
this.Handle = handle;
RegisterGlobalHotKey(hotkey, modifiers);
}
/// <summary>Register the hotkey</summary>
public void RegisterGlobalHotKey(int hotkey, int modifiers)
{
UnregisterGlobalHotKey();
try
{
// use the GlobalAddAtom API to get a unique ID (as suggested by MSDN)
string atomName = Thread.CurrentThread.ManagedThreadId.ToString("X8") + this.GetType().FullName;
HotkeyID = GlobalAddAtom(atomName);
if (HotkeyID == 0)
throw new Exception("Unable to generate unique hotkey ID. Error: " + Marshal.GetLastWin32Error().ToString());
// register the hotkey, throw if any error
if (!RegisterHotKey(this.Handle, HotkeyID, (uint)modifiers, (uint)hotkey))
throw new Exception("Unable to register hotkey. Error: " + Marshal.GetLastWin32Error().ToString());
}
catch (Exception ex)
{
// clean up if hotkey registration failed
Dispose();
Console.WriteLine(ex);
}
}
/// <summary>Unregister the hotkey</summary>
public void UnregisterGlobalHotKey()
{
if ( this.HotkeyID != 0 )
{
UnregisterHotKey(this.Handle, HotkeyID);
// clean up the atom list
GlobalDeleteAtom(HotkeyID);
HotkeyID = 0;
}
}
public void Dispose()
{
UnregisterGlobalHotKey();
}
}
"#
Add-Type -TypeDefinition $cCode
$hotkeyExecCode = { $form1.WindowState = "normal"}
[GlobalHotkeys]::RegisterHotKey( ???????? ) # <--------- How do i add the parameters here?
$form1.windowstate = "minimized"
$form1.ShowDialog()
I'm trying to set an already installed windows service to automatic delayed start in C#. How do I set a windows service to
Automatic (Delayed Start)
Can't find that value in the ServiceStartMode enum.
Edit:1
public class ServiceAutoStartInfo
{
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
private struct SERVICE_DELAYED_AUTO_START_INFO
{
public bool fDelayedAutostart;
}
[DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Auto)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool ChangeServiceConfig2(IntPtr hService, int dwInfoLevel, IntPtr lpInfo);
// Service configuration parameter
const int SERVICE_CONFIG_DELAYED_AUTO_START_INFO = 3;
public bool ChangeDelayedAutoStart(IntPtr hService, bool delayed)
{
// Validate service handle
if (hService != IntPtr.Zero)
{
// Create
SERVICE_DELAYED_AUTO_START_INFO info = new SERVICE_DELAYED_AUTO_START_INFO();
// Set the DelayedAutostart property
info.fDelayedAutostart = delayed;
// Allocate necessary memory
IntPtr hInfo = Marshal.AllocHGlobal(Marshal.SizeOf(
typeof(SERVICE_DELAYED_AUTO_START_INFO)));
// Convert structure to pointer
Marshal.StructureToPtr(info, hInfo, true);
// Change the configuration
bool result = ChangeServiceConfig2(hService, SERVICE_CONFIG_DELAYED_AUTO_START_INFO, hInfo);
// Release memory
Marshal.FreeHGlobal(hInfo);
return result;
}
return false;
}
}
This is how I call it:
var controller = new ServiceController(s.ServiceName);
var autoDelay = new ServiceAutoStartInfo();
autoDelay.ChangeDelayedAutoStart(controller.ServiceHandle.DangerousGetHandle(), true);
But with no result.
Look into calling the Windows ChangeServiceConfig2 function, with dwInfoLevel of SERVICE_CONFIG_DELAYED_AUTO_START_INFO and a SERVICE_DELAYED_AUTO_START_INFO struct with fDelayedAutostart set to TRUE.
Or, you can do this with the command line:
sc.exe config <servicename> start= delayed-auto
I'm using the following, which works for me on Windows 7 (when run as admin):
using System;
using System.ComponentModel;
using System.Runtime.InteropServices;
using System.ServiceProcess;
namespace ServiceManager
{
/// <summary>
/// Extensions to the ServiceController class.
/// </summary>
public static class ServiceControlerExtensions
{
/// <summary>
/// Set the start mode for the service.
/// </summary>
/// <param name="serviceController">The service controller.</param>
/// <param name="mode">The desired start mode.</param>
public static void SetStartMode(this ServiceController serviceController, ServiceStartModeEx mode)
{
IntPtr serviceManagerHandle = OpenServiceManagerHandle();
IntPtr serviceHandle = OpenServiceHandle(serviceController, serviceManagerHandle);
try
{
if (mode == ServiceStartModeEx.DelayedAutomatic)
{
ChangeServiceStartType(serviceHandle, ServiceStartModeEx.Automatic);
ChangeDelayedAutoStart(serviceHandle, true);
}
else
{
// Delayed auto-start overrides other settings, so it must be set first.
ChangeDelayedAutoStart(serviceHandle, false);
ChangeServiceStartType(serviceHandle, mode);
}
}
finally
{
if (serviceHandle != IntPtr.Zero)
{
CloseServiceHandle(serviceHandle);
}
if (serviceHandle != IntPtr.Zero)
{
CloseServiceHandle(serviceManagerHandle);
}
}
}
private static IntPtr OpenServiceHandle(ServiceController serviceController, IntPtr serviceManagerHandle)
{
var serviceHandle = OpenService(
serviceManagerHandle,
serviceController.ServiceName,
SERVICE_QUERY_CONFIG | SERVICE_CHANGE_CONFIG);
if (serviceHandle == IntPtr.Zero)
{
throw new ExternalException("Open Service Error");
}
return serviceHandle;
}
private static IntPtr OpenServiceManagerHandle()
{
IntPtr serviceManagerHandle = OpenSCManager(null, null, SC_MANAGER_ALL_ACCESS);
if (serviceManagerHandle == IntPtr.Zero)
{
throw new ExternalException("Open Service Manager Error");
}
return serviceManagerHandle;
}
private static void ChangeServiceStartType(IntPtr serviceHandle, ServiceStartModeEx mode)
{
bool result = ChangeServiceConfig(
serviceHandle,
SERVICE_NO_CHANGE,
(uint)mode,
SERVICE_NO_CHANGE,
null,
null,
IntPtr.Zero,
null,
null,
null,
null);
if (result == false)
{
ThrowLastWin32Error("Could not change service start type");
}
}
private static void ChangeDelayedAutoStart(IntPtr hService, bool delayed)
{
// Create structure that contains DelayedAutoStart property.
SERVICE_DELAYED_AUTO_START_INFO info = new SERVICE_DELAYED_AUTO_START_INFO();
// Set the DelayedAutostart property in that structure.
info.fDelayedAutostart = delayed;
// Allocate necessary memory.
IntPtr hInfo = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(SERVICE_DELAYED_AUTO_START_INFO)));
// Convert structure to pointer.
Marshal.StructureToPtr(info, hInfo, true);
// Change the configuration.
bool result = ChangeServiceConfig2(hService, SERVICE_CONFIG_DELAYED_AUTO_START_INFO, hInfo);
// Release memory.
Marshal.FreeHGlobal(hInfo);
if (result == false)
{
ThrowLastWin32Error("Could not set service to delayed automatic");
}
}
private static void ThrowLastWin32Error(string messagePrefix)
{
int nError = Marshal.GetLastWin32Error();
var win32Exception = new Win32Exception(nError);
string message = string.Format("{0}: {1}", messagePrefix, win32Exception.Message);
throw new ExternalException(message);
}
[DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Auto)]
private static extern IntPtr OpenService(
IntPtr hSCManager,
string lpServiceName,
uint dwDesiredAccess);
[DllImport("advapi32.dll", EntryPoint = "OpenSCManagerW", ExactSpelling = true, CharSet = CharSet.Unicode,
SetLastError = true)]
private static extern IntPtr OpenSCManager(
string machineName,
string databaseName,
uint dwAccess);
[DllImport("advapi32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
private static extern Boolean ChangeServiceConfig(
IntPtr hService,
UInt32 nServiceType,
UInt32 nStartType,
UInt32 nErrorControl,
String lpBinaryPathName,
String lpLoadOrderGroup,
IntPtr lpdwTagId,
[In] char[] lpDependencies,
String lpServiceStartName,
String lpPassword,
String lpDisplayName);
[DllImport("advapi32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool ChangeServiceConfig2(
IntPtr hService,
int dwInfoLevel,
IntPtr lpInfo);
[DllImport("advapi32.dll", EntryPoint = "CloseServiceHandle")]
private static extern int CloseServiceHandle(IntPtr hSCObject);
private const uint SERVICE_NO_CHANGE = 0xFFFFFFFF;
private const uint SERVICE_QUERY_CONFIG = 0x00000001;
private const uint SERVICE_CHANGE_CONFIG = 0x00000002;
private const uint SC_MANAGER_ALL_ACCESS = 0x000F003F;
private const int SERVICE_CONFIG_DELAYED_AUTO_START_INFO = 3;
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
private struct SERVICE_DELAYED_AUTO_START_INFO
{
public bool fDelayedAutostart;
}
}
}
namespace ServiceManager
{
public enum ServiceStartModeEx
{
Automatic = 2,
Manual = 3,
Disabled = 4,
DelayedAutomatic = 99
}
}
You call it like this:
var serviceController = new ServiceController("Windows Update");
try
{
serviceController.SetStartMode(ServiceStartModeEx.DelayedAutomatic);
}
finally
{
serviceController.Close();
}
Update: This only works for setting up new services and is not what the OP asked for:
You can use the DelayedAutoStart property of the ServiceInstaller.
installer.DelayedAutoStart = true;
I believe you need to combine both methods ChangeServiceConfig and ChangeServiceConfig2.
pseudo-code follows:
public static void ChangeServiceStartupType(ServiceStartupType type, ...)
{
if (type == AutomaticDelayed)
{
if (ChangeServiceConfig2(.., DelayedAutoStart, ..))
{
ChangeServiceConfig(.., Automatic, ..);
}
}
else
{
ChangeServiceConfig2(.., !DelayedAutoStart, ..)
ChangeServiceConfig(.., type, ..)
}
}
edit: you also need to remove "delayed-automatic" when requesting non-delayed startup-type. Otherwise it won't be possible to set "automatic" type. ("automatic-delayed" overrides "automatic")
SERVICE_DELAYED_AUTO_START_INFO structure documentation states:
fDelayedAutostart
If this member is TRUE, the service is started after other auto-start
services are started plus a short delay. Otherwise, the service is
started during system boot.
This setting is ignored unless the service is an auto-start service.
https://learn.microsoft.com/en-us/windows/win32/api/winsvc/ns-winsvc-service_delayed_auto_start_info?redirectedfrom=MSDN#members
So I think if the service is not auto-start it won't change it to auto delayed, you would have to use sc.exe
I'm interested in how plausible it would be to capture Win32 debug traces system-wide in the way DebugView does. I'm not interested in kernel messages thankfully, so I don't need any help there. This needs to be in C#, but I'm happy with unmanaged/unsafe if necessary.
Is there any global hook I can get or am I setting off down a difficult road?
I'm really not sure where the best place is to start on this.
I finally got there. It took some serious googling, but I found an article which helped...
All kudos go to Chritian Birkl for his rather excellent Code Project DbMon.NET - A simple .NET OutputDebugString capturer.
The code is pretty heavy-going, but here it is:
using System;
using System.Threading;
using System.Runtime.InteropServices;
public delegate void OnOutputDebugStringHandler(int pid, string text);
public sealed class DebugMonitor
{
private DebugMonitor()
{
;
}
#region Win32 API Imports
[StructLayout(LayoutKind.Sequential)]
private struct SECURITY_DESCRIPTOR
{
public byte revision;
public byte size;
public short control;
public IntPtr owner;
public IntPtr group;
public IntPtr sacl;
public IntPtr dacl;
}
[StructLayout(LayoutKind.Sequential)]
private struct SECURITY_ATTRIBUTES
{
public int nLength;
public IntPtr lpSecurityDescriptor;
public int bInheritHandle;
}
[Flags]
private enum PageProtection : uint
{
NoAccess = 0x01,
Readonly = 0x02,
ReadWrite = 0x04,
WriteCopy = 0x08,
Execute = 0x10,
ExecuteRead = 0x20,
ExecuteReadWrite = 0x40,
ExecuteWriteCopy = 0x80,
Guard = 0x100,
NoCache = 0x200,
WriteCombine = 0x400,
}
private const int WAIT_OBJECT_0 = 0;
private const uint INFINITE = 0xFFFFFFFF;
private const int ERROR_ALREADY_EXISTS = 183;
private const uint SECURITY_DESCRIPTOR_REVISION = 1;
private const uint SECTION_MAP_READ = 0x0004;
[DllImport("kernel32.dll", SetLastError = true)]
private static extern IntPtr MapViewOfFile(IntPtr hFileMappingObject, uint
dwDesiredAccess, uint dwFileOffsetHigh, uint dwFileOffsetLow,
uint dwNumberOfBytesToMap);
[DllImport("kernel32.dll", SetLastError = true)]
private static extern bool UnmapViewOfFile(IntPtr lpBaseAddress);
[DllImport("advapi32.dll", SetLastError = true)]
private static extern bool InitializeSecurityDescriptor(ref SECURITY_DESCRIPTOR sd, uint dwRevision);
[DllImport("advapi32.dll", SetLastError = true)]
private static extern bool SetSecurityDescriptorDacl(ref SECURITY_DESCRIPTOR sd, bool daclPresent, IntPtr dacl, bool daclDefaulted);
[DllImport("kernel32.dll")]
private static extern IntPtr CreateEvent(ref SECURITY_ATTRIBUTES sa, bool bManualReset, bool bInitialState, string lpName);
[DllImport("kernel32.dll")]
private static extern bool PulseEvent(IntPtr hEvent);
[DllImport("kernel32.dll")]
private static extern bool SetEvent(IntPtr hEvent);
[DllImport("kernel32.dll", SetLastError = true)]
private static extern IntPtr CreateFileMapping(IntPtr hFile,
ref SECURITY_ATTRIBUTES lpFileMappingAttributes, PageProtection flProtect, uint dwMaximumSizeHigh,
uint dwMaximumSizeLow, string lpName);
[DllImport("kernel32.dll", SetLastError = true)]
private static extern bool CloseHandle(IntPtr hHandle);
[DllImport("kernel32", SetLastError = true, ExactSpelling = true)]
private static extern Int32 WaitForSingleObject(IntPtr handle, uint milliseconds);
#endregion
public static event OnOutputDebugStringHandler OnOutputDebugString;
private static IntPtr m_AckEvent = IntPtr.Zero;
private static IntPtr m_ReadyEvent = IntPtr.Zero;
private static IntPtr m_SharedFile = IntPtr.Zero;
private static IntPtr m_SharedMem = IntPtr.Zero;
private static Thread m_Capturer = null;
private static object m_SyncRoot = new object();
private static Mutex m_Mutex = null;
public static void Start()
{
lock (m_SyncRoot)
{
if (m_Capturer != null)
throw new ApplicationException("This DebugMonitor is already started.");
if (Environment.OSVersion.ToString().IndexOf("Microsoft") == -1)
throw new NotSupportedException("This DebugMonitor is only supported on Microsoft operating systems.");
bool createdNew = false;
m_Mutex = new Mutex(false, typeof(DebugMonitor).Namespace, out createdNew);
if (!createdNew)
throw new ApplicationException("There is already an instance of 'DbMon.NET' running.");
SECURITY_DESCRIPTOR sd = new SECURITY_DESCRIPTOR();
if (!InitializeSecurityDescriptor(ref sd, SECURITY_DESCRIPTOR_REVISION))
{
throw CreateApplicationException("Failed to initializes the security descriptor.");
}
if (!SetSecurityDescriptorDacl(ref sd, true, IntPtr.Zero, false))
{
throw CreateApplicationException("Failed to initializes the security descriptor");
}
SECURITY_ATTRIBUTES sa = new SECURITY_ATTRIBUTES();
m_AckEvent = CreateEvent(ref sa, false, false, "DBWIN_BUFFER_READY");
if (m_AckEvent == IntPtr.Zero)
{
throw CreateApplicationException("Failed to create event 'DBWIN_BUFFER_READY'");
}
m_ReadyEvent = CreateEvent(ref sa, false, false, "DBWIN_DATA_READY");
if (m_ReadyEvent == IntPtr.Zero)
{
throw CreateApplicationException("Failed to create event 'DBWIN_DATA_READY'");
}
m_SharedFile = CreateFileMapping(new IntPtr(-1), ref sa, PageProtection.ReadWrite, 0, 4096, "DBWIN_BUFFER");
if (m_SharedFile == IntPtr.Zero)
{
throw CreateApplicationException("Failed to create a file mapping to slot 'DBWIN_BUFFER'");
}
m_SharedMem = MapViewOfFile(m_SharedFile, SECTION_MAP_READ, 0, 0, 512);
if (m_SharedMem == IntPtr.Zero)
{
throw CreateApplicationException("Failed to create a mapping view for slot 'DBWIN_BUFFER'");
}
m_Capturer = new Thread(new ThreadStart(Capture));
m_Capturer.Start();
}
}
private static void Capture()
{
try
{
IntPtr pString = new IntPtr(
m_SharedMem.ToInt32() + Marshal.SizeOf(typeof(int))
);
while (true)
{
SetEvent(m_AckEvent);
int ret = WaitForSingleObject(m_ReadyEvent, INFINITE);
if (m_Capturer == null)
break;
if (ret == WAIT_OBJECT_0)
{
FireOnOutputDebugString(
Marshal.ReadInt32(m_SharedMem),
Marshal.PtrToStringAnsi(pString));
}
}
}
catch
{
throw;
}
finally
{
Dispose();
}
}
private static void FireOnOutputDebugString(int pid, string text)
{
if (OnOutputDebugString == null)
return;
#if !DEBUG
try
{
#endif
OnOutputDebugString(pid, text);
#if !DEBUG
}
catch (Exception ex)
{
Console.WriteLine("An 'OnOutputDebugString' handler failed to execute: " + ex.ToString());
}
#endif
}
private static void Dispose()
{
if (m_AckEvent != IntPtr.Zero)
{
if (!CloseHandle(m_AckEvent))
{
throw CreateApplicationException("Failed to close handle for 'AckEvent'");
}
m_AckEvent = IntPtr.Zero;
}
if (m_ReadyEvent != IntPtr.Zero)
{
if (!CloseHandle(m_ReadyEvent))
{
throw CreateApplicationException("Failed to close handle for 'ReadyEvent'");
}
m_ReadyEvent = IntPtr.Zero;
}
if (m_SharedFile != IntPtr.Zero)
{
if (!CloseHandle(m_SharedFile))
{
throw CreateApplicationException("Failed to close handle for 'SharedFile'");
}
m_SharedFile = IntPtr.Zero;
}
if (m_SharedMem != IntPtr.Zero)
{
if (!UnmapViewOfFile(m_SharedMem))
{
throw CreateApplicationException("Failed to unmap view for slot 'DBWIN_BUFFER'");
}
m_SharedMem = IntPtr.Zero;
}
if (m_Mutex != null)
{
m_Mutex.Close();
m_Mutex = null;
}
}
public static void Stop()
{
lock (m_SyncRoot)
{
if (m_Capturer == null)
throw new ObjectDisposedException("DebugMonitor", "This DebugMonitor is not running.");
m_Capturer = null;
PulseEvent(m_ReadyEvent);
while (m_AckEvent != IntPtr.Zero)
;
}
}
private static ApplicationException CreateApplicationException(string text)
{
if (text == null || text.Length < 1)
throw new ArgumentNullException("text", "'text' may not be empty or null.");
return new ApplicationException(string.Format("{0}. Last Win32 Error was {1}",
text, Marshal.GetLastWin32Error()));
}
}
I wrote a tool that does it. It's not 100% equivalent to DebugView, but it has some other nice features (like the ability to colorize traces) :-).
It's available here: TraceSpy
Since it's 100% open source, it contains C# code that demonstrates how to do it.
I would start with the TraceListener class, although I'm not sure if this can be used to capture Win32 debug tracing.