GUID & UUID

Post your working scripts, libraries and tools for AHK v1.1 and older
User avatar
jNizM
Posts: 3183
Joined: 30 Sep 2013, 01:33
Contact:

GUID & UUID

30 Sep 2014, 05:05

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
[AHK] v2.0.5 | [WIN] 11 Pro (Version 22H2) | [GitHub] Profile
User avatar
jNizM
Posts: 3183
Joined: 30 Sep 2013, 01:33
Contact:

Re: Class & Func GUID / UUID

30 Sep 2014, 08:09

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
[AHK] v2.0.5 | [WIN] 11 Pro (Version 22H2) | [GitHub] Profile
User avatar
evilC
Posts: 4822
Joined: 27 Feb 2014, 12:30

Re: Class & Func GUID / UUID

18 May 2016, 13:39

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.
User avatar
evilC
Posts: 4822
Joined: 27 Feb 2014, 12:30

Re: Class & Func GUID / UUID

18 May 2016, 14:59

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?
qwerty12
Posts: 468
Joined: 04 Mar 2016, 04:33
Contact:

Re: Class & Func GUID / UUID

18 May 2016, 16:02

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.
User avatar
evilC
Posts: 4822
Joined: 27 Feb 2014, 12:30

Re: Class & Func GUID / UUID

18 May 2016, 16:23

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.
qwerty12
Posts: 468
Joined: 04 Mar 2016, 04:33
Contact:

Re: Class & Func GUID / UUID

18 May 2016, 16:37

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.
User avatar
jNizM
Posts: 3183
Joined: 30 Sep 2013, 01:33
Contact:

Re: Class & Func GUID / UUID

19 May 2016, 01:19

IsEqualGUID function?

I rewrite the 2 top posts
[AHK] v2.0.5 | [WIN] 11 Pro (Version 22H2) | [GitHub] Profile
User avatar
evilC
Posts: 4822
Joined: 27 Feb 2014, 12:30

Re: GUID & UUID

19 May 2016, 06:22

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.
User avatar
jNizM
Posts: 3183
Joined: 30 Sep 2013, 01:33
Contact:

Re: GUID & UUID

19 May 2016, 06:58

Can you show a small example how it looks like what you do?
[AHK] v2.0.5 | [WIN] 11 Pro (Version 22H2) | [GitHub] Profile
User avatar
evilC
Posts: 4822
Joined: 27 Feb 2014, 12:30

Re: GUID & UUID

19 May 2016, 14:57

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"
}
lexikos
Posts: 9553
Joined: 30 Sep 2013, 04:07
Contact:

Re: GUID & UUID

25 Feb 2018, 03:54

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).
Ferry
Posts: 13
Joined: 10 Jul 2014, 15:55

Re: GUID & UUID

28 Feb 2018, 16:41

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
	}
burque505
Posts: 1731
Joined: 22 Jan 2017, 19:37

Re: GUID & UUID

28 Feb 2018, 17:22

:bravo: Nice one, Ferry.
lexikos
Posts: 9553
Joined: 30 Sep 2013, 04:07
Contact:

Re: GUID & UUID

05 Mar 2018, 04:10

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.
Helgef
Posts: 4709
Joined: 17 Jul 2016, 01:02
Contact:

Re: GUID & UUID

09 Mar 2018, 02:13

Great stuff, as always, thanks for sharing jNizM :thumbup:.
Cheers
Spoiler
User avatar
jeeswg
Posts: 6902
Joined: 19 Dec 2016, 01:58
Location: UK

Re: GUID & UUID

09 Mar 2018, 09:36

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!?
homepage | tutorials | wish list | fun threads | donate
WARNING: copy your posts/messages before hitting Submit as you may lose them due to CAPTCHA
Helgef
Posts: 4709
Joined: 17 Jul 2016, 01:02
Contact:

Re: GUID & UUID

09 Mar 2018, 10:27

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.

Return to “Scripts and Functions (v1)”

Who is online

Users browsing this forum: KruschenZ and 106 guests