View RSS Feed

Development Team Blog

Create a New Universally Unique IDentifier (UUID)

Rating: 17 votes, 5.00 average.
If you are in a need to have a unique ID in your application you might want to make use of a Universally Unique IDentifier (UUID). The term UUID is interchangeable with GUID (Globally Unique ID). A UUID is a string that contains five blocks of hexadecimal digits. The Windows API has functions for this and in this blog I show you how to create UUIDs in Visual DataFlex.

An example of a valid UUID string is: 2CA263F1-5C94-11E0-84CC-002170FBAC5B. As you can see it consists of 8 hexadecimal digits followed by a dash, followed by three groups of 4 hexadecimal digits separated by a dash, followed by 12 hexadecimal digits prefixed with a dash.

Data Structure
For the API functions we use below we need a structure with four members. Define the following structure definition in your application:
Code:
Struct tUUID
    UInteger uiData1
    UShort usData2
    UShort usData3
    UChar[8] ucData4   
End_Struct
Random or Sequential
The Windows API has two different functions to generate a UUID. One generates a sequential UUID and the other one a random one. UUIDs are always unique but it might sound a bit weird when someone sees UUIDs that have a sequential, incremental value.
A sample of 3 randomly generated UUID is:
Code:
1: ADFF9239-85B6-42EC-9FDD-41DC4C8AF6FA
2: F9B0CC28-A63A-4672-BF42-3B0E7508299E
3: 1E345F18-FC3B-492D-B22D-E18ABBDC7BE9
And an example of 3 sequential UUIDs is:
Code:
1: 57F2B390-5D82-11E0-84CC-002170FBAC5B
2: 57F2B391-5D82-11E0-84CC-002170FBAC5B
3: 57F2B392-5D82-11E0-84CC-002170FBAC5B
As you can see the UUIDs are almost the same. If you use such a UUID as a record identifier it would not matter at all since you are already used to use sequential numbers for customer, order and not to forget record numbers.

So how to get these UUID's? The Windows API function definitions are:
Code:
External_Function WinAPI_UuidCreate "UuidCreate" Rpcrt4.dll Address Uuid Returns Integer
External_Function WinAPI_UuidCreateSequential "UuidCreateSequential" Rpcrt4.dll Address aUuid Returns Integer
For the use of these functions make use of the following DataFlex code functions:
Code:
Function CreateUUID Returns tUUIDEx
    tUUIDEx UUID
    Integer iRetval
    
    Move (WinAPI_UuidCreate (AddressOf (UUID.UUID))) to UUID.iStatus

    Function_Return UUID
End_Function

Function CreateSeqUUID Returns tUUIDEx
    tUUIDEx UUID
    Integer iRetval
    
    Move (WinAPI_UuidCreateSequential (AddressOf (UUID.UUID))) to UUID.iStatus

    Function_Return UUID
End_Function
If you look at the above code you will see that the UUID variables do not use the earlier in this blog mentioned structure definition. The variables are made from a structure named tUUIDEx. This is an extended version of tUUID that I created to store a function status value. The definition of tUUIDEx is:
Code:
Struct tUUIDEx
    tUUID UUID
    Integer iStatus
End_Struct
Now we have a UUID how do we get that nice hexadecimal string out of the values? You can do this with the Windows API function UuidToString and to use this in your Visual DataFlex application add the following code to your application:
Code:
External_Function WinAPI_UuidToString  "UuidToStringA" Rpcrt4.dll Address aUuid Address lpUUIDString Returns Integer
External_Function WinAPI_RpcStringFree "RpcStringFreeA" Rpcrt4.dll Address pStr Returns Integer
External_Function WinAPI_UuidFromString "UuidFromStringA" Rpcrt4.dll Address lpUUIDString Address aUuid Returns Integer
The first function is the conversion function and the second frees up the memory allocated by the funciton call. The 3rd function is to do the reverse of the first one. It will convert a UUID string into a tUUID struct value collection. Use the 3 functions via the following methods:
Code:
Function UUIDToString tUUID UUID Returns String
    Address UUIDPointer
    String sUUID
    Integer iRetval
    
    Move 0 to UUIDPointer
    Move (WinAPI_UuidToString (AddressOf (UUID), AddressOf (UUIDPointer))) to iRetVal
    If (iRetval = RPC_S_OK) Begin
        Move UUIDPointer to sUUID
        Move (WinAPI_RpcStringFree (AddressOf (UUIDPointer))) to iRetval
        Move (Uppercase (sUUID)) to sUUID
    End
    Else Begin
        Get Error_Text of Desktop 10 to sUUID
    End
    
    Function_Return sUUID
End_Function

Function UUIDFromString String sUUID Returns tUUIDEx
    tUUIDEx UUID
    Integer iRetval
    
    Move (WinAPI_UuidFromString (AddressOf (sUUID), AddressOf (UUID.UUID))) to UUID.iStatus
    
    Function_Return UUID
End_Function
Nill
The Windows API also has a function to create a NILL (not NULL) UUID. You could see this as recnum that is zero when no record has been saved. A NILL UUID is always looking as: 00000000-0000-0000-0000-000000000000.

The API definition is:
Code:
External_Function WinAPI_UuidCreateNil "UuidCreateNil" rpcrt4.dll Address aUuid Returns Integer
You use the above function as follows:

Code:
Function NilUUID Returns tUUID
    tUUID UUID
    Integer iRetval
    
    Move (WinAPI_UuidCreateNil (AddressOf (UUID))) to iRetval
    
    Function_Return UUID
End_Function
Compare
The Windows API set also has a set of functions to compare the UUIDs with eachother. There is a function to test if two UUIDs are identical, if they are greater or bigger than eachother of if a UUID is a NILL UUID. The Windows API function codes are:
Code:
External_Function WinAPI_UuidEqual "UuidEqual" rpcrt4.dll Address aUuid1 Address aUuid2 Address aUUIDStatus Returns Integer
External_Function WinAPI_UuidCompare "UuidCompare" rpcrt4.dll Address aUuid1 Address aUuid2 Address aUUIDStatus Returns Integer
External_Function WinAPI_UuidIsNil "UuidIsNil" rpcrt4.dll Address aUuid Address aUUIDStatus Returns UShort
And you use the with:

Code:
Function UUIDEqual tUUID UUID1 tUUID UUID2 Returns Boolean
    Integer iStatus iRetval
    
    Move 0 to iStatus
    Move (WinAPI_UuidEqual (AddressOf (UUID1), AddressOf (UUID2), AddressOf (iStatus))) to iRetval
    
    Function_Return iRetval
End_Function

Function UuidCompare tUUID UUID1 tUUID UUID2 Returns Integer
    Integer iStatus iRetval
    
    Move 0 to iRetval
    Move (WinAPI_UuidCompare (AddressOf (UUID1), AddressOf (UUID2), AddressOf (iStatus))) to iRetval
    
    Function_Return iRetval
End_Function

Function UUIDIsNil tUUID UUID Returns Boolean
    Integer iStatus iRetval
    
    Move 0 to iStatus
    Move (WinAPI_UuidIsNil (AddressOf (UUID), AddressOf (iStatus))) to iRetval
    
    Function_Return iRetval
End_Function
The Compare function returns -1 if UUID1 is less than UUID2, 0 if they are identical and 1 if UUID1 is greater than UUID2.

Constants
To complete the code to generate and compare UUIDs you need to add the following constant definitions to your code.

Code:
Define ERROR_SUCCESS                 for 0    // The operation completed successfully.
Define ERROR_INVALID_HANDLE          for 6    // The handle is invalid.
Define ERROR_OUTOFMEMORY             for 14   // Not enough storage is available to complete this operation.
Define ERROR_INVALID_PARAMETER       for 87   // The parameter is incorrect.
Define ERROR_INSUFFICIENT_BUFFER     for 122  // The data area passed to a system call is too small.
Define ERROR_MAX_THRDS_REACHED       for 164  // No more threads can be created in the system.
Define ERROR_IO_PENDING              for 997  // Overlapped I/O operation is in progress.
Define ERROR_NONE_MAPPED             for 1332 // No mapping between account names and security IDs was done.
Define ERROR_INVALID_SECURITY_DESCR  for 1338 // The security descriptor structure is invalid.
Define ERROR_TIMEOUT                 for 1460 // This operation returned because the timeout period expired.
Define RPC_S_INVALID_STRING_UUID     for 1705 // The string universal unique identifier (UUID) is invalid.
Define RPC_S_INVALID_TAG             for 1733 // The tag is invalid.
Define RPC_S_UUID_NO_ADDRESS         for 1739 // No network address is available to use to construct a universal unique identifier (UUID).
Define RPC_S_INVALID_BOUND           for 1734 // The array bounds are invalid.
Define RPC_X_ENUM_VALUE_OUT_OF_RANGE for 1781 // The enumeration value is out of range.
Define ERROR_INVALID_USER_BUFFER     for 1784 // The supplied user buffer is not valid for the requested operation.
Define ERROR_NOT_ENOUGH_QUOTA        for 1816 // Not enough quota is available to process this command.
Define RPC_S_UUID_LOCAL_ONLY         for 1824 // A UUID that is valid only on this computer has been allocated.
Define RPC_X_WRONG_PIPE_ORDER        for 1831 // An invalid operation was attempted on an RPC pipe object.
Define RPC_X_WRONG_PIPE_VERSION      for 1832 // Unsupported RPC pipe version.

Define RPC_S_OK                     for ERROR_SUCCESS
Define RPC_S_INVALID_ARG            for ERROR_INVALID_PARAMETER
Define RPC_S_OUT_OF_MEMORY          for ERROR_OUTOFMEMORY
Define RPC_S_OUT_OF_THREADS         for ERROR_MAX_THRDS_REACHED
Define RPC_S_INVALID_LEVEL          for ERROR_INVALID_PARAMETER
Define RPC_S_BUFFER_TOO_SMALL       for ERROR_INSUFFICIENT_BUFFER
Define RPC_S_INVALID_SECURITY_DESC  for ERROR_INVALID_SECURITY_DESCR
Define RPC_S_ACCESS_DENIED          for ERROR_ACCESS_DENIED
Define RPC_S_SERVER_OUT_OF_MEMORY   for ERROR_NOT_ENOUGH_SERVER_MEMORY
Define RPC_S_ASYNC_CALL_PENDING     for ERROR_IO_PENDING
Define RPC_S_UNKNOWN_PRINCIPAL      for ERROR_NONE_MAPPED
Define RPC_S_TIMEOUT                for ERROR_TIMEOUT
Define RPC_S_NOT_ENOUGH_QUOTA       for ERROR_NOT_ENOUGH_QUOTA

Define RPC_X_NO_MEMORY              for RPC_S_OUT_OF_MEMORY
Define RPC_X_INVALID_BOUND          for RPC_S_INVALID_BOUND
Define RPC_X_INVALID_TAG            for RPC_S_INVALID_TAG
Define RPC_X_ENUM_VALUE_TOO_LARGE   for RPC_X_ENUM_VALUE_OUT_OF_RANGE
Define RPC_X_SS_CONTEXT_MISMATCH    for ERROR_INVALID_HANDLE
Define RPC_X_INVALID_BUFFER         for ERROR_INVALID_USER_BUFFER
Define RPC_X_PIPE_APP_MEMORY        for ERROR_OUTOFMEMORY
Define RPC_X_INVALID_PIPE_OPERATION for RPC_X_WRONG_PIPE_ORDER
With all the above you will be able to create and compare UUIDs. Have fun!

Comments