【问题标题】:Getting Windows serial number (was: Getting MachineGuid from Registry)获取 Windows 序列号(原为:Getting MachineGuid from Registry)
【发布时间】:2013-01-08 10:35:06
【问题描述】:

我正在尝试从注册表中获取MachineGuid,以便为我的许可证系统创建与操作系统的某种级别的绑定。从我可以使用的文档中

string key = "HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Cryptography";
string r = (string)Registry.GetValue(key, "MachineGuid", (object)"default");

得到它。此外,文档告诉我,当找不到名称时我会得到"default",如果密钥不存在,我会得到null。如果我无权访问,我应该得到一个安全异常。

上面的代码给了我"default",这意味着找不到这个名字。但是,如果我用 RegEdit 查看注册表,它就在那里。如何在没有管理员权限的情况下从应用程序中获取 MachineGuid 值?

更新:当使用reg.exe 时,获取值没有问题。

更新:我更新了标题,因此寻找独特方法来确定 Windows 安装的人也可以在这里找到。

【问题讨论】:

  • 这似乎是一个注册表重定向问题:stackoverflow.com/questions/5262830/…。好像注册表还不够坏。
  • 您是否在 64 位机器上运行 32 位进程?
  • @Clemens 您使用的是哪个操作系统?如果它与 OP 的版本不同,那么这很可能有充分的理由为您而不是他工作
  • @BartFriederichs 你用的是什么操作系统?和克莱门斯一样吗?

标签: c# windows registry wmi serial-number


【解决方案1】:

这可能是它在不同版本的 Windows 中无法可靠运行的原因

不,这不是原因。此问题是由您的 EXE 项目的平台目标选择引起的。项目 + 属性、构建选项卡、平台目标组合框。您将其设置为 x86 而不是 AnyCPU。在 VS2012 上,“首选 32 位”复选框很重要。此设置强制您的程序在 64 位版本的 Windows 上以 32 位模式运行。这有许多副作用,这里重要的是重定向对注册表项的访问。您的程序实际上正在读取 HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Microsoft\Cryptography\MachineGuid 的值。哪个不存在。

x86 选项是 VS2010 及更高版本的默认选项,以前 AnyCPU 是默认选项。 Microsoft 更喜欢 x86,Visual Studio 更适合 32 位模式进程。特别是在调试时,VS 本身是一个 32 位进程,因此如果您的程序在 64 位模式下执行,则需要远程调试器。其中有一些限制,例如不支持混合模式调试。编辑 + 继续功能仅适用于 32 位代码。但是,如果您在 AnyCPU 上进行了设置,您的程序本身会“更好”运行,包括不会被文件系统和注册表重定向 appcompat 内置在 Windows 中的功能所困扰。

如果您真的坚持使用 x86 模式,通常是因为您依赖于无法更新的 32 位本机代码,那么下一个解决方法是使用 .NET 4+ RegistryKey.OpenBaseKey() 方法。这允许您通过 RegistryView.Registry64,确保您将读取非重定向键。

当然,使用 WMI 是一种解决方法。请记住,当您使用 Win32_OperatingSystem.SerialNumber 时,您不会阅读相同的信息。我不清楚不同机器上的密钥在多大程度上是可靠随机的,我们只是说这个值对于那些对为您的产品支付许可费不太感兴趣的用户来说是一个非常有吸引力的目标。

最后但同样重要的是,请注意生成完全不依赖于 Windows 的您自己的唯一 ID 非常容易。具有相当大的优势,即当您的客户在他的机器上更新 Windows 时,您不会惹恼他。只需使用一次 Guid.NewGuid() 并将值存储在文件中。当驱动器出现故障时,它会丢失,但这通常也会导致您的产品失效。

【讨论】:

  • 感谢您的冗长解释。我的许可证代码使用两种不同的安全措施。首先,它经过加密签名以防止篡改。因此,当有人只是将序列号放在许可证文件中时,它是不被接受的。其次,它(现在)使用 Windows 序列号来防止从一台计算机复制到另一台计算机。对于我们的用例(相当小众的产品)来说,这已经足够了。
  • 好吧,我确实试图指出不同的机器有不同的 Windows 序列号并不能完全保证。也许您没有完成答案?但是,是的,确实有一点你会厌倦想要处理这个问题。然后你只需要买一个加密狗。
  • Windows 序列号的差异不会是一个大问题。如果两个人碰巧有同一个序列,彼此认识愿意盗版一个工业级软件,那就这样吧。我的许可证系统只是为了阻止基本的复制和许可证文件编辑。 (许可证文件也有一些使用限制。)在某些时候,花费我不知道多少时间来保护软件是不值得的,可能只有 1% 或 5% 的客户愿意盗版。
【解决方案2】:

正如其他人已经指出的那样,您不应该直接从注册表中获取该值(这可能是它在不同版本的 Windows 中无法可靠运行的原因)。

稍微搜索一下,我就找到了Win32_OperatingSystemWMI class。使用这个类,你实际上可以得到 Windows 序列号。我花了一些搜索和试验才能让它正确,但这是在 C# 中使用它的方法。

确保您的项目中有 System.Management.dll 引用:

using System.Management;

...

ManagementObject os = new ManagementObject("Win32_OperatingSystem=@");
string serial = (string)os["SerialNumber"];

使用[] 运算符,您可以获取类中的任何属性。

【讨论】:

  • 这个方法在windows XP (.NET3.5)中由于某种原因崩溃
  • 如果我有 Ghost 之类的克隆工具,每次部署此 Windows Syspreped 克隆映像时,此序列号是否会更改?它是 Windows 副本的唯一标识符吗?
  • 经过进一步调查,每个 Windows 克隆的 Windows 产品 ID 都不是唯一的。
  • 您说“您不应该直接从注册表中获取该值”.... 谁说的? .NET 中有一整套类可以与注册表一起使用....
  • 这个方法在你使用操作系统的GHOST镜像时是不合适的,它永远不会给你唯一的操作系统序列号。
【解决方案3】:

我的拙见,没有一个答案能满足这个问题;非常直接地要求一种从注册表中读取 MachineGuid 的方法......所以这是我的答案:您需要添加对“Microsoft.Win32”的引用。这段代码是为了演示目的而编写的,应该进行相应的调整。

编辑: 有人错误地指出 x64 代码是无用的。
在 64 位操作系统中可以找到正确的密钥。

所以这个答案是唯一满足这个问题的答案。

private void buttonGetMachineGuid_Click(object sender, RoutedEventArgs e)
{
  try
  {
    string x64Result = string.Empty;
    string x86Result = string.Empty;
    RegistryKey keyBaseX64 = RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, RegistryView.Registry64);
    RegistryKey keyBaseX86 = RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, RegistryView.Registry32);
    RegistryKey keyX64 = keyBaseX64.OpenSubKey(@"SOFTWARE\Microsoft\Cryptography", RegistryKeyPermissionCheck.ReadSubTree);
    RegistryKey keyX86 = keyBaseX86.OpenSubKey(@"SOFTWARE\Microsoft\Cryptography", RegistryKeyPermissionCheck.ReadSubTree);
    object resultObjX64 = keyX64.GetValue("MachineGuid", (object)"default");
    object resultObjX86 = keyX86.GetValue("MachineGuid", (object)"default");
    keyX64.Close();
    keyX86.Close();
    keyBaseX64.Close();
    keyBaseX86.Close();
    keyX64.Dispose();
    keyX86.Dispose();
    keyBaseX64.Dispose();
    keyBaseX86.Dispose();
    keyX64 = null;
    keyX86 = null;
    keyBaseX64 = null;
    keyBaseX86 = null;
    if(resultObjX64 != null && resultObjX64.ToString() != "default")
    {
      MessageBox.Show(resultObjX64.ToString());
    }
    if(resultObjX86 != null && resultObjX86.ToString() != "default")
    {
      MessageBox.Show(resultObjX86.ToString());
    }
  }
  catch(Exception)
  {
  }
}

希望这对某人有所帮助。

【讨论】:

  • 您不需要从 Registry64 中读取,因为 MachineGuid 仅存在于 32 位注册表区域中。
  • 另外只是指出其他答案没有示例代码;或者不直接从注册表中获取它。所以在我看来,这是唯一符合手头问题的答案。
  • Romil 错误,64 位进程需要 Registry64。我的情况是 resultObjX86 是默认值,而 resultObjX64 是正确的 GUID 字符串。
  • @TimSexton 为什么不发布您自己的答案?您的编辑与原始代码相比发生了巨大变化。
  • 感谢@Darkonekt!你的答案一针见血!
猜你喜欢
  • 1970-01-01
  • 2022-12-02
  • 1970-01-01
  • 2011-03-08
  • 1970-01-01
  • 1970-01-01
  • 2022-06-03
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多