【问题标题】:What is a good unique PC identifier?什么是好的唯一 PC 标识符?
【发布时间】:2011-03-27 11:09:42
【问题描述】:

我一直在查看this tutorial 中的代码,发现它使用My.Computer.Name 来保存不应在计算机之间漫游的设置。但是,用户完全有可能拥有两台名称相同的 PC。例如,如果他们想在每台 PC 上使用相同的用户名,他们很可能会得到两台名为 Username-PC 的 PC。

识别不同PC的好方法有哪些? PC 是否有与之关联的 GUID,或者我是否应该考虑从某些硬件中提取序列号?我不在乎识别是否通过重新安装 Windows 持续存在。

(我链接的教程在 VB.Net 中,但我在 C# 中实现它)

【问题讨论】:

标签: c# .net guid


【解决方案1】:

一些好的标识符:

  • MAC 地址:很容易获得,而且通常是唯一的。但是,它很容易被欺骗/更改,因此这取决于它需要有多独特。
  • CPU 序列号:它在许多旧系统上不可用,但它在那里。查看this MSDN page。它不会改变,但会绑定到计算机。
  • HDD 序列号:它可能不会更改,但如果 HD 出现故障可能会很麻烦。查看this MSDN page

【讨论】:

  • +1 是的,小心 MAC。在 MAC 更改和复制之前,我遇到了很多问题。皮塔饼。
  • 小心:我曾见过(笔记本电脑的)CPU 被烤焦而不得不更换的情况。新 CPU,新 CPUID。
  • @Steven:好点。不过,一般来说,我认为它是系统上“最可靠”的 ID 之一。 CPU 故障相对不常见(与 HD 故障相比)。还应该注意的是,当您开始处理虚拟化时,这个数字可能会被混淆。
  • 避免使用 MAC 的另一个原因:我认为第一个启用的卡的 MAC 地址对于笔记本电脑来说不是一个好主意(我仍然没有解决我的问题,我寻找不同的解决方案)。如果您连接网络电缆然后使用 WiFi,他们会更换卡
  • @bubi 获取 CPU 序列号的 SerialNumber 属性。查看您的 MSDN 链接和 Ctrl+F "SerialNumber" 并阅读有关该属性的操作系统的最后一条说明。
【解决方案2】:

如果您在 Windows 上,HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\ CurrentVersion\ProductId 对于每台机器/每个 Windows 安装都是唯一的。其中与 MAC 地址、Proc SN 和 HD SN 等其他一些答案一样,在 Windows 重新安装/双启动情况之间将保持不变。

【讨论】:

  • 这是 Windows 用来检测它何时安装到另一台机器上的数字吗?
  • 嗯,积极的一面是,任何能够更改此产品 ID 的东西也会对 Windows 本身造成很大的痛苦。 :-)
  • @Rory 我相信执行sysprep 会导致该ID 在您第一次启动系统时发生变化(如果您使用的是系统映像,您真的应该正在使用 sysprep)
  • 我知道已经有一段时间了,但是今天我在这里找到了一个other answer,因此我想知道HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\ CurrentVersion\ProductIdHKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Cryptography\MachineGuid 中的ID 有什么区别?
  • @Skipper ProductId 是用于 Windows 安装的唯一 ID。当您在“local machine”范围内使用“Protect Data”时使用 Windows 加密系统时使用 MachineGuid
【解决方案3】:

这个问题的真正答案:没有这样的东西。

有几种“足够接近”的解决方案,但每一种都有其自身的局限性。

所有硬件 ID - 硬件更改。而且,在许多情况下,您可以更改这些标识符(例如,MAC 欺骗)。

正如我已经评论过的,SID 也不是很好,因为如果计算机是从映像安装的,SID 不会改变。 SID 是由 Windows 安装生成的,如果没有安装 Windows,而是从图像复制,SID 不会改变(尽管由于myth about "security risk" 而重新生成它很常见 - 你不能指望那个) .

计算机名称 - 嗯,如前所述,它们应该是唯一的,但不会以任何方式强制执行。

您可以实施的另一个解决方案是生成您自己的唯一标识符并将其存储在本地(假设您可以这样做)。同样,如果您的计算机已使用您的应用程序进行映像,则此解决方案将不起作用。

最适合您的解决方案实际上取决于您要完成的工作。 我在一个相当大的网络上遇到了同样的问题,就我而言,最好的解决方案是计算机的名称。 如果您绝对确定您的进程不会被映像,我会使用 Guid 生成一个唯一标识符,因为它可能是最安全的。

【讨论】:

    【解决方案4】:

    这是一种唯一标识计算机的方法。 使用 System.Management 获取 Win32_BIOS,您可以从机器的 BIOS 中获取唯一值。

    参见:Win32_BIOS 类,http://msdn.microsoft.com/en-us/library/aa394077.aspx

    using System.Management;
    
    string UniqueMachineId()
    {
        StringBuilder builder = new StringBuilder();
    
        String query = "SELECT * FROM Win32_BIOS";
        ManagementObjectSearcher searcher = new ManagementObjectSearcher(query);
        //  This should only find one
        foreach (ManagementObject item in searcher.Get())
        {
            Object obj = item["Manufacturer"];
            builder.Append(Convert.ToString(obj));
            builder.Append(':');
            obj = item["SerialNumber"];
            builder.Append(Convert.ToString(obj));
        }
    
    return builder.ToString();
    }
    

    类似的逻辑,也可以单步执行“Win32_DiskDrive”; http://msdn.microsoft.com/en-us/library/aa394132.aspx; 并为每个物理驱动器获取“序列号”。在这种情况下,

        foreach (ManagementObject item in searcher.Get())
    

    应该找到多个项目

    【讨论】:

    • 使用 Bios 序列号非常不安全,因为您可能会得到很多这样的(相同的!)字符串:“由 O.E.M. 填写”。
    【解决方案5】:

    取三个半唯一和半常量的标识符。使用规则,即 3 分之 2 足以进行肯定识别。更新偶尔出错的 3 个中的 1 个的注册数据。

    【讨论】:

    • 这是唯一正确的答案。它们需要涵盖潜在可更换部件的范围。我会使用 BIOS 序列号、硬盘 ID、Windows 序列号。
    【解决方案6】:

    使用网卡的 MAC 地址。应该是独一无二的。不过,它可以改变。这取决于您期望用户的恶意程度以及您的应用程序的重要性。

    一些示例代码:

    public string GetMACAddress() {
        ManagementClass mc = new ManagementClass("Win32_NetworkAdapterConfiguration");
        ManagementObjectCollection moc = mc.GetInstances();
    
        string MACAddress = String.Empty;
    
        foreach (ManagementObject mo in moc) {
            if (MACAddress == String.Empty) { // only return MAC Address from first card
                if ((bool)mo["IPEnabled"] == true) MACAddress = mo["MacAddress"].ToString();
            }
            mo.Dispose();
        }
    
        return MACAddress;
    }
    

    【讨论】:

      【解决方案7】:

      您可以使用的一件事是任何网络接口的 MAC。您还可以组合多个信息源。像 HDD 序列号、mac、处理器类型一样从中计算哈希。

      【讨论】:

      • 您使用的来源越多,它就越可靠。但是,您使用的来源越多,您就越容易受到误报的影响。
      【解决方案8】:

      我认为不可能在同一个域中拥有两台同名的 PC。您是否尝试过捕获域名?

      【讨论】:

      • 我在同一个域中有 2 台同名电脑 :-) 我在 xp 和 win 7 之间双启动...虽然现在已经超过 2 周没有启动到 xp 了
      • @SQLMenace:除非它们具有相同的 SID,否则网络仍将它们视为不同的框。
      • 这是同一个盒子..我必须做一些花哨的东西才能从两个盒子进入域
      【解决方案9】:

      看这里:Getting Service Tag from Dell Machine using .net?

      您可以从注册表中获取一些独特的数据。

      【讨论】:

      • Steven,这些特定值是硬件特定的。
      • 硬件更改。磁盘驱动器,CPU,所有这些。当它破裂时,它会被替换。
      【解决方案10】:

      每台计算机都有一个在正常情况下唯一的 SID。

      【讨论】:

      • 为什么这是公认的答案?这个 SID 是什么以及如何获取它?
      • 而且它甚至是不正确的。请从此答案中删除标记。 SID(安全标识符)是安装windows时生成的标识符。两台计算机不太可能具有相同的 SID。 SID 最初的设计目的是为了在网络中唯一地识别用户。为什么还不够好?因为如果计算机不是从头开始安装,而是从映像安装,那么它将具有与原始计算机相同的 SID。这种安装计算机的方法在大型网络中很常见,因此SID在实践中是一个不好的选择。
      • @DiGMi,你说得对,克隆会导致相同的 SID。这就是为什么我说“在正常情况下”。正如您在回复中提到的,一些安装克隆映像的公司确实会重新生成 SID。
      • 问题是没有普遍的正确答案,只有根据情况更好或更差的答案。听起来 OP 更关心避免错误匹配而不是错误不匹配,在这种情况下,CPU 或 HD ID 会工作得最好。
      【解决方案11】:

      在托管网络环境中,最好、最可靠的标识符可能是您创建的,但也有一些缺点。

      一些(很多?)制造商提供了一个实用程序,允许您设置存储在固件中的资产标签。这可能是一个可启动的实用程序,或者它可能在 Windows 中运行,或者它甚至可能内置在固件设置中。这个“标签”是一个任意文本字符串,您可以将其设置为任何您想要的,然后使用WMIWin32_SystemEnclosure class 将其读回...

      string[] selectedProperties = new string[] { "SMBIOSAssetTag" };
      ObjectQuery enclosureQuery = new SelectQuery("Win32_SystemEnclosure", null, selectedProperties);
      
      using (ManagementObjectSearcher enclosureSearcher = new ManagementObjectSearcher(enclosureQuery))
      using (ManagementObjectCollection enclosureCollection = enclosureSearcher.Get())
      {
          foreach (ManagementObject enclosure in enclosureCollection)
          {
              string assetTag = (string) enclosure.GetPropertyValue("SMBIOSAssetTag");
          }
      }
      

      优点:

      • 您可以使用任何您想要的方案(例如,合并日期、部门、递增整数、GUID 等)。
      • 无论制造商如何,您都可以为所有机器使用一种方案,而不必处理制造商特定的方案。
      • 通过自己分配和跟踪标识符,您可以保证它们是唯一的。不依赖制造商设置的标识符意味着制造商内部或制造商之间不存在重复的风险。
      • 标识符存储在固件中,而不是硬盘驱动器中,因此它可以在重新格式化、升级等过程中继续存在,但也不会被备份/映像/克隆复制。

      缺点:

      • 需要实际设置资产标签;在您这样做之前,它们都将是空白的。
      • 设置机器的资产标签可能需要物理访问和重新启动。
      • 资产标签不是一次写入的,因此可以更改或删除。
        • 受密码保护的固件应该在更改标签之前要求输入密码,但这不能保证。
      • 通过自己分配和跟踪标识符,不仅会产生...分配和跟踪标识符的开销,而且如果您不小心可能会引入重复项。
      • 为此目的使用资产标签要求所有机器都支持设置资产标签正确地将其报告给 WMI。

      【讨论】:

        【解决方案12】:

        我们使用来自Win32_processorProcessorID 和来自Win32_ComputerSystemProductUUID 的组合:

        ManagementObjectCollection mbsList = null;
        ManagementObjectSearcher mos = new ManagementObjectSearcher("Select ProcessorID From Win32_processor");
        mbsList = mos.Get();
        string processorId = string.Empty;
        foreach (ManagementBaseObject mo in mbsList)
        {
            processorId = mo["ProcessorID"] as string;
        }
        
        mos = new ManagementObjectSearcher("SELECT UUID FROM Win32_ComputerSystemProduct");
        mbsList = mos.Get();
        string systemId = string.Empty;
        foreach (ManagementBaseObject mo in mbsList)
        {
            systemId = mo["UUID"] as string;
        }
        
        var compIdStr = $"{processorId}{systemId}";
        

        之前我们使用了一个组合:处理器ID("Select ProcessorID From Win32_processor")和主板序列号("SELECT SerialNumber FROM Win32_BaseBoard"),但后来发现主板的序列号可能没有填写,也可能是用统一的值填充:

        • 由 O.E.M. 填写
        • 默认字符串

        因此,这种情况值得考虑。

        另外请记住,ProcessorID 号码在不同的计算机上可能相同。

        【讨论】:

          【解决方案13】:

          此链接中有一个示例代码,其中包含完整的注释,用于获取 CPU 和 HD Drive ID:http://www.vcskicks.com/hardware_id.php

          将此 dll 添加到引用中

           System.Management.dll
          

          CPU ID:

          string cpuInfo = string.Empty;
          ManagementClass mc = new ManagementClass("win32_processor");
          ManagementObjectCollection moc = mc.GetInstances();
          
          foreach (ManagementObject mo in moc)
          {
               if (cpuInfo == "")
               {
                    //Get only the first CPU's ID
                    cpuInfo = mo.Properties["processorID"].Value.ToString();
                    break;
               }
          }
          return cpuInfo;
          

          对于硬盘 ID(卷序列号):

          ManagementObject dsk = new ManagementObject(@"win32_logicaldisk.deviceid=""" + drive + @":""");
          dsk.Get();
          string volumeSerial = dsk["VolumeSerialNumber"].ToString();
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 2023-03-17
            • 2011-03-16
            • 1970-01-01
            • 1970-01-01
            • 2012-08-03
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多