Ralph Arvesen
Vertigo Software, Inc.
March 2003
Summary: The Guid.NewGuid method generates new GUIDs (globally unique identifier) but is not supported in the Microsoft .NET Compact Framework. Learn how to generate Guid objects that follow the documented GUID specifications for Pocket PC applications. (11 printed pages)
Download GenerateGuid.msi.
Contents
Overview
The GUID Format
Generating Random Numbers
The PocketGuid Class
GUID Trivia
Overview
The .NET Compact Framework team constantly made tradeoffs between the framework footprint size, performance, and implementation time. The full .NET Framework Guid.NewGuid method calls the Windows API function CoCreateGuid that calls UuidCreate to generate globally unique 128-bit numbers. Unfortunately, these functions are not supported on the Pocket PC, so the Guid.NewGuid method was not implemented for the .NET Compact Framework.
It turns out that it's easy to write a custom implementation of the Guid.NewGuid method. The following shows a test application that generates GUIDs on the Pocket PC. It uses a custom class called PocketGuid, that uses the same algorithm as desktop GUIDs and is discussed in more detail later in this paper.
.gif)
Figure 1. Generating GUIDs on the Pocket PC
The GUID Format
GUIDs consist of random numbers grouped into several sections: timestamp, clock sequence and node. The different sections for the GUID 8743428c-ef91-4d05-9e7c-4a2e856e813a are shown in the following table.
Table 1. Different sections of a GUID.
| GUID Section | Comments |
8743428c | Low field of the timestamp |
ef91 | Middle field of the timestamp |
4d05 | High field of the timestamp with multiplexed version number |
9e | High field of the clock sequence with multiplexed variant type |
7c | Low field of the clock sequence |
4a2e856e813a | Spatially unique node identifier |
Variant
One to three bits of the clock sequence section are used to define the variant, or layout, of the GUID. Windows and the PocketGuid class generate variant type 2 GUIDs.
Table 2. Variant information stored in GUIDs
| Upper 3 Bits of Clock Sequence | Variant Type | Comments |
0 - - | 0 | Reserved for NCS (Network Computing System) backward compatibility |
1 0 - | 2 | Standard format |
1 1 0 | 6 | Reserved for Microsoft Corporation backward compatibility |
1 1 1 | 7 | Reserved for future definition |
Version
The upper four bits of the timestamp section contain the GUID's version that specifies the content of each section. Before Windows 2000, the CoCreateGuid function generated version 1 GUIDs. With Windows 2000, Microsoft switched to version 4 GUIDs, since embedding the MAC address was viewed as a security risk. The PocketGuid class also generates version 4 GUIDs.
Table 3. Version information stored in GUIDs
| Upper 4 Bits of Timestamp | Version | Comments |
0 0 0 1 | 1 | Time-based version
Uses timestamp, clock sequence, and MAC network card address |
0 0 1 0 | 2 | Reserved |
0 0 1 1 | 3 | Name-based version
Constructs values from a name for all sections |
0 1 0 0 | 4 | Random version
Use random numbers for all sections |
The following sites contain more information on the GUID specification:
Generating Random Numbers
The PocketGuid class uses the CryptGenRandom API function to generate random numbers. The first argument to the function is a CSP (cryptographic service provider) handle, that is created by calling CryptAcquireContext, and is released with the CryptReleaseContext function.
The CryptGenRandom function fills a buffer with cryptographically-random bytes that are more random (less predictable and more evenly distributed) than using the System.Random class. Entropy, the measure of uncertainty, is generated for the CryptGenRandom function from the following sources on Windows CE:
- Thread and kernel switches
- The current process identifier
- The current thread identifier
- Ticks since boot
- Current time
- Memory information
- Object store statistics
The PocketGuid Class
Now that we know the GUID format (128 bit random number with multiplexed variant and version bits), and how to generate random numbers (use crypto API functions), it's very easy to implement the NewGuid method. The code for the PocketGuid class is shown below in C# and VB.NET.
The class contains two private enums, GuidVariant and GuidVersion, that list the different variant and version options. The private classes Const and WinApi contain constants used in the class and the DllImport statements for the crypto API functions. The static method PocketGuid.NewGuid does the following:
- Calls CryptAcquireContext to get a crypto provider handle
- Calls CryptGenRandom to generate a 128 bit cryptographically random number
- Sets the variant and version bits
- Creates a new System.Guid object and passes the byte array to the constructor
- Calls CryptReleaseContext to release the crypto provider handle
- Returns a new System.Guid object if successful, otherwise a SystemException is thrown
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 Trivia
Here are some GUID trivia questions you can use to impress your co-workers. You can run the PocketGuid test application or guidgen.exe on your desktop to check the questions; however, checking the last question might take a while.
Question: What is the only nibble (4 bits) that is always the same?
Answer: The 13th nibble specifies the GUID version and is always set to 4 for Windows 2000 and later. Example, 8743428c-ef91-4d05-9e7c-4a2e856e813a.
Question: What nibble only contains the values 8, 9, A, or B?
Answer: The 17th nibble contains the variant information so it will always be 8, 9, A or B since the upper bit is always set and next bit is always cleared. Example, 8743428c-ef91-4d05-9e7c-4a2e856e813a.
Question: How many GUID combinations are there?
Answer: There are 122 random bits (128 – 2 for variant - 4 for version) so this calculates to 2^122 or 5,316,911,983,139,663,491,615,228,241,121,400,000 possible combinations.