Skip to content

Commit eaf1b2c

Browse files
committed
Initial Commit
1 parent 745fe21 commit eaf1b2c

File tree

10 files changed

+445
-1
lines changed

10 files changed

+445
-1
lines changed

App.config

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
<?xml version="1.0" encoding="utf-8" ?>
2+
<configuration>
3+
<startup>
4+
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" />
5+
</startup>
6+
</configuration>

Program.cs

Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Diagnostics;
4+
using System.Linq;
5+
using System.Text;
6+
using System.Threading;
7+
using System.Threading.Tasks;
8+
9+
namespace Supervisor {
10+
public class Program {
11+
static void Main(string[] args) {
12+
var programArgs = ParseArgs(args);
13+
if (programArgs.Count == 0) {
14+
Console.WriteLine("Please specify applications to start and watch. For example:");
15+
Console.WriteLine("Start and monitor one app:");
16+
Console.WriteLine("supervisor myapp.exe");
17+
Console.WriteLine("Start and monitor two apps:");
18+
Console.WriteLine("supervisor app1.exe app2.exe");
19+
Console.WriteLine("Start and monitor two apps with arguments:");
20+
Console.WriteLine("supervisor \"app1.exe \"\"argument 1\"\"\" \"\"\"Folder Name\\app1.exe\"\" \"\"argument 1\"\"\"");
21+
return;
22+
}
23+
24+
var threads = new List<MonitorThread>(programArgs.Count);
25+
26+
for (int i = 0; i < programArgs.Count; i++) {
27+
var t = new MonitorThread(programArgs[i]);
28+
threads.Add(t);
29+
}
30+
31+
Console.WriteLine("Press enter to stop all processes.");
32+
Console.ReadLine();
33+
for (int i = 0; i < threads.Count; i++) {
34+
threads[i].Stop();
35+
}
36+
for (int i = 0; i < threads.Count; i++) {
37+
threads[i].Join();
38+
}
39+
}
40+
41+
public static List<ProgramArgs> ParseArgs(string[] args) {
42+
var res = new List<ProgramArgs>(args.Length);
43+
for (int i = 0; i < args.Length; i++) {
44+
var arg = args[i];
45+
if (arg[0] == '\"') {
46+
var quoteIndex = arg.IndexOf('\"', 1);
47+
res.Add(new ProgramArgs {
48+
ExeName = arg.Substring(1, quoteIndex - 1),
49+
Arguments = arg.Substring(quoteIndex + 1).Trim()
50+
});
51+
} else {
52+
var spaceIndex = arg.IndexOf(' ');
53+
res.Add(new ProgramArgs {
54+
ExeName = arg.Substring(0, spaceIndex),
55+
Arguments = arg.Substring(spaceIndex + 1)
56+
});
57+
}
58+
}
59+
return res;
60+
}
61+
62+
}
63+
64+
public class MonitorThread {
65+
public readonly ProgramArgs Args;
66+
67+
public Process Process {
68+
get;
69+
private set;
70+
}
71+
72+
private bool IsCancelled;
73+
74+
private readonly Thread Thread;
75+
76+
public MonitorThread(ProgramArgs args) {
77+
Args = args;
78+
Thread = new Thread(DoMonitor);
79+
Thread.Start();
80+
}
81+
82+
private void DoMonitor() {
83+
Console.WriteLine("DoMonitor");
84+
Process = Process.Start(Args.ExeName, Args.Arguments);
85+
Console.WriteLine("DoMonitor1");
86+
while (true) {
87+
if (Process.WaitForExit(1000) && !IsCancelled) {
88+
Process = Process.Start(Args.ExeName, Args.Arguments);
89+
}
90+
if (IsCancelled) {
91+
if (!Process.HasExited)
92+
Process.Kill();
93+
return;
94+
}
95+
}
96+
}
97+
98+
public void Stop() {
99+
IsCancelled = true;
100+
}
101+
102+
public void Join() {
103+
if (Thread.IsAlive)
104+
Thread.Join();
105+
}
106+
107+
public bool Join(TimeSpan timeout) {
108+
if (Thread.IsAlive)
109+
return Thread.Join(timeout);
110+
else
111+
return true;
112+
}
113+
}
114+
115+
public class ProgramArgs {
116+
public string ExeName;
117+
public string Arguments;
118+
119+
public override bool Equals(object obj) {
120+
if (obj is ProgramArgs) {
121+
var b = obj as ProgramArgs;
122+
return ExeName == b.ExeName && Arguments == b.Arguments;
123+
} else
124+
return false;
125+
}
126+
127+
public override string ToString() {
128+
return "\"" + ExeName + "\" " + Arguments;
129+
}
130+
131+
public override int GetHashCode() {
132+
var result = ExeName.GetHashCode();
133+
result = 31 * result + Arguments.GetHashCode();
134+
return result;
135+
}
136+
}
137+
}

Properties/AssemblyInfo.cs

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
using System.Reflection;
2+
using System.Runtime.CompilerServices;
3+
using System.Runtime.InteropServices;
4+
5+
// General Information about an assembly is controlled through the following
6+
// set of attributes. Change these attribute values to modify the information
7+
// associated with an assembly.
8+
[assembly: AssemblyTitle("supervisor")]
9+
[assembly: AssemblyDescription("Process supervisor for Windows")]
10+
[assembly: AssemblyConfiguration("")]
11+
[assembly: AssemblyCompany("")]
12+
[assembly: AssemblyProduct("supervisor")]
13+
[assembly: AssemblyCopyright("Copyright © 2019 Ivan Nikitin")]
14+
[assembly: AssemblyTrademark("")]
15+
[assembly: AssemblyCulture("")]
16+
17+
// Setting ComVisible to false makes the types in this assembly not visible
18+
// to COM components. If you need to access a type in this assembly from
19+
// COM, set the ComVisible attribute to true on that type.
20+
[assembly: ComVisible(false)]
21+
22+
// The following GUID is for the ID of the typelib if this project is exposed to COM
23+
[assembly: Guid("2b76cdfe-5034-4e8d-9192-9f4407e4b84a")]
24+
25+
// Version information for an assembly consists of the following four values:
26+
//
27+
// Major Version
28+
// Minor Version
29+
// Build Number
30+
// Revision
31+
//
32+
// You can specify all the values or you can default the Build and Revision Numbers
33+
// by using the '*' as shown below:
34+
// [assembly: AssemblyVersion("1.0.*")]
35+
[assembly: AssemblyVersion("1.0.0.0")]
36+
[assembly: AssemblyFileVersion("1.0.0.0")]

README.md

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,15 @@
11
# Supervisor
2-
Simple process supervisor for Windows
2+
Simple process supervisor for Windows.
3+
4+
## Usage examples
5+
Start and monitor one app:
6+
7+
supervisor myapp.exe
8+
9+
Start and monitor two apps:
10+
11+
supervisor app1.exe "Path With Spaces\app2.exe"
12+
13+
Start two apps with arguments:
14+
15+
supervisor "app1.exe arg1" """Path With Spaces\app1.exe"" ""argument with spaces"""
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
using System.Reflection;
2+
using System.Runtime.CompilerServices;
3+
using System.Runtime.InteropServices;
4+
5+
[assembly: AssemblyTitle("Supervisor.Tests")]
6+
[assembly: AssemblyDescription("")]
7+
[assembly: AssemblyConfiguration("")]
8+
[assembly: AssemblyCompany("")]
9+
[assembly: AssemblyProduct("Supervisor.Tests")]
10+
[assembly: AssemblyCopyright("Copyright © 2019")]
11+
[assembly: AssemblyTrademark("")]
12+
[assembly: AssemblyCulture("")]
13+
14+
[assembly: ComVisible(false)]
15+
16+
[assembly: Guid("918bfe74-5bbd-416f-820c-e906fc4cd915")]
17+
18+
// [assembly: AssemblyVersion("1.0.*")]
19+
[assembly: AssemblyVersion("1.0.0.0")]
20+
[assembly: AssemblyFileVersion("1.0.0.0")]
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<Project ToolsVersion="15.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
3+
<Import Project="..\..\packages\MSTest.TestAdapter.1.3.2\build\net45\MSTest.TestAdapter.props" Condition="Exists('..\..\packages\MSTest.TestAdapter.1.3.2\build\net45\MSTest.TestAdapter.props')" />
4+
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
5+
<PropertyGroup>
6+
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
7+
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
8+
<ProjectGuid>{918BFE74-5BBD-416F-820C-E906FC4CD915}</ProjectGuid>
9+
<OutputType>Library</OutputType>
10+
<AppDesignerFolder>Properties</AppDesignerFolder>
11+
<RootNamespace>Supervisor.Tests</RootNamespace>
12+
<AssemblyName>Supervisor.Tests</AssemblyName>
13+
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
14+
<FileAlignment>512</FileAlignment>
15+
<ProjectTypeGuids>{3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
16+
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">15.0</VisualStudioVersion>
17+
<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
18+
<ReferencePath>$(ProgramFiles)\Common Files\microsoft shared\VSTT\$(VisualStudioVersion)\UITestExtensionPackages</ReferencePath>
19+
<IsCodedUITest>False</IsCodedUITest>
20+
<TestProjectType>UnitTest</TestProjectType>
21+
<NuGetPackageImportStamp>
22+
</NuGetPackageImportStamp>
23+
</PropertyGroup>
24+
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
25+
<DebugSymbols>true</DebugSymbols>
26+
<DebugType>full</DebugType>
27+
<Optimize>false</Optimize>
28+
<OutputPath>bin\Debug\</OutputPath>
29+
<DefineConstants>DEBUG;TRACE</DefineConstants>
30+
<ErrorReport>prompt</ErrorReport>
31+
<WarningLevel>4</WarningLevel>
32+
</PropertyGroup>
33+
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
34+
<DebugType>pdbonly</DebugType>
35+
<Optimize>true</Optimize>
36+
<OutputPath>bin\Release\</OutputPath>
37+
<DefineConstants>TRACE</DefineConstants>
38+
<ErrorReport>prompt</ErrorReport>
39+
<WarningLevel>4</WarningLevel>
40+
</PropertyGroup>
41+
<ItemGroup>
42+
<Reference Include="Microsoft.VisualStudio.TestPlatform.TestFramework, Version=14.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
43+
<HintPath>..\..\packages\MSTest.TestFramework.1.3.2\lib\net45\Microsoft.VisualStudio.TestPlatform.TestFramework.dll</HintPath>
44+
</Reference>
45+
<Reference Include="Microsoft.VisualStudio.TestPlatform.TestFramework.Extensions, Version=14.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
46+
<HintPath>..\..\packages\MSTest.TestFramework.1.3.2\lib\net45\Microsoft.VisualStudio.TestPlatform.TestFramework.Extensions.dll</HintPath>
47+
</Reference>
48+
<Reference Include="System" />
49+
<Reference Include="System.Core" />
50+
</ItemGroup>
51+
<ItemGroup>
52+
<Compile Include="SupervisorTests.cs" />
53+
<Compile Include="Properties\AssemblyInfo.cs" />
54+
</ItemGroup>
55+
<ItemGroup>
56+
<None Include="packages.config" />
57+
</ItemGroup>
58+
<ItemGroup>
59+
<ProjectReference Include="..\..\supervisor.csproj">
60+
<Project>{2b76cdfe-5034-4e8d-9192-9f4407e4b84a}</Project>
61+
<Name>supervisor</Name>
62+
</ProjectReference>
63+
</ItemGroup>
64+
<Import Project="$(VSToolsPath)\TeamTest\Microsoft.TestTools.targets" Condition="Exists('$(VSToolsPath)\TeamTest\Microsoft.TestTools.targets')" />
65+
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
66+
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
67+
<PropertyGroup>
68+
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
69+
</PropertyGroup>
70+
<Error Condition="!Exists('..\..\packages\MSTest.TestAdapter.1.3.2\build\net45\MSTest.TestAdapter.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\packages\MSTest.TestAdapter.1.3.2\build\net45\MSTest.TestAdapter.props'))" />
71+
<Error Condition="!Exists('..\..\packages\MSTest.TestAdapter.1.3.2\build\net45\MSTest.TestAdapter.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\packages\MSTest.TestAdapter.1.3.2\build\net45\MSTest.TestAdapter.targets'))" />
72+
</Target>
73+
<Import Project="..\..\packages\MSTest.TestAdapter.1.3.2\build\net45\MSTest.TestAdapter.targets" Condition="Exists('..\..\packages\MSTest.TestAdapter.1.3.2\build\net45\MSTest.TestAdapter.targets')" />
74+
</Project>
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
using System;
2+
using System.Threading;
3+
using Microsoft.VisualStudio.TestTools.UnitTesting;
4+
5+
namespace Supervisor.Tests {
6+
[TestClass]
7+
public class SupervisorTests {
8+
[TestMethod]
9+
public void ParseArgs() {
10+
var args = Program.ParseArgs(new string[] {
11+
"test.exe 1 \"88\"",
12+
"\"C:\\Program Files\\test.exe\" \"33\""
13+
});
14+
Assert.AreEqual(2, args.Count);
15+
Assert.AreEqual(new ProgramArgs {
16+
ExeName = "test.exe",
17+
Arguments = "1 \"88\""
18+
}, args[0]);
19+
Assert.AreEqual(new ProgramArgs {
20+
ExeName = "C:\\Program Files\\test.exe",
21+
Arguments = "\"33\""
22+
}, args[1]);
23+
}
24+
25+
[TestMethod]
26+
public void MonitorProcess() {
27+
var args = new ProgramArgs {
28+
ExeName = "cmd.exe"
29+
};
30+
var monitor = new MonitorThread(args);
31+
try {
32+
// Process won't start until this thread is idle
33+
var waitForProcess = new Thread(delegate () {
34+
for (int i = 0; i < 30; i++) {
35+
if (monitor.Process != null)
36+
break;
37+
else
38+
Thread.Sleep((i + 1) * 100);
39+
}
40+
});
41+
waitForProcess.Start();
42+
waitForProcess.Join();
43+
Console.WriteLine("test");
44+
Assert.IsNotNull(monitor.Process);
45+
Assert.IsFalse(monitor.Process.HasExited);
46+
47+
var process1 = monitor.Process;
48+
monitor.Process.Kill();
49+
waitForProcess = new Thread(delegate () {
50+
for (int i = 0; i < 30; i++) {
51+
if (monitor.Process != process1)
52+
break;
53+
else
54+
Thread.Sleep((i + 1) * 100);
55+
}
56+
});
57+
waitForProcess.Start();
58+
waitForProcess.Join();
59+
Assert.IsNotNull(monitor.Process);
60+
61+
monitor.Stop();
62+
monitor.Join();
63+
Assert.IsTrue(monitor.Process.HasExited);
64+
} finally {
65+
monitor.Stop();
66+
monitor.Join();
67+
}
68+
}
69+
}
70+
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<packages>
3+
<package id="MSTest.TestAdapter" version="1.3.2" targetFramework="net45" />
4+
<package id="MSTest.TestFramework" version="1.3.2" targetFramework="net45" />
5+
</packages>

0 commit comments

Comments
 (0)