From e7f39a7bc13087253c8f42774cc76feed26b4804 Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 21 Oct 2026 06:41:15 +0000 Subject: [PATCH] =?UTF-8?q?=EA=B8=B0=EB=8A=A5=20=EC=B6=94=EA=B0=80=20Ctrl?= =?UTF-8?q?=20+Shift=20+=20P?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_019WT2pzDE45iP4QMAAo7cyz --- Program.cs | 237 +++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 167 insertions(+), 70 deletions(-) diff --git a/Program.cs b/Program.cs index 3184769..ba6af1d 100644 --- a/Program.cs +++ b/Program.cs @@ -247,6 +247,64 @@ namespace WindowsKeyInfo } } + /// 제한 화면에서 Ctrl+Shift+P로 여는 마스터 암호 입력 창 (틀리면 알림 후 다시 입력) + internal sealed class MasterPasswordDialog : Form + { + private readonly TextBox _box; + + public MasterPasswordDialog() + { + Text = "Windows Key"; + FormBorderStyle = FormBorderStyle.FixedDialog; + MaximizeBox = false; + MinimizeBox = false; + ShowInTaskbar = false; + StartPosition = FormStartPosition.CenterParent; + AutoScaleMode = AutoScaleMode.Dpi; + Font = new Font("맑은 고딕", 9.5f); + ClientSize = new Size(360, 130); + + var lbl = new Label + { + Text = "마스터 암호를 입력하세요.", + Location = new Point(14, 14), + Size = new Size(332, 22) + }; + _box = new TextBox + { + Location = new Point(14, 42), + Size = new Size(332, 28), + UseSystemPasswordChar = true + }; + + var btnOk = new Button { Text = "확인", Location = new Point(126, 84), Size = new Size(108, 32) }; + btnOk.Click += (s, e) => TryAccept(); + var btnCancel = new Button { Text = "취소", Location = new Point(240, 84), Size = new Size(106, 32), DialogResult = DialogResult.Cancel }; + + Controls.Add(lbl); + Controls.Add(_box); + Controls.Add(btnOk); + Controls.Add(btnCancel); + AcceptButton = btnOk; + CancelButton = btnCancel; + ActiveControl = _box; + } + + private void TryAccept() + { + if (PasswordStore.Verify(_box.Text)) + { + DialogResult = DialogResult.OK; + Close(); + return; + } + + MessageBox.Show(this, "암호가 틀렸습니다.", Text, MessageBoxButtons.OK, MessageBoxIcon.Warning); + _box.Clear(); + _box.Focus(); + } + } + /// 암호 변경 창 (새 암호 한 번 입력) internal sealed class ChangePasswordDialog : Form { @@ -409,15 +467,24 @@ namespace WindowsKeyInfo } } - /// 메인 창 (WinForms). fullMode=false면 키 정보 4줄 + 복사/파일로 저장/닫기만 표시 + /// 메인 창 (WinForms). 제한 모드면 키 정보 4줄 + 복사/파일로 저장/닫기만 표시, + /// 제한 모드에서 Ctrl+Shift+P → 마스터 암호 입력 → 맞으면 그 자리에서 전체 화면으로 전환 internal sealed class MainForm : Form { + private const Keys UnlockShortcut = Keys.Control | Keys.Shift | Keys.P; + private readonly KeyReader.KeyInfo _info; private readonly TextBox _txtProductName; private readonly TextBox _txtProductId; private readonly TextBox _txtInstalledKey; private readonly TextBox _txtBackupKey; + // 전체 모드에서만 보이는 컨트롤 + private readonly Button _btnChangePw; + private readonly FlowLayoutPanel _phone; + private readonly FlowLayoutPanel _links; + private bool _fullMode; + public MainForm(bool fullMode) { Text = "Windows 제품 키 백업"; @@ -426,7 +493,7 @@ namespace WindowsKeyInfo StartPosition = FormStartPosition.CenterScreen; AutoScaleMode = AutoScaleMode.Dpi; Font = new Font("맑은 고딕", 9.5f); - ClientSize = fullMode ? new Size(560, 344) : new Size(560, 240); + ClientSize = new Size(560, 344); var table = new TableLayoutPanel { @@ -465,77 +532,69 @@ namespace WindowsKeyInfo buttons.Controls.Add(btnSave); buttons.Controls.Add(btnCopy); - if (fullMode) - { - var btnChangePw = MakeButton("암호 변경"); - btnChangePw.Click += (s, e) => ChangePassword(); - buttons.Controls.Add(btnChangePw); - } + _btnChangePw = MakeButton("암호 변경"); + _btnChangePw.Click += (s, e) => ChangePassword(); + buttons.Controls.Add(_btnChangePw); + // 화면 하단 바로가기 버튼 (기본 브라우저로 열기) + _links = new FlowLayoutPanel + { + Dock = DockStyle.Bottom, + FlowDirection = FlowDirection.LeftToRight, + Height = 50, + Padding = new Padding(14, 4, 10, 10) + }; + + var lblLinks = new Label + { + Text = "바로가기:", + AutoSize = true, + Margin = new Padding(0, 9, 6, 0) + }; + var btnGetCidMe = MakeLinkButton("GETCID.ME", "https://getcid.me/"); + var btnGetCidInfo = MakeLinkButton("GETCID.INFO", "https://getcid.info/"); + var btnGetCidUs = MakeLinkButton("GETCID.US", "https://getcid.us/"); + + _links.Controls.Add(lblLinks); + _links.Controls.Add(btnGetCidMe); + _links.Controls.Add(btnGetCidInfo); + _links.Controls.Add(btnGetCidUs); + + // 전화 인증 (slmgr 명령어 방식 — slui 4 창이 막혀 있어도 동작) + _phone = new FlowLayoutPanel + { + Dock = DockStyle.Bottom, + FlowDirection = FlowDirection.LeftToRight, + Height = 44, + Padding = new Padding(14, 4, 10, 0) + }; + var lblPhone = new Label + { + Text = "전화 인증:", + AutoSize = true, + Margin = new Padding(0, 9, 6, 0) + }; + var btnShowIid = MakeActionButton("① 설치 ID 보기"); + btnShowIid.Click += (s, e) => ShowInstallationId(); + var btnEnterCid = MakeActionButton("② 확인 ID 입력"); + btnEnterCid.Click += (s, e) => EnterConfirmationId(); + + _phone.Controls.Add(lblPhone); + _phone.Controls.Add(btnShowIid); + _phone.Controls.Add(btnEnterCid); + + var tip = new ToolTip(); + tip.SetToolTip(btnShowIid, "설치 ID(Installation ID)를 확인합니다 (slmgr /dti)"); + tip.SetToolTip(btnEnterCid, "받은 확인 ID(Confirmation ID)를 입력해 인증합니다 (slmgr /atp, 관리자 권한 필요)"); + tip.SetToolTip(btnGetCidMe, "https://getcid.me/"); + tip.SetToolTip(btnGetCidInfo, "https://getcid.info/"); + tip.SetToolTip(btnGetCidUs, "https://getcid.us/"); + + // Dock=Bottom 은 나중에 추가한 것이 더 아래에 배치됨 Controls.Add(table); Controls.Add(buttons); - - if (fullMode) - { - // 화면 하단 바로가기 버튼 (기본 브라우저로 열기) - var links = new FlowLayoutPanel - { - Dock = DockStyle.Bottom, - FlowDirection = FlowDirection.LeftToRight, - Height = 50, - Padding = new Padding(14, 4, 10, 10) - }; - - var lblLinks = new Label - { - Text = "바로가기:", - AutoSize = true, - Margin = new Padding(0, 9, 6, 0) - }; - var btnGetCidMe = MakeLinkButton("GETCID.ME", "https://getcid.me/"); - var btnGetCidInfo = MakeLinkButton("GETCID.INFO", "https://getcid.info/"); - var btnGetCidUs = MakeLinkButton("GETCID.US", "https://getcid.us/"); - - links.Controls.Add(lblLinks); - links.Controls.Add(btnGetCidMe); - links.Controls.Add(btnGetCidInfo); - links.Controls.Add(btnGetCidUs); - - // 전화 인증 (slmgr 명령어 방식 — slui 4 창이 막혀 있어도 동작) - var phone = new FlowLayoutPanel - { - Dock = DockStyle.Bottom, - FlowDirection = FlowDirection.LeftToRight, - Height = 44, - Padding = new Padding(14, 4, 10, 0) - }; - var lblPhone = new Label - { - Text = "전화 인증:", - AutoSize = true, - Margin = new Padding(0, 9, 6, 0) - }; - var btnShowIid = MakeActionButton("① 설치 ID 보기"); - btnShowIid.Click += (s, e) => ShowInstallationId(); - var btnEnterCid = MakeActionButton("② 확인 ID 입력"); - btnEnterCid.Click += (s, e) => EnterConfirmationId(); - - phone.Controls.Add(lblPhone); - phone.Controls.Add(btnShowIid); - phone.Controls.Add(btnEnterCid); - - var tip = new ToolTip(); - tip.SetToolTip(btnShowIid, "설치 ID(Installation ID)를 확인합니다 (slmgr /dti)"); - tip.SetToolTip(btnEnterCid, "받은 확인 ID(Confirmation ID)를 입력해 인증합니다 (slmgr /atp, 관리자 권한 필요)"); - tip.SetToolTip(btnGetCidMe, "https://getcid.me/"); - tip.SetToolTip(btnGetCidInfo, "https://getcid.info/"); - tip.SetToolTip(btnGetCidUs, "https://getcid.us/"); - - // Dock=Bottom 은 나중에 추가한 것이 더 아래에 배치됨 - Controls.Add(phone); - Controls.Add(links); - } - + Controls.Add(_phone); + Controls.Add(_links); CancelButton = btnClose; try @@ -560,6 +619,44 @@ namespace WindowsKeyInfo _txtProductId.Text = _info.ProductId; _txtInstalledKey.Text = _info.InstalledKey; _txtBackupKey.Text = _info.BackupKey; + + // 처음 표시 모드 (DPI 배율 적용 전이므로 기준 크기로 지정) + _fullMode = fullMode; + _btnChangePw.Visible = fullMode; + _phone.Visible = fullMode; + _links.Visible = fullMode; + ClientSize = new Size(560, fullMode ? 344 : 240); + } + + /// 제한 모드 → 전체 모드 전환 (실행 중, DPI 배율이 적용된 실제 크기 기준으로 창 높이 증가) + private void SwitchToFullMode() + { + if (_fullMode) return; + _fullMode = true; + + float scale = _phone.Height / 44f; // 현재 DPI 배율 + int extra = _phone.Height + _links.Height + (int)Math.Round(10 * scale); // 344 - 240 = 104 = 44 + 50 + 10 + + SuspendLayout(); + _btnChangePw.Visible = true; + _phone.Visible = true; + _links.Visible = true; + ClientSize = new Size(ClientSize.Width, ClientSize.Height + extra); + ResumeLayout(true); + } + + /// 제한 모드에서 Ctrl+Shift+P → 마스터 암호 입력 창 + protected override bool ProcessCmdKey(ref Message msg, Keys keyData) + { + if (keyData == UnlockShortcut && !_fullMode) + { + using (var dlg = new MasterPasswordDialog()) + { + if (dlg.ShowDialog(this) == DialogResult.OK) SwitchToFullMode(); + } + return true; + } + return base.ProcessCmdKey(ref msg, keyData); } private static TextBox AddRow(TableLayoutPanel table, int row, string caption, bool monospace)