AutoHotkey Community

It is currently May 27th, 2012, 11:02 am

All times are UTC [ DST ]




Post new topic Reply to topic  [ 62 posts ]  Go to page 1, 2, 3, 4, 5  Next
Author Message
PostPosted: March 27th, 2007, 4:30 am 
Offline

Joined: February 12th, 2007, 7:54 am
Posts: 2462
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 August 22nd, 2007, 6:48 am, edited 2 times in total.

Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: March 27th, 2007, 6:47 pm 
Offline

Joined: June 4th, 2005, 1:30 am
Posts: 113
Location: Stuttgart, Germany
Very nice. Thank you!


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: March 31st, 2007, 10:50 pm 
Offline

Joined: July 15th, 2005, 3:19 pm
Posts: 140
Location: Denmark
Thanks a lot Sean. Do you have any idea what the performance would be using thousands of words? Does it depend on any libraries?


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: April 1st, 2007, 12:02 am 
Offline

Joined: February 12th, 2007, 7:54 am
Posts: 2462
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.


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: April 18th, 2007, 8:18 pm 
Offline

Joined: February 14th, 2005, 4:05 pm
Posts: 4710
Location: Boulder, CO
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?


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: April 18th, 2007, 9:06 pm 
Offline

Joined: May 24th, 2006, 2:49 pm
Posts: 4511
Location: Belgrade
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

_________________
Image


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: April 19th, 2007, 1:53 am 
Offline

Joined: February 12th, 2007, 7:54 am
Posts: 2462
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/viewtop ... c&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.


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: April 19th, 2007, 4:21 am 
Offline

Joined: February 14th, 2005, 4:05 pm
Posts: 4710
Location: Boulder, CO
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?


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: April 19th, 2007, 8:42 am 
Offline

Joined: February 12th, 2007, 7:54 am
Posts: 2462
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.


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: April 27th, 2007, 3:13 pm 
Offline

Joined: March 2nd, 2004, 3:36 pm
Posts: 10720
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?


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: April 27th, 2007, 3:23 pm 
Offline

Joined: May 24th, 2006, 2:49 pm
Posts: 4511
Location: Belgrade
2Chris
Why do you still support Win9x when even MS droped support for it long time ago ?

_________________
Image


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: April 27th, 2007, 3:46 pm 
Offline

Joined: February 14th, 2005, 4:05 pm
Posts: 4710
Location: Boulder, CO
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 May 12th, 2007, 8:47 pm, edited 2 times in total.

Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: April 27th, 2007, 4:24 pm 
Offline

Joined: February 12th, 2007, 7:54 am
Posts: 2462
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.


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: April 27th, 2007, 4:27 pm 
Offline

Joined: March 2nd, 2004, 3:36 pm
Posts: 10720
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.


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: April 27th, 2007, 4:38 pm 
Offline

Joined: February 14th, 2005, 4:05 pm
Posts: 4710
Location: Boulder, CO
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.


Report this post
Top
 Profile  
Reply with quote  
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 62 posts ]  Go to page 1, 2, 3, 4, 5  Next

All times are UTC [ DST ]


Who is online

Users browsing this forum: iDrug and 60 guests


You can post new topics in this forum
You can reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot post attachments in this forum

Search for:
Powered by phpBB® Forum Software © phpBB Group