From 29f799ca0817ffb04d27ff1d746c7de7493dbda1 Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 21 Oct 2026 04:59:10 +0000 Subject: [PATCH] =?UTF-8?q?=EC=B5=9C=EC=B4=88=20=EC=BB=A4=EB=B0=8B:=20Wind?= =?UTF-8?q?ows=20=EC=A0=9C=ED=92=88=20=ED=82=A4=20=EB=B0=B1=EC=97=85=20?= =?UTF-8?q?=ED=94=84=EB=A1=9C=EA=B7=B8=EB=9E=A8=20(WinForms,=20.NET=20Fram?= =?UTF-8?q?ework=204.8)?= 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 --- .gitignore | 11 + AssemblyInfo.cs | 7 + Program.cs | 623 ++++++++++++++++++++++++++++++++++++++++++ README.md | 28 ++ WindowsKeyInfo.csproj | 24 ++ 5 files changed, 693 insertions(+) create mode 100644 .gitignore create mode 100644 AssemblyInfo.cs create mode 100644 Program.cs create mode 100644 README.md create mode 100644 WindowsKeyInfo.csproj diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..9e1462b --- /dev/null +++ b/.gitignore @@ -0,0 +1,11 @@ +# 빌드 결과물 +bin/ +obj/ + +# Visual Studio +.vs/ +*.user +*.suo + +# 프로그램이 저장한 키 정보 파일 (개인 정보 — 커밋 금지) +WindowsKeyInfo_*.txt diff --git a/AssemblyInfo.cs b/AssemblyInfo.cs new file mode 100644 index 0000000..57f0265 --- /dev/null +++ b/AssemblyInfo.cs @@ -0,0 +1,7 @@ +using System.Reflection; +using System.Runtime.Versioning; +[assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")] +[assembly: AssemblyTitle("Windows 제품 키 백업")] +[assembly: AssemblyProduct("WindowsKeyInfo")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/Program.cs b/Program.cs new file mode 100644 index 0000000..28d815d --- /dev/null +++ b/Program.cs @@ -0,0 +1,623 @@ +using System; +using System.Drawing; +using System.IO; +using System.Text; +using System.Windows.Forms; +using Microsoft.Win32; + +namespace WindowsKeyInfo +{ + internal static class Program + { + [STAThread] + private static void Main() + { + Application.EnableVisualStyles(); + Application.SetCompatibleTextRenderingDefault(false); + Application.Run(new MainForm()); + } + } + + /// 레지스트리에서 윈도우 키 정보를 읽어오는 클래스 (원본 VBScript 로직 이식) + internal static class KeyReader + { + private const string CurrentVersionPath = @"SOFTWARE\Microsoft\Windows NT\CurrentVersion"; + private const string ComputerNamePath = @"SYSTEM\CurrentControlSet\Control\ComputerName\ComputerName"; + private const string NotFound = "없음"; + + public sealed class KeyInfo + { + public string ComputerName; + public string ProductName; + public string ProductId; + public string InstalledKey; + public string BackupKey; + } + + public static KeyInfo Read() + { + var info = new KeyInfo(); + + // 64비트 윈도우에서는 반드시 64비트 레지스트리 뷰로 읽어야 DigitalProductId가 정상적으로 보임 + RegistryView view = Environment.Is64BitOperatingSystem ? RegistryView.Registry64 : RegistryView.Default; + + using (RegistryKey hklm = RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, view)) + { + using (RegistryKey cv = hklm.OpenSubKey(CurrentVersionPath)) + { + info.ProductName = GetString(cv, "ProductName"); + info.ProductId = GetString(cv, "ProductId"); + + byte[] digitalId = cv == null ? null : cv.GetValue("DigitalProductId") as byte[]; + info.InstalledKey = ConvertToKey(digitalId); + } + + using (RegistryKey spp = hklm.OpenSubKey(CurrentVersionPath + @"\SoftwareProtectionPlatform")) + { + info.BackupKey = GetString(spp, "BackupProductKeyDefault"); + } + + using (RegistryKey cn = hklm.OpenSubKey(ComputerNamePath)) + { + string name = GetString(cn, "ComputerName"); + info.ComputerName = name == NotFound ? Environment.MachineName : name; + } + } + + return info; + } + + private static string GetString(RegistryKey key, string valueName) + { + if (key == null) return NotFound; + object value = key.GetValue(valueName); + string text = value == null ? null : value.ToString(); + return string.IsNullOrWhiteSpace(text) ? NotFound : text; + } + + /// DigitalProductId(바이너리) → 25자리 제품 키 변환 + public static string ConvertToKey(byte[] digitalId) + { + const int keyOffset = 52; + const string maps = "BCDFGHJKMPQRTVWXY2346789"; + + if (digitalId == null || digitalId.Length < keyOffset + 15) return NotFound; + + byte[] key = (byte[])digitalId.Clone(); // 원본 배열 보존 + + // Windows 8 이상 형식인지 확인 + int isWin8 = (key[66] / 6) & 1; + key[66] = (byte)((key[66] & 0xF7) | ((isWin8 & 2) * 4)); + + string keyOutput = string.Empty; + int last = 0; + + for (int i = 24; i >= 0; i--) + { + int current = 0; + for (int j = 14; j >= 0; j--) + { + current = current * 256; + current = key[j + keyOffset] + current; + key[j + keyOffset] = (byte)(current / 24); + current = current % 24; + } + keyOutput = maps[current] + keyOutput; + last = current; + } + + // Windows 8 이상 키는 'N' 문자 위치를 보정 + if (isWin8 == 1) + { + string keypart1 = keyOutput.Substring(1, last); + string keypart2 = keyOutput.Substring(last + 1); + keyOutput = keypart1 + "N" + keypart2; + } + + return keyOutput.Substring(0, 5) + "-" + + keyOutput.Substring(5, 5) + "-" + + keyOutput.Substring(10, 5) + "-" + + keyOutput.Substring(15, 5) + "-" + + keyOutput.Substring(20, 5); + } + } + + /// 메인 창 (WinForms) + internal sealed class MainForm : Form + { + private readonly KeyReader.KeyInfo _info; + private readonly TextBox _txtProductName; + private readonly TextBox _txtProductId; + private readonly TextBox _txtInstalledKey; + private readonly TextBox _txtBackupKey; + + public MainForm() + { + Text = "Windows 제품 키 백업"; + FormBorderStyle = FormBorderStyle.FixedDialog; + MaximizeBox = false; + StartPosition = FormStartPosition.CenterScreen; + AutoScaleMode = AutoScaleMode.Dpi; + Font = new Font("맑은 고딕", 9.5f); + ClientSize = new Size(560, 344); + + var table = new TableLayoutPanel + { + Dock = DockStyle.Top, + Height = 180, + Padding = new Padding(14, 14, 14, 0), + ColumnCount = 2, + RowCount = 4 + }; + table.ColumnStyles.Add(new ColumnStyle(SizeType.Absolute, 130)); + table.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 100)); + for (int r = 0; r < 4; r++) table.RowStyles.Add(new RowStyle(SizeType.Percent, 25)); + + _txtProductName = AddRow(table, 0, "제품 이름", false); + _txtProductId = AddRow(table, 1, "제품 ID", false); + _txtInstalledKey = AddRow(table, 2, "설치된 키", true); + _txtBackupKey = AddRow(table, 3, "백업 키", true); + + var buttons = new FlowLayoutPanel + { + Dock = DockStyle.Bottom, + FlowDirection = FlowDirection.RightToLeft, + Height = 56, + Padding = new Padding(10, 10, 10, 10) + }; + + var btnClose = MakeButton("닫기"); + btnClose.Click += (s, e) => Close(); + var btnSave = MakeButton("파일로 저장"); + btnSave.Click += (s, e) => SaveToFile(); + var btnCopy = MakeButton("복사"); + btnCopy.Click += (s, e) => CopyToClipboard(); + + buttons.Controls.Add(btnClose); + buttons.Controls.Add(btnSave); + buttons.Controls.Add(btnCopy); + + // 화면 하단 바로가기 버튼 (기본 브라우저로 열기) + 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(table); + Controls.Add(buttons); + Controls.Add(phone); + Controls.Add(links); + CancelButton = btnClose; + + try + { + _info = KeyReader.Read(); + } + catch (Exception ex) + { + MessageBox.Show("레지스트리에서 정보를 읽지 못했습니다.\n\n" + ex.Message, + Text, MessageBoxButtons.OK, MessageBoxIcon.Error); + _info = new KeyReader.KeyInfo + { + ComputerName = Environment.MachineName, + ProductName = "없음", + ProductId = "없음", + InstalledKey = "없음", + BackupKey = "없음" + }; + } + + _txtProductName.Text = _info.ProductName; + _txtProductId.Text = _info.ProductId; + _txtInstalledKey.Text = _info.InstalledKey; + _txtBackupKey.Text = _info.BackupKey; + } + + private static TextBox AddRow(TableLayoutPanel table, int row, string caption, bool monospace) + { + var label = new Label + { + Text = caption, + Dock = DockStyle.Fill, + TextAlign = ContentAlignment.MiddleLeft + }; + var box = new TextBox + { + ReadOnly = true, + Dock = DockStyle.Fill, + Anchor = AnchorStyles.Left | AnchorStyles.Right, + BackColor = SystemColors.Window + }; + if (monospace) box.Font = new Font("Consolas", 11f, FontStyle.Bold); + + table.Controls.Add(label, 0, row); + table.Controls.Add(box, 1, row); + return box; + } + + private static Button MakeButton(string text) + { + return new Button + { + Text = text, + Width = 110, + Height = 32, + Margin = new Padding(6, 0, 0, 0) + }; + } + + private Button MakeLinkButton(string text, string url) + { + var btn = new Button + { + Text = text, + Width = 130, + Height = 32, + Margin = new Padding(0, 0, 8, 0), + Cursor = Cursors.Hand + }; + btn.Click += (s, e) => OpenUrl(url); + return btn; + } + + private void OpenUrl(string url) + { + try + { + System.Diagnostics.Process.Start(new System.Diagnostics.ProcessStartInfo(url) { UseShellExecute = true }); + } + catch (Exception ex) + { + MessageBox.Show("브라우저를 열지 못했습니다.\n\n" + url + "\n\n" + ex.Message, Text, + MessageBoxButtons.OK, MessageBoxIcon.Error); + } + } + + private static Button MakeActionButton(string text) + { + return new Button + { + Text = text, + Width = 140, + Height = 32, + Margin = new Padding(0, 0, 8, 0), + Cursor = Cursors.Hand + }; + } + + /// System32 경로 (32비트 프로세스면 Sysnative 사용) + private static string SystemDir() + { + string windir = Environment.GetFolderPath(Environment.SpecialFolder.Windows); + string sysnative = Path.Combine(windir, "Sysnative"); + if (!Environment.Is64BitProcess && Directory.Exists(sysnative)) return sysnative; + return Path.Combine(windir, "System32"); + } + + /// ① 설치 ID 보기: cscript slmgr.vbs /dti 실행 후 결과 표시 + private void ShowInstallationId() + { + string sys = SystemDir(); + string cscript = Path.Combine(sys, "cscript.exe"); + string slmgr = Path.Combine(sys, "slmgr.vbs"); + string output; + + Cursor = Cursors.WaitCursor; + try + { + var psi = new System.Diagnostics.ProcessStartInfo(cscript, "//nologo \"" + slmgr + "\" /dti") + { + UseShellExecute = false, + RedirectStandardOutput = true, + RedirectStandardError = true, + CreateNoWindow = true, + StandardOutputEncoding = Encoding.GetEncoding( + System.Globalization.CultureInfo.CurrentCulture.TextInfo.OEMCodePage) + }; + using (var p = System.Diagnostics.Process.Start(psi)) + { + output = p.StandardOutput.ReadToEnd() + p.StandardError.ReadToEnd(); + p.WaitForExit(60000); + } + } + catch (Exception ex) + { + Cursor = Cursors.Default; + MessageBox.Show("설치 ID를 가져오지 못했습니다.\n\n" + ex.Message, Text, + MessageBoxButtons.OK, MessageBoxIcon.Error); + return; + } + Cursor = Cursors.Default; + + // 출력에서 가장 긴 숫자열(설치 ID, 보통 63자리)을 추출 + string iid = null; + foreach (System.Text.RegularExpressions.Match m in + System.Text.RegularExpressions.Regex.Matches(output, @"\d{40,}")) + { + if (iid == null || m.Value.Length > iid.Length) iid = m.Value; + } + + if (iid == null) + { + MessageBox.Show("설치 ID를 찾지 못했습니다. 윈도우가 보낸 메시지:\n\n" + output.Trim(), Text, + MessageBoxButtons.OK, MessageBoxIcon.Warning); + return; + } + + using (var dlg = new InstallationIdDialog(iid)) + { + dlg.ShowDialog(this); + } + } + + /// ② 확인 ID 입력: 관리자 권한으로 slmgr.vbs /atp 확인ID 실행 + private void EnterConfirmationId() + { + string cid; + using (var dlg = new ConfirmationIdDialog()) + { + if (dlg.ShowDialog(this) != DialogResult.OK) return; + cid = dlg.ConfirmationId; + } + + string sys = SystemDir(); + string wscript = Path.Combine(sys, "wscript.exe"); + string slmgr = Path.Combine(sys, "slmgr.vbs"); + + try + { + // wscript로 실행하면 slmgr가 결과(성공/오류)를 자체 메시지 창으로 보여 줌 + var psi = new System.Diagnostics.ProcessStartInfo(wscript, "\"" + slmgr + "\" /atp " + cid) + { + UseShellExecute = true, + Verb = "runas" // 관리자 권한 요청 (UAC 창) + }; + System.Diagnostics.Process.Start(psi); + } + catch (System.ComponentModel.Win32Exception ex) when (ex.NativeErrorCode == 1223) + { + MessageBox.Show("관리자 권한 요청이 취소되었습니다.\n확인 ID 입력에는 관리자 권한이 필요합니다.", Text, + MessageBoxButtons.OK, MessageBoxIcon.Information); + } + catch (Exception ex) + { + MessageBox.Show("확인 ID 입력을 실행하지 못했습니다.\n\n" + ex.Message, Text, + MessageBoxButtons.OK, MessageBoxIcon.Error); + } + } + + /// 원본 스크립트와 같은 형식의 텍스트 + private string BuildText() + { + var sb = new StringBuilder(); + sb.AppendLine("Product Name: " + _info.ProductName); + sb.AppendLine("Product ID: " + _info.ProductId); + sb.AppendLine("Orginal Installed Key: " + _info.InstalledKey); + sb.AppendLine("Backup Installed Key: " + _info.BackupKey); + return sb.ToString(); + } + + private void CopyToClipboard() + { + try + { + Clipboard.SetText(BuildText()); + MessageBox.Show("키 정보를 클립보드에 복사했습니다.", Text, + MessageBoxButtons.OK, MessageBoxIcon.Information); + } + catch (Exception ex) + { + MessageBox.Show("클립보드 복사에 실패했습니다.\n\n" + ex.Message, Text, + MessageBoxButtons.OK, MessageBoxIcon.Error); + } + } + + /// exe가 있는 폴더에 WindowsKeyInfo_컴퓨터이름_사용자이름.txt 로 저장 + private void SaveToFile() + { + string computer = (_info.ComputerName ?? Environment.MachineName).Replace(" ", ""); + string user = Environment.UserName; + string fileName = "WindowsKeyInfo_" + computer + "_" + user + ".txt"; + string folder = AppDomain.CurrentDomain.BaseDirectory; + string fullPath = Path.Combine(folder, fileName); + + try + { + File.WriteAllText(fullPath, BuildText(), new UTF8Encoding(true)); + MessageBox.Show("저장했습니다.\n\n" + fullPath, Text, + MessageBoxButtons.OK, MessageBoxIcon.Information); + } + catch (UnauthorizedAccessException) + { + MessageBox.Show("이 폴더에 쓸 권한이 없습니다.\n\n" + folder + + "\n\nexe를 바탕화면이나 문서 폴더 등 쓰기 가능한 곳으로 옮긴 뒤 다시 시도해 주세요.", + Text, MessageBoxButtons.OK, MessageBoxIcon.Warning); + } + catch (Exception ex) + { + MessageBox.Show("파일 저장에 실패했습니다.\n\n" + ex.Message, Text, + MessageBoxButtons.OK, MessageBoxIcon.Error); + } + } + } + + /// 설치 ID 표시 창 (7자리씩 9묶음) + internal sealed class InstallationIdDialog : Form + { + public InstallationIdDialog(string iid) + { + Text = "설치 ID (Installation ID)"; + FormBorderStyle = FormBorderStyle.FixedDialog; + MaximizeBox = false; + MinimizeBox = false; + ShowInTaskbar = false; + StartPosition = FormStartPosition.CenterParent; + AutoScaleMode = AutoScaleMode.Dpi; + Font = new Font("맑은 고딕", 9.5f); + ClientSize = new Size(620, 190); + + var sbGrouped = new StringBuilder(); + for (int i = 0; i < iid.Length; i += 7) + { + if (i > 0) sbGrouped.Append(' '); + sbGrouped.Append(iid.Substring(i, Math.Min(7, iid.Length - i))); + } + string grouped = sbGrouped.ToString(); + + var lbl = new Label + { + Text = "아래 설치 ID를 전화 자동 응답에 입력하거나 확인 ID 발급처에 전달하세요.\n" + + "받은 확인 ID는 메인 창의 [② 확인 ID 입력]에 넣으면 됩니다.", + Location = new Point(14, 12), + Size = new Size(592, 42) + }; + var box = new TextBox + { + Text = grouped, + ReadOnly = true, + Multiline = true, + Location = new Point(14, 60), + Size = new Size(592, 64), + Font = new Font("Consolas", 12f, FontStyle.Bold), + BackColor = SystemColors.Window + }; + + var btnCopy = new Button { Text = "복사 (숫자만)", Location = new Point(366, 140), Size = new Size(120, 32) }; + btnCopy.Click += (s, e) => + { + try + { + Clipboard.SetText(iid); + MessageBox.Show("설치 ID를 클립보드에 복사했습니다.", Text, MessageBoxButtons.OK, MessageBoxIcon.Information); + } + catch (Exception ex) + { + MessageBox.Show("복사에 실패했습니다.\n\n" + ex.Message, Text, MessageBoxButtons.OK, MessageBoxIcon.Error); + } + }; + var btnClose = new Button { Text = "닫기", Location = new Point(494, 140), Size = new Size(112, 32), DialogResult = DialogResult.OK }; + + Controls.Add(lbl); + Controls.Add(box); + Controls.Add(btnCopy); + Controls.Add(btnClose); + AcceptButton = btnClose; + CancelButton = btnClose; + } + } + + /// 확인 ID 입력 창 (48자리 숫자, 하이픈/공백 허용) + internal sealed class ConfirmationIdDialog : Form + { + private readonly TextBox _box; + public string ConfirmationId { get; private set; } + + public ConfirmationIdDialog() + { + Text = "확인 ID (Confirmation ID) 입력"; + FormBorderStyle = FormBorderStyle.FixedDialog; + MaximizeBox = false; + MinimizeBox = false; + ShowInTaskbar = false; + StartPosition = FormStartPosition.CenterParent; + AutoScaleMode = AutoScaleMode.Dpi; + Font = new Font("맑은 고딕", 9.5f); + ClientSize = new Size(620, 170); + + var lbl = new Label + { + Text = "받은 확인 ID 48자리를 입력하세요. 하이픈(-)이나 공백은 넣어도 자동으로 제거됩니다.\n" + + "[인증]을 누르면 관리자 권한 확인 창이 뜨고, 결과는 윈도우 메시지 창으로 표시됩니다.", + Location = new Point(14, 12), + Size = new Size(592, 42) + }; + _box = new TextBox + { + Location = new Point(14, 62), + Size = new Size(592, 30), + Font = new Font("Consolas", 12f, FontStyle.Bold) + }; + + var btnOk = new Button { Text = "인증", Location = new Point(366, 120), Size = new Size(120, 32) }; + btnOk.Click += (s, e) => TryAccept(); + var btnCancel = new Button { Text = "취소", Location = new Point(494, 120), Size = new Size(112, 32), DialogResult = DialogResult.Cancel }; + + Controls.Add(lbl); + Controls.Add(_box); + Controls.Add(btnOk); + Controls.Add(btnCancel); + AcceptButton = btnOk; + CancelButton = btnCancel; + } + + private void TryAccept() + { + var digits = new StringBuilder(); + foreach (char c in _box.Text) + { + if (c >= '0' && c <= '9') digits.Append(c); + } + + if (digits.Length != 48) + { + MessageBox.Show("확인 ID는 숫자 48자리여야 합니다.\n현재 입력된 숫자: " + digits.Length + "자리", Text, + MessageBoxButtons.OK, MessageBoxIcon.Warning); + _box.Focus(); + return; + } + + ConfirmationId = digits.ToString(); + DialogResult = DialogResult.OK; + Close(); + } + } +} diff --git a/README.md b/README.md new file mode 100644 index 0000000..9880a43 --- /dev/null +++ b/README.md @@ -0,0 +1,28 @@ +# WindowsKeyInfo + +Windows 제품 키 정보를 확인하고 백업하는 WinForms 프로그램입니다. + +## 주요 기능 + +- 레지스트리에서 제품 이름, 제품 ID, 설치된 키, 백업 키 확인 +- 키 정보 클립보드 복사 +- 키 정보를 `WindowsKeyInfo_컴퓨터이름_사용자이름.txt` 파일로 저장 (exe와 같은 폴더) +- 전화 인증 지원 + - ① 설치 ID 보기 (`slmgr /dti`) + - ② 확인 ID 입력 (`slmgr /atp`, 관리자 권한 필요) +- 확인 ID 발급 사이트 바로가기 버튼 + +## 요구 사항 + +- Windows +- .NET Framework 4.8 + +## 빌드 + +``` +dotnet build -c Release +``` + +## 주의 + +저장된 `WindowsKeyInfo_*.txt` 파일에는 제품 키가 들어 있으므로 외부에 공유하지 마세요. diff --git a/WindowsKeyInfo.csproj b/WindowsKeyInfo.csproj new file mode 100644 index 0000000..9264b54 --- /dev/null +++ b/WindowsKeyInfo.csproj @@ -0,0 +1,24 @@ + + + + WinExe + net48 + 7.3 + WindowsKeyInfo + WindowsKeyInfo + AnyCPU + false + disable + true + Windows 제품 키 백업 + WindowsKeyInfo + 1.0.0 + none + + + + + + + +