首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> .NET 網頁程式設計 >> C# >> 關於C#

C# 讀取硬體資訊:MAC 位址與硬碟序號完整實作

     編輯:關於C#

前言

取得裝置唯一識別碼常用於軟體授權、資產管理、裝置追蹤。本文示範使用 WMI (System.Management) 查詢網卡 MAC 位址與硬碟卷標序號,並處理多網卡、虛擬網卡、權限等實務問題。

核心實作類別


using System;
using System.Management;
using System.Net.NetworkInformation;
using System.Linq;

public class HardwareInfoReader
{
    /// 
    /// 取得第一張啟用的實體網卡 MAC 位址
    /// 
    public string GetMacAddress()
    {
        try
        {
            // 方法一:NetworkInformation (較簡單、無需 WMI 權限)
            var nics = NetworkInterface.GetAllNetworkInterfaces()
                .Where(n => n.OperationalStatus == OperationalStatus.Up
                         && n.NetworkInterfaceType != NetworkInterfaceType.Loopback
                         && n.NetworkInterfaceType != NetworkInterfaceType.Tunnel
                         && !n.Description.Contains("Virtual")
                         && !n.Description.Contains("VMware")
                         && !n.Description.Contains("Hyper-V"))
                .OrderBy(n => n.NetworkInterfaceType); // 優先乙太網路
            
            foreach (var nic in nics)
            {
                string mac = nic.GetPhysicalAddress().ToString();
                if (!string.IsNullOrEmpty(mac) && mac != "000000000000")
                    return FormatMacAddress(mac);
            }
        }
        catch { }
        
        // 方法二:WMI 備用
        try
        {
            using (var mc = new ManagementClass("Win32_NetworkAdapterConfiguration"))
            using (var moc = mc.GetInstances())
            {
                foreach (ManagementObject mo in moc)
                {
                    if ((bool)mo["IPEnabled"])
                    {
                        string mac = mo["MacAddress"]?.ToString();
                        if (!string.IsNullOrEmpty(mac))
                            return mac.Replace(":", "-").ToUpper();
                    }
                }
            }
        }
        catch { }
        
        return "UNKNOWN";
    }

    private string FormatMacAddress(string mac)
    {
        if (mac.Length == 12)
            return string.Join("-", Enumerable.Range(0, 6).Select(i => mac.Substring(i * 2, 2)));
        return mac;
    }

    /// 
    /// 取得系統碟 (C:) 卷標序號
    /// 
    public string GetDiskVolumeSerialNumber(string driveLetter = "C")
    {
        try
        {
            // 方法一:WMI 查詢
            using (var mo = new ManagementObject($"win32_logicaldisk.deviceid=\"{driveLetter}:\""))
            {
                mo.Get();
                return mo.GetPropertyValue("VolumeSerialNumber")?.ToString()?.ToUpper() ?? "UNKNOWN";
            }
        }
        catch { }
        
        try
        {
            // 方法二:Kernel32 GetVolumeInformation (P/Invoke)
            return GetVolumeSerialNumberPInvoke(driveLetter);
        }
        catch { }
        
        return "UNKNOWN";
    }

    [System.Runtime.InteropServices.DllImport("kernel32.dll", CharSet = System.Runtime.InteropServices.CharSet.Auto)]
    private static extern bool GetVolumeInformation(
        string lpRootPathName,
        System.Text.StringBuilder lpVolumeNameBuffer,
        int nVolumeNameSize,
        out uint lpVolumeSerialNumber,
        out uint lpMaximumComponentLength,
        out uint lpFileSystemFlags,
        System.Text.StringBuilder lpFileSystemNameBuffer,
        int nFileSystemNameSize);

    private string GetVolumeSerialNumberPInvoke(string driveLetter)
    {
        uint serialNumber = 0;
        uint maxCompLen = 0;
        uint fsFlags = 0;
        var volName = new System.Text.StringBuilder(256);
        var fsName = new System.Text.StringBuilder(256);
        
        bool result = GetVolumeInformation(
            $"{driveLetter}:\\", volName, volName.Capacity,
            out serialNumber, out maxCompLen, out fsFlags, fsName, fsName.Capacity);
        
        return result ? serialNumber.ToString("X8") : "UNKNOWN";
    }

    /// 
    /// 取得 CPU ID
    /// 
    public string GetCpuId()
    {
        try
        {
            using (var mc = new ManagementClass("Win32_Processor"))
            using (var moc = mc.GetInstances())
            {
                foreach (ManagementObject mo in moc)
                {
                    return mo["ProcessorId"]?.ToString()?.Trim() ?? "UNKNOWN";
                }
            }
        }
        catch { }
        return "UNKNOWN";
    }

    /// 
    /// 取得主機板序號
    /// 
    public string GetMotherboardSerial()
    {
        try
        {
            using (var mc = new ManagementClass("Win32_BaseBoard"))
            using (var moc = mc.GetInstances())
            {
                foreach (ManagementObject mo in moc)
                {
                    return mo["SerialNumber"]?.ToString()?.Trim() ?? "UNKNOWN";
                }
            }
        }
        catch { }
        return "UNKNOWN";
    }

    /// 
    /// 取得 BIOS 序號
    /// 
    public string GetBiosSerial()
    {
        try
        {
            using (var mc = new ManagementClass("Win32_BIOS"))
            using (var moc = mc.GetInstances())
            {
                foreach (ManagementObject mo in moc)
                {
                    return mo["SerialNumber"]?.ToString()?.Trim() ?? "UNKNOWN";
                }
            }
        }
        catch { }
        return "UNKNOWN";
    }
}

組合裝置指紋


public class DeviceFingerprint
{
    private readonly HardwareInfoReader _reader = new HardwareInfoReader();

    public string GenerateFingerprint()
    {
        var parts = new[]
        {
            _reader.GetCpuId(),
            _reader.GetMotherboardSerial(),
            _reader.GetBiosSerial(),
            _reader.GetDiskVolumeSerialNumber(),
            _reader.GetMacAddress()
        };
        
        // 過濾未知值,組合並雜湊
        string combined = string.Join("|", parts.Where(p => p != "UNKNOWN"));
        return ComputeHash(combined);
    }

    private string ComputeHash(string input)
    {
        using (var sha256 = System.Security.Cryptography.SHA256.Create())
        {
            byte[] hash = sha256.ComputeHash(System.Text.Encoding.UTF8.GetBytes(input));
            return BitConverter.ToString(hash).Replace("-", "").Substring(0, 16);
        }
    }
}

授權驗證應用範例


public class LicenseValidator
{
    private readonly DeviceFingerprint _fingerprint = new DeviceFingerprint();
    private readonly string _expectedFingerprint; // 從授權檔/伺服器取得

    public LicenseValidator(string expectedFingerprint)
    {
        _expectedFingerprint = expectedFingerprint;
    }

    public bool Validate()
    {
        string current = _fingerprint.GenerateFingerprint();
        return string.Equals(current, _expectedFingerprint, StringComparison.OrdinalIgnoreCase);
    }

    public LicenseInfo GetLicenseInfo()
    {
        return new LicenseInfo
        {
            IsValid = Validate(),
            CurrentFingerprint = _fingerprint.GenerateFingerprint(),
            ExpectedFingerprint = _expectedFingerprint,
            HardwareDetails = new HardwareInfoReader().GetAllDetails()
        };
    }
}

public class LicenseInfo
{
    public bool IsValid { get; set; }
    public string CurrentFingerprint { get; set; }
    public string ExpectedFingerprint { get; set; }
    public HardwareDetails HardwareDetails { get; set; }
}

public class HardwareDetails
{
    public string CpuId { get; set; }
    public string MotherboardSerial { get; set; }
    public string BiosSerial { get; set; }
    public string DiskSerial { get; set; }
    public string MacAddress { get; set; }
}

權限與相容性注意事項

項目需求解決方案
WMI 查詢管理員權限或 WMI 權限應用程式清單設定 requireAdministrator,或設定 WMI 命名空間權限
ManagementClass.NET Framework 完整支援.NET Core 需 NuGet: System.Management
虛擬機器MAC/序號可能相同組合多項硬體資訊,或使用雲端驗證
網卡變更更換網卡導致 MAC 變更授權綁定多項硬體,允許部分變更
硬碟重新格式化卷標序號改變改用硬碟實體序號 (Win32_DiskDrive.SerialNumber)

改用實體硬碟序號 (較穩定)


public string GetPhysicalDiskSerial()
{
    try
    {
        using (var mc = new ManagementClass("Win32_DiskDrive"))
        using (var moc = mc.GetInstances())
        {
            foreach (ManagementObject mo in moc)
            {
                // 取得第一顆實體磁碟序號
                return mo["SerialNumber"]?.ToString()?.Trim() ?? "UNKNOWN";
            }
        }
    }
    catch { }
    return "UNKNOWN";
}

非 WMI 替代方案


// 純 .NET Core 跨平台取得 MAC
public string GetMacAddressCrossPlatform()
{
    var nics = NetworkInterface.GetAllNetworkInterfaces()
        .Where(n => n.OperationalStatus == OperationalStatus.Up
                 && n.NetworkInterfaceType != NetworkInterfaceType.Loopback);
    
    foreach (var nic in nics)
    {
        var mac = nic.GetPhysicalAddress().ToString();
        if (!string.IsNullOrEmpty(mac) && mac != "000000000000")
            return FormatMacAddress(mac);
    }
    return "UNKNOWN";
}

// 取得機器名稱 (跨平台)
public string GetMachineName() => Environment.MachineName;

// 取得作業系統資訊
public string GetOsInfo() => $"{Environment.OSVersion} {RuntimeInformation.OSDescription}";

總結

硬體資訊讀取首選 NetworkInterface 取得 MAC (無需 WMI 權限、跨平台),次選 WMI 查詢硬碟/CPU/主機板序號。實務授權建議組合多項硬體特徵產生指紋,並允許部分硬體變更容錯。注意虛擬環境、容器化部署下的硬體資訊可能不唯一,生產環境應搭配雲端驗證機制。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved