This article has been provided courtesy of MSDN.
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)
Contents
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.
Figure 1. Generating GUIDs on the Pocket PC
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:
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
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
{
public class PocketGuid
{
private enum GuidVariant
{
ReservedNCS = 0x00,
Standard = 0x02,
ReservedMicrosoft = 0x06,
ReservedFuture = 0x07
}
private enum GuidVersion
{
TimeBased = 0x01,
Reserved = 0x02,
NameBased = 0x03,
Random = 0x04
}
private class Const
{
public const int ByteArraySize = 16;
public const int VariantByte = 8;
public const int VariantByteMask = 0x3f;
public const int VariantByteShift = 6;
public const int VersionByte = 7;
public const int VersionByteMask = 0x0f;
public const int VersionByteShift = 4;
}
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);
}
private PocketGuid()
{
}
public static Guid NewGuid()
{
IntPtr hCryptProv = IntPtr.Zero;
Guid guid = Guid.Empty;
try
{
byte[] bits = new byte[Const.ByteArraySize];
if (!WinApi.CryptAcquireContext(ref hCryptProv, null, null,
WinApi.PROV_RSA_FULL, WinApi.CRYPT_VERIFYCONTEXT))
{
throw new SystemException(
"Failed to acquire cryptography handle.");
}
if (!WinApi.CryptGenRandom(hCryptProv, bits.Length, bits))
{
throw new SystemException(
"Failed to generate cryptography random bytes.");
}
bits[Const.VariantByte] &= Const.VariantByteMask;
bits[Const.VariantByte] |=
((int)GuidVariant.Standard << Const.VariantByteShift);
bits[Const.VersionByte] &= Const.VersionByteMask;
bits[Const.VersionByte] |=
((int)GuidVersion.Random << Const.VersionByteShift);
guid = new Guid(bits);
}
finally
{
if (hCryptProv != IntPtr.Zero)
WinApi.CryptReleaseContext(hCryptProv, 0);
}
return guid;
}
}
}
VB.NET
Imports System.Runtime.InteropServices
Public Class PocketGuid
Private Enum GuidVariant
ReservedNCS = &H0
Standard = &H2
ReservedMicrosoft = &H6
ReservedFuture = &H7
End Enum
Private Enum GuidVersion
TimeBased = &H1
Reserved = &H2
NameBased = &H3
Random = &H4
End Enum
Private Class ConstValues
Public Const ByteArraySize As Integer = 16
Public Const VariantByte As Integer = 8
Public Const VariantByteMask As Integer = &H3F
Public Const VariantByteShift As Integer = 6
Public Const VersionByte As Integer = 7
Public Const VersionByteMask As Integer = &HF
Public Const VersionByteShift As Integer = 4
End Class
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
Private Sub New()
End Sub
Public Shared Function NewGuid() As Guid
Dim hCryptProv As IntPtr = IntPtr.Zero
Dim guid As Guid = guid.Empty
Try
Dim bits(ConstValues.ByteArraySize - 1) As Byte
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
If Not WinApi.CryptGenRandom(hCryptProv, bits.Length, bits) Then
Throw New SystemException( _
"Failed to generate cryptography random bytes.")
End If
bits(ConstValues.VariantByte) = bits(ConstValues.VariantByte) And _
CByte(ConstValues.VariantByteMask)
bits(ConstValues.VariantByte) = bits(ConstValues.VariantByte) Or _
CByte(GuidVariant.Standard << ConstValues.VariantByteShift)
bits(ConstValues.VersionByte) = bits(ConstValues.VersionByte) And _
CByte(ConstValues.VersionByteMask)
bits(ConstValues.VersionByte) = bits(ConstValues.VersionByte) Or _
CByte(GuidVersion.Random << ConstValues.VersionByteShift)
guid = New Guid(bits)
Finally
If Not hCryptProv.Equals(IntPtr.Zero) Then
WinApi.CryptReleaseContext(hCryptProv, 0)
End If
End Try
Return guid
End Function
End Class
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.
Links