Updated 2014-06-12 00:22:19 by RLE

UUIDs (Universally Unique Identifier) or GUIDs (Globally Unique Identifier) are defined in this document: [1]


PT 8-Jul-2004: I have just added a UUID module to tcllib that implements type 4 uuids from the draft specification document. eg:
 % package require uuid
 1.0.0
 % set id [uuid::uuid generate]
 140d7c2c-4502-4144-6ae0-a4692e8ed819
 % if {[uuid::uuid equal $id $id]} { puts "ids equal" }

JMN 2005-11-26. On one of my windows machines - UUID version 1.0.0 calls to [uuid:uuid generate] take around 4.5 seconds to complete. This makes the function impractical for most uses. The delay seems to occur in a call to 'fconfigure <somesocket> -sockname'. I don't know if it's a peculiarity of my machine's network configuration or not - but even so it seems to me that this tcllib package should avoid calls that may take such a relatively long time.

PT 2005-11-27: you should raise a bug report on sourceforge if you have not already done so. My answer is that some time before the tcllib 1.8 release we stopped using information from the socket call because it can be a problem when the XP firewall is in place. Obtain uuid 1.0.1. A second method it to use ActiveTcl or build the critcl extensions for tcllib. The uuid package includes critcl code which it will use if it can and this calls directly to the Win32 UuidCreate API.

Windows provides an API for generating GUIDs and some extensions make use of this, but I needed a pure-Tcl UUID generator. So, I came up with this. This proc, by no means, conforms to the standards discussed in the above document, but it does produce unique* identifiers that are good enough for me. :) Damon Courtney
 proc uuid {} {
     ## Try and get a string unique to this host.
     set str [info hostname]$::tcl_platform(machine)$::tcl_platform(os)
     binary scan $str h* hex

     set    uuid [format %2.2x [clock seconds]]
     append uuid -[string range [format %2.2x [clock clicks]] 0 3]
     append uuid -[string range [format %2.2x [clock clicks]] 2 5]
     append uuid -[string range [format %2.2x [clock clicks]] 4 end]
     append uuid -[string range $hex 0 11]

     return $uuid
 }

 proc guid {} {
     ## Return a GUID for Windows, which is just an uppercase UUID with braces.
     return \{[string toupper [uuid]]\}
 }

*JMN 2005-11-26 Note that the above 'guid' function does not produce unique values for quick successive calls.

e.g using the following script, you will most likely get the same value in a, b & c.
 foreach {a b c} [list [guid] [guid] [guid]] {break}

For a result that is more likely to be unique - you may want to add a call to [expr {rand()}] or similar.

Scott Nichols

I wrote a Windows C based Tcl extension for this (TclGetGUID) a few months back, and Pat helped by recommending the use of the Tcl C API call Tcl_UtfToUpper for converting a string to uppercase. The C source is really short and simple. It simply calls some underlying Windows APIs.

DKF: Don't forget to include planet and stellar system identifiers in that UUID... :^D

Scott Beasley

I needed a working GUID generator that would be Multi Platform, generate unique id's, fast and in pure tcl. The Below code is what I came up with. I tried to use the tclib uuid, but it was just to slow for my needs and I could not ensure a critcl based compile on the hundreds of machines the code would run on. While posting this, I saw that the uuid proc above has some sameness :p Just a coincidence, only so many ways to do something simple I guess. This code has been tested and run on Windows XP, 2000-2003, Linux, FreeBSD and Mac OS X. It has generated over 400 million GUIDS without a dup in testing. I'm not guaranteeing anything though :)

To use, just call the guid_init to setup the seed and machine info before you call the guid proc.
 proc guid_init { } {
    if {![info exists ::GuiD__SeEd__VaR]} {
       set ::GuiD__SeEd__VaR 0
    }

    if {![info exists ::GuiD__MaChInFo__VaR]} {
       set ::GuiD__MaChInFo__VaR $::tcl_platform(user)[info hostname]$::tcl_platform(machine)$::tcl_platform(os)
    }
 }

 proc guid { } {
    # String together the Seed and Machine Info along with a random number for a end base.
    set MachInfo [expr {rand()}]$::GuiD__SeEd__VaR$::GuiD__MaChInFo__VaR
    binary scan $MachInfo h* MachInfo_Hex

    # Set the first part as the datetime in seconds.
    set guid [format %2.2x [clock seconds]]

    # Pick though clock clicks for a good "Random" sequence.
    append guid -[string range [format %2.2x [clock clicks]] 0 3] \
                -[string range [format %2.2x [clock clicks]] 1 4] \
                -[string range [format %2.2x [clock clicks]] 4 end] \
                -[string range $MachInfo_Hex 0 11]
    incr ::GuiD__SeEd__VaR

    return [string toupper $guid]
 }

Scott Beasley 2008-06-05: Per a Text exchange with Mr. Lars H I have thrown info cmdcount into the mix to hopefully help out on older machines in regards to uniqueness. The Above seems to work fine on newer faster (2001 onward) machines, this one on older (Year 2000 and before). This is still a work in progress.
 proc guid { } {
    set MachInfo [expr {rand()}]$::GuiD__SeEd__VaR$::GuiD__MaChInFo__VaR
    binary scan $MachInfo h* MachInfo_Hex
    set CmdCntAndSeq [string range "[info cmdcount]$::GuiD__SeEd__VaR$::GuiD__SeEd__VaR" 0 8]
    binary scan [expr {rand()}] h* Rand_Hex

    set guid [format %2.2x [clock seconds]]
    # Pick though clock clicks for a good sequence.
    append guid -[string range [format %2.2x [clock clicks]] 0 3] \
                -[string range [format %2.2x $CmdCntAndSeq] 0 3] \
                -[string range $Rand_Hex 3 6] \
                -[string range $MachInfo_Hex 0 11]
    incr ::GuiD__SeEd__VaR

    return [string toupper $guid]
 }

NEB: 2010-03-24 I'm new to TCL, so take this all with a grain of salt: I needed a way to format GUIDs pulled from LDAP, and puts them. Unfortunately, they dump as binary, so I needed to format them to a string. I couldn't find a way in the ldap modules, but did find the uuid module in tcllib. Unfortunately, although I can dig in and use ::uuid::tostring (once I found it) it doesn't format in the proper sequence. It seems to just take the array of bytes, and hexify them. GUIDS (at least on Windows) store the first three blocks smaller-byte-first. Note that tcllib's version should be perfectly sufficient for a generated UUID... it should be just as 'random' as if they were in the 'correct' order; but it will show the wrong string if you pass an existing binary GUID through it.

Here is an example, along with a procedure of my solution:
 # HKEY_CLASSES_ROOT\device\CLSID
 # Device = {4315D437-5B8C-11D0-BD3B-00A0C911CE86}
 # Prog ID from CLSID dumped to a byte array:
 # \x37\xD4\x15\x43\x8C\x5B\xD0\x11\xBD\x3B\x0\xA0\xC9\x11\xCE\x86

 set binGuid \x37\xD4\x15\x43\x8C\x5B\xD0\x11\xBD\x3B\x0\xA0\xC9\x11\xCE\x86
 puts "binGuid=$binGuid"
 puts "String GUID Should be\n{4315D437-5B8C-11D0-BD3B-00A0C911CE86}\n"

 proc GUID2String { guid } {
         if {[binary scan "$guid" iu1su1su1Su1Iu1Su1 p0 p1 p2 p3 p4 p5]==6} {
                 return [format "{%08X-%04X-%04X-%04X-%08X%04X}" $p0 $p1 $p2 $p3 $p4 $p5]
         }
 }

 puts "Binary scan conversions:"
 puts [GUID2String \x37\xD4\x15\x43\x8C\x5B\xD0\x11\xBD\x3B\x0\xA0\xC9\x11\xCE\x86]
 puts [GUID2String $binGuid]

 puts "Via the tcllib::uuid conversion:"
 package require Tcl
 package require uuid
 puts "\{[::uuid::tostring \x37\xD4\x15\x43\x8C\x5B\xD0\x11\xBD\x3B\x0\xA0\xC9\x11\xCE\x86]\}"

I was going to pull a given GUID from the system, and pass the binary through, but that doesn't seem to be working for me. The code below prints two strings--although the API calls should both return a struct. Notably, the second one returns what I put into it (!) so either I am doing something wrong (highly likely) or it isn't a straight pass-through as the website led me to believe.
 puts "Grabbing Class ID"
 package require twapi
 puts [::twapi::CLSIDFromProgID Device]
 puts [::twapi::CLSIDFromString "{4315D437-5B8C-11D0-BD3B-00A0C911CE86}"]

I'll know more as I figure out how to read the source . . .

APE: 2013-10-08 ...and the way to generate uuids with twapi (this one is very fast: 4 ms to generate 1000 uuids)
 ::twapi::new_uuid