I edited a little Sean's great script: changed some function names, added more test cases, replaced the enumerate function with NextKey (which is of lower level, providing more flexibility) removed the safe array functions (which don't work for me) and copied over the used COM helper functions, so there will be no dependency from another file. (If your project uses other COM functions, you may prefer the original #include.) This script is still Sean's, only written in another style:
Code:
DllCall("ole32\CoInitialize", UInt,0)
pdic := CreateDictionary() ; param = 1: Case of Key is ignored. 0 (Default): case-sensitive
Add(pdic,"Test1", "This is a Test1")
Add(pdic,"Test2", "This is a Test2")
Add(pdic,"Test1", "This is a Test3") ; no effect, already exists
Add(pdic,"Test3", "This is a Test9")
Add(pdic,"Test0", "")
Set(pdic,"Result1", "This is a Result1") ; create and set
Set(pdic,"Result2", "This is a Result2")
Set(pdic,"Result1", "This is a Result3") ; overwrite
Rename(pdic, "Result1", "Result3")
Remove(pdic, "Test3")
MsgBox, % "Hash of 'Test9': " . HashVal(pdic,"Test9")
MsgBox, % "Count: " . Count(pdic)
MsgBox % "Test1:" . Get(pdic,"Test1")
MsgBox % "test2:" . Get(pdic,"test2") ; empty
Loop {
k := NextKey(penum,pdic)
IfEqual penum,, Break ; k (Itm) can be empty
MsgBox % "[1/" . A_Index . "]" . k . ":" . Get(pdic,k)
}
Loop % Count(pdic) {
k := NextKey(penum, pdic)
MsgBox % "[2a/" . A_Index . "]" . k . ":" . Get(pdic,k)
k := NextKey(penu1,pdic)
MsgBox % "[2b/" . A_Index . "]" . k . ":" . Get(pdic,k)
}
DestroyDictionary(pdic)
DllCall("ole32\CoUninitialize")
Return
CreateDictionary(nCompMode = 0) { ; Compare mode = 0: Binary, 1: Text, 2: Database, n: LCID
CLSID_Dictionary := "{EE09B103-97E0-11CF-978F-00A02463E06F}"
IID_IDictionary := "{42C642C1-97E1-11CF-978F-00A02463E06F}"
pdic := CreateObject(CLSID_Dictionary, IID_IDictionary)
DllCall(VTable(pdic,18), UInt,pdic, Int,nCompMode) ; Set compare mode
Return pdic
}
DestroyDictionary(pdic) {
DllCall(UInt@(UInt@(pdic)+8), UInt,pdic)
}
Add(pdic, sKey, sItm) { ; If key exists: no effect (<--> Set)
AllocBString(pKey, var1, sKey)
AllocBString(pItm, var2, sItm)
DllCall(VTable(pdic,10), UInt,pdic, UInt,&var1, UInt,&var2)
DllCall("oleaut32\SysFreeString", UInt,pKey)
DllCall("oleaut32\SysFreeString", UInt,pItm)
}
Set(pdic, sKey, sItm) { ; If key exists, update the item, Else create a new entry
AllocBString(pKey, var1, sKey)
AllocBString(pItm, var2, sItm)
DllCall(VTable(pdic,8), UInt,pdic, UInt,&var1, UInt,&var2) ; 8 (Set0 -> 7)
DllCall("oleaut32\SysFreeString", UInt,pKey)
DllCall("oleaut32\SysFreeString", UInt,pItm)
}
Get(pdic, sKey) { ; empty if not exists
AllocBString(pKey, var1, sKey)
DllCall(VTable(pdic,12), UInt,pdic, UInt,&var1, IntP,bExist)
If bExist { ; to avoid creating an unwanted new entry
VarSetCapacity(var2, 16, 0)
DllCall(VTable(pdic,9), UInt,pdic, UInt,&var1, UInt,&var2)
pItm := *(&var2+8) | *(&var2+9) << 8 | *(&var2+10) << 16 | *(&var2+11) << 24
Unicode2Ansi(pItm, sItm)
DllCall("oleaut32\SysFreeString", UInt,pItm)
}
DllCall("oleaut32\SysFreeString", UInt,pKey)
Return sItm
}
Count(pdic) { ; #entries
DllCall(VTable(pdic,11), UInt,pdic, IntP,nCount)
Return nCount
}
Exists(pdic, sKey) {
AllocBString(pKey, var, sKey)
DllCall(VTable(pdic,12), UInt,pdic, UInt,&var, IntP,bExist)
DllCall("oleaut32\SysFreeString", UInt,pKey)
Return bExist
}
Rename(pdic, sKeyFr, sKeyTo) {
AllocBString(pKeyFr, var1, sKeyFr)
AllocBString(pKeyTo, var2, sKeyTo)
DllCall(VTable(pdic,14), UInt,pdic, UInt,&var1, UInt,&var2)
DllCall("oleaut32\SysFreeString", UInt,pKeyFr)
DllCall("oleaut32\SysFreeString", UInt,pKeyTo)
}
Remove(pdic, sKey) {
AllocBString(pKey, var, sKey)
DllCall(VTable(pdic,16), UInt,pdic, UInt,&var)
DllCall("oleaut32\SysFreeString", UInt,pKey)
}
RemoveAll(pdic) {
Return DllCall(VTable(pdic,17), UInt,pdic)
}
GetCompareMode(pdic) {
DllCall(VTable(pdic,19), UInt,pdic, IntP,nCompMode)
Return nCompMode
}
HashVal(pdic,sKey) {
AllocBString(pKey, var1, sKey)
DllCall(VTable(pdic,12), UInt,pdic, UInt,&var1, IntP,bExist)
VarSetCapacity(var2, 16, 0)
DllCall(VTable(pdic,21), UInt,pdic, UInt,&var1, UInt,&var2)
nHashVal := *(&var2+8) | *(&var2+9) << 8 | *(&var2+10) << 16 | *(&var2+11) << 24
DllCall("oleaut32\SysFreeString", UInt,pKey)
Return nHashVal
}
NextKey(ByRef penum, pdic="") { ; penum = "": create new list. pdic=0,"Clear": destroy list
If penum = ; not static: allow multiple independent lists
DllCall(VTable(pdic,20), UInt,pdic, UIntP,penum) ; create key-list in penum
VarSetCapacity(var, 16, 0)
If (InStr("Clear0",pdic) || DllCall(VTable(penum,3), UInt,penum, UInt,1, UInt,&var, UInt,0)) {
DllCall(VTable(penum,2), UInt,penum) ; END: destroy key-list
penum = ; signal end of list
Return ; empty
}
pKey := UInt@(&var + 8)
Unicode2Ansi(pKey, sKey)
DllCall("oleaut32\SysFreeString", UInt,pKey)
Return skey
}
AllocBString(ByRef Key, ByRef Var, sString) {
Ansi2Unicode(sString, wString)
Key := DllCall("oleaut32\SysAllocString", Str,wString)
VarSetCapacity(Var, 16, 0)
DllCall("ntdll\RtlFillMemoryUlong", UInt,&Var, UInt,4, UInt,8)
DllCall("ntdll\RtlFillMemoryUlong", UInt,&Var+8,UInt,4, UInt,Key)
}
UInt@(ptr) {
Return *ptr | *(ptr+1) << 8 | *(ptr+2) << 16 | *(ptr+3) << 24
}
;;; COM Helper functions
CreateObject(ByRef CLSID, ByRef IID, CLSCTX = 5) {
If StrLen(CLSID) = 38
GUID4String(CLSID, CLSID)
If StrLen(IID) = 38
GUID4String(IID, IID)
DllCall("ole32\CoCreateInstance", Str,CLSID, UInt,0, UInt,CLSCTX, Str,IID, UIntP,ppv)
Return ppv
}
GUID4String(Byref CLSID, sString) {
VarSetCapacity(CLSID, 16)
Ansi2Unicode(sString, wString, 38)
DllCall("ole32\CLSIDFromString", Str,wString, Str,CLSID)
}
Ansi2Unicode(ByRef sString, ByRef wString, nLen = 0) {
If !nLen
nLen := DllCall("MultiByteToWideChar", UInt,0, UInt,0, UInt,&sString, Int,-1, UInt,0, Int,0)
VarSetCapacity(wString, nLen*2 + 1)
DllCall("MultiByteToWideChar", UInt,0, UInt,0, UInt,&sString, Int,-1, UInt,&wString, Int,nLen)
}
VTable(ppv, idx) {
Return UInt@(UInt@(ppv) + idx*4)
}
Unicode2Ansi(ByRef wString, ByRef sString, nLen = 0) {
pString := wString + 0 > 65535 ? wString : &wString
If !nLen
nLen := DllCall("WideCharToMultiByte", UInt,0, UInt,0, UInt,pString, Int,-1, UInt,0, Int,0, UInt,0, UInt,0)
VarSetCapacity(sString, nLen)
DllCall("WideCharToMultiByte", UInt,0, UInt,0, UInt,pString, Int,-1, Str,sString, Int,nLen, UInt,0, UInt,0)
}
It looks like dictionaries, key lists are created in the COM memory space, which is destroyed when the script exits. Accordingly, crashed scripts or scripts terminated without explicitly destroying these data structures do not cause memory leaks. I tested it with XP SP2. If you experience problems, please post it here!
Edit 20070427: Changed function name AllocString to AllocBString
Edit 20070512: moved SetCompareMode into CreateDictionary, and added option to the NextKey function: if its 2nd parameter is 0 or a substring of "Clear", the penum list will be destroyed. (Thanks Toralf.)