버튼 이름 변경 및 Windows 종료/재시작 숫자 확인 추가
- 선택 Start/선택 Stop 버튼을 Start/Stop으로 변경 - Windows 강제종료/재시작 시 무작위 숫자 6자리를 입력해야만 실행 (붙여넣기 불가) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013U497kUNL8ArhV7jkzCpKamain
parent
84cafc48a2
commit
bb34662582
|
|
@ -16,8 +16,8 @@ Windows 서비스 기동/종료 관리 프로그램 (C# WPF, .NET 8)
|
|||
### 초기화면
|
||||
- **추가**: Windows 서비스 전체 목록에서 관리할 서비스 선택
|
||||
- **삭제**: 체크한 서비스를 목록에서 제외 (Windows 서비스 자체는 건드리지 않음)
|
||||
- **선택 Start / 선택 Stop**: 체크한 서비스를 차례로 시작/중지
|
||||
- **Windows 강제종료 / Windows 재시작**: 확인 후 즉시 실행 (설정에서 버튼 숨김 가능)
|
||||
- **Start / Stop**: 체크한 서비스를 차례로 시작/중지
|
||||
- **Windows 강제종료 / Windows 재시작**: 확인창에 표시된 무작위 숫자 6자리를 그대로 입력해야만 실행 (붙여넣기 불가, 설정에서 버튼 숨김 가능)
|
||||
- **설정**
|
||||
- 서비스마다 **Start / Stop / Status / Log** 버튼, 상태는 2초마다 자동 갱신 (초록=실행 중, 빨강=중지됨)
|
||||
- 서비스 줄 **우클릭**: 로그 파일 지정 / 로그 폴더 지정 / 경로 해제
|
||||
|
|
|
|||
|
|
@ -20,8 +20,8 @@
|
|||
<Button x:Name="btnAdd" Content="추가" ToolTip="관리할 Windows 서비스를 선택합니다."/>
|
||||
<Button x:Name="btnDelete" Content="삭제" ToolTip="체크한 서비스를 목록에서 뺍니다. (Windows 서비스는 그대로 유지)"/>
|
||||
<Rectangle Width="1" Fill="#CFD8DC" Margin="8,4"/>
|
||||
<Button x:Name="btnStartChecked" Content="선택 Start" ToolTip="체크한 서비스를 차례로 시작합니다."/>
|
||||
<Button x:Name="btnStopChecked" Content="선택 Stop" ToolTip="체크한 서비스를 차례로 중지합니다."/>
|
||||
<Button x:Name="btnStartChecked" Content="Start" ToolTip="체크한 서비스를 차례로 시작합니다."/>
|
||||
<Button x:Name="btnStopChecked" Content="Stop" ToolTip="체크한 서비스를 차례로 중지합니다."/>
|
||||
</WrapPanel>
|
||||
</DockPanel>
|
||||
</Border>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,166 @@
|
|||
using System.Security.Cryptography;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Input;
|
||||
using System.Windows.Media;
|
||||
|
||||
namespace JJServiceManager
|
||||
{
|
||||
/// <summary>
|
||||
/// 위험 작업 확인창: 무작위 숫자 6자리를 보여주고, 그 숫자를 정확히 입력해야만 확인됨
|
||||
/// (Windows 강제종료 / Windows 재시작)
|
||||
/// </summary>
|
||||
public sealed class CodeConfirmWindow : Window
|
||||
{
|
||||
private readonly string _code;
|
||||
private readonly TextBox _input;
|
||||
private readonly Button _ok;
|
||||
private readonly TextBlock _hint;
|
||||
|
||||
public CodeConfirmWindow(string title, string message, string actionText)
|
||||
{
|
||||
_code = RandomNumberGenerator.GetInt32(0, 1_000_000).ToString("D6");
|
||||
|
||||
Title = title;
|
||||
Width = 440;
|
||||
SizeToContent = SizeToContent.Height;
|
||||
ResizeMode = ResizeMode.NoResize;
|
||||
WindowStartupLocation = WindowStartupLocation.CenterOwner;
|
||||
ShowInTaskbar = false;
|
||||
Topmost = true;
|
||||
FontFamily = new FontFamily("Malgun Gothic");
|
||||
FontSize = 13;
|
||||
Background = Brushes2.Make("#FAFAFA");
|
||||
AppIcon.Apply(this);
|
||||
|
||||
var root = new StackPanel { Margin = new Thickness(20, 18, 20, 16) };
|
||||
|
||||
// 경고 문구
|
||||
root.Children.Add(new TextBlock
|
||||
{
|
||||
Text = "⚠ " + message,
|
||||
TextWrapping = TextWrapping.Wrap,
|
||||
Foreground = Brushes2.Make("#B71C1C"),
|
||||
FontWeight = FontWeights.SemiBold,
|
||||
Margin = new Thickness(0, 0, 0, 14)
|
||||
});
|
||||
|
||||
root.Children.Add(new TextBlock
|
||||
{
|
||||
Text = "계속하려면 아래 숫자 6자리를 그대로 입력하세요.",
|
||||
Foreground = Brushes2.Make("#455A64"),
|
||||
Margin = new Thickness(0, 0, 0, 8)
|
||||
});
|
||||
|
||||
// 무작위 숫자 (복사할 수 없는 글자로 표시)
|
||||
var codeBox = new Border
|
||||
{
|
||||
Background = Brushes2.Make("#FFF8E1"),
|
||||
BorderBrush = Brushes2.Make("#FFB300"),
|
||||
BorderThickness = new Thickness(1),
|
||||
CornerRadius = new CornerRadius(4),
|
||||
Padding = new Thickness(0, 8, 0, 8),
|
||||
Margin = new Thickness(0, 0, 0, 12),
|
||||
Child = new TextBlock
|
||||
{
|
||||
Text = string.Join(" ", _code.ToCharArray()),
|
||||
FontFamily = new FontFamily("Consolas"),
|
||||
FontSize = 30,
|
||||
FontWeight = FontWeights.Bold,
|
||||
Foreground = Brushes2.Make("#E65100"),
|
||||
HorizontalAlignment = HorizontalAlignment.Center,
|
||||
IsHitTestVisible = false
|
||||
}
|
||||
};
|
||||
root.Children.Add(codeBox);
|
||||
|
||||
// 입력칸 (숫자만, 붙여넣기 불가)
|
||||
_input = new TextBox
|
||||
{
|
||||
MaxLength = 6,
|
||||
FontFamily = new FontFamily("Consolas"),
|
||||
FontSize = 24,
|
||||
Height = 42,
|
||||
HorizontalContentAlignment = HorizontalAlignment.Center,
|
||||
VerticalContentAlignment = VerticalAlignment.Center,
|
||||
ToolTip = "위의 숫자 6자리를 입력"
|
||||
};
|
||||
InputMethod.SetIsInputMethodEnabled(_input, false);
|
||||
_input.PreviewTextInput += (s, e) =>
|
||||
{
|
||||
foreach (var c in e.Text)
|
||||
if (c < '0' || c > '9') { e.Handled = true; return; }
|
||||
};
|
||||
DataObject.AddPastingHandler(_input, (s, e) => e.CancelCommand());
|
||||
_input.TextChanged += (s, e) => UpdateState();
|
||||
root.Children.Add(_input);
|
||||
|
||||
_hint = new TextBlock
|
||||
{
|
||||
Text = " ",
|
||||
Foreground = Brushes2.Make("#C62828"),
|
||||
Margin = new Thickness(0, 6, 0, 0),
|
||||
HorizontalAlignment = HorizontalAlignment.Center
|
||||
};
|
||||
root.Children.Add(_hint);
|
||||
|
||||
// 버튼
|
||||
var buttons = new StackPanel
|
||||
{
|
||||
Orientation = Orientation.Horizontal,
|
||||
HorizontalAlignment = HorizontalAlignment.Right,
|
||||
Margin = new Thickness(0, 10, 0, 0)
|
||||
};
|
||||
_ok = new Button { Content = actionText, IsEnabled = false, MinWidth = 110 };
|
||||
_ok.SetResourceReference(StyleProperty, "DangerButton");
|
||||
_ok.Click += (s, e) => Submit();
|
||||
var cancel = new Button { Content = "취소", MinWidth = 80, IsCancel = true };
|
||||
buttons.Children.Add(_ok);
|
||||
buttons.Children.Add(cancel);
|
||||
root.Children.Add(buttons);
|
||||
|
||||
Content = root;
|
||||
|
||||
Loaded += (s, e) => _input.Focus();
|
||||
PreviewKeyDown += (s, e) =>
|
||||
{
|
||||
if (e.Key == Key.Enter)
|
||||
{
|
||||
e.Handled = true;
|
||||
Submit();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private void UpdateState()
|
||||
{
|
||||
var t = _input.Text;
|
||||
_ok.IsEnabled = t == _code;
|
||||
if (t.Length == 6 && t != _code) _hint.Text = "숫자가 다릅니다. 다시 입력하세요.";
|
||||
else _hint.Text = " ";
|
||||
}
|
||||
|
||||
private void Submit()
|
||||
{
|
||||
if (_input.Text == _code)
|
||||
{
|
||||
DialogResult = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
_hint.Text = _input.Text.Length == 0 ? "숫자를 입력하세요." : "숫자가 다릅니다. 다시 입력하세요.";
|
||||
_input.SelectAll();
|
||||
_input.Focus();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>확인창을 띄우고, 숫자를 정확히 입력해 확인했으면 true</summary>
|
||||
public static bool Ask(Window owner, string title, string message, string actionText)
|
||||
{
|
||||
var w = new CodeConfirmWindow(title, message, actionText);
|
||||
if (owner != null && owner.IsVisible) w.Owner = owner;
|
||||
else w.WindowStartupLocation = WindowStartupLocation.CenterScreen;
|
||||
return w.ShowDialog() == true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -454,8 +454,21 @@ namespace JJServiceManager
|
|||
private void Shutdown(bool restart)
|
||||
{
|
||||
var what = restart ? "재시작" : "강제 종료";
|
||||
if (!Dialogs.Confirm(this, $"Windows를 지금 {what}합니다.\n저장하지 않은 작업은 모두 사라집니다.\n\n계속할까요?", "Windows " + what))
|
||||
if (_modalOpen) return;
|
||||
bool confirmed;
|
||||
_modalOpen = true;
|
||||
try
|
||||
{
|
||||
confirmed = CodeConfirmWindow.Ask(this, "Windows " + what,
|
||||
$"Windows를 지금 {what}합니다.\n저장하지 않은 작업은 모두 사라집니다.",
|
||||
restart ? "Windows 재시작" : "Windows 강제종료");
|
||||
}
|
||||
finally { _modalOpen = false; }
|
||||
if (!confirmed)
|
||||
{
|
||||
Log($"Windows {what} 취소");
|
||||
return;
|
||||
}
|
||||
Log($"Windows {what} 실행", LogLevel.Warning);
|
||||
try
|
||||
{
|
||||
|
|
|
|||
Loading…
Reference in New Issue