Skip to content

Commit 3ba52ab

Browse files
committed
feat: Add Windows service management functionality and enhance logging
- Introduced a batch file for managing the Windows service. - Added WindowsServiceHelper class for service installation, uninstallation, and status checks. - Updated Program.cs to handle command line arguments for service management. - Enhanced logging configuration for Windows service mode.
1 parent 2dda98a commit 3ba52ab

6 files changed

Lines changed: 555 additions & 6 deletions

File tree

src/ClaudeCodeProxy.Host/ClaudeCodeProxy.Host.csproj

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,10 +25,12 @@
2525
<PrivateAssets>all</PrivateAssets>
2626
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
2727
</PackageReference>
28+
<PackageReference Include="Microsoft.Extensions.Hosting.WindowsServices" Version="9.0.0" />
2829
<PackageReference Include="Scalar.AspNetCore" Version="2.6.5" />
2930
<PackageReference Include="Serilog" Version="4.3.0" />
3031
<PackageReference Include="Serilog.AspNetCore" Version="9.0.0" />
3132
<PackageReference Include="Serilog.Sinks.Console" Version="6.0.0" />
33+
<PackageReference Include="Serilog.Sinks.File" Version="6.0.0" />
3234
</ItemGroup>
3335

3436
<ItemGroup>
Lines changed: 260 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,260 @@
1+
using System.Diagnostics;
2+
using System.Runtime.InteropServices;
3+
using System.Security.Principal;
4+
5+
namespace ClaudeCodeProxy.Host.Helper;
6+
7+
public static class WindowsServiceHelper
8+
{
9+
private const string ServiceName = "ClaudeCodeProxyService";
10+
private const string ServiceDisplayName = "Claude Code Proxy Service";
11+
private const string ServiceDescription = "Claude Code Proxy API服务,用于代理Claude AI的API请求";
12+
13+
/// <summary>
14+
/// 检查是否为Windows系统
15+
/// </summary>
16+
public static bool IsWindows()
17+
{
18+
return RuntimeInformation.IsOSPlatform(OSPlatform.Windows);
19+
}
20+
21+
/// <summary>
22+
/// 检查是否以管理员权限运行
23+
/// </summary>
24+
public static bool IsRunningAsAdministrator()
25+
{
26+
if (!IsWindows())
27+
return false;
28+
29+
try
30+
{
31+
var identity = WindowsIdentity.GetCurrent();
32+
var principal = new WindowsPrincipal(identity);
33+
return principal.IsInRole(WindowsBuiltInRole.Administrator);
34+
}
35+
catch
36+
{
37+
return false;
38+
}
39+
}
40+
41+
/// <summary>
42+
/// 安装Windows服务
43+
/// </summary>
44+
public static async Task<bool> InstallServiceAsync()
45+
{
46+
if (!IsWindows())
47+
{
48+
Console.WriteLine("当前系统不是Windows,无需安装服务。");
49+
return false;
50+
}
51+
52+
if (!IsRunningAsAdministrator())
53+
{
54+
Console.WriteLine("需要管理员权限才能安装服务。请以管理员身份运行此程序。");
55+
return false;
56+
}
57+
58+
try
59+
{
60+
var executablePath = Environment.ProcessPath;
61+
if (string.IsNullOrEmpty(executablePath))
62+
{
63+
Console.WriteLine("无法获取可执行文件路径。");
64+
return false;
65+
}
66+
67+
// 检查服务是否已存在
68+
if (await IsServiceInstalledAsync())
69+
{
70+
Console.WriteLine($"服务 '{ServiceName}' 已存在。");
71+
return true;
72+
}
73+
74+
// 使用sc命令安装服务
75+
var arguments = $"create \"{ServiceName}\" binpath= \"{executablePath}\" displayname= \"{ServiceDisplayName}\" start= auto";
76+
var result = await RunScCommandAsync(arguments);
77+
78+
if (result.Success)
79+
{
80+
// 设置服务描述
81+
await RunScCommandAsync($"description \"{ServiceName}\" \"{ServiceDescription}\"");
82+
Console.WriteLine($"服务 '{ServiceDisplayName}' 安装成功!");
83+
Console.WriteLine("服务将在系统启动时自动启动。");
84+
return true;
85+
}
86+
else
87+
{
88+
Console.WriteLine($"服务安装失败:{result.Error}");
89+
return false;
90+
}
91+
}
92+
catch (Exception ex)
93+
{
94+
Console.WriteLine($"安装服务时发生错误:{ex.Message}");
95+
return false;
96+
}
97+
}
98+
99+
/// <summary>
100+
/// 卸载Windows服务
101+
/// </summary>
102+
public static async Task<bool> UninstallServiceAsync()
103+
{
104+
if (!IsWindows())
105+
{
106+
Console.WriteLine("当前系统不是Windows,无需卸载服务。");
107+
return false;
108+
}
109+
110+
if (!IsRunningAsAdministrator())
111+
{
112+
Console.WriteLine("需要管理员权限才能卸载服务。请以管理员身份运行此程序。");
113+
return false;
114+
}
115+
116+
try
117+
{
118+
// 检查服务是否存在
119+
if (!await IsServiceInstalledAsync())
120+
{
121+
Console.WriteLine($"服务 '{ServiceName}' 不存在。");
122+
return true;
123+
}
124+
125+
// 停止服务
126+
await StopServiceAsync();
127+
128+
// 删除服务
129+
var result = await RunScCommandAsync($"delete \"{ServiceName}\"");
130+
131+
if (result.Success)
132+
{
133+
Console.WriteLine($"服务 '{ServiceDisplayName}' 卸载成功!");
134+
return true;
135+
}
136+
else
137+
{
138+
Console.WriteLine($"服务卸载失败:{result.Error}");
139+
return false;
140+
}
141+
}
142+
catch (Exception ex)
143+
{
144+
Console.WriteLine($"卸载服务时发生错误:{ex.Message}");
145+
return false;
146+
}
147+
}
148+
149+
/// <summary>
150+
/// 启动Windows服务
151+
/// </summary>
152+
public static async Task<bool> StartServiceAsync()
153+
{
154+
if (!IsWindows() || !await IsServiceInstalledAsync())
155+
return false;
156+
157+
var result = await RunScCommandAsync($"start \"{ServiceName}\"");
158+
if (result.Success)
159+
{
160+
Console.WriteLine($"服务 '{ServiceDisplayName}' 启动成功!");
161+
return true;
162+
}
163+
else
164+
{
165+
Console.WriteLine($"服务启动失败:{result.Error}");
166+
return false;
167+
}
168+
}
169+
170+
/// <summary>
171+
/// 停止Windows服务
172+
/// </summary>
173+
public static async Task<bool> StopServiceAsync()
174+
{
175+
if (!IsWindows() || !await IsServiceInstalledAsync())
176+
return false;
177+
178+
var result = await RunScCommandAsync($"stop \"{ServiceName}\"");
179+
if (result.Success)
180+
{
181+
Console.WriteLine($"服务 '{ServiceDisplayName}' 停止成功!");
182+
return true;
183+
}
184+
else
185+
{
186+
Console.WriteLine($"服务停止失败:{result.Error}");
187+
return false;
188+
}
189+
}
190+
191+
/// <summary>
192+
/// 检查服务是否已安装
193+
/// </summary>
194+
public static async Task<bool> IsServiceInstalledAsync()
195+
{
196+
if (!IsWindows())
197+
return false;
198+
199+
var result = await RunScCommandAsync($"query \"{ServiceName}\"");
200+
return result.Success;
201+
}
202+
203+
/// <summary>
204+
/// 运行sc命令
205+
/// </summary>
206+
private static async Task<(bool Success, string Output, string Error)> RunScCommandAsync(string arguments)
207+
{
208+
try
209+
{
210+
using var process = new Process();
211+
process.StartInfo.FileName = "sc";
212+
process.StartInfo.Arguments = arguments;
213+
process.StartInfo.UseShellExecute = false;
214+
process.StartInfo.RedirectStandardOutput = true;
215+
process.StartInfo.RedirectStandardError = true;
216+
process.StartInfo.CreateNoWindow = true;
217+
218+
process.Start();
219+
220+
var output = await process.StandardOutput.ReadToEndAsync();
221+
var error = await process.StandardError.ReadToEndAsync();
222+
223+
await process.WaitForExitAsync();
224+
225+
return (process.ExitCode == 0, output, error);
226+
}
227+
catch (Exception ex)
228+
{
229+
return (false, "", ex.Message);
230+
}
231+
}
232+
233+
/// <summary>
234+
/// 显示服务状态
235+
/// </summary>
236+
public static async Task ShowServiceStatusAsync()
237+
{
238+
if (!IsWindows())
239+
{
240+
Console.WriteLine("当前系统不是Windows系统。");
241+
return;
242+
}
243+
244+
Console.WriteLine($"服务名称: {ServiceName}");
245+
Console.WriteLine($"显示名称: {ServiceDisplayName}");
246+
247+
var isInstalled = await IsServiceInstalledAsync();
248+
Console.WriteLine($"安装状态: {(isInstalled ? "已安装" : "未安装")}");
249+
250+
if (isInstalled)
251+
{
252+
var result = await RunScCommandAsync($"query \"{ServiceName}\"");
253+
if (result.Success)
254+
{
255+
Console.WriteLine("服务详细信息:");
256+
Console.WriteLine(result.Output);
257+
}
258+
}
259+
}
260+
}

0 commit comments

Comments
 (0)