HOME преди 5 години
родител
ревизия
7961ae7663

+ 6 - 0
MultiConsole.Test/App.config

@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<configuration>
+  <startup>
+    <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" />
+  </startup>
+</configuration>

+ 65 - 0
MultiConsole.Test/MultiConsole.Test.csproj

@@ -0,0 +1,65 @@
+<?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>{23E39891-5733-4FB7-9A2E-3EA6AF2D21DC}</ProjectGuid>
+    <OutputType>Exe</OutputType>
+    <AppDesignerFolder>Properties</AppDesignerFolder>
+    <RootNamespace>MultiConsole.Test</RootNamespace>
+    <AssemblyName>MultiConsole.Test</AssemblyName>
+    <TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
+    <FileAlignment>512</FileAlignment>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
+    <PlatformTarget>AnyCPU</PlatformTarget>
+    <DebugSymbols>true</DebugSymbols>
+    <DebugType>full</DebugType>
+    <Optimize>false</Optimize>
+    <OutputPath>bin\Debug\</OutputPath>
+    <DefineConstants>DEBUG;TRACE</DefineConstants>
+    <ErrorReport>prompt</ErrorReport>
+    <WarningLevel>4</WarningLevel>
+    <DocumentationFile>bin\Debug\MultiConsole.Test.XML</DocumentationFile>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
+    <PlatformTarget>AnyCPU</PlatformTarget>
+    <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.Core" />
+    <Reference Include="System.Xml.Linq" />
+    <Reference Include="System.Data.DataSetExtensions" />
+    <Reference Include="Microsoft.CSharp" />
+    <Reference Include="System.Data" />
+    <Reference Include="System.Xml" />
+  </ItemGroup>
+  <ItemGroup>
+    <Compile Include="Program.cs" />
+    <Compile Include="Properties\AssemblyInfo.cs" />
+  </ItemGroup>
+  <ItemGroup>
+    <None Include="App.config" />
+  </ItemGroup>
+  <ItemGroup>
+    <ProjectReference Include="..\MultiConsole\MultiConsole.csproj">
+      <Project>{54aca053-6a1b-4fce-a0d9-3efa98dee3ef}</Project>
+      <Name>MultiConsole</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>

+ 106 - 0
MultiConsole.Test/Program.cs

@@ -0,0 +1,106 @@
+using System;
+using System.Threading;
+
+namespace MultiConsole.Test
+{
+    internal static class Program
+    {
+        private static void Main(string[] args)
+        {
+            //TestGetFromCurrentProcess();
+            //TestCreateNewConsone();
+            TestMultiConsole();
+        }
+
+        private static void TestMultiConsole()
+        {
+            Console.Title = "Multi console host";
+            var rnd = new Random();
+            var threads = new Thread[5];
+            for (var i = 0; i < threads.Length; i++)
+            {
+                var localVarIndex = i;
+
+                threads[localVarIndex] = new Thread(() =>
+                {
+                    try
+                    {
+                        Console.WriteLine("Creating Console {0}...", localVarIndex);
+                        var con = ConsoleFactory.Create(string.Format("Sub Console {0}", localVarIndex));
+                        Console.WriteLine("Sub Console {0} Created.", localVarIndex);
+
+                        con.ForegroundColor = (ConsoleColor)rnd.Next(7,16);
+                        con.BackgroundColor = (ConsoleColor) rnd.Next(0, 7);
+
+                        int l1, l2;
+                        con.SetCursorPosition(l1 = rnd.Next(0,5), rnd.Next(0,5));
+                        Console.WriteLine("Sub Console {0} say hello...", localVarIndex);
+
+                        do
+                        {
+                            con.SetCursorPosition(l2 = rnd.Next(0,5), rnd.Next(0,5));
+                        } while (l1 == l2);
+
+                        con.WriteLine("Hello!");
+                        Console.WriteLine("Sub Console {0} said hello.", localVarIndex);
+
+                        Console.WriteLine("Sub Console {0} waiting input...", localVarIndex);
+                        con.SetCursorPosition(rnd.Next(0, 10), rnd.Next(0, 10));
+                        con.Write("Input something:");
+                        var input = con.ReadLine();
+                        Console.WriteLine("Sub Console {0} got input: {1}", localVarIndex, input);
+
+                        Console.WriteLine("Sub Console {0} shutting down...", localVarIndex);
+                        ConsoleFactory.CloseConsole(con);
+                        Console.WriteLine("Sub Console {0} Finished.", localVarIndex);
+                    }
+                    catch (Exception ex)
+                    {
+                        Console.WriteLine("Sub Console {0} Dropped -- {1}", localVarIndex, ex);
+                    }
+
+                })
+                {
+                    Name = string.Format("Sub Console {0} Thread", i),
+                };
+            }
+
+            foreach (var thread in threads)
+            {
+                thread.Start();
+            }
+
+            foreach (var thread in threads)
+            {
+                thread.Join();
+            }
+
+            Console.WriteLine();
+            Console.Write("Finished. press ENTER to exit.");
+            Console.ReadLine();
+        }
+
+        private static void TestCreateNewConsone()
+        {
+            // ReSharper disable once InconsistentNaming
+            var Console = ConsoleFactory.Create("The new console");
+            Console.Write("OK, type some input,and ENTER:");
+            var x = Console.ReadLine();
+            Console.WriteLine("echo your input:{0}", x);
+            Console.Write("Finished.");
+            ConsoleFactory.CloseConsole(Console);
+        }
+
+        private static void TestGetFromCurrentProcess()
+        {
+            // ReSharper disable once InconsistentNaming
+            var Console = ConsoleFactory.GetFromCurrentProcess();
+            Console.Title = "GetFromCurrentProcess";
+            Console.Write("OK, type some input,and ENTER:");
+            var x = Console.ReadLine();
+            Console.WriteLine("echo your input:{0}", x);
+            Console.Write("Finished.");
+            ConsoleFactory.CloseConsole(Console);
+        }
+    }
+}

+ 35 - 0
MultiConsole.Test/Properties/AssemblyInfo.cs

@@ -0,0 +1,35 @@
+using System.Reflection;
+using System.Runtime.InteropServices;
+
+// 有关程序集的常规信息通过以下
+// 特性集控制。更改这些特性值可修改
+// 与程序集关联的信息。
+[assembly: AssemblyTitle("MultiConsole.Test")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("Microsoft")]
+[assembly: AssemblyProduct("MultiConsole.Test")]
+[assembly: AssemblyCopyright("Copyright © Microsoft 2015")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// 将 ComVisible 设置为 false 使此程序集中的类型
+// 对 COM 组件不可见。  如果需要从 COM 访问此程序集中的类型,
+// 则将该类型上的 ComVisible 特性设置为 true。
+[assembly: ComVisible(false)]
+
+// 如果此项目向 COM 公开,则下列 GUID 用于类型库的 ID
+[assembly: Guid("1013f207-fd13-4e51-96ba-cae3a5ad0594")]
+
+// 程序集的版本信息由下面四个值组成:
+//
+//      主版本
+//      次版本
+//      生成号
+//      修订号
+//
+// 可以指定所有这些值,也可以使用“生成号”和“修订号”的默认值,
+// 方法是按如下所示使用“*”:
+// [assembly: AssemblyVersion("1.0.*")]
+[assembly: AssemblyVersion("1.0.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]

+ 32 - 0
MultiConsole.sln

@@ -0,0 +1,32 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio 2013
+VisualStudioVersion = 12.0.31010.0
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MultiConsole", "MultiConsole\MultiConsole.csproj", "{54ACA053-6A1B-4FCE-A0D9-3EFA98DEE3EF}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MultiConsole.Test", "MultiConsole.Test\MultiConsole.Test.csproj", "{23E39891-5733-4FB7-9A2E-3EA6AF2D21DC}"
+EndProject
+Global
+	GlobalSection(SubversionScc) = preSolution
+		Svn-Managed = True
+		Manager = AnkhSVN - Subversion Support for Visual Studio
+	EndGlobalSection
+	GlobalSection(SolutionConfigurationPlatforms) = preSolution
+		Debug|Any CPU = Debug|Any CPU
+		Release|Any CPU = Release|Any CPU
+	EndGlobalSection
+	GlobalSection(ProjectConfigurationPlatforms) = postSolution
+		{54ACA053-6A1B-4FCE-A0D9-3EFA98DEE3EF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{54ACA053-6A1B-4FCE-A0D9-3EFA98DEE3EF}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{54ACA053-6A1B-4FCE-A0D9-3EFA98DEE3EF}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{54ACA053-6A1B-4FCE-A0D9-3EFA98DEE3EF}.Release|Any CPU.Build.0 = Release|Any CPU
+		{23E39891-5733-4FB7-9A2E-3EA6AF2D21DC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{23E39891-5733-4FB7-9A2E-3EA6AF2D21DC}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{23E39891-5733-4FB7-9A2E-3EA6AF2D21DC}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{23E39891-5733-4FB7-9A2E-3EA6AF2D21DC}.Release|Any CPU.Build.0 = Release|Any CPU
+	EndGlobalSection
+	GlobalSection(SolutionProperties) = preSolution
+		HideSolutionNode = FALSE
+	EndGlobalSection
+EndGlobal

+ 26 - 0
MultiConsole/ConsoleClient.cs

@@ -0,0 +1,26 @@
+using System;
+
+namespace MultiConsole
+{
+    internal static class ConsoleClient
+    {
+        private static void Main(string[] args)
+        {
+            if (args.Length != 1)
+                return;
+
+            try
+            {
+                new ConsoleProxyClient(args[0]).Work();
+            }
+            catch (Exception ex)
+            {
+                Console.WriteLine(ex);
+            }
+
+            Console.WriteLine();
+            //Console.Write("Finished. Press ENTER to close...");
+            //Console.ReadLine();           
+        }
+    }
+}

+ 30 - 0
MultiConsole/ConsoleFactory.cs

@@ -0,0 +1,30 @@
+using ImpromptuInterface;
+using System;
+
+namespace MultiConsole
+{
+    using InternalUtility;
+
+    public static class ConsoleFactory
+    {
+        public static IConsole GetFromCurrentProcess()
+        {
+            return new StaticWrapper(typeof(Console)).ActLike();
+        }
+
+        public static IConsole Create(string title)
+        {
+            IConsole con = new ConsoleProxyServer().ActLike();
+            con.Title = title;
+
+            return con;
+        }
+
+        public static void CloseConsole(IConsole console)
+        {
+            var svr = console.UndoActLike() as ConsoleProxyServer;
+            if (svr != null)
+                svr.Shutdown();
+        }
+    }
+}

+ 91 - 0
MultiConsole/ConsoleProxyClient.cs

@@ -0,0 +1,91 @@
+using System;
+using System.IO.Pipes;
+using System.Reflection;
+using System.Runtime.Serialization;
+using System.Runtime.Serialization.Formatters.Binary;
+
+namespace MultiConsole
+{
+    internal class ConsoleProxyClient
+    {
+        private readonly NamedPipeClientStream _pipe;
+
+        public ConsoleProxyClient(string pipeName)
+        {
+            _pipe = new NamedPipeClientStream(pipeName);
+            _pipe.Connect(5000);
+        }
+
+        public void Work()
+        {
+            var bf = new BinaryFormatter();
+            var flgRuning = true;
+            while (flgRuning)
+            {
+                var rsp = new ConsoleProxyResponse();
+
+                ConsoleProxyCommand cmd = null;
+                try
+                {
+
+                    cmd = (ConsoleProxyCommand)bf.Deserialize(_pipe);
+                }
+                catch (SerializationException ex)
+                {
+                    rsp.Success = false;
+                    rsp.FailMessage = "Can not recognize command.";
+                }
+
+                if (cmd != null)
+                {
+                    try
+                    {
+                        switch (cmd.ControlCmd)
+                        {
+                            case ControlCmd.Shutdown:
+                                flgRuning = false;
+                                rsp.Success = true;
+                                break;
+
+                            case ControlCmd.Invoke:
+                                rsp.Return = typeof(Console).InvokeMember(cmd.Name,
+                                    BindingFlags.Public | BindingFlags.Static | BindingFlags.InvokeMethod, null, null,
+                                    cmd.Param);
+                                rsp.Success = true;
+                                break;
+
+                            case ControlCmd.Get:
+                                rsp.Return = typeof(Console).InvokeMember(cmd.Name,
+                                    BindingFlags.Public | BindingFlags.Static | BindingFlags.GetProperty, null, null, null);
+                                rsp.Success = true;
+                                break;
+
+                            case ControlCmd.Set:
+                                rsp.Return = typeof(Console).InvokeMember(cmd.Name,
+                                    BindingFlags.Public | BindingFlags.Static | BindingFlags.SetProperty, null, null,
+                                    cmd.Param);
+                                rsp.Success = true;
+                                break;
+
+                            default:
+                                rsp.Success = false;
+                                rsp.FailMessage = "Can not recognize command.";
+                                break;
+                        }
+                    }
+
+                    catch (Exception ex)
+                    {
+                        rsp.Success = false;
+                        rsp.FailMessage = ex.Message;
+                    }
+                }
+
+
+                bf.Serialize(_pipe, rsp);
+            }
+
+            _pipe.Close();
+        }
+    }
+}

+ 22 - 0
MultiConsole/ConsoleProxyCommand.cs

@@ -0,0 +1,22 @@
+using System;
+
+namespace MultiConsole
+{
+    internal enum ControlCmd
+    {
+        Shutdown,
+        Invoke,
+        Get,
+        Set,
+    }
+
+    [Serializable]
+    internal class ConsoleProxyCommand
+    {
+        public ControlCmd ControlCmd { get; set; }
+
+        public string Name { get; set; }
+
+        public object[] Param { get; set; }
+    }
+}

+ 14 - 0
MultiConsole/ConsoleProxyResponse.cs

@@ -0,0 +1,14 @@
+using System;
+
+namespace MultiConsole
+{
+    [Serializable]
+    internal class ConsoleProxyResponse
+    {
+        public bool Success { get; set; }
+
+        public Object Return { get; set; }
+
+        public string FailMessage { get; set; }
+    }
+}

+ 134 - 0
MultiConsole/ConsoleProxyServer.cs

@@ -0,0 +1,134 @@
+using System;
+using System.Diagnostics;
+using System.Dynamic;
+using System.IO.Pipes;
+using System.Reflection;
+using System.Runtime.Serialization.Formatters.Binary;
+using System.Text;
+using System.Linq;
+
+namespace MultiConsole
+{
+    internal class ConsoleProxyServer : DynamicObject
+    {
+        private static readonly string CurrentAssemblyName;
+        private static readonly string CurrentAsselblyLocation;
+
+        private Process _process;
+
+        static ConsoleProxyServer()
+        {
+            var currentAssembly = Assembly.GetExecutingAssembly();
+            CurrentAssemblyName = currentAssembly.FullName;
+            CurrentAsselblyLocation = currentAssembly.Location;
+        }
+
+        private readonly NamedPipeServerStream _pipe;
+
+        public ConsoleProxyServer()
+        {
+            var pipeName = CurrentAssemblyName + ", " + Guid.NewGuid().ToString();
+            var encpName = Convert.ToBase64String(Encoding.UTF8.GetBytes(pipeName));
+
+            _pipe = new NamedPipeServerStream(encpName);
+            _process = Process.Start(CurrentAsselblyLocation, encpName);
+
+            _pipe.WaitForConnection();
+        }
+
+        internal void Shutdown()
+        {
+            new BinaryFormatter().Serialize(_pipe, new ConsoleProxyCommand
+            {
+                ControlCmd = ControlCmd.Shutdown
+            });
+
+            var response = (ConsoleProxyResponse)new BinaryFormatter().Deserialize(_pipe);
+            if (response.Success == false)
+                throw new MultiConsoleException(response.FailMessage);
+
+            _pipe.Close();
+        }
+
+        public override bool TryInvokeMember(InvokeMemberBinder binder, object[] args, out object result)
+        {
+            if (binder.Name.StartsWith("Write") && args.Length == 2)
+            {
+                if (args.Length == 2 && args[1] is Array)
+                {
+                    new BinaryFormatter().Serialize(_pipe, new ConsoleProxyCommand
+                    {
+                        ControlCmd = ControlCmd.Invoke,
+                        Name = binder.Name,
+                        Param = new object[] { string.Format((string)args[0], (object[])args[1]) },
+                    });
+                }
+                else
+                {
+                    new BinaryFormatter().Serialize(_pipe, new ConsoleProxyCommand
+                    {
+                        ControlCmd = ControlCmd.Invoke,
+                        Name = binder.Name,
+                        Param = new object[] { string.Format((string)args[0], args.Skip(1).ToArray()) },
+                    });
+                }
+            }
+            else
+            {
+                new BinaryFormatter().Serialize(_pipe, new ConsoleProxyCommand
+                {
+                    ControlCmd = ControlCmd.Invoke,
+                    Name = binder.Name,
+                    Param = args,
+                });
+            }
+
+            var response = (ConsoleProxyResponse)new BinaryFormatter().Deserialize(_pipe);
+            if (response.Success == false)
+                throw new MultiConsoleException(response.FailMessage);
+
+            result = response.Return;
+            return true;
+        }
+
+        public override bool TryGetMember(GetMemberBinder binder, out object result)
+        {
+            new BinaryFormatter().Serialize(_pipe, new ConsoleProxyCommand
+            {
+                ControlCmd = ControlCmd.Get,
+                Name = binder.Name,
+            });
+
+            var response = (ConsoleProxyResponse)new BinaryFormatter().Deserialize(_pipe);
+            if (response.Success == false)
+                throw new MultiConsoleException(response.FailMessage);
+
+            result = response.Return;
+            return true;
+        }
+
+        public override bool TrySetMember(SetMemberBinder binder, object value)
+        {
+            new BinaryFormatter().Serialize(_pipe, new ConsoleProxyCommand
+            {
+                ControlCmd = ControlCmd.Set,
+                Name = binder.Name,
+                Param = new[] { value },
+            });
+
+            var response = (ConsoleProxyResponse)new BinaryFormatter().Deserialize(_pipe);
+            if (response.Success == false)
+                throw new MultiConsoleException(response.FailMessage);
+
+            return true;
+        }
+    }
+
+    internal class MultiConsoleException : Exception
+    {
+        public MultiConsoleException(string message)
+            : base(message)
+        {
+        }
+    }
+}

Файловите разлики са ограничени, защото са твърде много
+ 776 - 0
MultiConsole/IConsole.cs


+ 75 - 0
MultiConsole/InternalUtility/StaticWrapper.cs

@@ -0,0 +1,75 @@
+using System;
+using System.Reflection;
+
+namespace MultiConsole.InternalUtility
+{
+    internal class StaticWrapper: System.Dynamic.DynamicObject
+    {
+        private readonly Type _t;
+
+        public StaticWrapper(Type t)
+        {
+            _t = t;
+        }
+
+        public override bool TryInvokeMember(System.Dynamic.InvokeMemberBinder binder, object[] args, out object result)
+        {
+            try
+            {
+                result = _t.InvokeMember(binder.Name, BindingFlags.Static | BindingFlags.Public | BindingFlags.InvokeMethod, null, null, args);
+                return true;
+            }
+            catch
+            {
+                result = null;
+                return false;
+            }
+        }
+
+        public override bool TryGetMember(System.Dynamic.GetMemberBinder binder, out object result)
+        {
+            try
+            {
+                var p = _t.GetProperty(binder.Name);
+                if (p != null)
+                    result = p.GetValue(null, null);
+                else
+                {
+                    var f = _t.GetField(binder.Name);
+                    if (f != null)
+                        result = f.GetValue(null);
+                    else { result = null; return false; }
+                }
+                return true;
+            }
+            catch
+            {
+                result = null;
+                return false;
+            }
+        }
+
+        public override bool TrySetMember(System.Dynamic.SetMemberBinder binder, object value)
+        {
+            try
+            {
+                var p = _t.GetProperty(binder.Name);
+                if (p != null)
+                    p.SetValue(null, value, null);
+                else
+                {
+                    var f = _t.GetField(binder.Name);
+                    if (f != null)
+                        f.SetValue(null, value);
+                    else
+                        return false;
+                }
+                return true;
+            }
+            catch (SystemException)
+            {
+                return false;
+            }
+        }
+    }
+}

+ 624 - 0
MultiConsole/JetBrains.Annotations/ReSharper.cs

@@ -0,0 +1,624 @@
+using System;
+
+#pragma warning disable 1591
+// ReSharper disable UnusedMember.Global
+// ReSharper disable UnusedParameter.Local
+// ReSharper disable MemberCanBePrivate.Global
+// ReSharper disable UnusedAutoPropertyAccessor.Global
+// ReSharper disable IntroduceOptionalParameters.Global
+// ReSharper disable MemberCanBeProtected.Global
+// ReSharper disable InconsistentNaming
+
+namespace JetBrains.Annotations
+{
+    /// <summary>
+    /// Indicates that the value of the marked element could be <c>null</c> sometimes,
+    /// so the check for <c>null</c> is necessary before its usage
+    /// </summary>
+    /// <example><code>
+    /// [CanBeNull] public object Test() { return null; }
+    /// public void UseTest() {
+    ///   var p = Test();
+    ///   var s = p.ToString(); // Warning: Possible 'System.NullReferenceException'
+    /// }
+    /// </code></example>
+    [AttributeUsage(
+      AttributeTargets.Method | AttributeTargets.Parameter |
+      AttributeTargets.Property | AttributeTargets.Delegate |
+      AttributeTargets.Field, AllowMultiple = false, Inherited = true)]
+    public sealed class CanBeNullAttribute: Attribute { }
+
+    /// <summary>
+    /// Indicates that the value of the marked element could never be <c>null</c>
+    /// </summary>
+    /// <example><code>
+    /// [NotNull] public object Foo() {
+    ///   return null; // Warning: Possible 'null' assignment
+    /// }
+    /// </code></example>
+    [AttributeUsage(
+      AttributeTargets.Method | AttributeTargets.Parameter |
+      AttributeTargets.Property | AttributeTargets.Delegate |
+      AttributeTargets.Field, AllowMultiple = false, Inherited = true)]
+    public sealed class NotNullAttribute: Attribute { }
+
+    /// <summary>
+    /// Indicates that the marked method builds string by format pattern and (optional) arguments.
+    /// Parameter, which contains format string, should be given in constructor. The format string
+    /// should be in <see cref="string.Format(IFormatProvider,string,object[])"/>-like form
+    /// </summary>
+    /// <example><code>
+    /// [StringFormatMethod("message")]
+    /// public void ShowError(string message, params object[] args) { /* do something */ }
+    /// public void Foo() {
+    ///   ShowError("Failed: {0}"); // Warning: Non-existing argument in format string
+    /// }
+    /// </code></example>
+    [AttributeUsage(
+      AttributeTargets.Constructor | AttributeTargets.Method,
+      AllowMultiple = false, Inherited = true)]
+    public sealed class StringFormatMethodAttribute: Attribute
+    {
+        /// <param name="formatParameterName">
+        /// Specifies which parameter of an annotated method should be treated as format-string
+        /// </param>
+        public StringFormatMethodAttribute(string formatParameterName)
+        {
+            FormatParameterName = formatParameterName;
+        }
+
+        public string FormatParameterName { get; private set; }
+    }
+
+    /// <summary>
+    /// Indicates that the function argument should be string literal and match one
+    /// of the parameters of the caller function. For example, ReSharper annotates
+    /// the parameter of <see cref="System.ArgumentNullException"/>
+    /// </summary>
+    /// <example><code>
+    /// public void Foo(string param) {
+    ///   if (param == null)
+    ///     throw new ArgumentNullException("par"); // Warning: Cannot resolve symbol
+    /// }
+    /// </code></example>
+    [AttributeUsage(AttributeTargets.Parameter, AllowMultiple = false, Inherited = true)]
+    public sealed class InvokerParameterNameAttribute: Attribute { }
+
+    /// <summary>
+    /// Indicates that the method is contained in a type that implements
+    /// <see cref="System.ComponentModel.INotifyPropertyChanged"/> interface
+    /// and this method is used to notify that some property value changed
+    /// </summary>
+    /// <remarks>
+    /// The method should be non-static and conform to one of the supported signatures:
+    /// <list>
+    /// <item><c>NotifyChanged(string)</c></item>
+    /// <item><c>NotifyChanged(params string[])</c></item>
+    /// <item><c>NotifyChanged{T}(Expression{Func{T}})</c></item>
+    /// <item><c>NotifyChanged{T,U}(Expression{Func{T,U}})</c></item>
+    /// <item><c>SetProperty{T}(ref T, T, string)</c></item>
+    /// </list>
+    /// </remarks>
+    /// <example><code>
+    /// public class Foo : INotifyPropertyChanged {
+    ///   public event PropertyChangedEventHandler PropertyChanged;
+    ///   [NotifyPropertyChangedInvocator]
+    ///   protected virtual void NotifyChanged(string propertyName) { ... }
+    ///
+    ///   private string _name;
+    ///   public string Name {
+    ///     get { return _name; }
+    ///     set { _name = value; NotifyChanged("LastName"); /* Warning */ }
+    ///   }
+    /// }
+    /// </code>
+    /// Examples of generated notifications:
+    /// <list>
+    /// <item><c>NotifyChanged("Property")</c></item>
+    /// <item><c>NotifyChanged(() =&gt; Property)</c></item>
+    /// <item><c>NotifyChanged((VM x) =&gt; x.Property)</c></item>
+    /// <item><c>SetProperty(ref myField, value, "Property")</c></item>
+    /// </list>
+    /// </example>
+    [AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = true)]
+    public sealed class NotifyPropertyChangedInvocatorAttribute: Attribute
+    {
+        public NotifyPropertyChangedInvocatorAttribute() { }
+        public NotifyPropertyChangedInvocatorAttribute(string parameterName)
+        {
+            ParameterName = parameterName;
+        }
+
+        public string ParameterName { get; private set; }
+    }
+
+    /// <summary>
+    /// Describes dependency between method input and output
+    /// </summary>
+    /// <syntax>
+    /// <p>Function Definition Table syntax:</p>
+    /// <list>
+    /// <item>FDT      ::= FDTRow [;FDTRow]*</item>
+    /// <item>FDTRow   ::= Input =&gt; Output | Output &lt;= Input</item>
+    /// <item>Input    ::= ParameterName: Value [, Input]*</item>
+    /// <item>Output   ::= [ParameterName: Value]* {halt|stop|void|nothing|Value}</item>
+    /// <item>Value    ::= true | false | null | notnull | canbenull</item>
+    /// </list>
+    /// If method has single input parameter, it's name could be omitted.<br/>
+    /// Using <c>halt</c> (or <c>void</c>/<c>nothing</c>, which is the same)
+    /// for method output means that the methos doesn't return normally.<br/>
+    /// <c>canbenull</c> annotation is only applicable for output parameters.<br/>
+    /// You can use multiple <c>[ContractAnnotation]</c> for each FDT row,
+    /// or use single attribute with rows separated by semicolon.<br/>
+    /// </syntax>
+    /// <examples><list>
+    /// <item><code>
+    /// [ContractAnnotation("=> halt")]
+    /// public void TerminationMethod()
+    /// </code></item>
+    /// <item><code>
+    /// [ContractAnnotation("halt &lt;= condition: false")]
+    /// public void Assert(bool condition, string text) // regular assertion method
+    /// </code></item>
+    /// <item><code>
+    /// [ContractAnnotation("s:null => true")]
+    /// public bool IsNullOrEmpty(string s) // string.IsNullOrEmpty()
+    /// </code></item>
+    /// <item><code>
+    /// // A method that returns null if the parameter is null, and not null if the parameter is not null
+    /// [ContractAnnotation("null => null; notnull => notnull")]
+    /// public object Transform(object data) 
+    /// </code></item>
+    /// <item><code>
+    /// [ContractAnnotation("s:null=>false; =>true,result:notnull; =>false, result:null")]
+    /// public bool TryParse(string s, out Person result)
+    /// </code></item>
+    /// </list></examples>
+    [AttributeUsage(AttributeTargets.Method, AllowMultiple = true, Inherited = true)]
+    public sealed class ContractAnnotationAttribute: Attribute
+    {
+        public ContractAnnotationAttribute([NotNull] string contract)
+            : this(contract, false) { }
+
+        public ContractAnnotationAttribute([NotNull] string contract, bool forceFullStates)
+        {
+            Contract = contract;
+            ForceFullStates = forceFullStates;
+        }
+
+        public string Contract { get; private set; }
+        public bool ForceFullStates { get; private set; }
+    }
+
+    /// <summary>
+    /// Indicates that marked element should be localized or not
+    /// </summary>
+    /// <example><code>
+    /// [LocalizationRequiredAttribute(true)]
+    /// public class Foo {
+    ///   private string str = "my string"; // Warning: Localizable string
+    /// }
+    /// </code></example>
+    [AttributeUsage(AttributeTargets.All, AllowMultiple = false, Inherited = true)]
+    public sealed class LocalizationRequiredAttribute: Attribute
+    {
+        public LocalizationRequiredAttribute() : this(true) { }
+        public LocalizationRequiredAttribute(bool required)
+        {
+            Required = required;
+        }
+
+        public bool Required { get; private set; }
+    }
+
+    /// <summary>
+    /// Indicates that the value of the marked type (or its derivatives)
+    /// cannot be compared using '==' or '!=' operators and <c>Equals()</c>
+    /// should be used instead. However, using '==' or '!=' for comparison
+    /// with <c>null</c> is always permitted.
+    /// </summary>
+    /// <example><code>
+    /// [CannotApplyEqualityOperator]
+    /// class NoEquality { }
+    /// class UsesNoEquality {
+    ///   public void Test() {
+    ///     var ca1 = new NoEquality();
+    ///     var ca2 = new NoEquality();
+    ///     if (ca1 != null) { // OK
+    ///       bool condition = ca1 == ca2; // Warning
+    ///     }
+    ///   }
+    /// }
+    /// </code></example>
+    [AttributeUsage(
+      AttributeTargets.Interface | AttributeTargets.Class |
+      AttributeTargets.Struct, AllowMultiple = false, Inherited = true)]
+    public sealed class CannotApplyEqualityOperatorAttribute: Attribute { }
+
+    /// <summary>
+    /// When applied to a target attribute, specifies a requirement for any type marked
+    /// with the target attribute to implement or inherit specific type or types.
+    /// </summary>
+    /// <example><code>
+    /// [BaseTypeRequired(typeof(IComponent)] // Specify requirement
+    /// public class ComponentAttribute : Attribute { }
+    /// [Component] // ComponentAttribute requires implementing IComponent interface
+    /// public class MyComponent : IComponent { }
+    /// </code></example>
+    [AttributeUsage(AttributeTargets.Class, AllowMultiple = true, Inherited = true)]
+    [BaseTypeRequired(typeof(Attribute))]
+    public sealed class BaseTypeRequiredAttribute: Attribute
+    {
+        public BaseTypeRequiredAttribute([NotNull] Type baseType)
+        {
+            BaseType = baseType;
+        }
+
+        [NotNull]
+        public Type BaseType { get; private set; }
+    }
+
+    /// <summary>
+    /// Indicates that the marked symbol is used implicitly
+    /// (e.g. via reflection, in external library), so this symbol
+    /// will not be marked as unused (as well as by other usage inspections)
+    /// </summary>
+    [AttributeUsage(AttributeTargets.All, AllowMultiple = false, Inherited = true)]
+    public sealed class UsedImplicitlyAttribute: Attribute
+    {
+        public UsedImplicitlyAttribute()
+            : this(ImplicitUseKindFlags.Default, ImplicitUseTargetFlags.Default) { }
+
+        public UsedImplicitlyAttribute(ImplicitUseKindFlags useKindFlags)
+            : this(useKindFlags, ImplicitUseTargetFlags.Default) { }
+
+        public UsedImplicitlyAttribute(ImplicitUseTargetFlags targetFlags)
+            : this(ImplicitUseKindFlags.Default, targetFlags) { }
+
+        public UsedImplicitlyAttribute(
+          ImplicitUseKindFlags useKindFlags, ImplicitUseTargetFlags targetFlags)
+        {
+            UseKindFlags = useKindFlags;
+            TargetFlags = targetFlags;
+        }
+
+        public ImplicitUseKindFlags UseKindFlags { get; private set; }
+        public ImplicitUseTargetFlags TargetFlags { get; private set; }
+    }
+
+    /// <summary>
+    /// Should be used on attributes and causes ReSharper
+    /// to not mark symbols marked with such attributes as unused
+    /// (as well as by other usage inspections)
+    /// </summary>
+    [AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = true)]
+    public sealed class MeansImplicitUseAttribute: Attribute
+    {
+        public MeansImplicitUseAttribute()
+            : this(ImplicitUseKindFlags.Default, ImplicitUseTargetFlags.Default) { }
+
+        public MeansImplicitUseAttribute(ImplicitUseKindFlags useKindFlags)
+            : this(useKindFlags, ImplicitUseTargetFlags.Default) { }
+
+        public MeansImplicitUseAttribute(ImplicitUseTargetFlags targetFlags)
+            : this(ImplicitUseKindFlags.Default, targetFlags) { }
+
+        public MeansImplicitUseAttribute(
+          ImplicitUseKindFlags useKindFlags, ImplicitUseTargetFlags targetFlags)
+        {
+            UseKindFlags = useKindFlags;
+            TargetFlags = targetFlags;
+        }
+
+        [UsedImplicitly]
+        public ImplicitUseKindFlags UseKindFlags { get; private set; }
+        [UsedImplicitly]
+        public ImplicitUseTargetFlags TargetFlags { get; private set; }
+    }
+
+    [Flags]
+    public enum ImplicitUseKindFlags
+    {
+        Default = Access | Assign | InstantiatedWithFixedConstructorSignature,
+        /// <summary>Only entity marked with attribute considered used</summary>
+        Access = 1,
+        /// <summary>Indicates implicit assignment to a member</summary>
+        Assign = 2,
+        /// <summary>
+        /// Indicates implicit instantiation of a type with fixed constructor signature.
+        /// That means any unused constructor parameters won't be reported as such.
+        /// </summary>
+        InstantiatedWithFixedConstructorSignature = 4,
+        /// <summary>Indicates implicit instantiation of a type</summary>
+        InstantiatedNoFixedConstructorSignature = 8,
+    }
+
+    /// <summary>
+    /// Specify what is considered used implicitly
+    /// when marked with <see cref="MeansImplicitUseAttribute"/>
+    /// or <see cref="UsedImplicitlyAttribute"/>
+    /// </summary>
+    [Flags]
+    public enum ImplicitUseTargetFlags
+    {
+        Default = Itself,
+        Itself = 1,
+        /// <summary>Members of entity marked with attribute are considered used</summary>
+        Members = 2,
+        /// <summary>Entity marked with attribute and all its members considered used</summary>
+        WithMembers = Itself | Members
+    }
+
+    /// <summary>
+    /// This attribute is intended to mark publicly available API
+    /// which should not be removed and so is treated as used
+    /// </summary>
+    [MeansImplicitUse]
+    public sealed class PublicAPIAttribute: Attribute
+    {
+        public PublicAPIAttribute() { }
+        public PublicAPIAttribute([NotNull] string comment)
+        {
+            Comment = comment;
+        }
+
+        [NotNull]
+        public string Comment { get; private set; }
+    }
+
+    /// <summary>
+    /// Tells code analysis engine if the parameter is completely handled
+    /// when the invoked method is on stack. If the parameter is a delegate,
+    /// indicates that delegate is executed while the method is executed.
+    /// If the parameter is an enumerable, indicates that it is enumerated
+    /// while the method is executed
+    /// </summary>
+    [AttributeUsage(AttributeTargets.Parameter, Inherited = true)]
+    public sealed class InstantHandleAttribute: Attribute { }
+
+    /// <summary>
+    /// Indicates that a method does not make any observable state changes.
+    /// The same as <c>System.Diagnostics.Contracts.PureAttribute</c>
+    /// </summary>
+    /// <example><code>
+    /// [Pure] private int Multiply(int x, int y) { return x * y; }
+    /// public void Foo() {
+    ///   const int a = 2, b = 2;
+    ///   Multiply(a, b); // Waring: Return value of pure method is not used
+    /// }
+    /// </code></example>
+    [AttributeUsage(AttributeTargets.Method, Inherited = true)]
+    public sealed class PureAttribute: Attribute { }
+
+    /// <summary>
+    /// Indicates that a parameter is a path to a file or a folder
+    /// within a web project. Path can be relative or absolute,
+    /// starting from web root (~)
+    /// </summary>
+    [AttributeUsage(AttributeTargets.Parameter)]
+    public class PathReferenceAttribute: Attribute
+    {
+        public PathReferenceAttribute() { }
+        public PathReferenceAttribute([PathReference] string basePath)
+        {
+            BasePath = basePath;
+        }
+
+        [NotNull]
+        public string BasePath { get; private set; }
+    }
+
+    // ASP.NET MVC attributes
+
+    [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]
+    public sealed class AspMvcAreaMasterLocationFormatAttribute: Attribute
+    {
+        public AspMvcAreaMasterLocationFormatAttribute(string format) { }
+    }
+
+    [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]
+    public sealed class AspMvcAreaPartialViewLocationFormatAttribute: Attribute
+    {
+        public AspMvcAreaPartialViewLocationFormatAttribute(string format) { }
+    }
+
+    [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]
+    public sealed class AspMvcAreaViewLocationFormatAttribute: Attribute
+    {
+        public AspMvcAreaViewLocationFormatAttribute(string format) { }
+    }
+
+    [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]
+    public sealed class AspMvcMasterLocationFormatAttribute: Attribute
+    {
+        public AspMvcMasterLocationFormatAttribute(string format) { }
+    }
+
+    [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]
+    public sealed class AspMvcPartialViewLocationFormatAttribute: Attribute
+    {
+        public AspMvcPartialViewLocationFormatAttribute(string format) { }
+    }
+
+    [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]
+    public sealed class AspMvcViewLocationFormatAttribute: Attribute
+    {
+        public AspMvcViewLocationFormatAttribute(string format) { }
+    }
+
+    /// <summary>
+    /// ASP.NET MVC attribute. If applied to a parameter, indicates that the parameter
+    /// is an MVC action. If applied to a method, the MVC action name is calculated
+    /// implicitly from the context. Use this attribute for custom wrappers similar to
+    /// <c>System.Web.Mvc.Html.ChildActionExtensions.RenderAction(HtmlHelper, String)</c>
+    /// </summary>
+    [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Method)]
+    public sealed class AspMvcActionAttribute: Attribute
+    {
+        public AspMvcActionAttribute() { }
+        public AspMvcActionAttribute([NotNull] string anonymousProperty)
+        {
+            AnonymousProperty = anonymousProperty;
+        }
+
+        [NotNull]
+        public string AnonymousProperty { get; private set; }
+    }
+
+    /// <summary>
+    /// ASP.NET MVC attribute. Indicates that a parameter is an MVC area.
+    /// Use this attribute for custom wrappers similar to
+    /// <c>System.Web.Mvc.Html.ChildActionExtensions.RenderAction(HtmlHelper, String)</c>
+    /// </summary>
+    [AttributeUsage(AttributeTargets.Parameter)]
+    public sealed class AspMvcAreaAttribute: PathReferenceAttribute
+    {
+        public AspMvcAreaAttribute() { }
+        public AspMvcAreaAttribute([NotNull] string anonymousProperty)
+        {
+            AnonymousProperty = anonymousProperty;
+        }
+
+        [NotNull]
+        public string AnonymousProperty { get; private set; }
+    }
+
+    /// <summary>
+    /// ASP.NET MVC attribute. If applied to a parameter, indicates that
+    /// the parameter is an MVC controller. If applied to a method,
+    /// the MVC controller name is calculated implicitly from the context.
+    /// Use this attribute for custom wrappers similar to 
+    /// <c>System.Web.Mvc.Html.ChildActionExtensions.RenderAction(HtmlHelper, String, String)</c>
+    /// </summary>
+    [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Method)]
+    public sealed class AspMvcControllerAttribute: Attribute
+    {
+        public AspMvcControllerAttribute() { }
+        public AspMvcControllerAttribute([NotNull] string anonymousProperty)
+        {
+            AnonymousProperty = anonymousProperty;
+        }
+
+        [NotNull]
+        public string AnonymousProperty { get; private set; }
+    }
+
+    /// <summary>
+    /// ASP.NET MVC attribute. Indicates that a parameter is an MVC Master.
+    /// Use this attribute for custom wrappers similar to
+    /// <c>System.Web.Mvc.Controller.View(String, String)</c>
+    /// </summary>
+    [AttributeUsage(AttributeTargets.Parameter)]
+    public sealed class AspMvcMasterAttribute: Attribute { }
+
+    /// <summary>
+    /// ASP.NET MVC attribute. Indicates that a parameter is an MVC model type.
+    /// Use this attribute for custom wrappers similar to
+    /// <c>System.Web.Mvc.Controller.View(String, Object)</c>
+    /// </summary>
+    [AttributeUsage(AttributeTargets.Parameter)]
+    public sealed class AspMvcModelTypeAttribute: Attribute { }
+
+    /// <summary>
+    /// ASP.NET MVC attribute. If applied to a parameter, indicates that
+    /// the parameter is an MVC partial view. If applied to a method,
+    /// the MVC partial view name is calculated implicitly from the context.
+    /// Use this attribute for custom wrappers similar to
+    /// <c>System.Web.Mvc.Html.RenderPartialExtensions.RenderPartial(HtmlHelper, String)</c>
+    /// </summary>
+    [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Method)]
+    public sealed class AspMvcPartialViewAttribute: PathReferenceAttribute { }
+
+    /// <summary>
+    /// ASP.NET MVC attribute. Allows disabling all inspections
+    /// for MVC views within a class or a method.
+    /// </summary>
+    [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
+    public sealed class AspMvcSupressViewErrorAttribute: Attribute { }
+
+    /// <summary>
+    /// ASP.NET MVC attribute. Indicates that a parameter is an MVC display template.
+    /// Use this attribute for custom wrappers similar to 
+    /// <c>System.Web.Mvc.Html.DisplayExtensions.DisplayForModel(HtmlHelper, String)</c>
+    /// </summary>
+    [AttributeUsage(AttributeTargets.Parameter)]
+    public sealed class AspMvcDisplayTemplateAttribute: Attribute { }
+
+    /// <summary>
+    /// ASP.NET MVC attribute. Indicates that a parameter is an MVC editor template.
+    /// Use this attribute for custom wrappers similar to
+    /// <c>System.Web.Mvc.Html.EditorExtensions.EditorForModel(HtmlHelper, String)</c>
+    /// </summary>
+    [AttributeUsage(AttributeTargets.Parameter)]
+    public sealed class AspMvcEditorTemplateAttribute: Attribute { }
+
+    /// <summary>
+    /// ASP.NET MVC attribute. Indicates that a parameter is an MVC template.
+    /// Use this attribute for custom wrappers similar to
+    /// <c>System.ComponentModel.DataAnnotations.UIHintAttribute(System.String)</c>
+    /// </summary>
+    [AttributeUsage(AttributeTargets.Parameter)]
+    public sealed class AspMvcTemplateAttribute: Attribute { }
+
+    /// <summary>
+    /// ASP.NET MVC attribute. If applied to a parameter, indicates that the parameter
+    /// is an MVC view. If applied to a method, the MVC view name is calculated implicitly
+    /// from the context. Use this attribute for custom wrappers similar to
+    /// <c>System.Web.Mvc.Controller.View(Object)</c>
+    /// </summary>
+    [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Method)]
+    public sealed class AspMvcViewAttribute: PathReferenceAttribute { }
+
+    /// <summary>
+    /// ASP.NET MVC attribute. When applied to a parameter of an attribute,
+    /// indicates that this parameter is an MVC action name
+    /// </summary>
+    /// <example><code>
+    /// [ActionName("Foo")]
+    /// public ActionResult Login(string returnUrl) {
+    ///   ViewBag.ReturnUrl = Url.Action("Foo"); // OK
+    ///   return RedirectToAction("Bar"); // Error: Cannot resolve action
+    /// }
+    /// </code></example>
+    [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Property)]
+    public sealed class AspMvcActionSelectorAttribute: Attribute { }
+
+    [AttributeUsage(
+      AttributeTargets.Parameter | AttributeTargets.Property |
+      AttributeTargets.Field, Inherited = true)]
+    public sealed class HtmlElementAttributesAttribute: Attribute
+    {
+        public HtmlElementAttributesAttribute() { }
+        public HtmlElementAttributesAttribute([NotNull] string name)
+        {
+            Name = name;
+        }
+
+        [NotNull]
+        public string Name { get; private set; }
+    }
+
+    [AttributeUsage(
+      AttributeTargets.Parameter | AttributeTargets.Field |
+      AttributeTargets.Property, Inherited = true)]
+    public sealed class HtmlAttributeValueAttribute: Attribute
+    {
+        public HtmlAttributeValueAttribute([NotNull] string name)
+        {
+            Name = name;
+        }
+
+        [NotNull]
+        public string Name { get; private set; }
+    }
+
+    // Razor attributes
+
+    /// <summary>
+    /// Razor attribute. Indicates that a parameter or a method is a Razor section.
+    /// Use this attribute for custom wrappers similar to 
+    /// <c>System.Web.WebPages.WebPageBase.RenderSection(String)</c>
+    /// </summary>
+    [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Method, Inherited = true)]
+    public sealed class RazorSectionAttribute: Attribute { }
+}

+ 71 - 0
MultiConsole/MultiConsole.csproj

@@ -0,0 +1,71 @@
+<?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>{54ACA053-6A1B-4FCE-A0D9-3EFA98DEE3EF}</ProjectGuid>
+    <OutputType>Exe</OutputType>
+    <AppDesignerFolder>Properties</AppDesignerFolder>
+    <RootNamespace>MultiConsole</RootNamespace>
+    <AssemblyName>MultiConsole</AssemblyName>
+    <TargetFrameworkVersion>v4.5</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>
+    <DocumentationFile>bin\Debug\MultiConsole.XML</DocumentationFile>
+  </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>
+    <StartupObject />
+  </PropertyGroup>
+  <ItemGroup>
+    <Reference Include="ImpromptuInterface">
+      <HintPath>..\packages\ImpromptuInterface.6.2.2\lib\net40\ImpromptuInterface.dll</HintPath>
+    </Reference>
+    <Reference Include="System" />
+    <Reference Include="System.Core" />
+    <Reference Include="System.Xml.Linq" />
+    <Reference Include="System.Data.DataSetExtensions" />
+    <Reference Include="Microsoft.CSharp" />
+    <Reference Include="System.Data" />
+    <Reference Include="System.Xml" />
+  </ItemGroup>
+  <ItemGroup>
+    <Compile Include="ConsoleClient.cs" />
+    <Compile Include="ConsoleFactory.cs" />
+    <Compile Include="ConsoleProxyClient.cs" />
+    <Compile Include="ConsoleProxyCommand.cs" />
+    <Compile Include="ConsoleProxyResponse.cs" />
+    <Compile Include="ConsoleProxyServer.cs" />
+    <Compile Include="IConsole.cs" />
+    <Compile Include="Properties\AssemblyInfo.cs" />
+    <Compile Include="InternalUtility\StaticWrapper.cs" />
+    <Compile Include="JetBrains.Annotations\ReSharper.cs" />
+  </ItemGroup>
+  <ItemGroup>
+    <None Include="packages.config" />
+  </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>

+ 35 - 0
MultiConsole/Properties/AssemblyInfo.cs

@@ -0,0 +1,35 @@
+using System.Reflection;
+using System.Runtime.InteropServices;
+
+// 有关程序集的常规信息通过以下
+// 特性集控制。更改这些特性值可修改
+// 与程序集关联的信息。
+[assembly: AssemblyTitle("MultiConsole")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("Microsoft")]
+[assembly: AssemblyProduct("MultiConsole")]
+[assembly: AssemblyCopyright("Copyright © Microsoft 2015")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// 将 ComVisible 设置为 false 使此程序集中的类型
+// 对 COM 组件不可见。  如果需要从 COM 访问此程序集中的类型,
+// 则将该类型上的 ComVisible 特性设置为 true。
+[assembly: ComVisible(false)]
+
+// 如果此项目向 COM 公开,则下列 GUID 用于类型库的 ID
+[assembly: Guid("aafac257-e2c5-4f1b-aed0-8e679846acae")]
+
+// 程序集的版本信息由下面四个值组成:
+//
+//      主版本
+//      次版本
+//      生成号
+//      修订号
+//
+// 可以指定所有这些值,也可以使用“生成号”和“修订号”的默认值,
+// 方法是按如下所示使用“*”:
+// [assembly: AssemblyVersion("1.0.*")]
+[assembly: AssemblyVersion("1.0.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]

+ 4 - 0
MultiConsole/packages.config

@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<packages>
+  <package id="ImpromptuInterface" version="6.2.2" targetFramework="net45" />
+</packages>