Skip to content

Commit 9ba427d

Browse files
committed
Release 1.1.0
Fix launch command not working due to working directory Fix restarted process unable to hook streams due to old streams still being open Fix restart delay comparing in the wrong direction Fix InvalidOperationException after closing process Add options for launch command and launch arguments Change config processor to be tolerant of any file format, as long as value is on the line after the directive name Add logging output for bot stop and restart detection Add logging output for exceptions (Except config or log file exceptions)
1 parent 0d55649 commit 9ba427d

File tree

5 files changed

+124
-25
lines changed

5 files changed

+124
-25
lines changed

PhantomBotService/PhantomBotService.cs

Lines changed: 90 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,9 @@ namespace PhantomBotService
1010
{
1111
public partial class PhantomBotService : ServiceBase
1212
{
13-
private Process phantomBotProcess;
13+
private readonly Process phantomBotProcess;
1414
private FileStream f;
15-
private bool log = false;
15+
private readonly bool log = false;
1616
private bool allowExit = false;
1717
private DateTime nextAttempt;
1818
private System.Timers.Timer t;
@@ -21,12 +21,12 @@ public PhantomBotService()
2121
{
2222
this.InitializeComponent();
2323

24-
((ISupportInitialize)(this.EventLog)).BeginInit();
24+
((ISupportInitialize)this.EventLog).BeginInit();
2525
if (!EventLog.SourceExists(this.EventLog.Source))
2626
{
2727
EventLog.CreateEventSource(this.EventLog.Source, this.EventLog.Log);
2828
}
29-
((ISupportInitialize)(this.EventLog)).EndInit();
29+
((ISupportInitialize)this.EventLog).EndInit();
3030

3131
this.EventLog.Source = this.ServiceName;
3232
this.EventLog.Log = "Application";
@@ -67,13 +67,45 @@ public PhantomBotService()
6767

6868
string[] sdata = configData.Split(new string[] { Environment.NewLine }, StringSplitOptions.None);
6969

70-
string workingDir = sdata[1];
71-
this.log = sdata[4].Equals("true", StringComparison.OrdinalIgnoreCase);
70+
string workingDir = null;
71+
string launchCommand = @"java-runtime\bin\java";
72+
string arguments = "--add-exports java.base/sun.security.x509=ALL-UNNAMED --add-opens java.base/java.lang=ALL-UNNAMED -Duser.language=en -Djava.security.policy=config/security -Dinteractive -Xms1m -Dfile.encoding=UTF-8 -jar \"PhantomBot.jar\"";
73+
for (int i = 0; i < sdata.Length; i++)
74+
{
75+
if (i + 1 < sdata.Length && !string.IsNullOrWhiteSpace(sdata[i + 1])) {
76+
if (sdata[i].Trim().Equals("[Bot Install Directory]", StringComparison.OrdinalIgnoreCase))
77+
{
78+
workingDir = sdata[i + 1].Trim();
79+
}
80+
else if (sdata[i].Trim().Equals("[Logging Enabled]", StringComparison.OrdinalIgnoreCase))
81+
{
82+
this.log = sdata[i + 1].Trim().Equals("true", StringComparison.OrdinalIgnoreCase);
83+
}
84+
else if (sdata[i].Trim().Equals("[Launch Command]", StringComparison.OrdinalIgnoreCase))
85+
{
86+
launchCommand = sdata[i + 1].Trim();
87+
}
88+
else if (sdata[i].Trim().Equals("[Launch Arguments]", StringComparison.OrdinalIgnoreCase))
89+
{
90+
arguments = sdata[i + 1].Trim();
91+
}
92+
}
93+
}
94+
95+
if (string.IsNullOrWhiteSpace(workingDir))
96+
{
97+
this.EventLog.WriteEntry("Missing or invalid [Bot Install Directory] directive in config", EventLogEntryType.Error);
98+
}
99+
100+
if (!workingDir.EndsWith(@"\"))
101+
{
102+
workingDir += @"\";
103+
}
72104

73105
this.phantomBotProcess = new Process();
74106
this.phantomBotProcess.StartInfo.WorkingDirectory = workingDir;
75-
this.phantomBotProcess.StartInfo.FileName = "java-runtime\\bin\\java.exe";
76-
this.phantomBotProcess.StartInfo.Arguments = "--add-opens java.base/java.lang=ALL-UNNAMED -Djava.security.policy=config/security -Dinteractive -Xms1m -Dfile.encoding=UTF-8 -jar \"PhantomBot.jar\"";
107+
this.phantomBotProcess.StartInfo.FileName = workingDir + launchCommand;
108+
this.phantomBotProcess.StartInfo.Arguments = arguments;
77109
this.phantomBotProcess.StartInfo.CreateNoWindow = true;
78110
this.phantomBotProcess.StartInfo.UseShellExecute = false;
79111
this.phantomBotProcess.EnableRaisingEvents = true;
@@ -90,13 +122,21 @@ public PhantomBotService()
90122
}
91123
}
92124

93-
private void PhantomBotProcess_ErrorDataReceived(object sender, DataReceivedEventArgs e) => this.logLine("!! " + e.Data + Environment.NewLine);
94-
private void PhantomBotProcess_OutputDataReceived(object sender, DataReceivedEventArgs e) => this.logLine(">> " + e.Data + Environment.NewLine);
125+
private void PhantomBotProcess_ErrorDataReceived(object sender, DataReceivedEventArgs e) => this.LogLine("!! " + e.Data + Environment.NewLine);
126+
private void PhantomBotProcess_OutputDataReceived(object sender, DataReceivedEventArgs e) => this.LogLine(">> " + e.Data + Environment.NewLine);
95127

96128
private void PhantomBotProcess_Exited(object sender, EventArgs e)
97129
{
130+
if (this.log)
131+
{
132+
this.LogLine(">> PhantomBot has exited" + Environment.NewLine);
133+
}
98134
if (this.allowExit)
99135
{
136+
if (this.log)
137+
{
138+
this.LogLine(">> Service stopping, allowing exit" + Environment.NewLine);
139+
}
100140
return;
101141
}
102142

@@ -109,13 +149,24 @@ private void PhantomBotProcess_Exited(object sender, EventArgs e)
109149

110150
if (this.phantomBotProcess.HasExited)
111151
{
112-
if (DateTime.Compare(this.nextAttempt, DateTime.Now) >= 0)
152+
if (this.log)
153+
{
154+
this.LogLine(">> Restarting the bot" + Environment.NewLine);
155+
}
156+
if (DateTime.Compare(this.nextAttempt, DateTime.Now) <= 0)
113157
{
114158
this.nextAttempt = DateTime.Now.AddSeconds(5);
159+
this.phantomBotProcess.CancelErrorRead();
160+
this.phantomBotProcess.CancelOutputRead();
115161
this.OnStart(null);
116162
}
117163
else
118164
{
165+
if (this.log)
166+
{
167+
this.LogLine(">> Delaying restart 5 seconds" + Environment.NewLine);
168+
}
169+
119170
this.t = new System.Timers.Timer
120171
{
121172
Interval = 5000,
@@ -131,7 +182,7 @@ private void PhantomBotProcess_Exited(object sender, EventArgs e)
131182

132183
protected override void OnStart(string[] args)
133184
{
134-
string path = this.phantomBotProcess.StartInfo.WorkingDirectory + "\\PhantomBotService." + DateTime.Now.ToFileTime() + ".log";
185+
string path = this.phantomBotProcess.StartInfo.WorkingDirectory + "\\PhantomBotService." + DateTime.Now.ToString("yyyyMMdd_HHmmss") + ".log";
135186

136187
try
137188
{
@@ -142,7 +193,7 @@ protected override void OnStart(string[] args)
142193
this.f.Close();
143194
}
144195
this.f = File.Open(path, FileMode.OpenOrCreate, FileAccess.Write, FileShare.ReadWrite);
145-
this.logLine(DateTime.Now.ToShortDateString() + " @ " + DateTime.Now.ToShortTimeString() + " >> Starting PhantomBot" + Environment.NewLine);
196+
this.LogLine(DateTime.Now.ToShortDateString() + " @ " + DateTime.Now.ToShortTimeString() + " >> Starting PhantomBot" + Environment.NewLine);
146197
}
147198

148199
}
@@ -153,19 +204,25 @@ protected override void OnStart(string[] args)
153204

154205
try
155206
{
156-
this.phantomBotProcess.Start();
207+
_ = this.phantomBotProcess.Start();
157208
this.phantomBotProcess.BeginErrorReadLine();
158209
this.phantomBotProcess.BeginOutputReadLine();
159210
}
160211
catch (Exception e)
161212
{
162-
this.EventLog.WriteEntry("Failed to start process: " + this.phantomBotProcess.StartInfo.WorkingDirectory + "\\" + this.phantomBotProcess.StartInfo.FileName + Environment.NewLine + e.GetType().FullName + ": " + e.Message + Environment.NewLine + e.StackTrace, EventLogEntryType.Error);
213+
string msg = "Failed to start process: " + this.phantomBotProcess.StartInfo.WorkingDirectory + Environment.NewLine + this.phantomBotProcess.StartInfo.FileName + Environment.NewLine + e.GetType().FullName + ": " + e.Message + Environment.NewLine + e.StackTrace;
214+
if (this.log)
215+
{
216+
this.LogLine("!! " + msg + Environment.NewLine);
217+
}
218+
this.EventLog.WriteEntry(msg, EventLogEntryType.Error);
163219
}
164220

165221
}
166222

167223
protected override void OnStop()
168224
{
225+
bool exited = false;
169226
try
170227
{
171228
this.allowExit = true;
@@ -179,6 +236,9 @@ protected override void OnStop()
179236
{
180237
if (this.phantomBotProcess.HasExited)
181238
{
239+
exited = true;
240+
this.phantomBotProcess.CancelErrorRead();
241+
this.phantomBotProcess.CancelOutputRead();
182242
this.phantomBotProcess.Close();
183243
break;
184244
}
@@ -188,26 +248,36 @@ protected override void OnStop()
188248
}
189249
catch (Exception e)
190250
{
191-
this.EventLog.WriteEntry("Failed to end process" + Environment.NewLine + e.GetType().FullName + ": " + e.Message + Environment.NewLine + e.StackTrace, EventLogEntryType.Error);
251+
string msg = "Failed to end process" + Environment.NewLine + e.GetType().FullName + ": " + e.Message + Environment.NewLine + e.StackTrace;
252+
if (this.log)
253+
{
254+
this.LogLine("!! " + msg + Environment.NewLine);
255+
}
256+
this.EventLog.WriteEntry(msg, EventLogEntryType.Error);
192257
}
193258

194259
try
195260
{
196-
if (!this.phantomBotProcess.HasExited)
261+
if (!exited && !this.phantomBotProcess.HasExited)
197262
{
198263
this.phantomBotProcess.Kill();
199264
}
200265
}
201266
catch (Exception e)
202267
{
203-
this.EventLog.WriteEntry("Failed to kill process" + Environment.NewLine + e.GetType().FullName + ": " + e.Message + Environment.NewLine + e.StackTrace, EventLogEntryType.Error);
268+
string msg = "Failed to kill process" + Environment.NewLine + e.GetType().FullName + ": " + e.Message + Environment.NewLine + e.StackTrace;
269+
if (this.log)
270+
{
271+
this.LogLine("!! " + msg + Environment.NewLine);
272+
}
273+
this.EventLog.WriteEntry(msg, EventLogEntryType.Error);
204274
}
205275

206276
try
207277
{
208278
if (this.log)
209279
{
210-
this.logLine(DateTime.Now.ToShortDateString() + " @ " + DateTime.Now.ToShortTimeString() + " >> Stopping PhantomBot" + Environment.NewLine);
280+
this.LogLine(DateTime.Now.ToShortDateString() + " @ " + DateTime.Now.ToShortTimeString() + " >> Stopping PhantomBot" + Environment.NewLine);
211281
this.f.Close();
212282
}
213283
}
@@ -217,7 +287,7 @@ protected override void OnStop()
217287
}
218288
}
219289

220-
private void logLine(string logline)
290+
private void LogLine(string logline)
221291
{
222292
try
223293
{

PhantomBotService/PhantomBotService.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
<FileAlignment>512</FileAlignment>
1313
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
1414
<Deterministic>true</Deterministic>
15+
<IsWebBootstrapper>false</IsWebBootstrapper>
1516
<PublishUrl>publish\</PublishUrl>
1617
<Install>true</Install>
1718
<InstallFrom>Disk</InstallFrom>
@@ -24,7 +25,6 @@
2425
<MapFileExtensions>true</MapFileExtensions>
2526
<ApplicationRevision>0</ApplicationRevision>
2627
<ApplicationVersion>1.0.0.%2a</ApplicationVersion>
27-
<IsWebBootstrapper>false</IsWebBootstrapper>
2828
<UseApplicationTrust>false</UseApplicationTrust>
2929
<BootstrapperEnabled>true</BootstrapperEnabled>
3030
</PropertyGroup>

PhantomBotService/Properties/AssemblyInfo.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
[assembly: AssemblyConfiguration("")]
1111
[assembly: AssemblyCompany("")]
1212
[assembly: AssemblyProduct("PhantomBotService")]
13-
[assembly: AssemblyCopyright("Copyright © gmt2001 2019")]
13+
[assembly: AssemblyCopyright("Copyright © gmt2001 2019-2022")]
1414
[assembly: AssemblyTrademark("")]
1515
[assembly: AssemblyCulture("")]
1616

@@ -32,5 +32,5 @@
3232
// You can specify all the values or you can default the Build and Revision Numbers
3333
// by using the '*' as shown below:
3434
// [assembly: AssemblyVersion("1.0.*")]
35-
[assembly: AssemblyVersion("1.0.0.0")]
36-
[assembly: AssemblyFileVersion("1.0.0.0")]
35+
[assembly: AssemblyVersion("1.1.0.0")]
36+
[assembly: AssemblyFileVersion("1.1.0.0")]

PhantomBotServiceSetup/PhantomBotServiceSetup.vdproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -271,7 +271,7 @@
271271
"RemovePreviousVersions" = "11:TRUE"
272272
"DetectNewerInstalledVersion" = "11:TRUE"
273273
"InstallAllUsers" = "11:TRUE"
274-
"ProductVersion" = "8:1.0.0"
274+
"ProductVersion" = "8:1.1.0"
275275
"Manufacturer" = "8:gmt2001"
276276
"ARPHELPTELEPHONE" = "8:"
277277
"ARPHELPLINK" = "8:"

README.md

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,31 @@
11
# PhantomBotService
22
Runs phantomBot as a service on Windows using .Net 4.7.2
3+
4+
This requires .NET Framework 4.7.2, setup.exe should automatically install this for you.
5+
6+
NOTE: You need BOTH setup.exe and PhantomBotServiceSetup.msi to perform the install. If you install .NET Framework 4.7.2 manually (or already have it), then you should be okay with just PhantomBotServiceSetup.msi
7+
8+
To use the service:
9+
10+
Install PhantomBotService wherever you like using setup.exe, it may be easiest to set the install location to the same folder as PhantomBot. NOTE: If setup.exe successfully installs .NET Framework 4.7.2, it will automatically run PhantomBotServiceSetup.msi for you
11+
In the folder where you installed PhantomBotService, edit PhantomBotService.config in a text editor, such as Notepad or Notepad++, and ensure that the path on Line 2 is set to the same folder as your PhantomBot.jar file. Optionally enable logging.
12+
Start the service from Microsoft Management Console. The easiest way to get there is to right-click the Start button, click Computer Management and then go to Computer Management > Services and Applications > Services on the left pane
13+
14+
The logging feature will save a log file in the same folder as PhantomBot.jar containing the output of the bot console. The file name will be unique each time the server starts the bot
15+
16+
The service will attempt to restart the bot if you try to use any method to stop it other than stopping the service through Microsoft Management Console or shutting down the computer
17+
18+
If the service encounters an Exception at the service level while attempting to start the bot, such as File Access Errors, it will be logged in Windows Event Viewer on the Microsoft Management Console. Right-click the Start button, click Computer Management and then go to Computer Management > System Tools > Event Viewer > Windows Logs > Application on the left pane. Look for entries with a Source of PhantomBotService
19+
20+
If setup.exe fails to install .NET Framework 4.7.2, follow these steps
21+
22+
Download the offline installer for .NET Framework 4.7.2 from https://support.microsoft.com/en-us/help/4054530/microsoft-net-framework-4-7-2-offline-installer-for-windows
23+
Install .NET Framework 4.7.2 from the offline installer
24+
Run PhantomBotServiceSetup.msi to install the service
25+
26+
Available options in PhantomBotService.config:
27+
28+
\[Bot Install Directory] - _(Required)_ The line under this directive must be the full path to the bot installation directory (where PhantomBot.jar is located)
29+
\[Logging Enabled] - _(Optional, Defaults to false)_ The line under this directive can be changed to read ***true*** to enable a log of the bot console output to be created in the bot installation directory
30+
\[Launch Command] - _(Optional, Defaults to the java included with the bot)_ The line under this directive can be used to override the command used to launch the bot
31+
\[Launch Arguments] - _(Optional, Defaults to the arguments used by launch.bat)_ The line under this directive can be used to override the command line arguments provided to the launch command

0 commit comments

Comments
 (0)