Merge pull request #74 from oleg-nenashev/winsw-2.0-extensions

[WiP] - WinSW Extensions Engine
pull/71/head
Oleg Nenashev 2015-02-16 21:50:40 +03:00
commit 2dc80e57b4
39 changed files with 1148 additions and 35 deletions

3
.gitignore vendored
View File

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

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

@ -14,13 +14,16 @@ using log4net.Core;
using log4net.Layout;
using log4net.Repository.Hierarchy;
using Microsoft.Win32;
using winsw.Extensions;
using winsw.Util;
using WMI;
using ServiceType = WMI.ServiceType;
using winsw.Native;
using System.Reflection;
namespace winsw
{
public class WrapperService : ServiceBase, EventLogger
public class WrapperService : ServiceBase, EventLogger, IEventWriter
{
private SERVICE_STATUS _wrapperServiceStatus;
@ -28,6 +31,8 @@ namespace winsw
private readonly ServiceDescriptor _descriptor;
private Dictionary<string, string> _envs;
internal WinSWExtensionManager ExtensionManager { private set; get; }
private static readonly ILog Log = LogManager.GetLogger("WinSW");
/// <summary>
@ -52,6 +57,7 @@ namespace winsw
{
_descriptor = descriptor;
ServiceName = _descriptor.Id;
ExtensionManager = new WinSWExtensionManager(_descriptor);
CanShutdown = true;
CanStop = true;
CanPauseAndContinue = false;
@ -246,6 +252,22 @@ namespace winsw
LogEvent("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);
// send stdout and stderr to its respective output file.
@ -327,6 +349,17 @@ namespace winsw
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)
{
Console.Beep();

View File

@ -1,4 +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

@ -9,14 +9,14 @@
<OutputType>Exe</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>winsw</RootNamespace>
<AssemblyName>winsw</AssemblyName>
<AssemblyName>WindowsService</AssemblyName>
<TargetFrameworkVersion>v2.0</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<StartupObject>
</StartupObject>
<SignManifests>false</SignManifests>
<SignAssembly>true</SignAssembly>
<AssemblyOriginatorKeyFile>..\..\..\winsw_cert.pfx</AssemblyOriginatorKeyFile>
<AssemblyOriginatorKeyFile>$(SolutionDir)..\winsw_key.snk</AssemblyOriginatorKeyFile>
<FileUpgradeFlags>
</FileUpgradeFlags>
<UpgradeBackupLocation>
@ -74,20 +74,11 @@
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="Advapi32.cs" />
<Compile Include="Download.cs" />
<Compile Include="DynamicProxy.cs" />
<Compile Include="Kernel32.cs" />
<Compile Include="LogAppenders.cs" />
<Compile Include="Main.cs">
<SubType>Component</SubType>
</Compile>
<Compile Include="PeriodicRollingCalendar.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="ServiceDescriptor.cs" />
<Compile Include="SigIntHelper.cs" />
<Compile Include="Wmi.cs" />
<Compile Include="WmiSchema.cs" />
</ItemGroup>
<ItemGroup>
<Content Include="manifest.xml" />
@ -100,7 +91,10 @@
</ItemGroup>
<ItemGroup>
<None Include="..\..\..\winsw_cert.pfx" />
<None Include="packages.config" />
<None Include="packages.config">
<SubType>Designer</SubType>
</None>
<None Include="$(AssemblyOriginatorKeyFile)" />
</ItemGroup>
<ItemGroup>
<BootstrapperPackage Include="Microsoft.Net.Client.3.5">
@ -114,6 +108,17 @@
<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.
@ -122,10 +127,6 @@
<Target Name="AfterBuild">
</Target>
-->
<PropertyGroup>
<PostBuildEvent>
</PostBuildEvent>
</PropertyGroup>
<Import Project="$(SolutionDir)\.nuget\NuGet.targets" Condition="Exists('$(SolutionDir)\.nuget\NuGet.targets')" />
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
<PropertyGroup>
@ -133,4 +134,46 @@
</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
namespace winsw
namespace winsw.Native
{
class ServiceManager : IDisposable
public class ServiceManager : IDisposable
{
private IntPtr _handle;
@ -39,7 +39,7 @@ namespace winsw
}
}
class Service : IDisposable
public class Service : IDisposable
{
internal IntPtr Handle;
@ -87,7 +87,7 @@ namespace winsw
}
}
static class LogonAsAService
public static class LogonAsAService
{
public static void AddLogonAsAServiceRight(string username)
{
@ -251,7 +251,7 @@ namespace winsw
/// Advapi32.dll wrapper for performing additional service related operations that are not
/// available in WMI.
/// </summary>
internal class Advapi32
public class Advapi32
{
[DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
[return: MarshalAs(UnmanagedType.Bool)]
@ -272,7 +272,7 @@ namespace winsw
internal static extern bool CloseServiceHandle(IntPtr hSCObject);
[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)]
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.Runtime.InteropServices;
namespace winsw
namespace winsw.Native
{
/// <summary>
/// kernel32.dll P/Invoke wrappers
/// </summary>
internal class Kernel32
public class Kernel32
{
[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)]
internal static extern bool CreateProcess(string lpApplicationName,
public static extern bool CreateProcess(string lpApplicationName,
string lpCommandLine, IntPtr lpProcessAttributes,
IntPtr lpThreadAttributes, bool bInheritHandles,
uint dwCreationFlags, IntPtr lpEnvironment, string lpCurrentDirectory,
@ -20,11 +20,11 @@ namespace winsw
out PROCESS_INFORMATION lpProcessInformation);
[DllImport("kernel32.dll")]
internal static extern int GetLastError();
public static extern int GetLastError();
}
[StructLayout(LayoutKind.Sequential)]
internal struct PROCESS_INFORMATION
public struct PROCESS_INFORMATION
{
public IntPtr hProcess;
public IntPtr hThread;
@ -33,7 +33,7 @@ namespace winsw
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
struct STARTUPINFO
public struct STARTUPINFO
{
public Int32 cb;
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.Reflection;
using System.Xml;
using winsw.Native;
using winsw.Util;
using WMI;
namespace winsw
@ -227,9 +229,7 @@ namespace winsw
}
}
/// <summary>
/// Optional working directory.
/// </summary>
public string WorkingDirectory {
get {
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>
/// Combines the contents of all the elements of the given name,
/// 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.extensions.shared_dirs.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

@ -52,9 +52,11 @@
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="Extensions\WinSWExtensionManagerTest.cs" />
<Compile Include="MainTest.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="ServiceDescriptorTests.cs" />
<Compile Include="Util\TestLogger.cs" />
<Compile Include="Util\CLITestHelper.cs" />
</ItemGroup>
<ItemGroup>
@ -62,10 +64,19 @@
<Project>{0DE77F55-ADE5-43C1-999A-0BC81153B039}</Project>
<Name>winsw</Name>
</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>
<None Include="packages.config" />
</ItemGroup>
<ItemGroup />
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<Import Project="$(SolutionDir)\.nuget\NuGet.targets" Condition="Exists('$(SolutionDir)\.nuget\NuGet.targets')" />
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">

View File

@ -1,5 +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>

View File

@ -1,4 +1,3 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 2013
VisualStudioVersion = 12.0.31101.0
@ -11,25 +10,91 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nuget", ".nuget", "{6BDF40
ProjectSection(SolutionItems) = preProject
.nuget\NuGet.Config = .nuget\NuGet.Config
.nuget\NuGet.exe = .nuget\NuGet.exe
.nuget\NuGet.targets = .nuget\NuGet.targets
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>