Skip to content

Commit 7a25778

Browse files
authored
Merge pull request #14 from FrApp42/dev/main
Implement System and Web Libs
2 parents 1d1d0dc + df7024f commit 7a25778

File tree

11 files changed

+951
-0
lines changed

11 files changed

+951
-0
lines changed
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
namespace FrApps42.System.Computer.Shutdown.Helpers
2+
{
3+
internal sealed class ShutdownArgs
4+
{
5+
public const string BaseCommand = "";
6+
public const string LogoutArg = "/l";
7+
public const string ShutdownArg = "/s";
8+
public const string ShutdownAndSignOnArg = "/sg";
9+
public const string RebootArg = "/r";
10+
public const string HibernateArg = "/h";
11+
public const string SoftArg = "/soft";
12+
public const string BootOptionsArg = "/o";
13+
public const string ForceArg = "/f";
14+
public const string MachineArg = "/m";
15+
public const string TimeoutArg = "/t";
16+
public const string CommentArg = "/c";
17+
}
18+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
namespace FrApps42.System.Computer.Shutdown
2+
{
3+
public class ShutdownResult
4+
{
5+
public int ExitCode { get; set; }
6+
7+
public string? ErrorMessage { get; set; }
8+
}
9+
}
Lines changed: 282 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,282 @@
1+
using FrApps42.System.Computer.Shutdown.Helpers;
2+
using FrApps42.System.Net;
3+
using System.Diagnostics;
4+
using System.Net;
5+
using System.Text;
6+
7+
namespace FrApps42.System.Computer.Shutdown
8+
{
9+
public partial class Shutdown
10+
{
11+
/// <summary>
12+
/// StringBuilder instance used to build the shutdown command.
13+
/// </summary>
14+
private readonly StringBuilder _commandBuilder;
15+
16+
/// <summary>
17+
/// Machine hostname.
18+
/// </summary>
19+
private string _hostname = "";
20+
21+
/// <summary>
22+
/// Will the machine be pinged before shutdowning it?
23+
/// </summary>
24+
private bool _pingEnabled = false;
25+
26+
/// <summary>
27+
/// Timeout of machine ping.
28+
/// </summary>
29+
private int _pingTimeout = 5;
30+
31+
/// <summary>
32+
/// Default constructor
33+
/// </summary>
34+
public Shutdown()
35+
{
36+
_commandBuilder = new StringBuilder(ShutdownArgs.BaseCommand);
37+
}
38+
39+
/// <summary>
40+
/// Iniialize Shutdowner with a hostname
41+
/// </summary>
42+
/// <param name="hostname"></param>
43+
public Shutdown(string hostname): this()
44+
{
45+
46+
SetMachine(hostname);
47+
}
48+
49+
/// <summary>
50+
/// Iniialize Shutdowner with an IPAddress
51+
/// </summary>
52+
/// <param name="address"></param>
53+
public Shutdown(IPAddress address) : this(address.ToString()) { }
54+
55+
/// <summary>
56+
/// Adds a shutdown argument to the command if it is not already present.
57+
/// </summary>
58+
/// <param name="arg">The argument to add.</param>
59+
/// <param name="value">Optional value associated with the argument.</param>
60+
private void AddArgument(string arg, string value = "")
61+
{
62+
string currentCommand = this._commandBuilder.ToString();
63+
64+
// Prevent adding /l if /m or /t is already present
65+
if (arg == ShutdownArgs.LogoutArg && (currentCommand.Contains(ShutdownArgs.MachineArg) || currentCommand.Contains(ShutdownArgs.TimeoutArg)))
66+
return;
67+
68+
// Prevent adding /m or /t if /l is already present
69+
if ((arg == ShutdownArgs.MachineArg || arg == ShutdownArgs.TimeoutArg) && currentCommand.Contains(ShutdownArgs.LogoutArg))
70+
return;
71+
72+
if (!this._commandBuilder.ToString().Contains(arg))
73+
{
74+
if (string.IsNullOrEmpty(value))
75+
{
76+
this._commandBuilder.Append($" {arg}");
77+
}
78+
else
79+
{
80+
this._commandBuilder.Append($" {arg} {value}");
81+
}
82+
}
83+
}
84+
85+
/// <summary>
86+
/// Displays the command of the current command.
87+
/// </summary>
88+
/// <returns>Current built command</returns>
89+
public string GetCommand()
90+
{
91+
return this._commandBuilder.ToString();
92+
}
93+
94+
/// <summary>
95+
/// Logs off the current user immediately, with no time-out period.
96+
/// Can't be used with SetMachine and/or SetTimeOut options.
97+
/// </summary>
98+
/// <returns>Instance</returns>
99+
public Shutdown LogoutUser()
100+
{
101+
AddArgument(ShutdownArgs.LogoutArg);
102+
return this;
103+
}
104+
105+
/// <summary>
106+
/// Shuts down the computer.
107+
/// </summary>
108+
/// <returns>Instance</returns>
109+
public Shutdown ShutdownComputer()
110+
{
111+
this.AddArgument(ShutdownArgs.ShutdownArg);
112+
return this;
113+
}
114+
115+
/// <summary>
116+
/// Shuts down the computer. On the next boot, if Automatic Restart Sign-On is enabled,
117+
/// the device automatically signs in and locks based on the last interactive user.
118+
/// After sign in, it restarts any registered applications.
119+
/// </summary>
120+
/// <returns>Instance</returns>
121+
public Shutdown ShutdownAndSignOn()
122+
{
123+
this.AddArgument(ShutdownArgs.ShutdownAndSignOnArg);
124+
return this;
125+
}
126+
127+
/// <summary>
128+
/// Restarts the computer
129+
/// </summary>
130+
/// <returns>Instance</returns>
131+
public Shutdown Reboot()
132+
{
133+
this.AddArgument(ShutdownArgs.RebootArg);
134+
return this;
135+
}
136+
137+
/// <summary>
138+
/// Puts the local computer into hibernation, if hibernation is enabled.
139+
/// The ForceShutdown option can be used with the Hibernate option.
140+
/// </summary>
141+
/// <returns>Instance</returns>
142+
public Shutdown Hibernate()
143+
{
144+
this.AddArgument(ShutdownArgs.HibernateArg);
145+
return this;
146+
}
147+
148+
/// <summary>
149+
/// Allows running processes and applications to gracefully close instead of forcibly terminating.
150+
/// </summary>
151+
/// <returns>Instance</returns>
152+
public Shutdown Soft()
153+
{
154+
this.AddArgument(ShutdownArgs.SoftArg);
155+
return this;
156+
}
157+
158+
/// <summary>
159+
/// Goes to the Advanced boot options menu and restarts the device.
160+
/// This option must be used with the /r option.
161+
/// </summary>
162+
/// <returns>Instance</returns>
163+
public Shutdown OpenBootOptions()
164+
{
165+
this.AddArgument(ShutdownArgs.BootOptionsArg);
166+
return this;
167+
}
168+
169+
/// <summary>
170+
/// Forces running applications to close without warning users.
171+
/// </summary>
172+
/// <returns></returns>
173+
public Shutdown ForceShutdown()
174+
{
175+
this.AddArgument(ShutdownArgs.ForceArg);
176+
return this;
177+
}
178+
179+
/// <summary>
180+
/// Specifies the target computer.
181+
/// Can't be used with LogoutUser option.
182+
/// </summary>
183+
/// <param name="machineName">Machine hostname</param>
184+
/// <returns>Instance</returns>
185+
public Shutdown SetMachine(string machineName)
186+
{
187+
_hostname = machineName;
188+
AddArgument(ShutdownArgs.MachineArg, machineName);
189+
return this;
190+
}
191+
192+
/// <summary>
193+
/// Sets the timeout period for the shutdown command. Min = 0, Max = 315360000 (10 years)
194+
/// Can't be used with LogoutUser option.
195+
/// </summary>
196+
/// <param name="time">Timeout period in seconds</param>
197+
/// <returns>Instance</returns>
198+
public Shutdown SetTimeOut(int time)
199+
{
200+
if (time < 0) time = 0;
201+
if (time > 315360000) time = 315360000;
202+
203+
this.AddArgument(ShutdownArgs.TimeoutArg, time.ToString());
204+
return this;
205+
}
206+
207+
/// <summary>
208+
/// Creates a custom reason for the system shutdown or restart, which must be enclosed.
209+
/// You can use a maximum of 512 characters. If the comment is longer, it will be shortened to 512.
210+
/// </summary>
211+
/// <param name="comment">Custom reason for shutdown, max 512 characters.</param>
212+
/// <returns>Instance</returns>
213+
public Shutdown SetComment(string comment)
214+
{
215+
if (comment.Length > 512)
216+
comment = comment.Substring(0, 512);
217+
218+
this.AddArgument(ShutdownArgs.CommentArg, $"\"{comment}\"");
219+
return this;
220+
}
221+
222+
/// <summary>
223+
/// Enables the ping before running the shutdown command.
224+
/// </summary>
225+
/// <param name="timeout">Timeout of ping to the machine</param>
226+
/// <returns></returns>
227+
public Shutdown CheckIsOnline(int timeout = 5)
228+
{
229+
if (timeout < 0) timeout = 0;
230+
231+
_pingEnabled = true;
232+
_pingTimeout = timeout;
233+
234+
return this;
235+
}
236+
237+
/// <summary>
238+
/// Runs the built shutdown command.
239+
/// If ping is enabled, it will try to ping the machine. If the ping if unsuccesful it returns null.
240+
/// </summary>
241+
/// <returns>A tuple containing the exit code and an error message if an error occurred.</returns>
242+
public async Task<ShutdownResult> Run()
243+
{
244+
if (_pingEnabled)
245+
{
246+
IsOnline isOnline = new IsOnline(_hostname);
247+
248+
if(!isOnline.Check())
249+
return new ShutdownResult()
250+
{
251+
ExitCode = 1,
252+
ErrorMessage = "Machine is unreachable"
253+
};
254+
}
255+
256+
return await Task.Run(() =>
257+
{
258+
ProcessStartInfo processStartInfo = new("shutdown.exe", _commandBuilder.ToString())
259+
{
260+
UseShellExecute = false,
261+
CreateNoWindow = true,
262+
RedirectStandardError = true,
263+
};
264+
265+
using (Process process = new() { StartInfo = processStartInfo })
266+
{
267+
process.Start();
268+
269+
string errorMessage = process.StandardError.ReadToEnd();
270+
271+
process.WaitForExit();
272+
273+
return new ShutdownResult()
274+
{
275+
ExitCode = process.ExitCode,
276+
ErrorMessage = string.IsNullOrEmpty(errorMessage) ? null : errorMessage
277+
};
278+
}
279+
});
280+
}
281+
}
282+
}

System/Net/IsOnline.cs

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
using System.Net;
2+
using System.Net.NetworkInformation;
3+
using System.Text;
4+
5+
namespace FrApps42.System.Net
6+
{
7+
public class IsOnline
8+
{
9+
private string _hostname;
10+
private IPAddress _address;
11+
12+
public int TimeOut { get; set; } = 5;
13+
14+
public IsOnline(string hostname)
15+
{
16+
_hostname = hostname;
17+
}
18+
19+
public IsOnline(string hostname, int timeout): this(hostname)
20+
{
21+
TimeOut = timeout;
22+
}
23+
24+
public IsOnline(IPAddress address)
25+
{
26+
_address = address;
27+
}
28+
29+
public IsOnline(IPAddress address, int timeout): this(address)
30+
{
31+
TimeOut = timeout;
32+
}
33+
34+
public bool Check()
35+
{
36+
string target = !(string.IsNullOrEmpty(_hostname)) ? _hostname : _address.ToString();
37+
38+
Ping sender = new();
39+
PingOptions options = new()
40+
{
41+
DontFragment = true,
42+
};
43+
44+
string data = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
45+
byte[] buffer = Encoding.ASCII.GetBytes(data);
46+
47+
PingReply reply = sender.Send(target, TimeOut, buffer, options);
48+
49+
if (reply.Status != IPStatus.Success)
50+
return false;
51+
52+
return true;
53+
}
54+
55+
}
56+
}

0 commit comments

Comments
 (0)