65.9K
CodeProject 正在变化。 阅读更多。
Home

在Pocket PC上生成GUID

2004 年 1 月 20 日

CPOL

5分钟阅读

viewsIcon

68470

downloadIcon

165

在Pocket PC上生成GUID

本文由 MSDN 提供。

摘要

Guid.NewGuid 方法生成新的 GUID(全局唯一标识符),但在 Microsoft .NET Compact Framework 中不受支持。了解如何生成符合 Pocket PC 应用程序文档 GUID 规范的 Guid 对象。(11 页)

目录

概述

.NET Compact Framework 团队不断在框架占用空间大小、性能和实现时间之间进行权衡。完整的 .NET Framework Guid.NewGuid 方法调用 Windows API 函数 CoCreateGuid,该函数又调用 UuidCreate 来生成全局唯一的 128 位数字。不幸的是,这些函数在 Pocket PC 上不受支持,因此 Guid.NewGuid 方法未在 .NET Compact Framework 中实现。

事实证明,编写 Guid.NewGuid 方法的自定义实现很容易。以下是一个在 Pocket PC 上生成 GUID 的测试应用程序。它使用一个名为 PocketGuid 的自定义类,该类使用与桌面 GUID 相同的算法,并在本文后面详细讨论。

图 1. 在 Pocket PC 上生成 GUID

GUID 格式

GUID 由分组到几个部分的随机数组成:时间戳、时钟序列和节点。下面表格中显示了 GUID 8743428c-ef91-4d05-9e7c-4a2e856e813a 的不同部分。

表 1. GUID 的不同部分。

GUID 部分 注释
8743428c 时间戳的低位字段
ef91 时间戳的中位字段
4d05 带有多路复用版本号的时间戳的高位字段
9e 带有多路复用变体类型时钟序列的高位字段
7c 时钟序列的低位字段
4a2e856e813a 空间唯一节点标识符

变体

时钟序列部分的一个到三个比特用于定义 GUID 的变体或布局。Windows 和 PocketGuid 类生成变体类型 2 的 GUID。

表 2. 存储在 GUID 中的变体信息

时钟序列的最高 3 位 变体类型 注释
0 - - 0 保留给 NCS(网络计算系统)向后兼容
1 0 - 2 标准格式
1 1 0 6 保留给 Microsoft Corporation 向后兼容
1 1 1 7 保留供将来定义

版本

时间戳部分的最高四位包含 GUID 的版本,该版本指定每个部分的内容。在 Windows 2000 之前,CoCreateGuid 函数生成版本 1 的 GUID。在 Windows 2000 中,Microsoft 改为使用版本 4 的 GUID,因为嵌入 MAC 地址被认为存在安全风险。PocketGuid 类也生成版本 4 的 GUID。

表 3. 存储在 GUID 中的版本信息

时间戳的最高 4 位 版本 注释
0 0 0 1 1 基于时间的版本
使用时间戳、时钟序列和 MAC 网卡地址
0 0 1 0 2 保留
0 0 1 1 3 基于名称的版本
从名称构建所有部分的值
0 1 0 0 4 随机版本
所有部分使用随机数

以下网站包含有关 GUID 规范的更多信息

生成随机数

PocketGuid 类使用 CryptGenRandom API 函数生成随机数。该函数的第一个参数是 CSP(加密服务提供商)句柄,通过调用 CryptAcquireContext 创建,并通过 CryptReleaseContext 函数释放。

CryptGenRandom 函数用加密随机字节填充缓冲区,这些字节比使用 System.Random 类生成的随机数更随机(更不可预测且分布更均匀)。CryptGenRandom 函数的熵(不确定性的度量)来自 Windows CE 上的以下源:

  • 线程和内核切换
  • 当前进程标识符
  • 当前线程标识符
  • 自启动以来的滴答数
  • 当前时间
  • 内存信息
  • 对象存储统计信息

PocketGuid 类

现在我们知道了 GUID 格式(带有嵌入式变体和版本比特的 128 位随机数),以及如何生成随机数(使用加密 API 函数),实现 NewGuid 方法就非常容易了。下面是用 C# 和 VB.NET 编写的 PocketGuid 类的代码。

该类包含两个私有枚举,GuidVariantGuidVersion,它们列出了不同的变体和版本选项。私有类 ConstWinApi 包含类中使用的常量以及加密 API 函数的 DllImport 语句。静态方法 PocketGuid.NewGuid 执行以下操作:

  • 调用 CryptAcquireContext 获取加密提供程序句柄
  • 调用 CryptGenRandom 生成 128 位加密随机数
  • 设置变体和版本比特
  • 创建新的 System.Guid 对象并将字节数组传递给构造函数
  • 调用 CryptReleaseContext 释放加密提供程序句柄
  • 如果成功,则返回新的 System.Guid 对象;否则,将抛出 SystemException

C#

using System;
using System.Runtime.InteropServices;

namespace PocketGuid
{
   /// <summary>
   /// Generate GUIDs on the .NET Compact Framework.
   /// </summary>
   public class PocketGuid
   {
      // guid variant types
      private enum GuidVariant
      {
         ReservedNCS = 0x00,
         Standard = 0x02,
         ReservedMicrosoft = 0x06,
         ReservedFuture = 0x07
      }

      // guid version types
      private enum GuidVersion
      {
         TimeBased = 0x01,
         Reserved = 0x02,
         NameBased = 0x03,
         Random = 0x04
      }
         
      // constants that are used in the class
      private class Const
      {
         // number of bytes in guid
         public const int ByteArraySize = 16;
         
         // multiplex variant info
         public const int VariantByte = 8;
         public const int VariantByteMask = 0x3f;
         public const int VariantByteShift = 6;

         // multiplex version info
         public const int VersionByte = 7;
         public const int VersionByteMask = 0x0f;
         public const int VersionByteShift = 4;
      }

      // imports for the crypto api functions
      private class WinApi
      {
         public const uint PROV_RSA_FULL = 1;
         public const uint CRYPT_VERIFYCONTEXT = 0xf0000000;

         [DllImport("coredll.dll")] 
         public static extern bool CryptAcquireContext(
            ref IntPtr phProv, string pszContainer, string pszProvider,
            uint dwProvType, uint dwFlags);

         [DllImport("coredll.dll")] 
         public static extern bool CryptReleaseContext( 
            IntPtr hProv, uint dwFlags);

         [DllImport("coredll.dll")] 
         public static extern bool CryptGenRandom(
            IntPtr hProv, int dwLen, byte[] pbBuffer);
      }
      
      // all static methods
      private PocketGuid()
      {
      }
      
      /// <summary>
      /// Return a new System.Guid object.
      /// </summary>
      public static Guid NewGuid()
      {
         IntPtr hCryptProv = IntPtr.Zero;
         Guid guid = Guid.Empty;
         
         try
         {
            // holds random bits for guid
            byte[] bits = new byte[Const.ByteArraySize];

            // get crypto provider handle
            if (!WinApi.CryptAcquireContext(ref hCryptProv, null, null, 
               WinApi.PROV_RSA_FULL, WinApi.CRYPT_VERIFYCONTEXT))
            {
               throw new SystemException(
               "Failed to acquire cryptography handle.");
            }
      
            // generate a 128 bit (16 byte) cryptographically random number
            if (!WinApi.CryptGenRandom(hCryptProv, bits.Length, bits))
            {
               throw new SystemException(
               "Failed to generate cryptography random bytes.");
            }
            
            // set the variant
            bits[Const.VariantByte] &= Const.VariantByteMask;
            bits[Const.VariantByte] |= 
               ((int)GuidVariant.Standard << Const.VariantByteShift);

            // set the version
            bits[Const.VersionByte] &= Const.VersionByteMask;
            bits[Const.VersionByte] |= 
               ((int)GuidVersion.Random << Const.VersionByteShift);
         
            // create the new System.Guid object
            guid = new Guid(bits);
         }
         finally
         {
            // release the crypto provider handle
            if (hCryptProv != IntPtr.Zero)
               WinApi.CryptReleaseContext(hCryptProv, 0);
         }
         
         return guid;
      }
   }
}

VB.NET

Imports System.Runtime.InteropServices

'
' Generate GUIDs on  the  .NET Compact Framework.
'
Public Class PocketGuid

   ' guid variant types
   Private Enum GuidVariant
      ReservedNCS = &H0
      Standard = &H2
      ReservedMicrosoft = &H6
      ReservedFuture = &H7
   End Enum

   ' guid version types
   Private Enum GuidVersion
      TimeBased = &H1
      Reserved = &H2
      NameBased = &H3
      Random = &H4
   End Enum

   ' constants  that are used in the class
   Private Class ConstValues
      ' number of  bytes in guid
      Public Const ByteArraySize As Integer = 16

      ' multiplex  variant  info
      Public Const VariantByte As Integer = 8
      Public Const VariantByteMask As Integer = &H3F
      Public Const VariantByteShift As Integer = 6

      ' multiplex  version  info
      Public Const VersionByte As Integer = 7
      Public Const VersionByteMask As Integer = &HF
      Public Const VersionByteShift As Integer = 4
   End Class

   ' imports for the crypto api functions
   Private Class WinApi
      Public Const PROV_RSA_FULL As Integer = 1
      Public Const CRYPT_VERIFYCONTEXT As Integer = &HF0000000

      <DllImport("coredll.dll")> _
      Public Shared Function CryptAcquireContext( _
         ByRef phProv As IntPtr, ByVal pszContainer As String, _
         ByVal pszProvider As String, ByVal dwProvType As Integer, _
         ByVal dwFlags As Integer) As Boolean
      End Function

      <DllImport("coredll.dll")> _
      Public Shared Function CryptReleaseContext( _
         ByVal hProv As IntPtr, ByVal dwFlags As Integer) As Boolean
      End Function

      <DllImport("coredll.dll")> _
      Public Shared Function CryptGenRandom( _
         ByVal hProv As IntPtr, ByVal dwLen As Integer, _
         ByVal pbBuffer() As Byte) As Boolean
      End Function
   End Class

   ' all static methods
   Private Sub New()
   End Sub

   ' Return a new System.Guid object.
   Public Shared Function NewGuid() As Guid
      Dim hCryptProv As IntPtr = IntPtr.Zero
      Dim guid As Guid = guid.Empty

      Try
         ' holds  random bits  for  guid
         Dim bits(ConstValues.ByteArraySize - 1) As Byte

         ' get crypto provider handle
         If Not WinApi.CryptAcquireContext(hCryptProv, Nothing, Nothing, _
            WinApi.PROV_RSA_FULL, WinApi.CRYPT_VERIFYCONTEXT) Then
            Throw New SystemException( _
               "Failed  to acquire cryptography  handle.")
         End If

         ' generate a 128 bit (16 byte) cryptographically random  number
         If Not WinApi.CryptGenRandom(hCryptProv, bits.Length, bits) Then
            Throw New SystemException( _
               "Failed  to generate  cryptography random  bytes.")
         End If

         ' set the variant
         bits(ConstValues.VariantByte) = bits(ConstValues.VariantByte) And _
            CByte(ConstValues.VariantByteMask)
         bits(ConstValues.VariantByte) = bits(ConstValues.VariantByte) Or _
            CByte(GuidVariant.Standard << ConstValues.VariantByteShift)

         ' set the version
         bits(ConstValues.VersionByte) = bits(ConstValues.VersionByte) And _
            CByte(ConstValues.VersionByteMask)
         bits(ConstValues.VersionByte) = bits(ConstValues.VersionByte) Or _
            CByte(GuidVersion.Random << ConstValues.VersionByteShift)

         ' create the new System.Guid object
         guid = New Guid(bits)
      Finally
         ' release the crypto provider handle
         If Not hCryptProv.Equals(IntPtr.Zero) Then
            WinApi.CryptReleaseContext(hCryptProv, 0)
         End If
      End Try

      Return guid
   End Function
End Class

GUID 趣闻

这里有一些 GUID 趣闻问题,您可以用它们来给同事留下深刻印象。您可以在 Pocket PC 上运行 PocketGuid 测试应用程序,或者在您的桌面上运行 guidgen.exe 来检查这些问题;但是,检查最后一个问题可能需要一些时间。

  • 问题:哪个四位一组(4 位)始终相同?
    • 答案:第 13 个四位一组指定了 GUID 版本,对于 Windows 2000 及更高版本,它始终设置为 4。例如: 8743428c-ef91-4d05-9e7c-4a2e856e813a
  • 问题:哪个四位一组仅包含 8、9、A 或 B?
    • 答案:第 17 个四位一组包含变体信息,因此它将始终为 8、9、A 或 B,因为最高位始终设置为 1,下一位始终清除为 0。例如: 8743428c-ef91-4d05-9e7c-4a2e856e813a
  • 问题:有多少种 GUID 组合?
    • 答案:有 122 个随机位(128 - 2 位用于变体 - 4 位用于版本),计算结果为 2^122,即 5,316,911,983,139,663,491,615,228,241,121,400,000 种可能的组合。

链接

© . All rights reserved.