 |
AutoHotkey Community Let's help each other out
|
| View previous topic :: View next topic |
| Author |
Message |
Sean
Joined: 12 Feb 2007 Posts: 1141
|
Posted: Tue Mar 27, 2007 4:30 am Post subject: Scripting.Dictionary Object as Associative Array |
|
|
I think the Scripting.Dictionary COM object is already an associative array, so, may be used as the replacement until AHK has a built-in support of it.
DOWNLOAD Dictionary.ahk and CoHelper.ahk.
Last edited by Sean on Wed Aug 22, 2007 6:48 am; edited 2 times in total |
|
| Back to top |
|
 |
olfen
Joined: 04 Jun 2005 Posts: 99 Location: Stuttgart, Germany
|
Posted: Tue Mar 27, 2007 6:47 pm Post subject: |
|
|
| Very nice. Thank you! |
|
| Back to top |
|
 |
David Andersen
Joined: 15 Jul 2005 Posts: 81 Location: Denmark
|
Posted: Sat Mar 31, 2007 10:50 pm Post subject: |
|
|
| Thanks a lot Sean. Do you have any idea what the performance would be using thousands of words? Does it depend on any libraries? |
|
| Back to top |
|
 |
Sean
Joined: 12 Feb 2007 Posts: 1141
|
Posted: Sun Apr 01, 2007 12:02 am Post subject: |
|
|
| David Andersen wrote: | | Thanks a lot Sean. Do you have any idea what the performance would be using thousands of words? Does it depend on any libraries? |
Thanks. I think it uses a hash table, so basically the look-up time should be independent on the number of items in the table. In reality, however, there could be collisions among them, depending on the implementations, so it could grow linearly with the number of items in the worst case, but I don't think it would ever happen. |
|
| Back to top |
|
 |
Laszlo
Joined: 14 Feb 2005 Posts: 3871 Location: Pittsburgh
|
Posted: Wed Apr 18, 2007 8:18 pm Post subject: |
|
|
It works perfectly! Thanks for sharing this excellent list of functions. I have a few questions, though.
- What are those magic numbers in the Dictionary function, like {EE09B103-97E0-11CF-978F-00A02463E06F}? (Where can we read about them?)
- Are they the same for every Windows version?
- What are computed by the functions Items() and Keys()?
- If you call "SafeArrayDestroy" at exit, will it invalidate their result? |
|
| Back to top |
|
 |
majkinetor
Joined: 24 May 2006 Posts: 3544 Location: Belgrade
|
Posted: Wed Apr 18, 2007 9:06 pm Post subject: |
|
|
| Quote: | | - What are those magic numbers in the Dictionary function, like {EE09B103-97E0-11CF-978F-00A02463E06F}? (Where can we read about them?) |
ITs called GUID, its just a unique number COM uses for id purposes. The human friendly name is called ProgId.
| Quote: | | - Are they the same for every Windows version? |
ofc _________________
 |
|
| Back to top |
|
 |
Sean
Joined: 12 Feb 2007 Posts: 1141
|
Posted: Thu Apr 19, 2007 1:53 am Post subject: |
|
|
| Laszlo wrote: | | It works perfectly! Thanks for sharing this excellent list of functions. I have a few questions, though. |
Thanks.
| Quote: | - What are those magic numbers in the Dictionary function, like {EE09B103-97E0-11CF-978F-00A02463E06F}? (Where can we read about them?)
- Are they the same for every Windows version? |
They are Class Identifier and Interface Identifier in GUID form.
The CLSID should be registered in the registry to be used, however, not neccessarily for IID but mostly registered too.
If the COM interface is of IDispatch type, then you may prefer to get them through the TypeLib using oleview.exe or tlb.exe mentioned here:
http://www.autohotkey.com/forum/viewtopic.php?t=16187&postdays=0&postorder=asc&start=15
And, yes, you may take it granted that they are version independent.
| Quote: | - What are computed by the functions Items() and Keys()?
- If you call "SafeArrayDestroy" at exit, will it invalidate their result? |
They return all defined keys/values in VB's Type-Safe arrays.
http://msdn2.microsoft.com/en-us/library/ms221482.aspx
As the SafeArray is not that intuitive to use comparing with the array in C/C++ (:MS itself urges to use dedicated functions to manipulate them), I don't recommend using them unless absolutely neccessary. I recommend using Enumerate() instead.
BTW, I don't think destroying the SafeArrays will affect the Dictionary itself. |
|
| Back to top |
|
 |
Laszlo
Joined: 14 Feb 2005 Posts: 3871 Location: Pittsburgh
|
Posted: Thu Apr 19, 2007 4:21 am Post subject: |
|
|
Thanks for the quick reply. | Sean wrote: | | I don't think destroying the SafeArrays will affect the Dictionary itself. | I did not mean that the dictionary was affected, but a SafeArray was returned (a pointer to a structure?), which just had been destroyed. Is the pointer after returning from the function is still pointing to allocated memory? |
|
| Back to top |
|
 |
Sean
Joined: 12 Feb 2007 Posts: 1141
|
Posted: Thu Apr 19, 2007 8:42 am Post subject: |
|
|
| Laszlo wrote: | | I did not mean that the dictionary was affected, but a SafeArray was returned (a pointer to a structure?), which just had been destroyed. Is the pointer after returning from the function is still pointing to allocated memory? |
In my tests, the memory pointed, which is the descriptor of the SafeArray, could still be accessed until the object itself was released. However, the releavant data contained in it were cleared out. |
|
| Back to top |
|
 |
Chris Site Admin
Joined: 02 Mar 2004 Posts: 10450
|
Posted: Fri Apr 27, 2007 3:13 pm Post subject: |
|
|
| Thanks for these great functions. I've linked to this topic from the bottom of the Arrays page. Does anyone know whether Windows 9x/NT supports Scripting.Dictionary? |
|
| Back to top |
|
 |
majkinetor
Joined: 24 May 2006 Posts: 3544 Location: Belgrade
|
Posted: Fri Apr 27, 2007 3:23 pm Post subject: |
|
|
2Chris
Why do you still support Win9x when even MS droped support for it long time ago ? _________________
 |
|
| Back to top |
|
 |
Laszlo
Joined: 14 Feb 2005 Posts: 3871 Location: Pittsburgh
|
Posted: Fri Apr 27, 2007 3:46 pm Post subject: |
|
|
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.)
Last edited by Laszlo on Sat May 12, 2007 8:47 pm; edited 2 times in total |
|
| Back to top |
|
 |
Sean
Joined: 12 Feb 2007 Posts: 1141
|
Posted: Fri Apr 27, 2007 4:24 pm Post subject: |
|
|
| Laszlo wrote: | | 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: |
Nice! I have a suggestion, though.
Would you retain the name SysAllocString?
I once got complaint from foom that I used different names from the API.
And, it's not mere allocation. The important property about it is that the string is owned by the system so that it can cross the process boundary. |
|
| Back to top |
|
 |
Chris Site Admin
Joined: 02 Mar 2004 Posts: 10450
|
Posted: Fri Apr 27, 2007 4:27 pm Post subject: |
|
|
Laszlo, thanks for the refinements.
Sean, to the extent you agree with Laszlo's changes, maybe you'd consider applying them to the topmost post. |
|
| Back to top |
|
 |
Laszlo
Joined: 14 Feb 2005 Posts: 3871 Location: Pittsburgh
|
Posted: Fri Apr 27, 2007 4:38 pm Post subject: |
|
|
| Sean wrote: | Would you retain the name SysAllocString?
I once got complaint from foom that I used different names from the API.
And, it's not mere allocation. The important property about it is that the string is owned by the system so that it can cross the process boundary. | AllocString() does more than SysAllocString() in CoHelper.ahk (fill memory). The AllocString name was meant to avoid conflicts with the original. It does allocate a string now, so foom might accept this name for a different functionality. |
|
| Back to top |
|
 |
|
|
You can post new topics in this forum You can reply to topics in this forum
|
Powered by phpBB © 2001, 2005 phpBB Group
|