針對軟體授權保護需求,設計一套「光碟即金鑰」機制:
核心技術點:System.Management (WMI)、Microsoft.Win32.Registry、WinForms UI。
查詢 Win32_CDROMDrive 類別,屬性 VolumeSerialNumber 即為光碟唯一序號(格式如 ABCD-EF12)。
using System.Management;
public string GetFirstCdRomSerialNumber()
{
using (var searcher = new ManagementObjectSearcher("SELECT VolumeSerialNumber FROM Win32_CDROMDrive"))
{
foreach (ManagementObject drive in searcher.Get())
{
string sn = drive["VolumeSerialNumber"]?.ToString().Trim();
if (!string.IsNullOrEmpty(sn)) return sn;
}
}
return null;
}
Registry.LocalMachine 需管理員權限;建議改用 Registry.CurrentUser 避免 UAC 提示。Software\{Vendor}\{AppName}\CdKey,避免汙染系統區。RegistryView.Registry64 確保 64 位元系統正確讀寫。using Microsoft.Win32;
public bool SaveSerialNumber(string serialNumber)
{
try
{
using (var key = Registry.CurrentUser.CreateSubKey(@"Software\MyApp\CdKey", true))
{
key.SetValue("VolumeSerial", serialNumber, RegistryValueKind.String);
}
return true;
}
catch { return false; }
}
public string LoadSerialNumber()
{
using (var key = Registry.CurrentUser.OpenSubKey(@"Software\MyApp\CdKey"))
{
return key?.GetValue("VolumeSerial") as string;
}
}
using System;
using System.Management;
using System.Windows.Forms;
using Microsoft.Win32;
namespace CdKeyRegistrar
{
public partial class MainForm : Form
{
private const string RegPath = @"Software\MyApp\CdKey";
private const string RegValue = "VolumeSerial";
public MainForm()
{
InitializeComponent();
btnRegister.Text = "開始註冊";
lblStatus.Text = "請插入啟動光碟,點擊按鈕註冊";
}
private void btnRegister_Click(object sender, EventArgs e)
{
if (btnRegister.Text == "開始註冊")
{
btnRegister.Enabled = false;
btnRegister.Text = "註冊中...";
RegisterCdKey();
}
else
{
Close();
}
}
private void RegisterCdKey()
{
string serial = GetFirstCdRomSerialNumber();
if (serial == null)
{
ShowMessage("未偵測到光碟或無法讀取序號,請確認光碟機狀態。", "錯誤", MessageBoxIcon.Error);
ResetButton();
return;
}
bool success = SaveToRegistry(serial);
if (success)
{
lblStatus.Text = $"註冊成功!序號:{serial}";
btnRegister.Text = "關閉";
}
else
{
ShowMessage("寫入註冊表失敗,請以管理員身分執行或檢查權限。", "失敗", MessageBoxIcon.Error);
ResetButton();
}
}
private string GetFirstCdRomSerialNumber()
{
try
{
using (var mc = new ManagementClass("Win32_CDROMDrive"))
{
foreach (ManagementObject mo in mc.GetInstances())
{
string sn = mo["VolumeSerialNumber"]?.ToString().Trim();
if (!string.IsNullOrEmpty(sn)) return sn;
}
}
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine($"WMI Error: {ex.Message}");
}
return null;
}
private bool SaveToRegistry(string serial)
{
try
{
using (var key = Registry.CurrentUser.CreateSubKey(RegPath, true))
{
key.SetValue(RegValue, serial, RegistryValueKind.String);
}
return true;
}
catch { return false; }
}
private void ShowMessage(string text, string caption, MessageBoxIcon icon)
{
MessageBox.Show(this, text, caption, MessageBoxButtons.OK, icon);
}
private void ResetButton()
{
btnRegister.Enabled = true;
btnRegister.Text = "開始註冊";
}
}
}
public bool ValidateCdKey()
{
string currentSerial = GetFirstCdRomSerialNumber();
string savedSerial = LoadSerialNumber();
if (string.IsNullOrEmpty(savedSerial))
{
ShowMessage("尚未註冊啟動光碟,請先執行註冊程式。", "未授權", MessageBoxIcon.Warning);
return false;
}
if (string.IsNullOrEmpty(currentSerial))
{
ShowMessage("請插入啟動光碟後再啟動程式。", "需要光碟", MessageBoxIcon.Information);
return false;
}
if (string.Equals(currentSerial, savedSerial, StringComparison.OrdinalIgnoreCase))
return true;
ShowMessage("光碟不匹配,請插入正確的啟動光碟。", "驗證失敗", MessageBoxIcon.Error);
return false;
}
| 風險點 | 緩解措施 |
|---|---|
| 註冊表可被竄改/複製 | 序號加密儲存(AES + 機器碼綁定),或改用檔案授權 + 數位簽章 |
| 虛擬光碟機掛載 ISO 繞過 | 檢測光碟機型號 MediaType、Capabilities 判斷實體光碟機 |
| WMI 查詢需權限/服務啟動 | 捕獲 ManagementException,提供降級方案(如讀取實體檔案特徵) |
| 註冊表路徑洩露資訊 | 使用雜湊路徑、加密 Value、混淆 Key 名稱 |
光碟授權已屬傳統模式,現代軟體建議採用:
Portable.Licensing, LicenseManager, SKGL本文提供的方案適合離線環境、舊系統維護或教學示範;新專案請優先評估雲端授權架構。