Create a New Universally Unique IDentifier (UUID)
By Vincent Oorsprong
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 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:
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:
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:
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 UUIDs? The Windows API function definitions are:
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:
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:
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 DataFlex application add the following code to your application:
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:
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:
External_Function WinAPI_UuidCreateNil "UuidCreateNil" rpcrt4.dll Address aUuid Returns Integer
You use the above function as follows:
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:
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:
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.
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!