merged from IPC branch.

systray
Sean McArde 2016-10-02 11:39:06 -07:00
parent 3bfb8027cd
commit f77c3ed207
6 changed files with 75 additions and 513 deletions

View File

@ -1,347 +0,0 @@
using System;
using System.Collections;
using System.Configuration.Install;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.ServiceProcess;
using System.Windows.Forms;
using Microsoft.Win32;
namespace WifiSitter
{
public abstract class AbstractService : ServiceBase
{
public static AbstractService Current { get; private set; }
protected virtual string HelpTextPattern
{
get
{
#region Help Text
return
@"
USAGE
{0} [command]
WHERE [command] is one of
/console - run as a console application, for debugging
/service - run as a windows service
/install - install as a windows service
/uninstall - uninstall windows service
";
#endregion
}
}
public abstract string DisplayName { get; }
public abstract string ServiceDesc { get; }
public ServiceExecutionMode ServiceExecutionMode { get; private set; }
protected abstract Guid UninstallGuid { get; }
protected virtual string UninstallRegKeyPath
{
get
{
return @"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall";
}
}
protected AbstractService(string serviceName)
{
ServiceName = serviceName;
if (Current != null)
{
throw new InvalidOperationException(String.Format(
"Service {0} is instantiating but service {1} is already instantiated as current. References to AbstractService.Current will only point to the first service.",
GetType().FullName,
Current.GetType().FullName));
}
Current = this;
}
public void Run(string[] args)
{
Environment.CurrentDirectory = AppDomain.CurrentDomain.BaseDirectory;
if (args.Length == 0 && Debugger.IsAttached)
{
args = new[] { "/console" };
}
if (args.Length == 0)
{
Console.WriteLine(HelpTextPattern, Path.GetFileName(GetType().Assembly.CodeBase));
}
else
{
switch (args[0].ToLower())
{
case "/service":
ServiceExecutionMode = ServiceExecutionMode.Service;
Run(new[] { this });
break;
case "/console":
ServiceExecutionMode = ServiceExecutionMode.Console;
Console.WriteLine("Starting Service...");
OnStart(new string[0]);
OnStartCommandLine();
OnStop();
break;
case "/install":
ServiceExecutionMode = ServiceExecutionMode.Install;
InstallService();
break;
case "/uninstall":
ServiceExecutionMode = ServiceExecutionMode.Uninstall;
UninstallService();
break;
case "/uninstallprompt":
ServiceExecutionMode = ServiceExecutionMode.Uninstall;
if (ConfirmUninstall())
{
UninstallService();
InformUninstalled();
}
break;
default:
if (!OnCustomCommandLine(args))
{
Console.WriteLine(HelpTextPattern, Path.GetFileName(GetType().Assembly.CodeBase));
}
break;
}
}
}
protected override void OnStart(string[] args)
{
OnStartImpl(args);
AppDomain.CurrentDomain.UnhandledException += OnCurrentDomainUnhandledException;
}
protected virtual void OnStartCommandLine()
{
Console.WriteLine("Service is running... Hit ENTER to break.");
Console.ReadLine();
}
protected abstract void OnStartImpl(string[] args);
void OnCurrentDomainUnhandledException(object sender, UnhandledExceptionEventArgs e)
{
// do something useful here, log it..
}
protected override void OnShutdown()
{
Stop();
}
protected override void OnStop()
{
OnStopImpl();
}
protected abstract void OnStopImpl();
protected virtual bool OnCustomCommandLine(string[] args)
{
// for extension
return false;
}
private void InstallService()
{
GetInstaller(".InstallLog").Install(new Hashtable());
InstallServiceCommandLine();
CreateRegKeys();
CreateUninstaller();
}
internal abstract void CreateRegKeys();
private void InstallServiceCommandLine()
{
string keyParent = @"SYSTEM\CurrentControlSet\Services\" + ServiceName;
const string VALUE_NAME = "ImagePath";
try
{
using (RegistryKey key = Registry.LocalMachine.OpenSubKey(keyParent, true))
{
if (key == null)
{
throw new InvalidOperationException("Service not found in registry.");
}
var origPath = key.GetValue(VALUE_NAME) as string;
if (origPath == null)
{
throw new Exception("HKLM\\" + keyParent + "\\" + VALUE_NAME + " does not exist but was expected.");
}
key.SetValue(VALUE_NAME, origPath.Replace("\"\"", "\"") + " /service");
}
}
catch (Exception ex)
{
throw new Exception(
"Error updating service command line after installation. Unable to write to HKLM\\" + keyParent, ex);
}
}
private void CreateUninstaller()
{
using (RegistryKey parent = Registry.LocalMachine.OpenSubKey(UninstallRegKeyPath, true))
{
if (parent == null)
{
throw new Exception(String.Format("Uninstall registry key '{0}' not found.", UninstallRegKeyPath));
}
try
{
RegistryKey key = null;
try
{
string guidText = UninstallGuid.ToString("B");
key = parent.OpenSubKey(guidText, true) ??
parent.CreateSubKey(guidText);
if (key == null)
{
throw new Exception(String.Format("Unable to create uninstaller '{0}\\{1}'", UninstallRegKeyPath, guidText));
}
Assembly asm = GetType().Assembly;
Version v = asm.GetName().Version;
string exe = "\"" + asm.CodeBase.Substring(8).Replace("/", "\\\\") + "\"";
key.SetValue("DisplayName", DisplayName);
key.SetValue("ApplicationVersion", v.ToString());
key.SetValue("Publisher", "Sean McArdle");
key.SetValue("DisplayIcon", exe);
key.SetValue("DisplayVersion", v.ToString(2));
key.SetValue("URLInfoAbout", "https://github.com/sean-m/wifi-sitter");
key.SetValue("Contact", "DHS Service Desk");
key.SetValue("InstallDate", DateTime.Now.ToString("yyyyMMdd"));
key.SetValue("UninstallString", exe + " /uninstallprompt");
}
finally
{
if (key != null)
{
key.Close();
}
}
}
catch (Exception ex)
{
throw new Exception(
"An error occurred writing uninstall information to the registry. The service is fully installed but can only be uninstalled manually through the command line.",
ex);
}
}
}
private bool ConfirmUninstall()
{
string title = "Uninstall " + DisplayName;
string text = "Are you sure you want to remove " + DisplayName + " from your computer?";
return DialogResult.Yes ==
MessageBox.Show(text, title, MessageBoxButtons.YesNo, MessageBoxIcon.Question,
MessageBoxDefaultButton.Button2);
}
private void InformUninstalled()
{
string title = "Uninstall " + DisplayName;
string text = DisplayName + " has been uninstalled.";
MessageBox.Show(text, title, MessageBoxButtons.OK, MessageBoxIcon.Information);
}
private void UninstallService()
{
GetInstaller(".UninstallLog").Uninstall(null);
RemoveRegKeys();
RemoveUninstaller();
}
internal abstract void RemoveRegKeys();
private TransactedInstaller GetInstaller(string logExtension)
{
var ti = new TransactedInstaller();
ti.Installers.Add(new ServiceProcessInstaller
{
Account = ServiceAccount.LocalSystem
});
ti.Installers.Add(new ServiceInstaller {
DisplayName = DisplayName,
ServiceName = ServiceName,
StartType = ServiceStartMode.Automatic,
Description = ServiceDesc
});
string basePath = Assembly.GetEntryAssembly().Location;
String path = String.Format("/assemblypath=\"{0}\"", basePath);
ti.Context = new InstallContext(Path.ChangeExtension(basePath, logExtension), new[] { path });
return ti;
}
private void RemoveUninstaller()
{
using (RegistryKey key = Registry.LocalMachine.OpenSubKey(UninstallRegKeyPath, true))
{
if (key == null)
{
return;
}
try
{
string guidText = UninstallGuid.ToString("B");
RegistryKey child = key.OpenSubKey(guidText);
if (child != null)
{
child.Close();
key.DeleteSubKey(guidText);
}
}
catch (Exception ex)
{
throw new Exception(
"An error occurred removing uninstall information from the registry. The service was uninstalled will still show up in the add/remove program list. To remove it manually delete the entry HKLM\\" +
UninstallRegKeyPath + "\\" + UninstallGuid, ex);
}
}
}
}
public enum ServiceExecutionMode
{
Unknown,
Service,
Console,
Install,
Uninstall,
Custom
}
}

View File

@ -1,141 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Diagnostics;
namespace WifiSitter
{
class NetshHelper
{
public static List<NetshInterface> GetInterfaces()
{
List<NetshInterface> results = new List<NetshInterface>();
var proc = new Process();
proc.StartInfo.FileName = "netsh.exe";
proc.StartInfo.Arguments = "interface show interface";
proc.StartInfo.RedirectStandardError = true;
proc.StartInfo.RedirectStandardOutput = true;
proc.StartInfo.CreateNoWindow = true;
proc.StartInfo.UseShellExecute = false;
proc.Start();
string stdout;
string stderr;
using (StreamReader stdReader = proc.StandardOutput) {
using (StreamReader errReader = proc.StandardError) {
stdout = stdReader.ReadToEnd();
stderr = errReader.ReadToEnd();
}
}
bool threwError = String.IsNullOrEmpty(stderr);
if (!threwError)
return null;
bool startParse = false;
foreach (var line in stdout.Split(new char[] { '\r', '\n' }).Where(x => !String.IsNullOrEmpty(x)).ToArray()) {
if (startParse) {
string[] tokens = line.Split(null).Where(x => !String.IsNullOrEmpty(x)).ToArray();
results.Add(new NetshInterface(tokens[0], tokens[1], tokens[2], String.Join(" ",tokens.Skip(3))));
}
else {
startParse = line.Trim().StartsWith("------------");
}
}
return results;
}
public static bool EnableInterface(string InterfaceName)
{
if (String.IsNullOrEmpty(InterfaceName)) { throw new ArgumentException("InterfaceName cannot be null or empty"); }
List<NetshInterface> results = new List<NetshInterface>();
var proc = new Process();
proc.StartInfo.FileName = "netsh.exe";
proc.StartInfo.Arguments = String.Format("interface set interface name=\"{0}\" admin=ENABLED", InterfaceName);
proc.StartInfo.RedirectStandardError = true;
proc.StartInfo.RedirectStandardOutput = true;
proc.StartInfo.CreateNoWindow = true;
proc.StartInfo.UseShellExecute = false;
proc.Start();
while(!proc.HasExited) { System.Threading.Thread.Sleep(100); }
return proc.ExitCode == 0;
}
public static bool DisableInterface(string InterfaceName)
{
if (String.IsNullOrEmpty(InterfaceName)) { throw new ArgumentException("InterfaceName cannot be null or empty"); }
List<NetshInterface> results = new List<NetshInterface>();
var proc = new Process();
proc.StartInfo.FileName = "netsh.exe";
proc.StartInfo.Arguments = String.Format("interface set interface name=\"{0}\" admin=DISABLED", InterfaceName);
proc.StartInfo.RedirectStandardError = true;
proc.StartInfo.RedirectStandardOutput = true;
proc.StartInfo.CreateNoWindow = true;
proc.StartInfo.UseShellExecute = false;
proc.Start();
while (!proc.HasExited) { System.Threading.Thread.Sleep(100); }
return proc.ExitCode == 0;
}
public static bool ReleaseIp (string InterfaceName)
{
//ipconfig /release "Ethernet"
if (String.IsNullOrEmpty(InterfaceName)) { throw new ArgumentException("InterfaceName cannot be null or empty"); }
List<NetshInterface> results = new List<NetshInterface>();
var proc = new Process();
proc.StartInfo.FileName = "ipconfig.exe";
proc.StartInfo.Arguments = String.Format("/release \"{0}\"", InterfaceName);
proc.StartInfo.RedirectStandardError = true;
proc.StartInfo.RedirectStandardOutput = true;
proc.StartInfo.CreateNoWindow = true;
proc.StartInfo.UseShellExecute = false;
proc.Start();
while (!proc.HasExited) { System.Threading.Thread.Sleep(100); }
return proc.ExitCode == 0;
}
}
public sealed class NetshInterface
{
// Admin State State Type Interface Name
private string _adminState;
private string _state;
private string _type;
private string _interfaceName;
public string AdminState { get { return _adminState; } }
public string State { get { return _state; } }
public string Type { get { return _type; } }
public string InterfaceName { get { return _interfaceName; } }
public NetshInterface(string AdminState, string State, string Type, string Name)
{
_adminState = AdminState;
_state = State;
_type = Type;
_interfaceName = Name;
}
}
}

View File

@ -8,14 +8,14 @@ namespace WifiSitter
/// <summary>
/// Class used to track state of detected network adapters
/// </summary>
public class NetworkState
{
public class NetworkState {
#region fields
private List<SitterNic> _nics;
private List<TrackedNic> _nics;
private bool _checkNet;
private bool _netAvailable;
private bool _processingState;
private string[] _ignoreAdapters; // List of Nic names to ignore during normal operation
private List<string[]> _originalNicState = new List<string[]>();
#endregion // fields
@ -25,12 +25,20 @@ namespace WifiSitter
if (NicWhitelist == null)
NicWhitelist = new string[] { };
this.Nics = QueryNetworkAdapters(NicWhitelist);
// Loop through nics and add id:state to _originalNicState list
Nics.ForEach(x => _originalNicState.Add(new string[] { x.Id, x.IsEnabled.ToString() }));
_ignoreAdapters = NicWhitelist;
Initialize();
}
public NetworkState(List<SitterNic> Nics, string[] NicWhitelist) {
public NetworkState(List<TrackedNic> Nics, string[] NicWhitelist) {
this.Nics = Nics;
// Loop through nics and add id:state to _originalNicState list
Nics.ForEach(x => _originalNicState.Add(new string[] { x.Id, x.IsEnabled.ToString() }));
_ignoreAdapters = NicWhitelist;
Initialize();
}
@ -59,17 +67,21 @@ namespace WifiSitter
this.ProcessingState = false;
}
public void UpdateNics(List<SitterNic> Nics) {
public void UpdateNics(List<TrackedNic> Nics) {
foreach (var n in Nics) {
if (!_originalNicState.Any(x => x[0] == n.Id)) _originalNicState.Add(new string[] { n.Id, n.IsEnabled.ToString() });
}
this.Nics = Nics;
}
internal static List<SitterNic> QueryNetworkAdapters(string[] WhiteList) {
List<SitterNic> result = new List<SitterNic>();
internal static List<TrackedNic> QueryNetworkAdapters(string[] WhiteList) {
List<TrackedNic> result = new List<TrackedNic>();
foreach (var n in NetworkInterface.GetAllNetworkInterfaces().Where(x => (x.NetworkInterfaceType != NetworkInterfaceType.Loopback
&& x.NetworkInterfaceType != NetworkInterfaceType.Tunnel
&& !x.Description.ToLower().Contains("bluetooth")
&& !WhiteList.Any(y => x.Description.StartsWith(y))))) {
result.Add(new SitterNic(n));
result.Add(new TrackedNic(n));
}
return result;
}
@ -87,14 +99,17 @@ namespace WifiSitter
}
}
public List<SitterNic> Nics {
public List<TrackedNic> Nics {
get {
if (_nics == null) return new List<SitterNic>();
if (_nics == null) return new List<TrackedNic>();
return _nics;
}
private set { _nics = value; }
}
public List<string[]> OriginalNicState {
get { return _originalNicState; }
}
public bool CheckNet {
get { return _checkNet; }

View File

@ -3,25 +3,27 @@ using System.Collections.Generic;
using System.Linq;
using System.Net.NetworkInformation;
using WifiSitter.Helpers;
namespace WifiSitter
{
/// <summary>
/// Object that contains information from NetworkInterface objects
/// as well as netsh output (Admin State: Enabled/Disabled).
/// </summary>
public class SitterNic
public class TrackedNic
{
private NetworkInterface _nic;
private bool _isEnabled;
private bool _isConnected;
#region constructor
public SitterNic(NetworkInterface Nic) {
public TrackedNic(NetworkInterface Nic) {
this._nic = Nic;
_isEnabled = false;
}
public SitterNic(NetworkInterface Nic, bool IsEnabled) {
public TrackedNic(NetworkInterface Nic, bool IsEnabled) {
_nic = Nic;
_isEnabled = IsEnabled;
}

View File

@ -7,6 +7,9 @@ using System.Net.NetworkInformation;
using System.Threading;
using System.Reflection;
using WifiSitter.Helpers;
using System.Threading.Tasks;
namespace WifiSitter
{
public class WifiSitter : AbstractService
@ -33,6 +36,8 @@ namespace WifiSitter
this.AutoLog = true;
this.CanPauseAndContinue = true;
}
Intialize();
}
#endregion // constructor
@ -109,17 +114,17 @@ namespace WifiSitter
return results.ToArray();
}
public static List<SitterNic> DiscoverAllNetworkDevices(List<SitterNic> CurrentAdapters=null, bool quiet=false) {
public static List<TrackedNic> DiscoverAllNetworkDevices(List<TrackedNic> CurrentAdapters=null, bool quiet=false) {
if (!quiet) LogLine(ConsoleColor.Yellow, "Discovering all devices.");
var nics = (CurrentAdapters == null) ? NetworkState.QueryNetworkAdapters(_ignoreNics) : CurrentAdapters;
List<SitterNic> nicsPost;
List<TrackedNic> nicsPost;
var netsh = NetshHelper.GetInterfaces();
List<NetshInterface> notInNetstate = new List<NetshInterface>();
// Skip checking for disabled adapters we already know about
// Only check disabled adapters we don't already know about
foreach (var n in netsh) {
if (!nics.Any(x => x.Name == n.InterfaceName)) {
notInNetstate.Add(n);
@ -177,8 +182,7 @@ namespace WifiSitter
Console.WriteLine(" {0}", log);
Console.ResetColor();
}
public void WriteLog(LogType type, params string[] msg) {
if (this.ServiceExecutionMode == ServiceExecutionMode.Console) {
@ -232,9 +236,7 @@ namespace WifiSitter
}
private void WorkerThreadFunc() {
Intialize();
while (!_shutdownEvent.WaitOne(0)) {
if (_paused) {
@ -309,6 +311,34 @@ namespace WifiSitter
}
}
private void ResetNicState (NetworkState netstate) {
var taskList = new List<Task>();
foreach (var n in netstate.OriginalNicState) {
var id = n[0];
var stat = n[1];
TrackedNic now = netstate.Nics.Where(x => x.Id == id).FirstOrDefault();
if (now != null) {
if (stat.ToLower() != now.IsEnabled.ToString().ToLower()) {
if (stat == true.ToString()) {
var enableTask = new Task(() => { now.Enable(); });
enableTask.Start();
taskList.Add(enableTask);
}
else {
var disableTask = new Task(() => { now.Disable(); });
disableTask.Start();
taskList.Add(disableTask); }
}
}
}
try {
Task.WaitAll(taskList.ToArray());
}
catch (Exception e) {
WriteLog(LogType.error, "Exception when resetting nic state\n", e.InnerException.Message);
}
}
#endregion // methods
@ -322,6 +352,7 @@ namespace WifiSitter
}
protected override void OnStopImpl() {
ResetNicState(netstate);
_shutdownEvent.Set();
if (!_thread.Join(3000)) {
_thread.Abort();

View File

@ -91,10 +91,12 @@
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="AbstractService.cs">
<Compile Include="Helpers\AbstractService.cs">
<SubType>Component</SubType>
</Compile>
<Compile Include="NetshHelper.cs" />
<Compile Include="Helpers\Interop.cs" />
<Compile Include="Helpers\NativeWifi.cs" />
<Compile Include="Helpers\NetshHelper.cs" />
<Compile Include="NetworkState.cs" />
<Compile Include="Program.cs" />
<Compile Include="WifiSitter.cs">
@ -131,7 +133,7 @@
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<ProjectExtensions>
<VisualStudio>
<UserProperties BuildVersion_StartDate="2000/1/1" BuildVersion_BuildVersioningStyle="None.None.None.Increment" BuildVersion_UpdateFileVersion="False" />
<UserProperties BuildVersion_UpdateFileVersion="False" BuildVersion_BuildVersioningStyle="None.None.None.Increment" BuildVersion_StartDate="2000/1/1" />
</VisualStudio>
</ProjectExtensions>
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.