mirror of https://github.com/winsw/winsw
commit
929b87c383
|
@ -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
|
||||
|
|
Binary file not shown.
|
@ -0,0 +1,6 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<configuration>
|
||||
<solution>
|
||||
<add key="disableSourceControlIntegration" value="true" />
|
||||
</solution>
|
||||
</configuration>
|
Binary file not shown.
|
@ -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>
|
|
@ -0,0 +1,4 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="ILMerge.MSBuild.Tasks" version="1.0.0.3" />
|
||||
</packages>
|
|
@ -7,14 +7,23 @@ using System.Runtime.InteropServices;
|
|||
using System.ServiceProcess;
|
||||
using System.Text;
|
||||
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 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;
|
||||
|
||||
|
@ -22,6 +31,10 @@ 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>
|
||||
/// 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.
|
||||
|
@ -44,6 +57,7 @@ namespace winsw
|
|||
{
|
||||
_descriptor = descriptor;
|
||||
ServiceName = _descriptor.Id;
|
||||
ExtensionManager = new WinSWExtensionManager(_descriptor);
|
||||
CanShutdown = true;
|
||||
CanStop = true;
|
||||
CanPauseAndContinue = false;
|
||||
|
@ -183,22 +197,19 @@ namespace winsw
|
|||
|
||||
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)
|
||||
{
|
||||
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");
|
||||
StreamWriter log = new StreamWriter(logfilename, true);
|
||||
|
||||
log.WriteLine(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss") + " - " + message);
|
||||
log.Flush();
|
||||
log.Close();
|
||||
Log.Logger.Log(GetType(), logLevel ?? Level.Info, message, ex);
|
||||
}
|
||||
|
||||
protected override void OnStart(string[] _)
|
||||
|
@ -241,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.
|
||||
|
@ -322,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();
|
||||
|
@ -389,7 +427,7 @@ namespace winsw
|
|||
{
|
||||
try
|
||||
{
|
||||
WriteEvent("SIGINT to " + pid + " failed - Killing as fallback");
|
||||
WriteEvent("SIGINT to " + pid + " failed - Killing as fallback", Level.Warn);
|
||||
proc.Kill();
|
||||
}
|
||||
catch (ArgumentException)
|
||||
|
@ -504,6 +542,7 @@ namespace winsw
|
|||
|
||||
public static int Main(string[] args)
|
||||
{
|
||||
// Run app
|
||||
try
|
||||
{
|
||||
Run(args);
|
||||
|
@ -529,9 +568,18 @@ namespace winsw
|
|||
// ReSharper disable once InconsistentNaming
|
||||
public static void Run(string[] _args, ServiceDescriptor descriptor = null)
|
||||
{
|
||||
if (_args.Length > 0)
|
||||
{
|
||||
var d = descriptor ?? new ServiceDescriptor();
|
||||
bool isCLIMode = _args.Length > 0;
|
||||
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>();
|
||||
Win32Service s = svc.Select(d.Id);
|
||||
|
||||
|
@ -702,6 +750,7 @@ namespace winsw
|
|||
}
|
||||
if (args[0] == "status")
|
||||
{
|
||||
Log.Warn("User requested the status");
|
||||
if (s == null)
|
||||
Console.WriteLine("NonExistent");
|
||||
else if (s.Started)
|
||||
|
@ -738,6 +787,43 @@ namespace winsw
|
|||
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()
|
||||
{
|
||||
StringBuilder buf = new StringBuilder();
|
|
@ -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>
|
|
@ -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 "$(WindowsSdkPath.TrimEnd('\\'))" sn > $(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=""$(SNPath)" -p "$(AssemblyOriginatorKeyFile)" "$(CertificateTmpPubFile)"" />
|
||||
<Message Text="ILMerge @(MergeAsm) -> $(MergedAssembly)" Importance="high" />
|
||||
<ILMerge ToolPath="$(ILMergePath)" InputAssemblies="@(MergeAsm)" OutputFile="$(MergedAssembly)" TargetKind="SameAsPrimaryAssembly" KeyFile="$(CertificateTmpPubFile)" DelaySign="true" />
|
||||
<Exec Command=""$(SNPath)" -R "$(MergedAssembly)" "$(AssemblyOriginatorKeyFile)"" />
|
||||
</Target>
|
||||
</Project>
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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
|
||||
{
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
||||
}
|
|
@ -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,
|
|
@ -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;
|
|
@ -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")]
|
|
@ -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.
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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>
|
|
@ -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)
|
||||
{ }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="log4net" version="2.0.3" targetFramework="net20" />
|
||||
</packages>
|
|
@ -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")]
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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>
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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>
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
<NUnitProject>
|
||||
<Settings activeconfig="Debug" />
|
||||
<Config name="Debug">
|
||||
<assembly path="bin\Debug\winswTests.dll" />
|
||||
</Config>
|
||||
<Config name="Release"></Config>
|
||||
</NUnitProject>
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,4 +1,5 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="NUnit" version="2.6.4" targetFramework="net20" />
|
||||
<package id="JetBrains.Annotations" version="8.0.5.0" targetFramework="net20" />
|
||||
</packages>
|
|
@ -17,6 +17,8 @@
|
|||
<UpgradeBackupLocation>
|
||||
</UpgradeBackupLocation>
|
||||
<OldToolsVersion>3.5</OldToolsVersion>
|
||||
<SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">..\..\</SolutionDir>
|
||||
<RestorePackages>true</RestorePackages>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
|
@ -35,14 +37,14 @@
|
|||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="JetBrains.Annotations, Version=8.0.5.0, Culture=neutral, PublicKeyToken=1010a0d8d6380325, processorArchitecture=MSIL">
|
||||
<SpecificVersion>False</SpecificVersion>
|
||||
<HintPath>..\..\packages\JetBrains.Annotations.8.0.5.0\lib\net20\JetBrains.Annotations.dll</HintPath>
|
||||
</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>
|
||||
<HintPath>..\Lib\nunit.framework.dll</HintPath>
|
||||
<HintPath>..\..\packages\NUnit.2.6.4\lib\nunit.framework.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Data" />
|
||||
|
@ -50,21 +52,39 @@
|
|||
<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>
|
||||
<ProjectReference Include="..\..\winsw.csproj">
|
||||
<ProjectReference Include="..\..\Core\ServiceWrapper\winsw.csproj">
|
||||
<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">
|
||||
<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">
|
|
@ -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>
|
|
@ -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
|
|
@ -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>
|
28
winsw.sln
28
winsw.sln
|
@ -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
|
Loading…
Reference in New Issue