Page 1 of 1

GUID & UUID

Posted: 30 Sep 2014, 05:05
by jNizM
Globally Unique IDentifier (GUID) (wiki)
wiki wrote:A globally unique identifier is a unique reference number used as an identifier in computer software. The term "GUID" typically refers to various implementations of the universally unique identifier (UUID) standard.
GUIDs are usually stored as 128-bit values, and are commonly displayed as 32 hexadecimal digits with groups separated by hyphens.
For example: 21EC2020-3AEA-4069-A2DD-08002B30309D

CreateGUID()
Creates a GUID, a unique 128-bit integer used for CLSIDs and interface identifiers.

Code: Select all

CreateGUID()
{
    VarSetCapacity(pguid, 16, 0)
    if !(DllCall("ole32.dll\CoCreateGuid", "ptr", &pguid)) {
        size := VarSetCapacity(sguid, (38 << !!A_IsUnicode) + 1, 0)
        if (DllCall("ole32.dll\StringFromGUID2", "ptr", &pguid, "ptr", &sguid, "int", size))
            return StrGet(&sguid)
    }
    return ""
}
return:

Code: Select all

MsgBox % CreateGUID()    ; ==> {XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX}
ref:
- CoCreateGuid function
- StringFromGUID2 function
IsEqualGUID()
Determines whether two GUIDs are equal.

Code: Select all

IsEqualGUID(guid1, guid2)
{
    return DllCall("ole32\IsEqualGUID", "ptr", &guid1, "ptr", &guid2)
}
return

Code: Select all

GUID_1 := CreateGUID()
GUID_2 := CreateGUID()

MsgBox % IsEqualGUID(GUID_1, GUID_2)    ; ==> 0
MsgBox % IsEqualGUID(GUID_1, GUID_1)    ; ==> 1
ref:
- IsEqualGUID function

Re: Class & Func GUID / UUID

Posted: 30 Sep 2014, 08:09
by jNizM
Universally Unique IDentifier (UUID) (wiki)
wiki wrote:A UUID is a 16-octet (128-bit) number.
In its canonical form, a UUID is represented by 32 lowercase hexadecimal digits, displayed in five groups separated by hyphens, in the form 8-4-4-4-12 for a total of 36 characters (32 alphanumeric characters and four hyphens).
For example: 123e4567-e89b-12d3-a456-426655440000

CreateUUID()
Creates a new UUID.

Code: Select all

CreateUUID()
{
    VarSetCapacity(puuid, 16, 0)
    if !(DllCall("rpcrt4.dll\UuidCreate", "ptr", &puuid))
        if !(DllCall("rpcrt4.dll\UuidToString", "ptr", &puuid, "uint*", suuid))
            return StrGet(suuid), DllCall("rpcrt4.dll\RpcStringFree", "uint*", suuid)
    return ""
}
return

Code: Select all

MsgBox % CreateUUID()    ; ==>  xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
ref:
- UuidCreate function
- UuidToString function
- RpcStringFree function
UuidEqual()
Compare two UUIDs and determine whether they are equal.

Code: Select all

UuidEqual(uuid1, uuid2)
{
    return DllCall("rpcrt4.dll\UuidEqual", "ptr", &uuid1, "ptr", &uuid2, "ptr", &RPC_S_OK)
}
return

Code: Select all

GUID_1 := CreateUUID()
GUID_2 := CreateUUID()

MsgBox % UuidEqual(GUID_1, GUID_2)    ; ==> 0
MsgBox % UuidEqual(GUID_1, GUID_1)    ; ==> 1
ret:
- UuidEqual function

Re: Class & Func GUID / UUID

Posted: 18 May 2016, 13:39
by evilC
Just what I need, thank you!

Any advice on which would be best for me, GUIDs or UUIDs?
I need to create a unique ID for each plugin and each profile that a user creates while using my application.
The IDs only need to be unique across each install of the application - ie two different users on two different PCs having the same GUID for something is not a problem.

Part of my concern is that things that need a GUID are in a different scope, so having to check against existing IDs to see if a new one is unique would be a bit of a pain. Also, things may change scope (ie a plugin may get moved to a different profile), hence my desire for something that I can be reasonably confident is unique across the app.

Re: Class & Func GUID / UUID

Posted: 18 May 2016, 14:59
by evilC
OK, well I have implemented GUIDs in my app - GUIDs for now, not UUIDs, but that could be easily changed.

The thing is, aren't the GUIDs basically a number? A lot of things in my app compare GUIDs, so I am thinking a number comparison is going to be way way quicker than a string comparison.

Is there a way to express these GUIDs as a number? Can I just use fin_guid := NumGet(foo_guid) instead of the StringFromGUID2 call ?

I am guessing thinking that moving to a number base may not be do-able. I also have sparse arrays indexed by GUID, and I am not sure AHK supports array indexes that big as a number?

Re: Class & Func GUID / UUID

Posted: 18 May 2016, 16:02
by qwerty12
evilC wrote: The thing is, aren't the GUIDs basically a number? A lot of things in my app compare GUIDs, so I am thinking a number comparison is going to be way way quicker than a string comparison.
GUIDs are stored in a GUID struct. I think that NumGet command would only return the Data1 part of a GUID struct, maybe a bit of Data2 on x64. You can do something like if (DllCall("ntdll\RtlCompareMemory", "Ptr", &ptrToFirstGuidStruct, "Ptr", &ptrToSecondGuidStruct, "UInt", 16) == 16) to test if two GUID structs match.

Re: Class & Func GUID / UUID

Posted: 18 May 2016, 16:23
by evilC
Isn't that effectively doing the same thing as a string comparison? I don't see much scope for difference in speed there, it still has to walk all the bits of memory (or until the first difference) - unlike a number comparison, which is like load two values into registers and subtract, is result 0?

I guess probably my existing way is ok, maybe I should make sure I do == comparison to make sure i am not being wasteful.

Re: Class & Func GUID / UUID

Posted: 18 May 2016, 16:37
by qwerty12
evilC wrote:Isn't that effectively doing the same thing as a string comparison? I don't see much scope for difference in speed there, it still has to walk all the bits of memory (or until the first difference) - unlike a number comparison, which is like load two values into registers and subtract, is result 0?
Maybe. The only thing I can say for sure is that there won't be the overhead of converting to a string first to compare the GUID in its entirety.
I guess probably my existing way is ok, maybe I should make sure I do == comparison to make sure i am not being wasteful.
Like I said, you're only going to get the first part of the GUID returned with that NumGet. You'd have to keep adjusting the offsets to get the rest. If you're sure all the GUIDs you're going to be comparing do not begin with the first eight hex digits, then do that.

Re: Class & Func GUID / UUID

Posted: 18 May 2016, 17:51
by evilC
My existing method is just to do a compare on the GUID strings, so I just meant to do a case insensitive compare between strings.

Re: Class & Func GUID / UUID

Posted: 19 May 2016, 01:19
by jNizM
IsEqualGUID function?

I rewrite the 2 top posts

Re: GUID & UUID

Posted: 19 May 2016, 06:22
by evilC
Yeah, but it seems like you must pass the guid struct to IsEqualGUID, but my GUIDs are going to need to be serialized to JSON for saving to disk, so I guess I would need to re-build the struct on load if I were to use that.

Re: GUID & UUID

Posted: 19 May 2016, 06:58
by jNizM
Can you show a small example how it looks like what you do?

Re: GUID & UUID

Posted: 19 May 2016, 14:57
by evilC
Here is some sample code - it's what gets called when you delete a profile - it recursively deletes child profiles too.

Code: Select all

	; user clicked the Delete Profile button
	_DeleteProfile(){
		id := this.CurrentProfile.id
		if (id = 1 || id = 2)
			return
		pp := this.CurrentProfile.ParentProfile
		if pp != 0
			newprofile := pp
		else
			newprofile := 2
		this._DeleteChildProfiles(id)
		this.__DeleteProfile(id)
		this.UpdateProfileToolbox()
		this.ChangeProfile(newprofile)
	}

	; Actually deletes a profile
	__DeleteProfile(id){
		; Remove profile's entry from ProfileTree
		profile := this.Profiles[id]
		treenode := this.ProfileTree[profile.ParentProfile]
		for k, v in treenode {
			if (v == id){
				treenode.Remove(k)	; Use Remove, so indexes shuffle down.
				; If array is empty, remove from tree
				if (!treenode.length())
					this.ProfileTree.Delete(profile.ParentProfile)	; Sparse array - use Delete instead of Remove
				break
			}
		}
		; Terminate profile input thread
		this._SetProfileInputThreadState(profile.id,0)
		; Kill profile object
		this.profiles.Delete(profile.id)
	}
	
	; Recursively deletes child profiles
	_DeleteChildProfiles(id){
		for i, profile in this.Profiles{
			if (profile.ParentProfile = id){
				this._DeleteChildProfiles(profile.id)
				this.__DeleteProfile(profile.id)
			}
		}
	}
And here is an example of a serialized JSON settings file that features a child profile:

Code: Select all

{
 "CurrentPos": {
  "x": 3,
  "y": 2
 },
 "CurrentProfile": "9A9496BD-B4AF-40C4-A712-FE183A9B55DE",
 "CurrentSize": {
  "h": 300,
  "w": "835"
 },
 "Profiles": {
  "1": {
   "Name": "Global",
   "ParentProfile": "0",
   "PluginOrder": {},
   "Plugins": {}
  },
  "2": {
   "Name": "Default",
   "ParentProfile": "0",
   "PluginOrder": {},
   "Plugins": {}
  },
  "9A9496BD-B4AF-40C4-A712-FE183A9B55DE": {
   "Name": "Profile 2",
   "ParentProfile": "D813269D-C18E-4489-9CFD-2AD5C24B4EDC",
   "PluginOrder": {},
   "Plugins": {}
  },
  "D813269D-C18E-4489-9CFD-2AD5C24B4EDC": {
   "Name": "Profile 1",
   "ParentProfile": 0,
   "PluginOrder": [
    "8800F8E8-3316-4F57-8CEB-38F58219F143"
   ],
   "Plugins": {
    "8800F8E8-3316-4F57-8CEB-38F58219F143": {
     "GuiControls": {},
     "InputAxes": {},
     "InputButtons": {
      "IB1": {
       "Block": 0,
       "Buttons": [
        {
         "Code": "123",
         "DeviceID": 0,
         "IsVirtual": 0,
         "Type": 1,
         "UID": ""
        }
       ],
       "Suppress": 0,
       "Type": 1,
       "Wild": 0
      }
     },
     "InputDeltas": {},
     "name": "ButtonToButton 1",
     "OutputAxes": {},
     "OutputButtons": {
      "OB1": {
       "Block": 0,
       "Buttons": [
        {
         "Code": 1,
         "DeviceID": 1,
         "IsVirtual": 1,
         "Type": 2,
         "UID": ""
        }
       ],
       "Suppress": 0,
       "Type": 2,
       "Wild": 0
      }
     },
     "Type": "ButtonToButton"
    }
   }
  }
 },
 "ProfileTree": {
  "0": [
   1,
   2,
   "D813269D-C18E-4489-9CFD-2AD5C24B4EDC"
  ],
  "D813269D-C18E-4489-9CFD-2AD5C24B4EDC": [
   "9A9496BD-B4AF-40C4-A712-FE183A9B55DE"
  ]
 },
 "SettingsVersion": "0.0.4"
}

Re: GUID & UUID

Posted: 25 Feb 2018, 03:54
by lexikos
The current version of CreateGUID() does not work on ANSI builds, since StringFromGUID2 is Unicode-only. This version works:

Code: Select all

CreateGUID()
{
    VarSetCapacity(pguid, 16)
    if !(DllCall("ole32.dll\CoCreateGuid", "ptr", &pguid)) {
        VarSetCapacity(sguid, 38 * 2 + 1)
        if (DllCall("ole32.dll\StringFromGUID2", "ptr", &pguid, "ptr", &sguid, "int", 38 + 1))
            return StrGet(&sguid, "UTF-16")
    }
    return ""
}
Also note the size must be specified in characters, whereas VarSetCapacity returns a byte count (and VarSetCapacity excludes the null-terminator, whereas StringFromGUID2 includes it).

Re: GUID & UUID

Posted: 25 Feb 2018, 08:09
by evilC
Sweet, thanks!

Re: GUID & UUID

Posted: 28 Feb 2018, 16:41
by Ferry
You also can use COM :D

Code: Select all

GenerateGUID()
	{
	TypeLib := ComObjCreate("Scriptlet.TypeLib")
	RANDOMGUID := TypeLib.Guid
	Transform, RANDOMGUID, deref, %RANDOMGUID%
	ObjRelease(TypeLib)
	Return RANDOMGUID
	}

Re: GUID & UUID

Posted: 28 Feb 2018, 17:22
by burque505
:bravo: Nice one, Ferry.

Re: GUID & UUID

Posted: 05 Mar 2018, 04:10
by lexikos
Ferry wrote:You also can use COM
What is the purpose of using Transform Deref here?

This is not a valid use of ObjRelease(). There is no reason to call it here.

Code: Select all

MsgBox % ComObjCreate("Scriptlet.TypeLib").Guid
You get a random GUID because each new TypeLib should have its own ID. This is not the purpose of the TypeLib class. Creating a TypeLib just to get a random GUID is a neat trick, but most likely inefficient.

Re: GUID & UUID

Posted: 09 Mar 2018, 02:13
by Helgef
Great stuff, as always, thanks for sharing jNizM :thumbup:.
Cheers
Spoiler

Re: GUID & UUID

Posted: 09 Mar 2018, 09:36
by jeeswg
There's a similar script here:
To Share: Simple GUID (Globally Unique IDentifier) Generator - AutoHotkey Community
https://autohotkey.com/boards/viewtopic.php?f=6&t=38822

:? But how can we be sure that the GUID is unique!?

Re: GUID & UUID

Posted: 09 Mar 2018, 10:27
by Helgef
jeeswg wrote: :? But how can we be sure that the GUID is unique!?
Ofc you cannot, calling CreateGUID() enough times will eventually cause duplicates, however the amount of times needed is expected to be so astronomically large that the generated strings can be considered unique. But, if you start reusing guids, you are out on thin ice. Re. your link, it seemed familiar :lol: .

Cheers.