在Pocket PC上生成GUID






4.94/5 (9投票s)
在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 规范的更多信息
- http://www.webdav.org/specs/draft-leach-uuids-guids-01.txt
- http://dav.lyra.org/pipermail/dav-dev/1999-May/000245.html
- http://asg.web.cmu.edu/rfc/rfc2518.html#sec-6.4.1
生成随机数
PocketGuid
类使用 CryptGenRandom
API 函数生成随机数。该函数的第一个参数是 CSP(加密服务提供商)句柄,通过调用 CryptAcquireContext
创建,并通过 CryptReleaseContext
函数释放。
CryptGenRandom
函数用加密随机字节填充缓冲区,这些字节比使用 System.Random
类生成的随机数更随机(更不可预测且分布更均匀)。CryptGenRandom
函数的熵(不确定性的度量)来自 Windows CE 上的以下源:
- 线程和内核切换
- 当前进程标识符
- 当前线程标识符
- 自启动以来的滴答数
- 当前时间
- 内存信息
- 对象存储统计信息
PocketGuid 类
现在我们知道了 GUID 格式(带有嵌入式变体和版本比特的 128 位随机数),以及如何生成随机数(使用加密 API 函数),实现 NewGuid
方法就非常容易了。下面是用 C# 和 VB.NET 编写的 PocketGuid
类的代码。
该类包含两个私有枚举,GuidVariant
和 GuidVersion
,它们列出了不同的变体和版本选项。私有类 Const
和 WinApi 包含类中使用的常量以及加密 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
。
- 答案:第 13 个四位一组指定了 GUID 版本,对于 Windows 2000 及更高版本,它始终设置为 4。例如:
- 问题:哪个四位一组仅包含 8、9、A 或 B?
- 答案:第 17 个四位一组包含变体信息,因此它将始终为 8、9、A 或 B,因为最高位始终设置为 1,下一位始终清除为 0。例如:
8743428c-ef91-4d05-9e7c-4a2e856e813a
。
- 答案:第 17 个四位一组包含变体信息,因此它将始终为 8、9、A 或 B,因为最高位始终设置为 1,下一位始终清除为 0。例如:
- 问题:有多少种 GUID 组合?
- 答案:有 122 个随机位(128 - 2 位用于变体 - 4 位用于版本),计算结果为 2^122,即 5,316,911,983,139,663,491,615,228,241,121,400,000 种可能的组合。