ScpDriverInstaller/MainWindow.xaml.cs (611 lines of code) (raw):
using log4net;
using log4net.Appender;
using log4net.Core;
using log4net.Repository.Hierarchy;
using Mantin.Controls.Wpf.Notification;
using Ookii.Dialogs.Wpf;
using ScpControl.Bluetooth;
using ScpControl.Driver;
using ScpControl.ScpCore;
using ScpControl.Usb.Ds3;
using ScpControl.Usb.Ds4;
using ScpControl.Usb.PnP;
using ScpControl.Utilities;
using ScpDriverInstaller.Properties;
using ScpDriverInstaller.Utilities;
using ScpDriverInstaller.View_Models;
using System;
using System.Collections;
using System.ComponentModel;
using System.Configuration.Install;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.InteropServices;
using System.ServiceProcess;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Interop;
using System.Windows.Navigation;
namespace ScpDriverInstaller
{
/// <summary>
/// Where the wizard does its black magic
/// </summary>
public partial class MainWindow : Window, IAppender
{
#region Ctor
public MainWindow()
{
InitializeComponent();
AppDomain.CurrentDomain.UnhandledException +=
(sender, args) => { Log.FatalFormat("An unexpected exception occurred: {0}", args.ExceptionObject); };
InstallGrid.DataContext = _viewModel;
}
#endregion Ctor
#region Misc. Helpers
public void DoAppend(LoggingEvent loggingEvent)
{
if (!IsInitialized)
return;
var level = loggingEvent.Level;
if (level == Level.Info)
{
Dispatcher.Invoke(
() => ShowPopup("Information", loggingEvent.RenderedMessage, NotificationType.Information));
}
if (level == Level.Warn)
{
Dispatcher.Invoke(
() => ShowPopup("Warning", loggingEvent.RenderedMessage, NotificationType.Warning));
}
if (level == Level.Error)
{
Dispatcher.Invoke(
() => ShowPopup("Error", loggingEvent.RenderedMessage, NotificationType.Error));
}
}
#endregion Misc. Helpers
#region Private methods
private void ShowPopup(string title, string message, NotificationType type)
{
var popup = new ToastPopUp(title, message, type)
{
Background = Background,
FontFamily = FontFamily
};
popup.Show();
}
#endregion Private methods
#region Device notification events
private void OnUsbDeviceAddedOrRemoved()
{
var usbDevices = WdiWrapper.Instance.UsbDeviceList.ToList();
var supportedBluetoothDevices = IniConfig.Instance.BthDongleDriver.HardwareIds;
var regex = new Regex("VID_([0-9A-Z]{4})&PID_([0-9A-Z]{4})", RegexOptions.IgnoreCase);
// HidUsb devices
{
DualShockStackPanelHidUsb.Children.Clear();
_viewModel.InstallDs3ButtonEnabled = false;
foreach (
var usbDevice in
usbDevices.Where(
d => d.VendorId == _hidUsbDs3.VendorId
&& (d.ProductId == _hidUsbDs3.ProductId || d.ProductId == _hidUsbDs4.ProductId)
&& !string.IsNullOrEmpty(d.CurrentDriver)
&& d.CurrentDriver.Equals("HidUsb"))
)
{
DualShockStackPanelHidUsb.Children.Add(new TextBlock
{
Text = string.Format("Device #{0}: {1}", DualShockStackPanelHidUsb.Children.Count, usbDevice),
Tag = usbDevice,
VerticalAlignment = VerticalAlignment.Center,
Margin = new Thickness(10, 0, 0, 10)
});
_viewModel.InstallDs3ButtonEnabled = true;
}
}
// WinUsb devices
{
DualShockStackPanelWinUsb.Children.Clear();
foreach (
var usbDevice in
usbDevices.Where(
d => d.VendorId == _hidUsbDs3.VendorId
&& (d.ProductId == _hidUsbDs3.ProductId || d.ProductId == _hidUsbDs4.ProductId)
&& !string.IsNullOrEmpty(d.CurrentDriver)
&& d.CurrentDriver.Equals("WinUSB"))
)
{
DualShockStackPanelWinUsb.Children.Add(new TextBlock
{
Text = string.Format("Device #{0}: {1}", DualShockStackPanelWinUsb.Children.Count, usbDevice),
Tag = usbDevice,
VerticalAlignment = VerticalAlignment.Center,
Margin = new Thickness(10, 0, 0, 10)
});
}
}
// refresh devices filtering on supported Bluetooth hardware IDs and BTHUSB driver (uninitialized)
{
var uninitialized =
usbDevices.Where(
d =>
!string.IsNullOrEmpty(d.CurrentDriver) &&
d.CurrentDriver.Equals("BTHUSB") &&
supportedBluetoothDevices.Any(s => s.Contains(regex.Match(d.HardwareId).Value)));
BluetoothStackPanelDefault.Children.Clear();
_viewModel.InstallBthButtonEnabled = false;
foreach (var usbDevice in uninitialized)
{
BluetoothStackPanelDefault.Children.Add(new TextBlock
{
Text = string.Format("Device #{0}: {1}", BluetoothStackPanelDefault.Children.Count, usbDevice),
Tag = usbDevice,
VerticalAlignment = VerticalAlignment.Center,
Margin = new Thickness(10, 0, 0, 10)
});
_viewModel.InstallBthButtonEnabled = true;
}
}
// refresh devices filtering on supported Bluetooth hardware IDs and WinUSB driver (initialized)
{
var initialized =
usbDevices.Where(
d =>
!string.IsNullOrEmpty(d.CurrentDriver) &&
d.CurrentDriver.Equals("WinUSB") &&
supportedBluetoothDevices.Any(s => s.Contains(regex.Match(d.HardwareId).Value)));
BluetoothStackPanelWinUsb.Children.Clear();
foreach (var usbDevice in initialized)
{
BluetoothStackPanelWinUsb.Children.Add(new TextBlock
{
Text = string.Format("Device #{0}: {1}", BluetoothStackPanelWinUsb.Children.Count, usbDevice),
Tag = usbDevice,
VerticalAlignment = VerticalAlignment.Center,
Margin = new Thickness(10, 0, 0, 10)
});
}
}
}
#endregion Device notification events
#region Wizard events
private void Wizard_OnHelp(object sender, RoutedEventArgs e)
{
Process.Start("https://github.com/nefarius/ScpToolkit/wiki");
}
private void Hyperlink_OnRequestNavigate(object sender, RequestNavigateEventArgs e)
{
Process.Start(e.Uri.ToString());
}
#endregion Wizard events
#region Private static fields
private static readonly ILog Log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
private readonly InstallationOptionsViewModel _viewModel = new InstallationOptionsViewModel();
#endregion Private static fields
#region Private fields
private IntPtr _hWnd;
private readonly UsbNotifier _hidUsbDs3 = new UsbNotifier(0x054C, 0x0268);
private readonly UsbNotifier _winUsbDs3 = new UsbNotifier(0x054C, 0x0268, UsbDs3.DeviceClassGuid);
private readonly UsbNotifier _hidUsbDs4 = new UsbNotifier(0x054C, 0x05C4);
private readonly UsbNotifier _winUsbDs4 = new UsbNotifier(0x054C, 0x05C4, UsbDs4.DeviceClassGuid);
/// <summary>
/// The GUID_BTHPORT_DEVICE_INTERFACE device interface class is defined for Bluetooth radios.
/// </summary>
/// <remarks>https://msdn.microsoft.com/en-us/library/windows/hardware/ff545033(v=vs.85).aspx</remarks>
private readonly UsbNotifier _genericBluetoothHost =
new UsbNotifier(Guid.Parse("{0850302A-B344-4fda-9BE9-90576B8D46F0}"));
private readonly UsbNotifier _winUsbBluetoothHost = new UsbNotifier(BthDongle.DeviceClassGuid);
#endregion Private fields
#region Button events
private async void InstallDsOnClick(object sender, RoutedEventArgs routedEventArgs)
{
MainBusyIndicator.IsBusy = !MainBusyIndicator.IsBusy;
var rebootRequired = false;
var failed = false;
uint result = 0;
var ds3InfPath = Path.Combine(GlobalConfiguration.AppDirectory, "WinUSB", "Ds3Controller.inf");
var ds4InfPath = Path.Combine(GlobalConfiguration.AppDirectory, "WinUSB", "Ds4Controller.inf");
MainBusyIndicator.SetContentThreadSafe(Properties.Resources.DualShockSetupInstalling3);
await Task.Run(() => result = Difx.Instance.Install(ds3InfPath,
DifxFlags.DRIVER_PACKAGE_ONLY_IF_DEVICE_PRESENT | DifxFlags.DRIVER_PACKAGE_FORCE, out rebootRequired));
// ERROR_NO_SUCH_DEVINST = 0xE000020B
if (result != 0 && result != 0xE000020B)
{
failed = true;
ExtendedMessageBox.Show(this,
Properties.Resources.SetupFailedTitle,
Properties.Resources.SetupFailedInstructions,
Properties.Resources.SetupFailedContent,
string.Format(Properties.Resources.SetupFailedVerbose,
new Win32Exception(Marshal.GetLastWin32Error()), Marshal.GetLastWin32Error()),
Properties.Resources.SetupFailedFooter,
TaskDialogIcon.Error);
}
MainBusyIndicator.SetContentThreadSafe(Properties.Resources.DualShockSetupInstalling4);
await Task.Run(() => result = Difx.Instance.Install(ds4InfPath,
DifxFlags.DRIVER_PACKAGE_ONLY_IF_DEVICE_PRESENT | DifxFlags.DRIVER_PACKAGE_FORCE, out rebootRequired));
// ERROR_NO_SUCH_DEVINST = 0xE000020B
if (result != 0 && result != 0xE000020B)
{
failed = true;
ExtendedMessageBox.Show(this,
Properties.Resources.SetupFailedTitle,
Properties.Resources.SetupFailedInstructions,
Properties.Resources.SetupFailedContent,
string.Format(Properties.Resources.SetupFailedVerbose,
new Win32Exception(Marshal.GetLastWin32Error()), Marshal.GetLastWin32Error()),
Properties.Resources.SetupFailedFooter,
TaskDialogIcon.Error);
}
MainBusyIndicator.IsBusy = !MainBusyIndicator.IsBusy;
if (!failed)
{
ExtendedMessageBox.Show(this,
Properties.Resources.SetupSuccessTitle,
Properties.Resources.DualShockSetupSuccessInstruction,
Properties.Resources.SetupSuccessContent,
string.Empty,
string.Empty,
TaskDialogIcon.Information);
}
if (rebootRequired)
{
MessageBox.Show(this,
Properties.Resources.RebootRequiredContent,
Properties.Resources.RebootRequiredTitle,
MessageBoxButton.OK,
MessageBoxImage.Warning);
}
}
private async void InstallBthHostOnClick(object sender, RoutedEventArgs e)
{
MainBusyIndicator.IsBusy = !MainBusyIndicator.IsBusy;
var rebootRequired = false;
uint result = 0;
var bhInfPath = Path.Combine(GlobalConfiguration.AppDirectory, "WinUSB", "BluetoothHost.inf");
MainBusyIndicator.SetContentThreadSafe(Properties.Resources.BluetoothSetupInstalling);
await Task.Run(() => result = Difx.Instance.Install(bhInfPath,
DifxFlags.DRIVER_PACKAGE_ONLY_IF_DEVICE_PRESENT | DifxFlags.DRIVER_PACKAGE_FORCE, out rebootRequired));
MainBusyIndicator.IsBusy = !MainBusyIndicator.IsBusy;
// ERROR_NO_SUCH_DEVINST = 0xE000020B
if (result != 0 && result != 0xE000020B)
{
// display error message
ExtendedMessageBox.Show(this,
Properties.Resources.SetupFailedTitle,
Properties.Resources.SetupFailedInstructions,
Properties.Resources.SetupFailedContent,
string.Format(Properties.Resources.SetupFailedVerbose,
new Win32Exception(Marshal.GetLastWin32Error()), Marshal.GetLastWin32Error()),
Properties.Resources.SetupFailedFooter,
TaskDialogIcon.Error);
return;
}
// display success message
ExtendedMessageBox.Show(this,
Properties.Resources.SetupSuccessTitle,
Properties.Resources.BluetoothSetupSuccessInstruction,
Properties.Resources.SetupSuccessContent,
string.Empty,
string.Empty,
TaskDialogIcon.Information);
// display reboot required message
if (rebootRequired)
{
MessageBox.Show(this,
Properties.Resources.RebootRequiredContent,
Properties.Resources.RebootRequiredTitle,
MessageBoxButton.OK,
MessageBoxImage.Warning);
}
}
private async void InstallVBusOnClick(object sender, RoutedEventArgs e)
{
MainBusyIndicator.IsBusy = !MainBusyIndicator.IsBusy;
var failed = false;
var rebootRequired = false;
await Task.Run(() =>
{
string devPath = string.Empty, instanceId = string.Empty;
try
{
var busInfPath = Path.Combine(
GlobalConfiguration.AppDirectory,
"ScpVBus",
Environment.Is64BitOperatingSystem ? "amd64" : "x86",
"ScpVBus.inf");
Log.DebugFormat("ScpVBus.inf path: {0}", busInfPath);
// check for existence of Scp VBus
if (!Devcon.Find(Settings.Default.VirtualBusClassGuid, ref devPath, ref instanceId))
{
MainBusyIndicator.SetContentThreadSafe(Properties.Resources.VirtualBusSetupAddingDriverStore);
// if not detected, install Inf-file in Windows Driver Store
if (Devcon.Install(busInfPath, ref rebootRequired))
{
Log.Info("Virtual Bus Driver pre-installed in Windows Driver Store successfully");
MainBusyIndicator.SetContentThreadSafe(Properties.Resources.VirtualBusSetupCreating);
// create pseudo-device so the bus driver can attach to it later
if (Devcon.Create("System", new Guid("{4D36E97D-E325-11CE-BFC1-08002BE10318}"),
"root\\ScpVBus\0\0"))
{
Log.Info("Virtual Bus Created");
}
else
{
Log.Fatal("Virtual Bus Device creation failed");
failed = true;
}
}
else
{
Log.FatalFormat("Virtual Bus Driver pre-installation failed with Win32 error {0}",
(uint)Marshal.GetLastWin32Error());
failed = true;
}
}
MainBusyIndicator.SetContentThreadSafe(Properties.Resources.VirtualBusSetupInstalling);
// install Virtual Bus driver
var result = Difx.Instance.Install(busInfPath,
DifxFlags.DRIVER_PACKAGE_ONLY_IF_DEVICE_PRESENT | DifxFlags.DRIVER_PACKAGE_FORCE,
out rebootRequired);
if (result != 0)
{
failed = true;
}
}
catch (Exception ex)
{
Log.ErrorFormat("Error during installation: {0}", ex);
}
});
MainBusyIndicator.IsBusy = !MainBusyIndicator.IsBusy;
// display error message
if (failed)
{
ExtendedMessageBox.Show(this,
Properties.Resources.SetupFailedTitle,
Properties.Resources.SetupFailedInstructions,
Properties.Resources.SetupFailedContent,
string.Format(Properties.Resources.SetupFailedVerbose,
new Win32Exception(Marshal.GetLastWin32Error()), Marshal.GetLastWin32Error()),
Properties.Resources.SetupFailedFooter,
TaskDialogIcon.Error);
return;
}
// display success message
ExtendedMessageBox.Show(this,
Properties.Resources.SetupSuccessTitle,
Properties.Resources.VirtualBusSetupSuccessInstruction,
Properties.Resources.SetupSuccessContent,
string.Empty,
string.Empty,
TaskDialogIcon.Information);
// display reboot required message
if (rebootRequired)
{
MessageBox.Show(this,
Properties.Resources.RebootRequiredContent,
Properties.Resources.RebootRequiredTitle,
MessageBoxButton.OK,
MessageBoxImage.Warning);
}
}
private async void InstallWindowsServiceOnClick(object sender, RoutedEventArgs e)
{
MainBusyIndicator.IsBusy = !MainBusyIndicator.IsBusy;
var failed = false;
var rebootRequired = false;
await Task.Run(() =>
{
try
{
MainBusyIndicator.SetContentThreadSafe(Properties.Resources.ServiceSetupInstalling);
IDictionary state = new Hashtable();
var service =
new AssemblyInstaller(Path.Combine(GlobalConfiguration.AppDirectory, "ScpService.exe"), null);
state.Clear();
service.UseNewContext = true;
service.Install(state);
service.Commit(state);
if (StartService(Settings.Default.ScpServiceName))
{
Log.InfoFormat("{0} started", Settings.Default.ScpServiceName);
}
else
{
rebootRequired = true;
}
}
catch (Win32Exception w32Ex)
{
switch (w32Ex.NativeErrorCode)
{
case 1073: // ERROR_SERVICE_EXISTS
Log.Info("Service already exists");
break;
default:
Log.ErrorFormat("Win32-Error during installation: {0}", w32Ex);
failed = true;
break;
}
}
catch (InvalidOperationException iopex)
{
Log.ErrorFormat("Error during installation: {0}", iopex.Message);
failed = true;
}
catch (Exception ex)
{
Log.ErrorFormat("Error during installation: {0}", ex);
failed = true;
}
});
MainBusyIndicator.IsBusy = !MainBusyIndicator.IsBusy;
// display error message
if (failed)
{
ExtendedMessageBox.Show(this,
Properties.Resources.SetupFailedTitle,
Properties.Resources.SetupFailedInstructions,
Properties.Resources.SetupFailedContent,
string.Format(Properties.Resources.SetupFailedVerbose,
new Win32Exception(Marshal.GetLastWin32Error()), Marshal.GetLastWin32Error()),
Properties.Resources.SetupFailedFooter,
TaskDialogIcon.Error);
return;
}
// display success message
ExtendedMessageBox.Show(this,
Properties.Resources.SetupSuccessTitle,
Properties.Resources.ServiceSetupSuccessInstruction,
Properties.Resources.ServiceSetupSuccessContent,
string.Empty,
string.Empty,
TaskDialogIcon.Information);
// display reboot required message
if (rebootRequired)
{
MessageBox.Show(this,
Properties.Resources.RebootRequiredContent,
Properties.Resources.RebootRequiredTitle,
MessageBoxButton.OK,
MessageBoxImage.Warning);
}
}
#endregion Button events
#region Window events
private void Window_Initialized(object sender, EventArgs e)
{
Log.InfoFormat("SCP Driver Installer {0} [Built: {1}]", Assembly.GetExecutingAssembly().GetName().Version,
AssemblyHelper.LinkerTimestamp);
Log.InfoFormat("{0} detected", OsInfoHelper.OsInfo);
}
private void Window_Loaded(object sender, RoutedEventArgs e)
{
// add popup-appender to all loggers
foreach (var currentLogger in LogManager.GetCurrentLoggers())
{
((Logger)currentLogger.Logger).AddAppender(this);
}
// stop service if exists so no device is occupied
if (StopService(Settings.Default.ScpServiceName))
{
Log.InfoFormat("{0} stopped", Settings.Default.ScpServiceName);
}
#if NOPE
// link download progress to progress bar
RedistPackageInstaller.Instance.ProgressChanged +=
(o, args) => { Dispatcher.Invoke(() => MainProgressBar.Value = args.CurrentProgressPercentage); };
// link NotifyAppender to TextBlock
foreach (
var appender in
LogManager.GetCurrentLoggers()
.SelectMany(log => log.Logger.Repository.GetAppenders().OfType<NotifyAppender>()))
{
LogTextBlock.DataContext = appender;
}
#endif
}
private void MainWindow_OnClosing(object sender, CancelEventArgs e)
{
// remove popup-appender from all loggers
foreach (var currentLogger in LogManager.GetCurrentLoggers())
{
((Logger)currentLogger.Logger).RemoveAppender(this);
}
// unregister notifications
{
_hidUsbDs3.UnregisterHandle();
_winUsbDs3.UnregisterHandle();
_hidUsbDs4.UnregisterHandle();
_winUsbDs4.UnregisterHandle();
_genericBluetoothHost.UnregisterHandle();
}
if (StartService(Settings.Default.ScpServiceName))
{
Log.InfoFormat("{0} started", Settings.Default.ScpServiceName);
}
}
protected override void OnSourceInitialized(EventArgs e)
{
base.OnSourceInitialized(e);
// get native window handle
_hWnd = new WindowInteropHelper(this).Handle;
// listen for DualShock 3 plug-in events (HidUsb)
{
_hidUsbDs3.OnDeviceRemoved += (sender, args) => OnUsbDeviceAddedOrRemoved();
_hidUsbDs3.OnSpecifiedDeviceArrived += (sender, args) => OnUsbDeviceAddedOrRemoved();
_hidUsbDs3.RegisterHandle(_hWnd);
_hidUsbDs3.CheckDevicePresent();
}
// listen for DualShock 3 plug-in events (WinUSB)
{
_winUsbDs3.OnDeviceRemoved += (sender, args) => OnUsbDeviceAddedOrRemoved();
_winUsbDs3.OnSpecifiedDeviceArrived += (sender, args) => OnUsbDeviceAddedOrRemoved();
_winUsbDs3.RegisterHandle(_hWnd);
_winUsbDs3.CheckDevicePresent();
}
// listen for DualShock 4 plug-in events (HidUsb)
{
_hidUsbDs4.OnDeviceRemoved += (sender, args) => OnUsbDeviceAddedOrRemoved();
_hidUsbDs4.OnSpecifiedDeviceArrived += (sender, args) => OnUsbDeviceAddedOrRemoved();
_hidUsbDs4.RegisterHandle(_hWnd);
_hidUsbDs4.CheckDevicePresent();
}
// listen for DualShock 4 plug-in events (HidUsb)
{
_winUsbDs4.OnDeviceRemoved += (sender, args) => OnUsbDeviceAddedOrRemoved();
_winUsbDs4.OnSpecifiedDeviceArrived += (sender, args) => OnUsbDeviceAddedOrRemoved();
_winUsbDs4.RegisterHandle(_hWnd);
_winUsbDs4.CheckDevicePresent();
}
// listen for Bluetooth devices (BTHUSB or WinUSB)
{
_genericBluetoothHost.OnDeviceRemoved += (sender, args) => OnUsbDeviceAddedOrRemoved();
_genericBluetoothHost.OnDeviceArrived += (sender, args) => OnUsbDeviceAddedOrRemoved();
_genericBluetoothHost.RegisterHandle(_hWnd);
}
// listen for Bluetooth devices (WinUSB, initialized)
{
_winUsbBluetoothHost.OnDeviceRemoved += (sender, args) => OnUsbDeviceAddedOrRemoved();
_winUsbBluetoothHost.OnDeviceArrived += (sender, args) => OnUsbDeviceAddedOrRemoved();
_winUsbBluetoothHost.RegisterHandle(_hWnd);
}
// refresh all lists
OnUsbDeviceAddedOrRemoved();
// hook into WndProc
var source = PresentationSource.FromVisual(this) as HwndSource;
if (source != null) source.AddHook(WndProc);
}
private IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
{
_hidUsbDs3.ParseMessages(msg, wParam);
_winUsbDs3.ParseMessages(msg, wParam);
_hidUsbDs4.ParseMessages(msg, wParam);
_winUsbDs4.ParseMessages(msg, wParam);
_genericBluetoothHost.ParseMessages(msg, wParam);
_winUsbBluetoothHost.ParseMessages(msg, wParam);
return IntPtr.Zero;
}
#endregion Window events
#region Windows Service Helpers
private static bool StartService(string service)
{
try
{
var sc = new ServiceController(service);
if (sc.Status == ServiceControllerStatus.Stopped)
{
sc.Start();
sc.WaitForStatus(ServiceControllerStatus.Running);
return true;
}
}
catch (Exception ex)
{
Log.ErrorFormat("Couldn't start service: {0}", ex);
}
return false;
}
private static bool StopService(string service)
{
try
{
var sc = new ServiceController(service);
if (sc.Status == ServiceControllerStatus.Running)
{
sc.Stop();
sc.WaitForStatus(ServiceControllerStatus.Stopped);
return true;
}
}
catch (InvalidOperationException iopex)
{
if (!(iopex.InnerException is Win32Exception))
{
Log.ErrorFormat("Win32-Exception occured: {0}", iopex);
return false;
}
switch (((Win32Exception)iopex.InnerException).NativeErrorCode)
{
case 1060: // ERROR_SERVICE_DOES_NOT_EXIST
Log.Warn("Service doesn't exist, maybe it was uninstalled before");
break;
default:
Log.ErrorFormat("Win32-Error: {0}", (Win32Exception)iopex.InnerException);
break;
}
}
catch (Exception ex)
{
Log.ErrorFormat("Couldn't stop service: {0}", ex);
}
return false;
}
#endregion Windows Service Helpers
}
}