Your best option is to start the service yourself, inside your own application.
The problem of course is that your application normally isn't run as an administrator (nor do you want it to be). That means you can't start or stop services. That is why you should temporarily elevate, perform the task needed, then exit.
In other words, launch a second copy of your app, passing a command line option instructing yourself to start the service. The second copy does only that, then exits.
The Code
First we have a button the user can click to start the service. We first check if the user already is an administrator, in which case we don't have to do anything special:
private void button1_Click(object sender, EventArgs e)
{
//If we're an administrator, then do it
if (IsUserAnAdmin())
{
StartService("bthserv"); //"Bluetooth Support Service" for my sample test code
return;
}
//We're not running as an administrator.
//Relaunch ourselves as admin, telling us that we want to start the service
ExecuteAsAdmin(Application.ExecutablePath, "/serviceStart");
}
//helper function that tells us if we're already running with administrative rights
private Boolean IsUserAnAdmin()
{
//A user can be a member of the Administrator group, but not an administrator.
//Conversely, the user can be an administrator and not a member of the administrators group.
//Check if the current user has administrative privelages
var identity = WindowsIdentity.GetCurrent();
return (null != identity && new WindowsPrincipal(identity).IsInRole(WindowsBuiltInRole.Administrator));
}
private void ExecuteAsAdmin(string Filename, string Arguments)
{
//Launch process elevated
ProcessStartInfo startInfo = new ProcessStartInfo(Filename, Arguments);
startInfo.Verb = "runas"; //the runas verb is the secret to making UAC prompt come up
System.Diagnostics.Process.Start(startInfo);
}
Then, during startup, we just need to check if we're being called with the command line option. If so, start the service:
public Form1()
{
InitializeComponent();
//Ideally this would be in program.cs, before the call to Application.Run()
//But that would require me to refactor code out of the Form file, which is overkill for a demo
if (FindCmdLineSwitch("serviceStart", true))
{
StartService("bthserv"); //"Bluetooth Support Service"
Environment.Exit(0);
}
}
private bool FindCmdLineSwitch(string Switch, bool IgnoreCase)
{
foreach (String s in System.Environment.GetCommandLineArgs())
{
if (String.Compare(s, "/" + Switch, IgnoreCase) == 0)
return true;
if (String.Compare(s, "-" + Switch, IgnoreCase) == 0)
return true;
}
return false;
}
And finally to start the service:
private void StartService(String ServiceName)
{
TimeSpan timeout = TimeSpan.FromMilliseconds(30000); //30 seconds
using (ServiceController service = new ServiceController(ServiceName))
{
try
{
service.Start();
}
catch (Exception e)
{
MessageBox.Show(e.Message, "Error starting service");
return;
}
service.WaitForStatus(ServiceControllerStatus.Running, timeout);
}
}
Extra fancy
You can add arbitrary amounts of fancy code. Detect if the user is not an admin, and if not then use the proper Windows API (BCM_SETSHIELD message
) to add a UAC shield to your button:
That way users know to expect UAC to appear; because you followed the Microsoft UI guidelines:
Guidelines
UAC shield icon
Display controls with the UAC shield to indicate that the task requires immediate elevation when UAC is fully enabled, even if UAC isn't currently fully enabled. If all paths of a wizard and page flow require elevation, display the UAC shield at the task's entry point. Proper use of the UAC shield helps users predict when elevation is required.
Bonus Reading
- BCM_SETSHIELD message
- Windows Design Guidelines - User Account Control
- Designing UAC Applications for Windows Vista (Especially Step 4: Redesign Your UI for UAC Compatibility)
- http://msdn.microsoft.com/en-us/library/windows/desktop/aa511445.aspx
Note: Any code is released into the public domain. No attribution required.