Merge pull request #71 from kohsuke/winsw-2.0

WinSW 2.0
pull/135/head
Oleg Nenashev 2016-11-17 09:50:36 +01:00 committed by GitHub
commit 929b87c383
54 changed files with 1540 additions and 62 deletions

3
.gitignore vendored
View File

@ -5,3 +5,6 @@ obj
/winsw.csproj.user /winsw.csproj.user
/winsw_cert.pfx /winsw_cert.pfx
*.user *.user
/src/packages/NUnit.2.6.4
/src/packages/ILMerge.MSBuild.Tasks.1.0.0.3
/winsw_key.snk

Binary file not shown.

6
src/.nuget/NuGet.Config Normal file
View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<solution>
<add key="disableSourceControlIntegration" value="true" />
</solution>
</configuration>

BIN
src/.nuget/NuGet.exe Normal file

Binary file not shown.

144
src/.nuget/NuGet.targets Normal file
View File

@ -0,0 +1,144 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">$(MSBuildProjectDirectory)\..\</SolutionDir>
<!-- Enable the restore command to run before builds -->
<RestorePackages Condition=" '$(RestorePackages)' == '' ">false</RestorePackages>
<!-- Property that enables building a package from a project -->
<BuildPackage Condition=" '$(BuildPackage)' == '' ">false</BuildPackage>
<!-- Determines if package restore consent is required to restore packages -->
<RequireRestoreConsent Condition=" '$(RequireRestoreConsent)' != 'false' ">true</RequireRestoreConsent>
<!-- Download NuGet.exe if it does not already exist -->
<DownloadNuGetExe Condition=" '$(DownloadNuGetExe)' == '' ">false</DownloadNuGetExe>
</PropertyGroup>
<ItemGroup Condition=" '$(PackageSources)' == '' ">
<!-- Package sources used to restore packages. By default, registered sources under %APPDATA%\NuGet\NuGet.Config will be used -->
<!-- The official NuGet package source (https://www.nuget.org/api/v2/) will be excluded if package sources are specified and it does not appear in the list -->
<!--
<PackageSource Include="https://www.nuget.org/api/v2/" />
<PackageSource Include="https://my-nuget-source/nuget/" />
-->
</ItemGroup>
<PropertyGroup Condition=" '$(OS)' == 'Windows_NT'">
<!-- Windows specific commands -->
<NuGetToolsPath>$([System.IO.Path]::Combine($(SolutionDir), ".nuget"))</NuGetToolsPath>
</PropertyGroup>
<PropertyGroup Condition=" '$(OS)' != 'Windows_NT'">
<!-- We need to launch nuget.exe with the mono command if we're not on windows -->
<NuGetToolsPath>$(SolutionDir).nuget</NuGetToolsPath>
</PropertyGroup>
<PropertyGroup>
<PackagesProjectConfig Condition=" '$(OS)' == 'Windows_NT'">$(MSBuildProjectDirectory)\packages.$(MSBuildProjectName.Replace(' ', '_')).config</PackagesProjectConfig>
<PackagesProjectConfig Condition=" '$(OS)' != 'Windows_NT'">$(MSBuildProjectDirectory)\packages.$(MSBuildProjectName).config</PackagesProjectConfig>
</PropertyGroup>
<PropertyGroup>
<PackagesConfig Condition="Exists('$(MSBuildProjectDirectory)\packages.config')">$(MSBuildProjectDirectory)\packages.config</PackagesConfig>
<PackagesConfig Condition="Exists('$(PackagesProjectConfig)')">$(PackagesProjectConfig)</PackagesConfig>
</PropertyGroup>
<PropertyGroup>
<!-- NuGet command -->
<NuGetExePath Condition=" '$(NuGetExePath)' == '' ">$(NuGetToolsPath)\NuGet.exe</NuGetExePath>
<PackageSources Condition=" $(PackageSources) == '' ">@(PackageSource)</PackageSources>
<NuGetCommand Condition=" '$(OS)' == 'Windows_NT'">"$(NuGetExePath)"</NuGetCommand>
<NuGetCommand Condition=" '$(OS)' != 'Windows_NT' ">mono --runtime=v4.0.30319 "$(NuGetExePath)"</NuGetCommand>
<PackageOutputDir Condition="$(PackageOutputDir) == ''">$(TargetDir.Trim('\\'))</PackageOutputDir>
<RequireConsentSwitch Condition=" $(RequireRestoreConsent) == 'true' ">-RequireConsent</RequireConsentSwitch>
<NonInteractiveSwitch Condition=" '$(VisualStudioVersion)' != '' AND '$(OS)' == 'Windows_NT' ">-NonInteractive</NonInteractiveSwitch>
<PaddedSolutionDir Condition=" '$(OS)' == 'Windows_NT'">"$(SolutionDir) "</PaddedSolutionDir>
<PaddedSolutionDir Condition=" '$(OS)' != 'Windows_NT' ">"$(SolutionDir)"</PaddedSolutionDir>
<!-- Commands -->
<RestoreCommand>$(NuGetCommand) install "$(PackagesConfig)" -source "$(PackageSources)" $(NonInteractiveSwitch) $(RequireConsentSwitch) -solutionDir $(PaddedSolutionDir)</RestoreCommand>
<BuildCommand>$(NuGetCommand) pack "$(ProjectPath)" -Properties "Configuration=$(Configuration);Platform=$(Platform)" $(NonInteractiveSwitch) -OutputDirectory "$(PackageOutputDir)" -symbols</BuildCommand>
<!-- We need to ensure packages are restored prior to assembly resolve -->
<BuildDependsOn Condition="$(RestorePackages) == 'true'">
RestorePackages;
$(BuildDependsOn);
</BuildDependsOn>
<!-- Make the build depend on restore packages -->
<BuildDependsOn Condition="$(BuildPackage) == 'true'">
$(BuildDependsOn);
BuildPackage;
</BuildDependsOn>
</PropertyGroup>
<Target Name="CheckPrerequisites">
<!-- Raise an error if we're unable to locate nuget.exe -->
<Error Condition="'$(DownloadNuGetExe)' != 'true' AND !Exists('$(NuGetExePath)')" Text="Unable to locate '$(NuGetExePath)'" />
<!--
Take advantage of MsBuild's build dependency tracking to make sure that we only ever download nuget.exe once.
This effectively acts as a lock that makes sure that the download operation will only happen once and all
parallel builds will have to wait for it to complete.
-->
<MsBuild Targets="_DownloadNuGet" Projects="$(MSBuildThisFileFullPath)" Properties="Configuration=NOT_IMPORTANT;DownloadNuGetExe=$(DownloadNuGetExe)" />
</Target>
<Target Name="_DownloadNuGet">
<DownloadNuGet OutputFilename="$(NuGetExePath)" Condition=" '$(DownloadNuGetExe)' == 'true' AND !Exists('$(NuGetExePath)')" />
</Target>
<Target Name="RestorePackages" DependsOnTargets="CheckPrerequisites">
<Exec Command="$(RestoreCommand)"
Condition="'$(OS)' != 'Windows_NT' And Exists('$(PackagesConfig)')" />
<Exec Command="$(RestoreCommand)"
LogStandardErrorAsError="true"
Condition="'$(OS)' == 'Windows_NT' And Exists('$(PackagesConfig)')" />
</Target>
<Target Name="BuildPackage" DependsOnTargets="CheckPrerequisites">
<Exec Command="$(BuildCommand)"
Condition=" '$(OS)' != 'Windows_NT' " />
<Exec Command="$(BuildCommand)"
LogStandardErrorAsError="true"
Condition=" '$(OS)' == 'Windows_NT' " />
</Target>
<UsingTask TaskName="DownloadNuGet" TaskFactory="CodeTaskFactory" AssemblyFile="$(MSBuildToolsPath)\Microsoft.Build.Tasks.v4.0.dll">
<ParameterGroup>
<OutputFilename ParameterType="System.String" Required="true" />
</ParameterGroup>
<Task>
<Reference Include="System.Core" />
<Using Namespace="System" />
<Using Namespace="System.IO" />
<Using Namespace="System.Net" />
<Using Namespace="Microsoft.Build.Framework" />
<Using Namespace="Microsoft.Build.Utilities" />
<Code Type="Fragment" Language="cs">
<![CDATA[
try {
OutputFilename = Path.GetFullPath(OutputFilename);
Log.LogMessage("Downloading latest version of NuGet.exe...");
WebClient webClient = new WebClient();
webClient.DownloadFile("https://www.nuget.org/nuget.exe", OutputFilename);
return true;
}
catch (Exception ex) {
Log.LogErrorFromException(ex);
return false;
}
]]>
</Code>
</Task>
</UsingTask>
</Project>

View File

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="ILMerge.MSBuild.Tasks" version="1.0.0.3" />
</packages>

View File

@ -7,14 +7,23 @@ using System.Runtime.InteropServices;
using System.ServiceProcess; using System.ServiceProcess;
using System.Text; using System.Text;
using System.Threading; using System.Threading;
using log4net;
using log4net.Appender;
using log4net.Config;
using log4net.Core;
using log4net.Layout;
using log4net.Repository.Hierarchy;
using Microsoft.Win32; using Microsoft.Win32;
using winsw.Extensions;
using winsw.Util;
using WMI; using WMI;
using ServiceType = WMI.ServiceType; using ServiceType = WMI.ServiceType;
using winsw.Native;
using System.Reflection; using System.Reflection;
namespace winsw namespace winsw
{ {
public class WrapperService : ServiceBase, EventLogger public class WrapperService : ServiceBase, EventLogger, IEventWriter
{ {
private SERVICE_STATUS _wrapperServiceStatus; private SERVICE_STATUS _wrapperServiceStatus;
@ -22,6 +31,10 @@ namespace winsw
private readonly ServiceDescriptor _descriptor; private readonly ServiceDescriptor _descriptor;
private Dictionary<string, string> _envs; private Dictionary<string, string> _envs;
internal WinSWExtensionManager ExtensionManager { private set; get; }
private static readonly ILog Log = LogManager.GetLogger("WinSW");
/// <summary> /// <summary>
/// Indicates to the watch dog thread that we are going to terminate the process, /// Indicates to the watch dog thread that we are going to terminate the process,
/// so don't try to kill us when the child exits. /// so don't try to kill us when the child exits.
@ -44,6 +57,7 @@ namespace winsw
{ {
_descriptor = descriptor; _descriptor = descriptor;
ServiceName = _descriptor.Id; ServiceName = _descriptor.Id;
ExtensionManager = new WinSWExtensionManager(_descriptor);
CanShutdown = true; CanShutdown = true;
CanStop = true; CanStop = true;
CanPauseAndContinue = false; CanPauseAndContinue = false;
@ -183,22 +197,19 @@ namespace winsw
private void WriteEvent(Exception exception) private void WriteEvent(Exception exception)
{ {
WriteEvent(exception.Message + "\nStacktrace:" + exception.StackTrace); //TODO: pass exception to logger
WriteEvent(exception.Message + "\nStacktrace:" + exception.StackTrace, Level.Error);
} }
private void WriteEvent(String message, Exception exception) private void WriteEvent(String message, Exception exception)
{ {
WriteEvent(message + "\nMessage:" + exception.Message + "\nStacktrace:" + exception.StackTrace); //TODO: pass exception to logger
WriteEvent(message + "\nMessage:" + exception.Message + "\nStacktrace:" + exception.StackTrace, Level.Error);
} }
private void WriteEvent(String message) private void WriteEvent(String message, Level logLevel = null, Exception ex = null)
{ {
string logfilename = Path.Combine(_descriptor.LogDirectory, _descriptor.BaseName + ".wrapper.log"); Log.Logger.Log(GetType(), logLevel ?? Level.Info, message, ex);
StreamWriter log = new StreamWriter(logfilename, true);
log.WriteLine(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss") + " - " + message);
log.Flush();
log.Close();
} }
protected override void OnStart(string[] _) protected override void OnStart(string[] _)
@ -241,6 +252,22 @@ namespace winsw
LogEvent("Starting " + _descriptor.Executable + ' ' + startarguments); LogEvent("Starting " + _descriptor.Executable + ' ' + startarguments);
WriteEvent("Starting " + _descriptor.Executable + ' ' + startarguments); WriteEvent("Starting " + _descriptor.Executable + ' ' + startarguments);
// Load and start extensions
ExtensionManager.LoadExtensions(this);
try
{
ExtensionManager.OnStart(this);
}
catch (ExtensionException ex)
{
LogEvent("Failed to start extension " + ex.ExtensionId + "\n" + ex.Message, EventLogEntryType.Error);
WriteEvent("Failed to start extension " + ex.ExtensionId, ex);
//TODO: Exit on error?
}
LogEvent("Starting " + _descriptor.Executable + ' ' + startarguments);
WriteEvent("Starting " + _descriptor.Executable + ' ' + startarguments);
StartProcess(_process, startarguments, _descriptor.Executable); StartProcess(_process, startarguments, _descriptor.Executable);
// send stdout and stderr to its respective output file. // send stdout and stderr to its respective output file.
@ -322,6 +349,17 @@ namespace winsw
SignalShutdownComplete(); SignalShutdownComplete();
} }
// Stop extensions
try
{
ExtensionManager.OnStop(this);
}
catch (ExtensionException ex)
{
LogEvent("Failed to stop extension " + ex.ExtensionId + "\n" + ex.Message, EventLogEntryType.Error);
WriteEvent("Failed to stop extension " + ex.ExtensionId, ex);
}
if (_systemShuttingdown && _descriptor.BeepOnShutdown) if (_systemShuttingdown && _descriptor.BeepOnShutdown)
{ {
Console.Beep(); Console.Beep();
@ -389,7 +427,7 @@ namespace winsw
{ {
try try
{ {
WriteEvent("SIGINT to " + pid + " failed - Killing as fallback"); WriteEvent("SIGINT to " + pid + " failed - Killing as fallback", Level.Warn);
proc.Kill(); proc.Kill();
} }
catch (ArgumentException) catch (ArgumentException)
@ -504,6 +542,7 @@ namespace winsw
public static int Main(string[] args) public static int Main(string[] args)
{ {
// Run app
try try
{ {
Run(args); Run(args);
@ -529,9 +568,18 @@ namespace winsw
// ReSharper disable once InconsistentNaming // ReSharper disable once InconsistentNaming
public static void Run(string[] _args, ServiceDescriptor descriptor = null) public static void Run(string[] _args, ServiceDescriptor descriptor = null)
{ {
if (_args.Length > 0) bool isCLIMode = _args.Length > 0;
{
var d = descriptor ?? new ServiceDescriptor(); var d = descriptor ?? new ServiceDescriptor();
// Configure the wrapper-internal logging
// STDIN and STDOUT of the child process will be handled independently
InitLoggers(d, isCLIMode);
if (isCLIMode) // CLI mode
{
Log.Debug("Starting ServiceWrapper in CLI mode");
// Get service info for the future use
Win32Services svc = new WmiRoot().GetCollection<Win32Services>(); Win32Services svc = new WmiRoot().GetCollection<Win32Services>();
Win32Service s = svc.Select(d.Id); Win32Service s = svc.Select(d.Id);
@ -702,6 +750,7 @@ namespace winsw
} }
if (args[0] == "status") if (args[0] == "status")
{ {
Log.Warn("User requested the status");
if (s == null) if (s == null)
Console.WriteLine("NonExistent"); Console.WriteLine("NonExistent");
else if (s.Started) else if (s.Started)
@ -738,6 +787,43 @@ namespace winsw
Run(new WrapperService()); Run(new WrapperService());
} }
private static void InitLoggers(ServiceDescriptor d, bool enableCLILogging)
{
Level logLevel = Level.Debug;
// Legacy format from winsw-1.x: (DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss") + " - " + message);
PatternLayout pl = new PatternLayout { ConversionPattern = "%d %-5p - %m%n" };
pl.ActivateOptions();
// wrapper.log
String wrapperLogPath = Path.Combine(d.LogDirectory, d.BaseName + ".wrapper.log");
var wrapperLog = new FileAppender
{
AppendToFile = true,
File = wrapperLogPath,
ImmediateFlush = true,
Name = "Wrapper file log",
Threshold = logLevel,
LockingModel = new FileAppender.MinimalLock(),
Layout = pl
};
wrapperLog.ActivateOptions();
BasicConfigurator.Configure(wrapperLog);
// Also display logs in CLI if required
if (enableCLILogging)
{
var consoleAppender = new ConsoleAppender
{
Name = "Wrapper console log",
Threshold = logLevel,
Layout = pl
};
consoleAppender.ActivateOptions();
((Logger)Log.Logger).AddAppender(consoleAppender);
}
}
private static string ReadPassword() private static string ReadPassword()
{ {
StringBuilder buf = new StringBuilder(); StringBuilder buf = new StringBuilder();

View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="ILMerge" version="2.14.1208" targetFramework="net20" />
<package id="log4net" version="2.0.3" targetFramework="net20" />
<package id="MSBuildTasks" version="1.4.0.88" targetFramework="net20" />
</packages>

View File

@ -0,0 +1,179 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProductVersion>9.0.21022</ProductVersion>
<SchemaVersion>2.0</SchemaVersion>
<ProjectGuid>{0DE77F55-ADE5-43C1-999A-0BC81153B039}</ProjectGuid>
<OutputType>Exe</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>winsw</RootNamespace>
<AssemblyName>WindowsService</AssemblyName>
<TargetFrameworkVersion>v2.0</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<StartupObject>
</StartupObject>
<SignManifests>false</SignManifests>
<SignAssembly>true</SignAssembly>
<AssemblyOriginatorKeyFile>$(SolutionDir)..\winsw_key.snk</AssemblyOriginatorKeyFile>
<FileUpgradeFlags>
</FileUpgradeFlags>
<UpgradeBackupLocation>
</UpgradeBackupLocation>
<OldToolsVersion>3.5</OldToolsVersion>
<PublishUrl>publish\</PublishUrl>
<Install>true</Install>
<InstallFrom>Disk</InstallFrom>
<UpdateEnabled>false</UpdateEnabled>
<UpdateMode>Foreground</UpdateMode>
<UpdateInterval>7</UpdateInterval>
<UpdateIntervalUnits>Days</UpdateIntervalUnits>
<UpdatePeriodically>false</UpdatePeriodically>
<UpdateRequired>false</UpdateRequired>
<MapFileExtensions>true</MapFileExtensions>
<ApplicationRevision>0</ApplicationRevision>
<ApplicationVersion>1.0.0.%2a</ApplicationVersion>
<IsWebBootstrapper>false</IsWebBootstrapper>
<UseApplicationTrust>false</UseApplicationTrust>
<BootstrapperEnabled>true</BootstrapperEnabled>
<SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">..\..\</SolutionDir>
<RestorePackages>true</RestorePackages>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>TRACE;DEBUG</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<DocumentationFile>
</DocumentationFile>
</PropertyGroup>
<ItemGroup>
<Reference Include="log4net">
<HintPath>..\..\packages\log4net.2.0.3\lib\net20-full\log4net.dll</HintPath>
</Reference>
<Reference Include="System" />
<Reference Include="System.Drawing" />
<Reference Include="System.Management" />
<Reference Include="System.Data" />
<Reference Include="System.ServiceProcess" />
<Reference Include="System.Windows.Forms" />
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="Main.cs">
<SubType>Component</SubType>
</Compile>
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="SigIntHelper.cs" />
</ItemGroup>
<ItemGroup>
<Content Include="manifest.xml" />
<Content Include="pom.xml">
<SubType>Designer</SubType>
</Content>
<Content Include="winsw.xml">
<SubType>Designer</SubType>
</Content>
</ItemGroup>
<ItemGroup>
<None Include="..\..\..\winsw_cert.pfx" />
<None Include="packages.config">
<SubType>Designer</SubType>
</None>
<None Include="$(AssemblyOriginatorKeyFile)" />
</ItemGroup>
<ItemGroup>
<BootstrapperPackage Include="Microsoft.Net.Client.3.5">
<Visible>False</Visible>
<ProductName>.NET Framework 3.5 SP1 Client Profile</ProductName>
<Install>false</Install>
</BootstrapperPackage>
<BootstrapperPackage Include="Microsoft.Net.Framework.3.5.SP1">
<Visible>False</Visible>
<ProductName>.NET Framework 3.5 SP1</ProductName>
<Install>true</Install>
</BootstrapperPackage>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\Plugins\SharedDirectoryMapper\SharedDirectoryMapper.csproj">
<Project>{ca5c71db-c5a8-4c27-bf83-8e6daed9d6b5}</Project>
<Name>SharedDirectoryMapper</Name>
</ProjectReference>
<ProjectReference Include="..\WinSWCore\WinSWCore.csproj">
<Project>{9d0c63e2-b6ff-4a85-bd36-b3e5d7f27d06}</Project>
<Name>WinSWCore</Name>
</ProjectReference>
</ItemGroup>
<ItemGroup />
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">
</Target>
<Target Name="AfterBuild">
</Target>
-->
<Import Project="$(SolutionDir)\.nuget\NuGet.targets" Condition="Exists('$(SolutionDir)\.nuget\NuGet.targets')" />
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
<PropertyGroup>
<ErrorText>This project references NuGet package(s) that are missing on this computer. Enable NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
</PropertyGroup>
<Error Condition="!Exists('$(SolutionDir)\.nuget\NuGet.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(SolutionDir)\.nuget\NuGet.targets'))" />
</Target>
<!-- Merge plugins and other DLLs into winsw.exe -->
<UsingTask TaskName="MSBuild.Community.Tasks.ILMerge" AssemblyFile="$(SolutionDir)\packages\MSBuildTasks.1.4.0.88\tools\MSBuild.Community.Tasks.dll" />
<Target Name="AfterBuild">
<ItemGroup>
<MergeAsm Include="$(OutputPath)$(TargetFileName)" />
<MergeAsm Include="$(OutputPath)WinSWCore.dll" />
<MergeAsm Include="$(OutputPath)SharedDirectoryMapper.dll" />
<MergeAsm Include="$(OutputPath)log4net.dll" />
</ItemGroup>
<PropertyGroup>
<MergedAssembly>$(ProjectDir)$(OutDir)winsw.exe</MergedAssembly>
<CertificatePfxFile>$(AssemblyOriginatorKeyFile)</CertificatePfxFile>
</PropertyGroup>
<!-- Locate SN.EXE in Windows SDK, use the first found one-->
<PropertyGroup>
<SnPathTmpFile>$(OutputPath)sn-path.txt</SnPathTmpFile>
</PropertyGroup>
<GetFrameworkSdkPath>
<Output TaskParameter="Path" PropertyName="WindowsSdkPath" />
</GetFrameworkSdkPath>
<Message Text="Using SDK from $(WindowsSdkPath)" Importance="high" />
<Exec Command="WHERE /r &quot;$(WindowsSdkPath.TrimEnd('\\'))&quot; sn &gt; $(SnPathTmpFile)" />
<ReadLinesFromFile File="$(SnPathTmpFile)">
<Output TaskParameter="Lines" PropertyName="SNPath" />
</ReadLinesFromFile>
<Delete Files="$(SnPathTmpFile)" />
<PropertyGroup>
<SNPath>$([System.Text.RegularExpressions.Regex]::Replace('$(SNPath)', ';.*', ''))</SNPath>
</PropertyGroup>
<Error Condition="!Exists('$(SNPath)')" Text="Cannot find SN.EXE utility in $(WindowsSdkPath)" />
<Message Text="Using SN.EXE utility from $(SNPath)" Importance="high" />
<!-- Merge and re-sign assemblies -->
<PropertyGroup>
<ILMergePath>$(SolutionDir)packages\ilmerge.2.14.1208\tools</ILMergePath>
<CertificateTmpPubFile>$(OutputPath)winsw_cert.pub</CertificateTmpPubFile>
</PropertyGroup>
<Message Text="Extracting public key from $(AssemblyOriginatorKeyFile)" />
<Exec Command="&quot;$(SNPath)&quot; -p &quot;$(AssemblyOriginatorKeyFile)&quot; &quot;$(CertificateTmpPubFile)&quot;" />
<Message Text="ILMerge @(MergeAsm) -&gt; $(MergedAssembly)" Importance="high" />
<ILMerge ToolPath="$(ILMergePath)" InputAssemblies="@(MergeAsm)" OutputFile="$(MergedAssembly)" TargetKind="SameAsPrimaryAssembly" KeyFile="$(CertificateTmpPubFile)" DelaySign="true" />
<Exec Command="&quot;$(SNPath)&quot; -R &quot;$(MergedAssembly)&quot; &quot;$(AssemblyOriginatorKeyFile)&quot;" />
</Target>
</Project>

View File

@ -0,0 +1,27 @@
using System;
using System.Xml;
using winsw.Util;
namespace winsw.Extensions
{
public abstract class AbstractWinSWExtension : IWinSWExtension
{
public abstract String DisplayName { get; }
public WinSWExtensionDescriptor Descriptor { get; set; }
public virtual void Configure(ServiceDescriptor descriptor, XmlNode node, IEventWriter logger)
{
// Do nothing
}
public virtual void OnStart(IEventWriter eventWriter)
{
// Do nothing
}
public virtual void OnStop(IEventWriter eventWriter)
{
// Do nothing
}
}
}

View File

@ -0,0 +1,29 @@
using System;
namespace winsw.Extensions
{
public class ExtensionException : WinSWException
{
public String ExtensionId { get; private set; }
public ExtensionException(String extensionName, String message)
: base(message)
{
ExtensionId = extensionName;
}
public ExtensionException(String extensionName, String message, Exception innerException)
: base(message, innerException)
{
ExtensionId = extensionName;
}
public override string Message
{
get
{
return ExtensionId + ": " + base.Message;
}
}
}
}

View File

@ -0,0 +1,16 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace winsw.Extensions
{
/// <summary>
/// This attribute is used to identify extension points within the code
/// </summary>
/// <remarks>
/// Each extension point implements its own entry type.
/// </remarks>
class ExtensionPointAttribute
{
}
}

View File

@ -0,0 +1,47 @@
using System;
using System.Xml;
using winsw.Util;
namespace winsw.Extensions
{
/// <summary>
/// Interface for Win Service Wrapper Extension
/// </summary>
/// <remarks>
/// All implementations should provide the default empty constructor.
/// The initialization will be performed by Init methods
/// </remarks>
public interface IWinSWExtension
{
/// <summary>
/// Extension name to be displayed in logs
/// </summary>
String DisplayName { get; }
/// <summary>
/// Extension descriptor
/// </summary>
WinSWExtensionDescriptor Descriptor { get; set; }
/// <summary>
/// Init handler. Extension should load it's config during that step
/// </summary>
/// <param name="descriptor">Service descriptor</param>
/// <param name="node">Configuration node</param>
void Configure(ServiceDescriptor descriptor, XmlNode node, IEventWriter logger);
/// <summary>
/// Start handler. Called during startup of the service before the child process.
/// </summary>
/// <param name="logger">Logger</param>
/// <exception cref="ExtensionException">Any error during execution</exception>
void OnStart(IEventWriter logger);
/// <summary>
/// Stop handler. Called during stop of the service
/// </summary>
/// <param name="logger">Logger</param>
/// <exception cref="ExtensionException">Any error during execution</exception>
void OnStop(IEventWriter logger);
}
}

View File

@ -0,0 +1,45 @@
using System;
using System.Xml;
using winsw.Util;
namespace winsw.Extensions
{
/// <summary>
/// Describes WinSW extensions in <see cref="IWinSWExtension"/>
/// </summary>
/// <remarks>
/// Any extension has its own descriptor instance.
/// </remarks>
public class WinSWExtensionDescriptor
{
/// <summary>
/// Unique extension ID
/// </summary>
public String Id { get; private set; }
/// <summary>
/// Exception is enabled
/// </summary>
public bool Enabled { get; private set; }
/// <summary>
/// Extension classname
/// </summary>
public String ClassName { get; private set; }
private WinSWExtensionDescriptor(string id, string className, bool enabled)
{
Id = id;
Enabled = enabled;
ClassName = className;
}
public static WinSWExtensionDescriptor FromXml(XmlElement node)
{
bool enabled = XmlHelper.SingleAttribute(node, "enabled", true);
string className = XmlHelper.SingleAttribute<string>(node, "className");
string id = XmlHelper.SingleAttribute<string>(node, "id");
return new WinSWExtensionDescriptor(id, className, enabled);
}
}
}

View File

@ -0,0 +1,123 @@
using System;
using System.Collections.Generic;
using System.Xml;
using System.Reflection;
using System.Diagnostics;
using winsw.Util;
namespace winsw.Extensions
{
public class WinSWExtensionManager
{
public Dictionary<string, IWinSWExtension> Extensions { private set; get; }
public ServiceDescriptor ServiceDescriptor { private set; get; }
public WinSWExtensionManager(ServiceDescriptor serviceDescriptor)
{
ServiceDescriptor = serviceDescriptor;
Extensions = new Dictionary<string, IWinSWExtension>();
}
/// <summary>
/// Starts all extensions
/// </summary>
/// <exception cref="ExtensionException">Start failure</exception>
public void OnStart(IEventWriter logger)
{
foreach (var ext in Extensions)
{
ext.Value.OnStart(logger);
}
}
/// <summary>
/// Stops all extensions
/// </summary>
/// <exception cref="ExtensionException">Stop failure</exception>
public void OnStop(IEventWriter logger)
{
foreach (var ext in Extensions)
{
ext.Value.OnStop(logger);
}
}
//TODO: Implement loading of external extensions. Current version supports internal hack
#region Extension load management
public void LoadExtensions(IEventWriter logger)
{
var extensionIds = ServiceDescriptor.ExtensionIds;
foreach (String extensionId in extensionIds)
{
LoadExtension(extensionId, logger);
}
}
/// <summary>
/// Loads extensions from the configuration file
/// </summary>
/// <param name="id">Extension ID</param>
/// <param name="logger">Logger</param>
/// <exception cref="ExtensionException">Loading failure</exception>
private void LoadExtension(string id, IEventWriter logger)
{
if (Extensions.ContainsKey(id))
{
throw new ExtensionException(id, "Extension has been already loaded");
}
var extensionsConfig = ServiceDescriptor.ExtensionsConfiguration;
XmlElement configNode =(extensionsConfig != null) ? extensionsConfig.SelectSingleNode("extension[@id='"+id+"'][1]") as XmlElement : null;
if (configNode == null)
{
throw new ExtensionException(id, "Cannot get the configuration entry");
}
var descriptor = WinSWExtensionDescriptor.FromXml(configNode);
if (descriptor.Enabled)
{
IWinSWExtension extension = CreateExtensionInstance(descriptor.Id, descriptor.ClassName);
extension.Descriptor = descriptor;
extension.Configure(ServiceDescriptor, configNode, logger);
Extensions.Add(id, extension);
logger.LogEvent("Extension loaded: "+id, EventLogEntryType.Information);
}
else
{
logger.LogEvent("Extension is disabled: " + id, EventLogEntryType.Warning);
}
}
private IWinSWExtension CreateExtensionInstance(string id, string className)
{
ActivationContext ac = AppDomain.CurrentDomain.ActivationContext;
Assembly assembly = Assembly.GetCallingAssembly();
Object created;
try
{
Type t = Type.GetType(className);
if (t == null)
{
throw new ExtensionException(id, "Class "+className+" does not exist");
}
created = Activator.CreateInstance(t);
}
catch (Exception ex)
{
throw new ExtensionException(id, "Cannot load the class by name: "+className, ex);
}
var extension = created as IWinSWExtension;
if (extension == null)
{
throw new ExtensionException(id, "The loaded class is not a WinSW extension: " + className + ". Type is " + created.GetType());
}
return extension;
}
#endregion
}
}

View File

@ -6,9 +6,9 @@ using System.Text;
// ReSharper disable InconsistentNaming // ReSharper disable InconsistentNaming
namespace winsw namespace winsw.Native
{ {
class ServiceManager : IDisposable public class ServiceManager : IDisposable
{ {
private IntPtr _handle; private IntPtr _handle;
@ -39,7 +39,7 @@ namespace winsw
} }
} }
class Service : IDisposable public class Service : IDisposable
{ {
internal IntPtr Handle; internal IntPtr Handle;
@ -87,7 +87,7 @@ namespace winsw
} }
} }
static class LogonAsAService public static class LogonAsAService
{ {
public static void AddLogonAsAServiceRight(string username) public static void AddLogonAsAServiceRight(string username)
{ {
@ -251,7 +251,7 @@ namespace winsw
/// Advapi32.dll wrapper for performing additional service related operations that are not /// Advapi32.dll wrapper for performing additional service related operations that are not
/// available in WMI. /// available in WMI.
/// </summary> /// </summary>
internal class Advapi32 public class Advapi32
{ {
[DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Unicode)] [DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
[return: MarshalAs(UnmanagedType.Bool)] [return: MarshalAs(UnmanagedType.Bool)]
@ -272,7 +272,7 @@ namespace winsw
internal static extern bool CloseServiceHandle(IntPtr hSCObject); internal static extern bool CloseServiceHandle(IntPtr hSCObject);
[DllImport("advapi32.DLL")] [DllImport("advapi32.DLL")]
internal static extern bool SetServiceStatus(IntPtr hServiceStatus, ref SERVICE_STATUS lpServiceStatus); public static extern bool SetServiceStatus(IntPtr hServiceStatus, ref SERVICE_STATUS lpServiceStatus);
[DllImport("advapi32.dll", PreserveSig = true)] [DllImport("advapi32.dll", PreserveSig = true)]
internal static extern UInt32 LsaOpenPolicy(ref LSA_UNICODE_STRING SystemName, ref LSA_OBJECT_ATTRIBUTES ObjectAttributes, Int32 DesiredAccess, internal static extern UInt32 LsaOpenPolicy(ref LSA_UNICODE_STRING SystemName, ref LSA_OBJECT_ATTRIBUTES ObjectAttributes, Int32 DesiredAccess,

View File

@ -1,18 +1,18 @@
using System; using System;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
namespace winsw namespace winsw.Native
{ {
/// <summary> /// <summary>
/// kernel32.dll P/Invoke wrappers /// kernel32.dll P/Invoke wrappers
/// </summary> /// </summary>
internal class Kernel32 public class Kernel32
{ {
[DllImport("Kernel32.dll", SetLastError = true)] [DllImport("Kernel32.dll", SetLastError = true)]
internal static extern int SetStdHandle(int device, IntPtr handle); public static extern int SetStdHandle(int device, IntPtr handle);
[DllImport("kernel32.dll", SetLastError = true)] [DllImport("kernel32.dll", SetLastError = true)]
internal static extern bool CreateProcess(string lpApplicationName, public static extern bool CreateProcess(string lpApplicationName,
string lpCommandLine, IntPtr lpProcessAttributes, string lpCommandLine, IntPtr lpProcessAttributes,
IntPtr lpThreadAttributes, bool bInheritHandles, IntPtr lpThreadAttributes, bool bInheritHandles,
uint dwCreationFlags, IntPtr lpEnvironment, string lpCurrentDirectory, uint dwCreationFlags, IntPtr lpEnvironment, string lpCurrentDirectory,
@ -20,11 +20,11 @@ namespace winsw
out PROCESS_INFORMATION lpProcessInformation); out PROCESS_INFORMATION lpProcessInformation);
[DllImport("kernel32.dll")] [DllImport("kernel32.dll")]
internal static extern int GetLastError(); public static extern int GetLastError();
} }
[StructLayout(LayoutKind.Sequential)] [StructLayout(LayoutKind.Sequential)]
internal struct PROCESS_INFORMATION public struct PROCESS_INFORMATION
{ {
public IntPtr hProcess; public IntPtr hProcess;
public IntPtr hThread; public IntPtr hThread;
@ -33,7 +33,7 @@ namespace winsw
} }
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
struct STARTUPINFO public struct STARTUPINFO
{ {
public Int32 cb; public Int32 cb;
public string lpReserved; public string lpReserved;

View File

@ -0,0 +1,36 @@
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("WinSWCore")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("WinSWCore")]
[assembly: AssemblyCopyright("Copyright © 2015")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(false)]
// The following GUID is for the ID of the typelib if this project is exposed to COM
[assembly: Guid("8f845354-ba20-455d-82d1-9b6ec4e0e517")]
// Version information for an assembly consists of the following four values:
//
// Major Version
// Minor Version
// Build Number
// Revision
//
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]

View File

@ -5,6 +5,8 @@ using System.Diagnostics;
using System.IO; using System.IO;
using System.Reflection; using System.Reflection;
using System.Xml; using System.Xml;
using winsw.Native;
using winsw.Util;
using WMI; using WMI;
namespace winsw namespace winsw
@ -227,9 +229,7 @@ namespace winsw
} }
} }
/// <summary>
/// Optional working directory.
/// </summary>
public string WorkingDirectory { public string WorkingDirectory {
get { get {
var wd = SingleElement("workingdirectory", true); var wd = SingleElement("workingdirectory", true);
@ -237,6 +237,37 @@ namespace winsw
} }
} }
public List<string> ExtensionIds
{
get
{
List<string> res = new List<string>();
XmlNode argumentNode = ExtensionsConfiguration;
XmlNodeList extensions = argumentNode != null ? argumentNode.SelectNodes("extension") : null;
if ( extensions != null)
{
foreach (XmlNode e in extensions)
{
XmlElement extension = (XmlElement)e;
String extensionId = XmlHelper.SingleAttribute<string>(extension, "id");
res.Add(extensionId);
}
}
return res;
}
}
public XmlNode ExtensionsConfiguration
{
get
{
XmlNode argumentNode = dom.SelectSingleNode("//extensions");
return argumentNode;
}
}
/// <summary> /// <summary>
/// Combines the contents of all the elements of the given name, /// Combines the contents of all the elements of the given name,
/// or return null if no element exists. Handles whitespace quotation. /// or return null if no element exists. Handles whitespace quotation.

View File

@ -0,0 +1,13 @@
using System;
using System.Collections.Generic;
using System.Text;
using System.Diagnostics;
namespace winsw.Util
{
public interface IEventWriter
{
void LogEvent(String message);
void LogEvent(String message, EventLogEntryType type);
}
}

View File

@ -0,0 +1,75 @@
using System;
using System.Collections.Generic;
using System.Text;
using System.Xml;
using System.IO;
namespace winsw.Util
{
public class XmlHelper
{
/// <summary>
/// Retrieves a single string element
/// </summary>
/// <param name="node">Parent node</param>
/// <param name="tagName">Element name</param>
/// <param name="optional">If otional, don't throw an exception if the elemen is missing</param>
/// <returns>String value or null</returns>
/// <exception cref="InvalidDataException">The required element is missing</exception>
public static string SingleElement(XmlNode node, string tagName, Boolean optional)
{
var n = node.SelectSingleNode(tagName);
if (n == null && !optional) throw new InvalidDataException("<" + tagName + "> is missing in configuration XML");
return n == null ? null : Environment.ExpandEnvironmentVariables(n.InnerText);
}
/// <summary>
/// Retrieves a single node
/// </summary>
/// <param name="node">Parent node</param>
/// <param name="tagName">Element name</param>
/// <param name="optional">If otional, don't throw an exception if the elemen is missing</param>
/// <returns>String value or null</returns>
/// <exception cref="InvalidDataException">The required element is missing</exception>
public static XmlNode SingleNode(XmlNode node, string tagName, Boolean optional)
{
var n = node.SelectSingleNode(tagName);
if (n == null && !optional) throw new InvalidDataException("<" + tagName + "> is missing in configuration XML");
return n;
}
/// <summary>
/// Retrieves a single mandatory attribute
/// </summary>
/// <param name="node">Parent node</param>
/// <param name="attributeName">Attribute name</param>
/// <returns>Attribute value</returns>
/// <exception cref="InvalidDataException">The required attribute is missing</exception>
public static TAttributeType SingleAttribute <TAttributeType> (XmlElement node, string attributeName)
{
if (!node.HasAttribute(attributeName))
{
throw new InvalidDataException("Attribute <" + attributeName + "> is missing in configuration XML");
}
return SingleAttribute<TAttributeType>(node, attributeName, default(TAttributeType));
}
/// <summary>
/// Retrieves a single optional attribute
/// </summary>
/// <param name="node">Parent node</param>
/// <param name="attributeName">Attribute name</param>
/// <param name="defaultValue">Default value</param>
/// <returns>Attribute value (or default)</returns>
public static TAttributeType SingleAttribute<TAttributeType>(XmlElement node, string attributeName, TAttributeType defaultValue)
{
if (!node.HasAttribute(attributeName)) return defaultValue;
string rawValue = node.GetAttribute(attributeName);
string substitutedValue = Environment.ExpandEnvironmentVariables(rawValue);
var value = (TAttributeType)Convert.ChangeType(substitutedValue, typeof(TAttributeType));
return value;
}
}
}

View File

@ -0,0 +1,89 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{9D0C63E2-B6FF-4A85-BD36-B3E5D7F27D06}</ProjectGuid>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>winsw</RootNamespace>
<AssemblyName>WinSWCore</AssemblyName>
<TargetFrameworkVersion>v2.0</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">..\..\</SolutionDir>
<RestorePackages>true</RestorePackages>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup>
<SignAssembly>true</SignAssembly>
</PropertyGroup>
<PropertyGroup>
<AssemblyOriginatorKeyFile>..\..\..\winsw_cert.pfx</AssemblyOriginatorKeyFile>
</PropertyGroup>
<ItemGroup>
<Reference Include="log4net">
<HintPath>..\..\packages\log4net.2.0.3\lib\net20-full\log4net.dll</HintPath>
</Reference>
<Reference Include="System" />
<Reference Include="System.Data" />
<Reference Include="System.Management" />
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="Download.cs" />
<Compile Include="DynamicProxy.cs" />
<Compile Include="Extensions\AbstractWinSWExtension.cs" />
<Compile Include="Extensions\ExtensionException.cs" />
<Compile Include="Extensions\ExtensionPointAttribute.cs" />
<Compile Include="Extensions\IWinSWExtension.cs" />
<Compile Include="Extensions\WinSWExtensionDescriptor.cs" />
<Compile Include="Extensions\WinSWExtensionManager.cs" />
<Compile Include="LogAppenders.cs" />
<Compile Include="Native\Advapi32.cs" />
<Compile Include="Native\Kernel32.cs" />
<Compile Include="PeriodicRollingCalendar.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="ServiceDescriptor.cs" />
<Compile Include="Util\IEventWriter.cs" />
<Compile Include="Util\XmlHelper.cs" />
<Compile Include="WinSWException.cs" />
<Compile Include="Wmi.cs" />
<Compile Include="WmiSchema.cs" />
</ItemGroup>
<ItemGroup>
<None Include="..\..\..\winsw_cert.pfx" />
<None Include="packages.config" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<Import Project="$(SolutionDir)\.nuget\NuGet.targets" Condition="Exists('$(SolutionDir)\.nuget\NuGet.targets')" />
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
<PropertyGroup>
<ErrorText>This project references NuGet package(s) that are missing on this computer. Enable NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
</PropertyGroup>
<Error Condition="!Exists('$(SolutionDir)\.nuget\NuGet.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(SolutionDir)\.nuget\NuGet.targets'))" />
</Target>
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">
</Target>
<Target Name="AfterBuild">
</Target>
-->
</Project>

View File

@ -0,0 +1,17 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace winsw
{
public class WinSWException : Exception
{
public WinSWException(String message)
: base(message)
{ }
public WinSWException(String message, Exception innerException)
: base(message, innerException)
{ }
}
}

View File

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="log4net" version="2.0.3" targetFramework="net20" />
</packages>

View File

@ -0,0 +1,36 @@
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("SharedDirectoryMapper")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("SharedDirectoryMapper")]
[assembly: AssemblyCopyright("Copyright © 2015")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(false)]
// The following GUID is for the ID of the typelib if this project is exposed to COM
[assembly: Guid("d962c792-b900-4e60-8ae6-6c8d05b23a61")]
// Version information for an assembly consists of the following four values:
//
// Major Version
// Minor Version
// Build Number
// Revision
//
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]

View File

@ -0,0 +1,93 @@
using System;
using System.Collections.Generic;
using System.Xml;
using System.Diagnostics;
using winsw.Extensions;
using winsw.Util;
namespace winsw.Plugins.SharedDirectoryMapper
{
public class SharedDirectoryMapper : AbstractWinSWExtension
{
private readonly SharedDirectoryMappingHelper _mapper = new SharedDirectoryMappingHelper();
private readonly List<SharedDirectoryMapperConfig> _entries = new List<SharedDirectoryMapperConfig>();
public override String DisplayName { get { return "Shared Directory Mapper"; } }
public SharedDirectoryMapper()
{
}
public SharedDirectoryMapper(bool enableMapping, string directoryUNC, string driveLabel)
{
SharedDirectoryMapperConfig config = new SharedDirectoryMapperConfig(enableMapping, driveLabel, directoryUNC);
_entries.Add(config);
}
public override void Configure(ServiceDescriptor descriptor, XmlNode node, IEventWriter logger)
{
var nodes = XmlHelper.SingleNode(node, "mapping", false).SelectNodes("map");
if (nodes != null)
{
foreach (XmlNode mapNode in nodes)
{
var mapElement = mapNode as XmlElement;
if (mapElement != null)
{
var config = SharedDirectoryMapperConfig.FromXml(mapElement);
_entries.Add(config);
}
}
}
}
public override void OnStart(IEventWriter eventWriter)
{
foreach (SharedDirectoryMapperConfig config in _entries)
{
if (config.EnableMapping)
{
eventWriter.LogEvent(DisplayName + ": Mapping shared directory " + config.UNCPath + " to " + config.Label, EventLogEntryType.Information);
try
{
_mapper.MapDirectory(config.Label, config.UNCPath);
}
catch (MapperException ex)
{
HandleMappingError(config, eventWriter, ex);
}
}
else
{
eventWriter.LogEvent(DisplayName + ": Mapping of " + config.Label + " is disabled", EventLogEntryType.Warning);
}
}
}
public override void OnStop(IEventWriter eventWriter)
{
foreach (SharedDirectoryMapperConfig config in _entries)
{
if (config.EnableMapping)
{
try
{
_mapper.UnmapDirectory(config.Label);
}
catch (MapperException ex)
{
HandleMappingError(config, eventWriter, ex);
}
}
}
}
private void HandleMappingError(SharedDirectoryMapperConfig config, IEventWriter eventWriter, MapperException ex) {
String prefix = "Mapping of " + config.Label+ " ";
eventWriter.LogEvent(prefix + "STDOUT: " + ex.Process.StandardOutput.ReadToEnd(), EventLogEntryType.Information);
eventWriter.LogEvent(prefix + "STDERR: " + ex.Process.StandardError.ReadToEnd(), EventLogEntryType.Information);
throw new ExtensionException(Descriptor.Id, DisplayName + ": " + prefix + "failed", ex);
}
}
}

View File

@ -0,0 +1,57 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{CA5C71DB-C5A8-4C27-BF83-8E6DAED9D6B5}</ProjectGuid>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>winsw.Plugins.SharedDirectoryMapper</RootNamespace>
<AssemblyName>SharedDirectoryMapper</AssemblyName>
<TargetFrameworkVersion>v2.0</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
<Reference Include="System" />
<Reference Include="System.Data" />
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="SharedDirectoryMapper.cs" />
<Compile Include="SharedDirectoryMapperConfig.cs" />
<Compile Include="SharedDirectoryMapperHelper.cs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\Core\WinSWCore\WinSWCore.csproj">
<Project>{9d0c63e2-b6ff-4a85-bd36-b3e5d7f27d06}</Project>
<Name>WinSWCore</Name>
</ProjectReference>
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">
</Target>
<Target Name="AfterBuild">
</Target>
-->
</Project>

View File

@ -0,0 +1,31 @@
using System;
using System.Xml;
using winsw.Util;
namespace winsw.Plugins.SharedDirectoryMapper
{
/// <summary>
/// Stores configuration entries for SharedDirectoryMapper extension.
/// </summary>
public class SharedDirectoryMapperConfig
{
public bool EnableMapping { get; set; }
public String Label { get; set; }
public String UNCPath { get; set; }
public SharedDirectoryMapperConfig(bool enableMapping, string label, string uncPath)
{
EnableMapping = enableMapping;
Label = label;
UNCPath = uncPath;
}
public static SharedDirectoryMapperConfig FromXml(XmlElement node)
{
bool enableMapping = XmlHelper.SingleAttribute(node, "enabled", true);
string label = XmlHelper.SingleAttribute<string>(node, "label");
string uncPath = XmlHelper.SingleAttribute<string>(node, "uncpath");
return new SharedDirectoryMapperConfig(enableMapping, label, uncPath);
}
}
}

View File

@ -0,0 +1,73 @@
using System;
using System.Diagnostics;
using winsw.Util;
namespace winsw.Plugins.SharedDirectoryMapper
{
class SharedDirectoryMappingHelper
{
/// <summary>
/// Invokes a system command
/// </summary>
/// <see cref="SharedDirectoryMapper"/>
/// <param name="command">Command to be executed</param>
/// <param name="args">Command arguments</param>
/// <exception cref="MapperException">Operation failure</exception>
private void InvokeCommand(String command, String args)
{
Process p = new Process
{
StartInfo =
{
UseShellExecute = false,
CreateNoWindow = true,
RedirectStandardError = true,
RedirectStandardOutput = true,
FileName = command,
Arguments = args
}
};
p.Start();
p.WaitForExit();
if (p.ExitCode != 0)
{
throw new MapperException(p, command, args);
}
}
/// <summary>
/// Maps the remote directory
/// </summary>
/// <param name="label">Disk label</param>
/// <param name="uncPath">UNC path to the directory</param>
/// <exception cref="MapperException">Operation failure</exception>
public void MapDirectory(String label, String uncPath)
{
InvokeCommand("net.exe", " use " + label + " " + uncPath);
}
/// <summary>
/// Unmaps the label
/// </summary>
/// <param name="label">Disk label</param>
/// <exception cref="MapperException">Operation failure</exception>
public void UnmapDirectory(String label)
{
InvokeCommand("net.exe", " use /DELETE /YES " + label);
}
}
class MapperException : WinSWException
{
public String Call { get; private set; }
public Process Process { get; private set; }
public MapperException(Process process, string command, string args)
: base("Command " + command + " " + args + " failed with code " + process.ExitCode)
{
Call = command + " " + args;
Process = process;
}
}
}

View File

@ -0,0 +1,18 @@
<?xml version="1.0" encoding="utf-8" ?>
<service>
<id>SERVICE_NAME</id>
<name>Jenkins Slave</name>
<description>This service runs a slave for Jenkins continuous integration system.</description>
<executable>C:\Program Files\Java\jre7\bin\java.exe</executable>
<arguments>-Xrs -jar "%BASE%\slave.jar" -jnlpUrl ...</arguments>
<logmode>rotate</logmode>
<extensions>
<extension enabled="true" className="winsw.Plugins.SharedDirectoryMapper.SharedDirectoryMapper" id="mapNetworDirs">
<mapping>
<map enabled="false" label="N:" uncpath="\\UNC"/>
<map enabled="false" label="M:" uncpath="\\UNC2"/>
</mapping>
</extension>
</extensions>
</service>

View File

@ -0,0 +1,62 @@
using winsw;
using NUnit.Framework;
using winsw.Extensions;
using winsw.Plugins.SharedDirectoryMapper;
using winswTests.util;
namespace winswTests.extensions
{
[TestFixture]
class WinSWExtensionManagerTest
{
ServiceDescriptor _testServiceDescriptor;
readonly TestLogger _logger = new TestLogger();
[SetUp]
public void SetUp()
{
string testExtension = typeof (SharedDirectoryMapper).ToString();
string seedXml = "<?xml version=\"1.0\" encoding=\"utf-8\" ?>"
+ "<service> "
+ " <id>SERVICE_NAME</id> "
+ " <name>Jenkins Slave</name> "
+ " <description>This service runs a slave for Jenkins continuous integration system.</description> "
+ " <executable>C:\\Program Files\\Java\\jre7\\bin\\java.exe</executable> "
+ " <arguments>-Xrs -jar \\\"%BASE%\\slave.jar\\\" -jnlpUrl ...</arguments> "
+ " <logmode>rotate</logmode> "
+ " <extensions> "
+ " <extension enabled=\"true\" className=\"" + testExtension + "\" id=\"mapNetworDirs\"> "
+ " <mapping> "
+ " <map enabled=\"false\" label=\"N:\" uncpath=\"\\\\UNC\"/> "
+ " <map enabled=\"false\" label=\"M:\" uncpath=\"\\\\UNC2\"/> "
+ " </mapping> "
+ " </extension> "
+ " <extension enabled=\"true\" className=\"" + testExtension + "\" id=\"mapNetworDirs2\"> "
+ " <mapping> "
+ " <map enabled=\"false\" label=\"X:\" uncpath=\"\\\\UNC\"/> "
+ " <map enabled=\"false\" label=\"Y:\" uncpath=\"\\\\UNC2\"/> "
+ " </mapping> "
+ " </extension> "
+ " </extensions> "
+ "</service>";
_testServiceDescriptor = ServiceDescriptor.FromXML(seedXml);
}
[Test]
public void LoadExtensions()
{
WinSWExtensionManager manager = new WinSWExtensionManager(_testServiceDescriptor);
manager.LoadExtensions(_logger);
Assert.AreEqual(2, manager.Extensions.Count, "Two extensions should be loaded");
}
[Test]
public void StartStopExtension()
{
WinSWExtensionManager manager = new WinSWExtensionManager(_testServiceDescriptor);
manager.LoadExtensions(_logger);
manager.OnStart(_logger);
manager.OnStop(_logger);
}
}
}

View File

@ -0,0 +1,7 @@
<NUnitProject>
<Settings activeconfig="Debug" />
<Config name="Debug">
<assembly path="bin\Debug\winswTests.dll" />
</Config>
<Config name="Release"></Config>
</NUnitProject>

View File

@ -0,0 +1,19 @@
using System;
using System.Diagnostics;
using winsw.Util;
namespace winswTests.util
{
class TestLogger : IEventWriter
{
public void LogEvent(String message)
{
Console.WriteLine(message);
}
public void LogEvent(String message, EventLogEntryType type)
{
Console.WriteLine("[" + type + "]" + message);
}
}
}

View File

@ -1,4 +1,5 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<packages> <packages>
<package id="NUnit" version="2.6.4" targetFramework="net20" />
<package id="JetBrains.Annotations" version="8.0.5.0" targetFramework="net20" /> <package id="JetBrains.Annotations" version="8.0.5.0" targetFramework="net20" />
</packages> </packages>

View File

@ -17,6 +17,8 @@
<UpgradeBackupLocation> <UpgradeBackupLocation>
</UpgradeBackupLocation> </UpgradeBackupLocation>
<OldToolsVersion>3.5</OldToolsVersion> <OldToolsVersion>3.5</OldToolsVersion>
<SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">..\..\</SolutionDir>
<RestorePackages>true</RestorePackages>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' "> <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols> <DebugSymbols>true</DebugSymbols>
@ -40,9 +42,9 @@
<SpecificVersion>False</SpecificVersion> <SpecificVersion>False</SpecificVersion>
<HintPath>..\..\packages\JetBrains.Annotations.8.0.5.0\lib\net20\JetBrains.Annotations.dll</HintPath> <HintPath>..\..\packages\JetBrains.Annotations.8.0.5.0\lib\net20\JetBrains.Annotations.dll</HintPath>
</Reference> </Reference>
<Reference Include="nunit.framework, Version=2.6.2.12296, Culture=neutral, PublicKeyToken=96d09a1eb7f44a77, processorArchitecture=MSIL"> <Reference Include="nunit.framework, Version=2.6.4.14350, Culture=neutral, PublicKeyToken=96d09a1eb7f44a77, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion> <SpecificVersion>False</SpecificVersion>
<HintPath>..\Lib\nunit.framework.dll</HintPath> <HintPath>..\..\packages\NUnit.2.6.4\lib\nunit.framework.dll</HintPath>
</Reference> </Reference>
<Reference Include="System" /> <Reference Include="System" />
<Reference Include="System.Data" /> <Reference Include="System.Data" />
@ -50,21 +52,39 @@
<Reference Include="System.Xml" /> <Reference Include="System.Xml" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Compile Include="Extensions\WinSWExtensionManagerTest.cs" />
<Compile Include="MainTest.cs" /> <Compile Include="MainTest.cs" />
<Compile Include="Properties\AssemblyInfo.cs" /> <Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="ServiceDescriptorTests.cs" /> <Compile Include="ServiceDescriptorTests.cs" />
<Compile Include="Util\TestLogger.cs" />
<Compile Include="Util\CLITestHelper.cs" /> <Compile Include="Util\CLITestHelper.cs" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\..\winsw.csproj"> <ProjectReference Include="..\..\Core\ServiceWrapper\winsw.csproj">
<Project>{0DE77F55-ADE5-43C1-999A-0BC81153B039}</Project> <Project>{0DE77F55-ADE5-43C1-999A-0BC81153B039}</Project>
<Name>winsw</Name> <Name>winsw</Name>
</ProjectReference> </ProjectReference>
<ProjectReference Include="..\..\Core\WinSWCore\WinSWCore.csproj">
<Project>{9d0c63e2-b6ff-4a85-bd36-b3e5d7f27d06}</Project>
<Name>WinSWCore</Name>
</ProjectReference>
<ProjectReference Include="..\..\Plugins\SharedDirectoryMapper\SharedDirectoryMapper.csproj">
<Project>{ca5c71db-c5a8-4c27-bf83-8e6daed9d6b5}</Project>
<Name>SharedDirectoryMapper</Name>
</ProjectReference>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<None Include="packages.config" /> <None Include="packages.config" />
</ItemGroup> </ItemGroup>
<ItemGroup />
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<Import Project="$(SolutionDir)\.nuget\NuGet.targets" Condition="Exists('$(SolutionDir)\.nuget\NuGet.targets')" />
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
<PropertyGroup>
<ErrorText>This project references NuGet package(s) that are missing on this computer. Enable NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
</PropertyGroup>
<Error Condition="!Exists('$(SolutionDir)\.nuget\NuGet.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(SolutionDir)\.nuget\NuGet.targets'))" />
</Target>
<!-- To modify your build process, add your task inside one of the targets below and uncomment it. <!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets. Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild"> <Target Name="BeforeBuild">

View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<repositories>
<repository path="..\Core\ServiceWrapper\packages.config" />
<repository path="..\Core\WinSWCore\packages.config" />
<repository path="..\Test\winswTests\packages.config" />
</repositories>

100
src/winsw.sln Normal file
View File

@ -0,0 +1,100 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 2013
VisualStudioVersion = 12.0.31101.0
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "winsw", "Core\ServiceWrapper\winsw.csproj", "{0DE77F55-ADE5-43C1-999A-0BC81153B039}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "winswTests", "Test\winswTests\winswTests.csproj", "{93843402-842B-44B4-B303-AEE829BE0B43}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nuget", ".nuget", "{6BDF4025-D46C-4C69-BDB2-5CE434C857AA}"
ProjectSection(SolutionItems) = preProject
.nuget\NuGet.Config = .nuget\NuGet.Config
.nuget\NuGet.exe = .nuget\NuGet.exe
EndProjectSection
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SharedDirectoryMapper", "Plugins\SharedDirectoryMapper\SharedDirectoryMapper.csproj", "{CA5C71DB-C5A8-4C27-BF83-8E6DAED9D6B5}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tests", "Tests", "{077C2CEC-B687-4B53-86E9-C1A1BF5554E5}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Plugins", "Plugins", "{BC4AD891-E87E-4F30-867C-FD8084A29E5D}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Core", "Core", "{5297623A-1A95-4F89-9AAE-DA634081EC86}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WinSWCore", "Core\WinSWCore\WinSWCore.csproj", "{9D0C63E2-B6FF-4A85-BD36-B3E5D7F27D06}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".build", ".build", "{D8806424-4640-440C-952D-37790B603C27}"
ProjectSection(SolutionItems) = preProject
Build.proj = Build.proj
.build\MSBuild.Community.Tasks.dll = .build\MSBuild.Community.Tasks.dll
.build\MSBuild.Community.Tasks.targets = .build\MSBuild.Community.Tasks.targets
EndProjectSection
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Debug|Mixed Platforms = Debug|Mixed Platforms
Debug|Win32 = Debug|Win32
Release|Any CPU = Release|Any CPU
Release|Mixed Platforms = Release|Mixed Platforms
Release|Win32 = Release|Win32
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{0DE77F55-ADE5-43C1-999A-0BC81153B039}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{0DE77F55-ADE5-43C1-999A-0BC81153B039}.Debug|Any CPU.Build.0 = Debug|Any CPU
{0DE77F55-ADE5-43C1-999A-0BC81153B039}.Debug|Mixed Platforms.ActiveCfg = Release|Any CPU
{0DE77F55-ADE5-43C1-999A-0BC81153B039}.Debug|Mixed Platforms.Build.0 = Release|Any CPU
{0DE77F55-ADE5-43C1-999A-0BC81153B039}.Debug|Win32.ActiveCfg = Debug|Any CPU
{0DE77F55-ADE5-43C1-999A-0BC81153B039}.Debug|Win32.Build.0 = Debug|Any CPU
{0DE77F55-ADE5-43C1-999A-0BC81153B039}.Release|Any CPU.ActiveCfg = Release|Any CPU
{0DE77F55-ADE5-43C1-999A-0BC81153B039}.Release|Any CPU.Build.0 = Release|Any CPU
{0DE77F55-ADE5-43C1-999A-0BC81153B039}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
{0DE77F55-ADE5-43C1-999A-0BC81153B039}.Release|Mixed Platforms.Build.0 = Release|Any CPU
{0DE77F55-ADE5-43C1-999A-0BC81153B039}.Release|Win32.ActiveCfg = Release|Any CPU
{0DE77F55-ADE5-43C1-999A-0BC81153B039}.Release|Win32.Build.0 = Release|Any CPU
{93843402-842B-44B4-B303-AEE829BE0B43}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{93843402-842B-44B4-B303-AEE829BE0B43}.Debug|Any CPU.Build.0 = Debug|Any CPU
{93843402-842B-44B4-B303-AEE829BE0B43}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
{93843402-842B-44B4-B303-AEE829BE0B43}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
{93843402-842B-44B4-B303-AEE829BE0B43}.Debug|Win32.ActiveCfg = Debug|Any CPU
{93843402-842B-44B4-B303-AEE829BE0B43}.Debug|Win32.Build.0 = Debug|Any CPU
{93843402-842B-44B4-B303-AEE829BE0B43}.Release|Any CPU.ActiveCfg = Release|Any CPU
{93843402-842B-44B4-B303-AEE829BE0B43}.Release|Any CPU.Build.0 = Release|Any CPU
{93843402-842B-44B4-B303-AEE829BE0B43}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
{93843402-842B-44B4-B303-AEE829BE0B43}.Release|Mixed Platforms.Build.0 = Release|Any CPU
{93843402-842B-44B4-B303-AEE829BE0B43}.Release|Win32.ActiveCfg = Release|Any CPU
{93843402-842B-44B4-B303-AEE829BE0B43}.Release|Win32.Build.0 = Release|Any CPU
{CA5C71DB-C5A8-4C27-BF83-8E6DAED9D6B5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{CA5C71DB-C5A8-4C27-BF83-8E6DAED9D6B5}.Debug|Any CPU.Build.0 = Debug|Any CPU
{CA5C71DB-C5A8-4C27-BF83-8E6DAED9D6B5}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
{CA5C71DB-C5A8-4C27-BF83-8E6DAED9D6B5}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
{CA5C71DB-C5A8-4C27-BF83-8E6DAED9D6B5}.Debug|Win32.ActiveCfg = Debug|Any CPU
{CA5C71DB-C5A8-4C27-BF83-8E6DAED9D6B5}.Debug|Win32.Build.0 = Debug|Any CPU
{CA5C71DB-C5A8-4C27-BF83-8E6DAED9D6B5}.Release|Any CPU.ActiveCfg = Release|Any CPU
{CA5C71DB-C5A8-4C27-BF83-8E6DAED9D6B5}.Release|Any CPU.Build.0 = Release|Any CPU
{CA5C71DB-C5A8-4C27-BF83-8E6DAED9D6B5}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
{CA5C71DB-C5A8-4C27-BF83-8E6DAED9D6B5}.Release|Mixed Platforms.Build.0 = Release|Any CPU
{CA5C71DB-C5A8-4C27-BF83-8E6DAED9D6B5}.Release|Win32.ActiveCfg = Release|Any CPU
{CA5C71DB-C5A8-4C27-BF83-8E6DAED9D6B5}.Release|Win32.Build.0 = Release|Any CPU
{9D0C63E2-B6FF-4A85-BD36-B3E5D7F27D06}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{9D0C63E2-B6FF-4A85-BD36-B3E5D7F27D06}.Debug|Any CPU.Build.0 = Debug|Any CPU
{9D0C63E2-B6FF-4A85-BD36-B3E5D7F27D06}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
{9D0C63E2-B6FF-4A85-BD36-B3E5D7F27D06}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
{9D0C63E2-B6FF-4A85-BD36-B3E5D7F27D06}.Debug|Win32.ActiveCfg = Debug|Any CPU
{9D0C63E2-B6FF-4A85-BD36-B3E5D7F27D06}.Debug|Win32.Build.0 = Debug|Any CPU
{9D0C63E2-B6FF-4A85-BD36-B3E5D7F27D06}.Release|Any CPU.ActiveCfg = Release|Any CPU
{9D0C63E2-B6FF-4A85-BD36-B3E5D7F27D06}.Release|Any CPU.Build.0 = Release|Any CPU
{9D0C63E2-B6FF-4A85-BD36-B3E5D7F27D06}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
{9D0C63E2-B6FF-4A85-BD36-B3E5D7F27D06}.Release|Mixed Platforms.Build.0 = Release|Any CPU
{9D0C63E2-B6FF-4A85-BD36-B3E5D7F27D06}.Release|Win32.ActiveCfg = Release|Any CPU
{9D0C63E2-B6FF-4A85-BD36-B3E5D7F27D06}.Release|Win32.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{0DE77F55-ADE5-43C1-999A-0BC81153B039} = {5297623A-1A95-4F89-9AAE-DA634081EC86}
{93843402-842B-44B4-B303-AEE829BE0B43} = {077C2CEC-B687-4B53-86E9-C1A1BF5554E5}
{CA5C71DB-C5A8-4C27-BF83-8E6DAED9D6B5} = {BC4AD891-E87E-4F30-867C-FD8084A29E5D}
{9D0C63E2-B6FF-4A85-BD36-B3E5D7F27D06} = {5297623A-1A95-4F89-9AAE-DA634081EC86}
EndGlobalSection
EndGlobal

View File

@ -0,0 +1,3 @@
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=SW/@EntryIndexedValue">SW</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=UNC/@EntryIndexedValue">UNC</s:String></wpf:ResourceDictionary>

View File

@ -1,28 +0,0 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 2013
VisualStudioVersion = 12.0.31101.0
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "winsw", "winsw.csproj", "{0DE77F55-ADE5-43C1-999A-0BC81153B039}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "winswTests", "Tests\winswTests\winswTests.csproj", "{93843402-842B-44B4-B303-AEE829BE0B43}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Win32 = Debug|Win32
Release|Win32 = Release|Win32
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{0DE77F55-ADE5-43C1-999A-0BC81153B039}.Debug|Win32.ActiveCfg = Debug|Any CPU
{0DE77F55-ADE5-43C1-999A-0BC81153B039}.Debug|Win32.Build.0 = Debug|Any CPU
{0DE77F55-ADE5-43C1-999A-0BC81153B039}.Release|Win32.ActiveCfg = Release|Any CPU
{0DE77F55-ADE5-43C1-999A-0BC81153B039}.Release|Win32.Build.0 = Release|Any CPU
{93843402-842B-44B4-B303-AEE829BE0B43}.Debug|Win32.ActiveCfg = Debug|Any CPU
{93843402-842B-44B4-B303-AEE829BE0B43}.Debug|Win32.Build.0 = Debug|Any CPU
{93843402-842B-44B4-B303-AEE829BE0B43}.Release|Win32.ActiveCfg = Release|Any CPU
{93843402-842B-44B4-B303-AEE829BE0B43}.Release|Win32.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
EndGlobal