AutoHotkey Homepage AutoHotkey Community
Let's help each other out
 
 FAQFAQ   SearchSearch   MemberlistMemberlist   RegisterRegister 
 ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in 

Scripting.Dictionary Object as Associative Array
Goto page 1, 2, 3, 4  Next
 
Post new topic   Reply to topic    AutoHotkey Community Forum Index -> Scripts & Functions
View previous topic :: View next topic  
Author Message
Sean



Joined: 12 Feb 2007
Posts: 1141

PostPosted: Tue Mar 27, 2007 4:30 am    Post subject: Scripting.Dictionary Object as Associative Array Reply with quote

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
View user's profile Send private message
olfen



Joined: 04 Jun 2005
Posts: 99
Location: Stuttgart, Germany

PostPosted: Tue Mar 27, 2007 6:47 pm    Post subject: Reply with quote

Very nice. Thank you!
Back to top
View user's profile Send private message Visit poster's website
David Andersen



Joined: 15 Jul 2005
Posts: 81
Location: Denmark

PostPosted: Sat Mar 31, 2007 10:50 pm    Post subject: Reply with quote

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
View user's profile Send private message Send e-mail Visit poster's website
Sean



Joined: 12 Feb 2007
Posts: 1141

PostPosted: Sun Apr 01, 2007 12:02 am    Post subject: Reply with quote

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
View user's profile Send private message
Laszlo



Joined: 14 Feb 2005
Posts: 3871
Location: Pittsburgh

PostPosted: Wed Apr 18, 2007 8:18 pm    Post subject: Reply with quote

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
View user's profile Send private message Visit poster's website
majkinetor



Joined: 24 May 2006
Posts: 3544
Location: Belgrade

PostPosted: Wed Apr 18, 2007 9:06 pm    Post subject: Reply with quote

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
View user's profile Send private message MSN Messenger
Sean



Joined: 12 Feb 2007
Posts: 1141

PostPosted: Thu Apr 19, 2007 1:53 am    Post subject: Reply with quote

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
View user's profile Send private message
Laszlo



Joined: 14 Feb 2005
Posts: 3871
Location: Pittsburgh

PostPosted: Thu Apr 19, 2007 4:21 am    Post subject: Reply with quote

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
View user's profile Send private message Visit poster's website
Sean



Joined: 12 Feb 2007
Posts: 1141

PostPosted: Thu Apr 19, 2007 8:42 am    Post subject: Reply with quote

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
View user's profile Send private message
Chris
Site Admin


Joined: 02 Mar 2004
Posts: 10450

PostPosted: Fri Apr 27, 2007 3:13 pm    Post subject: Reply with quote

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
View user's profile Send private message Send e-mail
majkinetor



Joined: 24 May 2006
Posts: 3544
Location: Belgrade

PostPosted: Fri Apr 27, 2007 3:23 pm    Post subject: Reply with quote

2Chris
Why do you still support Win9x when even MS droped support for it long time ago ?
_________________
Back to top
View user's profile Send private message MSN Messenger
Laszlo



Joined: 14 Feb 2005
Posts: 3871
Location: Pittsburgh

PostPosted: Fri Apr 27, 2007 3:46 pm    Post subject: Reply with quote

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
View user's profile Send private message Visit poster's website
Sean



Joined: 12 Feb 2007
Posts: 1141

PostPosted: Fri Apr 27, 2007 4:24 pm    Post subject: Reply with quote

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
View user's profile Send private message
Chris
Site Admin


Joined: 02 Mar 2004
Posts: 10450

PostPosted: Fri Apr 27, 2007 4:27 pm    Post subject: Reply with quote

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
View user's profile Send private message Send e-mail
Laszlo



Joined: 14 Feb 2005
Posts: 3871
Location: Pittsburgh

PostPosted: Fri Apr 27, 2007 4:38 pm    Post subject: Reply with quote

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
View user's profile Send private message Visit poster's website
Display posts from previous:   
Post new topic   Reply to topic    AutoHotkey Community Forum Index -> Scripts & Functions All times are GMT
Goto page 1, 2, 3, 4  Next
Page 1 of 4

 
Jump to:  
You can post new topics in this forum
You can reply to topics in this forum


Powered by phpBB © 2001, 2005 phpBB Group