AutoComplete() (using iAutoComplete2) crashes script/ahk sometimes

Get help with using AutoHotkey (v1.1 and older) and its commands and hotkeys
just me
Posts: 9442
Joined: 02 Oct 2013, 08:51
Location: Germany

Re: AutoComplete() (using iAutoComplete2) crashes script/ahk sometimes

12 Aug 2016, 02:37

Well, seemingly I'm through. I has to learn about reference counting and it seems I finally got it. I could only test on WIn 10 so it might still be buggy in previous versions. If so, I'll give up.

Requires RegisterSyncCallback (for multi-threaded APIs)

Code: Select all

; ==================================================================================================================================
; Class-based version of a script by nepter
; -> autohotkey.com/board/topic/96129-ahk-l-custom-autocompletion-for-edit-control-with-drop-down-list/
; Namespaces:
;     IAutoComplete
;     IEnumString
; Requires:
;     RegisterSyncCallback()  -> autohotkey.com/boards/viewtopic.php?f=6&t=21223
; MSDN:
;     IAutoComplete           -> msdn.microsoft.com/en-us/library/bb776292(v=vs.85).aspx
;     IAutoComplete2          -> msdn.microsoft.com/en-us/library/bb776288(v=vs.85).aspx
;     IAutoCompleteDropDown   -> msdn.microsoft.com/en-us/library/bb776286(v=vs.85).aspx
;     AUTOCOMPLETEOPTIONS     -> msdn.microsoft.com/en-us/library/bb762479(v=vs.85).aspx
; ==================================================================================================================================
; Creates a new autocompletion object (lib compatible).
; Parameters:
;     HEDT        -  Handle to an edit control.
;     Strings     -  Simple array of autocompletion strings. If you pass a non-object value the string table will be empty.
;     Options     -  Simple array of autocomplete options (see SetOptions() method).
;                    Default: "" -> AUTOSUGGEST
;     WantReturn  -  Set to True to pass the Return key to single-line edit controls to close the autosuggest drop-down list.
;                    Note: The edit control will be subclassed in this case.
;                    Default: False
;     Enable      -  By default, autocompletion will be enabled after creation. Pass False to disable it.
; Return values:
;     On success, the function returns a new instance of the AutoComplete class; otherwise an empty string.
; ==================================================================================================================================
IAutoComplete_Create(HEDT, Strings, Options := "", WantReturn := False, Enable := True) {
   Return New IAutoComplete(HEDT, Strings, Options, WantReturn, Enable)
}
; ==================================================================================================================================
; Used internally to pass the return key to single-line edit controls.
; ==================================================================================================================================
IAutoComplete_SubclassProc(HWND, Msg, wParam, lParam, ID, Data) {
   If (Msg = 0x0087) && (wParam = 13) ; WM_GETDLGCODE, VK_RETURN
      Return 0x0004 ; DLGC_WANTALLKEYS
   If (Msg = 0x0002) { ; WM_DESTROY
      DllCall("RemoveWindowSubclass", "Ptr", HWND, "Ptr", Data, "Ptr", ID)
      DllCall("GlobalFree", "Ptr", Data)
      If (IAutoCompleteAC := Object(ID))
         IAutoCompleteAC.SubclassProc := 0
   }
   Return DllCall("Comctl32.dll\DefSubclassProc", "Ptr", HWND, "UInt", Msg, "Ptr", wParam, "Ptr", lParam)
}
; ==================================================================================================================================
Class IAutoComplete {
   Static Attached := []
   ; -------------------------------------------------------------------------------------------------------------------------------
   ; Constructor - see AutoComplete_Create()
   ; -------------------------------------------------------------------------------------------------------------------------------
   __New(HEDT, Strings, Options := "", WantReturn := False, Enable := True) {
      Static IAC2_Init := A_PtrSize * 3
      If AutoComplete.Attached[HEDT]
         Return ""
      This.HWND := HEDT
      This.SubclassProc := 0
      If !(IAC2 := ComObjCreate("{00BB2763-6A77-11D0-A535-00C04FD7D062}", "{EAC04BC0-3791-11d2-BB95-0060977B464C}"))
         Return ""
      This.IAC2 := IAC2
      If !(IES := IEnumString_Create())
         Return ""
      If !IEnumString_SetStrings(IES, Strings) {
         DllCall("GlobalFree", "Ptr", IES)
         Return ""
      }
      This.IES := IES
      This.VTBL := NumGet(IAC2 + 0, "UPtr")
      If DllCall(NumGet(This.VTBL + IAC2_Init, "UPtr"), "Ptr", IAC2 + 0, "Ptr", HEDT, "Ptr", IES, "Ptr", 0, "Ptr", 0, "UInt")
         Return ""
      This.SetOptions(Options = "" ? ["AUTOSUGGEST"] : Options)
      This.Enabled := True
      If !(Enable)
         This.Disable()
      If (WantReturn) {
         ControlGet, Styles, Style, , , ahk_id %HEDT%
         If !(Styles & 0x0004) && (CB := RegisterCallback("IAutoComplete_SubclassProc")) { ; !ES_MULTILINE
            If DllCall("SetWindowSubclass", "Ptr", HEDT, "Ptr", CB, "Ptr", &This, "Ptr", CB, "UInt")
               This.SubclassProc := CB
            Else
               DllCall("GlobalFree", "Ptr", CB, "Ptr")
         }
      }
      AutoComplete.Attached[HEDT] := True
   }
   ; -------------------------------------------------------------------------------------------------------------------------------
   ; Destructor
   ; -------------------------------------------------------------------------------------------------------------------------------
   __Delete() {
      ; The edit control keeps own references to IAC2. Hence autocompletion has to be disabled before it can be released.
      ; The only way to reenable autocompletion is to assign a new autocompletion object to the edit.
      If (This.IAC2) {
         This.Disable()
         ObjRelease(This.IAC2)
      }
      If (This.SubclassProc) {
         DllCall("RemoveWindowSubclass", "Ptr", This.HWND, "Ptr", This.SubclassProc, "Ptr", &This)
         DllCall("GlobalFree", "Ptr", This.SubclassProc)
      }
      AutoComplete.Attached.Delete(This.HWND)
   }
   ; -------------------------------------------------------------------------------------------------------------------------------
   ; Enables / disables autocompletion.
   ;     Enable   -  True or False
   ; -------------------------------------------------------------------------------------------------------------------------------
   Enable(Enable := True) {
      Static IAC2_Enable := A_PtrSize * 4
      If !(This.VTBL)
         Return False
      This.Enabled := !!Enable
      Return !DllCall(NumGet(This.VTBL + IAC2_Enable, "UPtr"), "Ptr", This.IAC2, "Int", !!Enable, "UInt")
   }
   ; -------------------------------------------------------------------------------------------------------------------------------
   ; Disables autocompletion.
   ; -------------------------------------------------------------------------------------------------------------------------------
   Disable() {
      Return This.Enable(False)
   }
   ; -------------------------------------------------------------------------------------------------------------------------------
   ;  Sets the autocompletion options.
   ;     Options  -  Simple array of option strings corresponding to the keys defined in ACO
   ; -------------------------------------------------------------------------------------------------------------------------------
   SetOptions(Options) {
      Static IAC2_SetOptions := A_PtrSize * 5
      Static ACO := {NONE: 0, AUTOSUGGEST: 1, AUTOAPPEND: 2, SEARCH: 4, FILTERPREFIXES: 8, USETAB: 16
                   , UPDOWNKEYDROPSLIST: 32, RTLREADING: 64, WORD_FILTER: 128, NOPREFIXFILTERING: 256}
      If !(This.VTBL)
         Return False
      Opts := 0
      For Each, Opt In Options
         Opts |= (Opt := ACO[Opt]) <> "" ? Opt : 0
      Return !DllCall(NumGet(This.VTBL + IAC2_SetOptions, "UPtr"), "Ptr", This.IAC2, "UInt", Opts, "UInt")
   }
   ; -------------------------------------------------------------------------------------------------------------------------------
   ; Updates the autocompletion strings.
   ;     Strings  -  Simple array of strings. If you pass a non-object value the string table will be emptied.
   ; -------------------------------------------------------------------------------------------------------------------------------
   UpdStrings(Strings) {
      Static IID_IACDD := "{3CD141F4-3C6A-11d2-BCAA-00C04FD929DB}" ; IAutoCompleteDropDown
           , IACDD_ResetEnumerator := A_PtrSize * 4
      If !(This.IES)
         Return False
      If !(IEnumString_SetStrings(This.IES, Strings))
         Return False
      If (IACDD := ComObjQuery(This.IAC2, IID_IACDD)) {
         DllCall(NumGet(NumGet(IACDD + 0, "UPtr") + IACDD_ResetEnumerator, "UPtr"), "Ptr", This.IAC2, "UInt")
         ObjRelease(IACDD)
      }
      Return True
   }
}
; ==================================================================================================================================
; IEnumString -> msdn.microsoft.com/en-us/library/ms687257(v=vs.85).aspx
; For internal use by AutoComplete only!!!
; ==================================================================================================================================
IEnumString_Create() {
   Static IESInit := True, IESSize := A_PtrSize * 10, IESVTBL
   If (IESInit) {
      VarSetCapacity(IESVTBL, IESSize, 0)
      Addr := &IESVTBL + A_PtrSize
      Addr := NumPut(RegisterSyncCallback("IEnumString_QueryInterface") , Addr + 0, "UPtr")
      Addr := NumPut(RegisterSyncCallback("IEnumString_AddRef")         , Addr + 0, "UPtr")
      Addr := NumPut(RegisterSyncCallback("IEnumString_Release")        , Addr + 0, "UPtr")
      Addr := NumPut(RegisterSyncCallback("IEnumString_Next")           , Addr + 0, "UPtr")
      Addr := NumPut(RegisterSyncCallback("IEnumString_Skip")           , Addr + 0, "UPtr")
      Addr := NumPut(RegisterSyncCallback("IEnumString_Reset")          , Addr + 0, "UPtr")
      Addr := NumPut(RegisterSyncCallback("IEnumString_Clone")          , Addr + 0, "UPtr")
      IESInit := False
   }
   If !(IES := DllCall("GlobalAlloc", "UInt", 0x40, "Ptr", IESSize, "UPtr"))
      Return False
   DllCall("RtlMoveMemory", "Ptr", IES, "Ptr", &IESVTBL, "Ptr", IESSize)
   NumPut(IES + A_PtrSize, IES + 0, "UPtr")
   Return IES
}
; ----------------------------------------------------------------------------------------------------------------------------------
IEnumString_SetStrings(IES, ByRef Strings) {
   PrevTbl := NumGet(IES + (A_PtrSize * 9), "UPtr")
   StrSize := 0
   StrArray := []
   Loop, % Strings.Length()
      If ((S := Strings[A_Index]) <> "")
         L := StrPut(S, "UTF-16") * 2
         , StrSize += L
         , StrArray.Push({S: S, L: L})
      Else
         Break
   StrCount := StrArray.Length()
   StrTblSize := (A_PtrSize * 2) + (StrCount * A_PtrSize * 2) + StrSize
   If !(StrTbl := DllCall("GlobalAlloc", "UInt", 0x40, "Ptr", StrTblSize, "UPtr"))
      Return False
   Addr := StrTbl + A_PtrSize
   Addr := NumPut(StrCount, Addr + 0, "UPtr")
   StrPtr := Addr + (StrCount * A_PtrSize * 2)
   For Each, Str In StrArray {
      Addr := NumPut(StrPtr, Addr + 0, "UPtr")
      Addr := NumPut(Str.L, Addr + 0, "UPtr")
      StrPut(Str.S, StrPtr, "UTF-16")
      StrPtr += Str.L
   }
   If (PrevTbl)
      DllCall("GlobalFree", "Ptr", PrevTbl)
   NumPut(StrTbl, IES + (A_PtrSize * 9), "UPtr")
   Return True
}
; ----------------------------------------------------------------------------------------------------------------------------------
; VTBL
; ----------------------------------------------------------------------------------------------------------------------------------
IEnumString_QueryInterface(IES, RIID, ObjPtr) {
   Static IID := "{00000101-0000-0000-C000-000000000046}", IID_IEnumString := 0
        , Init := VarSetCapacity(IID_IEnumString, 16, 0) + DllCall("Ole32.dll\IIDFromString", "WStr", IID, "Ptr", &IID_IEnumString)
   Critical
   If DllCall("Ole32.dll\IsEqualGUID", "Ptr", RIID, "Ptr", &IID_IEnumString, "UInt") {
      IEnumString_AddRef(IES)
      Return !(NumPut(IES, ObjPtr + 0, "UPtr"))
   }
   Else
      Return 0x80004002
}
; ----------------------------------------------------------------------------------------------------------------------------------
IEnumString_AddRef(IES) {
   NumPut(RefCount := NumGet(IES + (A_PtrSize * 8), "UPtr") + 1,  IES + (A_PtrSize * 8), "UPtr")
   Return RefCount
}
; ----------------------------------------------------------------------------------------------------------------------------------
IEnumString_Release(IES) {
   RefCount := NumGet(IES + (A_PtrSize * 8), "UPtr")
   If (RefCount > 0) {
      NumPut(--RefCount, IES + (A_PtrSize * 8), "UPtr")
      If (RefCount = 0) {
         DllCall("GlobalFree", "Ptr", NumGet(IES + (A_PtrSize * 9), "UPtr")) ; string table
         DllCall("GlobalFree", "Ptr", IES)
      }
   }
   Return RefCount
}
; ----------------------------------------------------------------------------------------------------------------------------------
IEnumString_Next(IES, Fetch, Strings, Fetched) {
   Critical
   I := 0
   , StrTbl := NumGet(IES + (A_PtrSize * 9), "UPtr")
   , Current := NumGet(StrTbl + 0, "UPtr")
   , Maximum := NumGet(StrTbl + A_PtrSize, "UPtr")
   , StrAddr := StrTbl + (A_PtrSize * 2) + (A_PtrSize * Current * 2)
   While (Current < Maximum) && (I < Fetch)
      Ptr := NumGet(StrAddr + 0, "UPtr")
      , Len := NumGet(StrAddr + A_PtrSize, "UPtr")
      , Mem := DllCall("Ole32.dll\CoTaskMemAlloc", "Ptr", Len, "UPtr")
      , DllCall("RtlMoveMemory", "Ptr", Mem, "Ptr", Ptr, "Ptr", Len)
      , NumPut(Mem, Strings + (I * A_PtrSize), "Ptr")
      , NumPut(++I, Fetched + 0, "UInt")
      , NumPut(++Current, StrTbl + 0, "UPtr")
      , StrAddr += A_PtrSize * 2
   Return (I = Fetch) ? 0 : 1
}
; ----------------------------------------------------------------------------------------------------------------------------------
IEnumString_Skip(IES, Skip) {
   Critical
   StrTbl := NumGet(IES + (A_PtrSize * 9), "UPtr")
   , Current := NumGet(StrTbl + 0, "UPtr")
   , Maximum := NumGet(StrTbl + A_PtrSize, "UPtr")
   If ((Current + Skip) <= Maximum)
      Return (NumPut(Current + Skip, StrTbl, "UPtr") & 0)
   Return 1
}
; ----------------------------------------------------------------------------------------------------------------------------------
IEnumString_Reset(IES) {
   Return (NumPut(0, NumGet(IES + (A_PtrSize * 9), "UPtr"), "UPtr") & 0)
}
; ----------------------------------------------------------------------------------------------------------------------------------
IEnumString_Clone(IES, ObjPtr) { ; Not sure about the reference counter (IES + (A_PtrSize * 8))!
   IESSize := DllCall("GlobalSize", "Ptr", IES, "Ptr")
   StrTbl := NumGet(IES + (A_PtrSize * 9), "UPtr")
   StrTblSize := DllCall("GlobalSize", "Ptr", StrTbl, "Ptr")
   If !(IESClone := DllCall("GlobalAlloc", "UInt", 0x40, "Ptr", IESSize, "UPtr"))
      Return False
   If !(StrTblClone := DllCall("GlobalAlloc", "UInt", 0x40, "Ptr", StrTblSize, "UPtr")) {
      DllCall("GlobalFree", "Ptr", IESClone)
      Return False
   }
   DllCall("RtlMoveMemory", "Ptr", IESClone, "Ptr", IES, "Ptr", IESSize)
   DllCall("RtlMoveMemory", "Ptr", StrTblClone, "Ptr", StrTbl, "Ptr", StrTblSize)
   NumPut(0, IESClone + (A_PtrSize * 8), "UPtr") ; Set the reference counter to zero or one in this case???
   NumPut(StrTblClone, IESCLone + (A_PtrSIze * 9), "UPtr")
   Return (NumPut(IESClone, ObjPtr + 0, "UPtr") & 0)
}
; ==================================================================================================================================
Edit: Added ACO_SEARCH option.

Code: Select all

#NoEnv
#SingleInstance, Off
#NoTrayIcon
SetWorkingDir %A_ScriptDir%
#Include IAutoComplete.ahk

S := ["and"
    , "array"
    , "as"
    , "asm"
    , "begin"
    , "case"
    , "class"
    , "const"
    , "constructor"
    , "destructor"
    , "dispinterface"
    , "div"
    , "do"
    , "downto"
    , "else"
    , "end"
    , "except"
    , "exports"
    , "file"
    , "finalization"
    , "finally"
    , "for"
    , "function"
    , "goto"
    , "if"
    , "implementation"
    , "in"
    , "inherited"
    , "initialization"
    , "inline"
    , "interface"
    , "is"
    , "label"
    , "library"
    , "mod"
    , "nil"
    , "not"
    , "object"
    , "of"
    , "or"
    , "out"
    , "packed"
    , "procedure"
    , "program"
    , "property"
    , "raise"
    , "record"
    , "repeat"
    , "resourcestring"
    , "set"
    , "shl"
    , "shr"
    , "string"
    , "then"
    , "threadvar"
    , "to"
    , "try"
    , "type"
    , "unit"
    , "until"
    , "uses"
    , "var"
    , "while"
    , "with"
    , "xor"]

KeyWord := InputGui("Find a Keyword...", "`n   Search for:", , S)

MsgBox, 0, Result, %KeyWord%

ExitApp
; Esc::ExitApp


InputGui(Title, Prompt, Default := "", Suggestions := "", Owner := "") {
   If( Owner <> "" ) {
      Gui %Owner%:+Disabled
      Gui InputGui:+Owner%Owner%
   }
   Gui, InputGui:+AlwaysOnTop +LabelInputGui +LastFound -SysMenu +OwnDialogs
   Gui, InputGui:Margin, 0, 0
   Gui, InputGui:Color, White
   Gui, InputGui:Font, s12 w400, Segoe UI
   Gui, InputGui:Add, Text, x10 y10 w320 c002299, %Prompt%
   Gui, InputGui:Font, s9 Normal
   Gui, InputGui:Add, Edit, x10 y+30 w320 hwndhEdit, %Default%
   Gui, InputGui:Add, Text, x0 y+30 w340 h30 -Background hwndhTxt
   Gui, InputGui:Font, s12
   Gui, InputGui:Add, Button, x120 yp+10 w100 gInputGuiOK Default, OK
   Gui, InputGui:Add, Button, x+10 yp w100 gInputGuiClose hwndhBtn, Cancel
   GuiControlGet, P, InputGui:Pos, %hBtn%
   GuiControl, InputGui:Move, %hTxt%, % "h" . (PH + 20)
   Gui, InputGui:Show, AutoSize, %Title%

   If IsObject(Suggestions)
      ACInput := IAutoComplete_Create(hEdit, Suggestions, ["AutoSuggest", "UseTab"])

   WinWaitClose
   Return Result
   ; ------------------------------------------------------------------------------
   InputGuiOK:
   GuiControlGet, Result, , %hEdit%
   ; InputGuiEscape:
   InputGuiClose:
   If(Owner <> "")
      Gui %Owner%:-Disabled
   Gui, Destroy
   Return
}



/*
    RegisterSyncCallback

    A replacement for RegisterCallback for use with APIs that will call
    the callback on the wrong thread.  Synchronizes with the script's main
    thread via a window message.

    This version tries to emulate RegisterCallback as much as possible
    without using RegisterCallback, so shares most of its limitations,
    and some enhancements that could be made are not.

    Other differences from v1 RegisterCallback:
      - Variadic mode can't be emulated exactly, so is not supported.
      - A_EventInfo can't be set in v1, so is not supported.
      - Fast mode is not supported (the option is ignored).
      - ByRef parameters are allowed (but ByRef is ignored).
      - Throws instead of returning "" on failure.
*/
RegisterSyncCallback(FunctionName, Options:="", ParamCount:="")
{
    if !(fn := Func(FunctionName)) || fn.IsBuiltIn
        throw Exception("Bad function", -1, FunctionName)
    if (ParamCount == "")
        ParamCount := fn.MinParams
    if (ParamCount > fn.MaxParams && !fn.IsVariadic || ParamCount+0 < fn.MinParams)
        throw Exception("Bad param count", -1, ParamCount)

    static sHwnd := 0, sMsg, sSendMessageW
    if !sHwnd
    {
        Gui RegisterSyncCallback: +Parent%A_ScriptHwnd% +hwndsHwnd
        OnMessage(sMsg := 0x8000, Func("RegisterSyncCallback_Msg"))
        sSendMessageW := DllCall("GetProcAddress", "ptr", DllCall("GetModuleHandle", "str", "user32.dll", "ptr"), "astr", "SendMessageW", "ptr")
    }

    if !(pcb := DllCall("GlobalAlloc", "uint", 0, "ptr", 96, "ptr"))
        throw
    DllCall("VirtualProtect", "ptr", pcb, "ptr", 96, "uint", 0x40, "uint*", 0)

    p := pcb
    if (A_PtrSize = 8)
    {
        /*
        48 89 4c 24 08  ; mov [rsp+8], rcx
        48 89 54'24 10  ; mov [rsp+16], rdx
        4c 89 44 24 18  ; mov [rsp+24], r8
        4c'89 4c 24 20  ; mov [rsp+32], r9
        48 83 ec 28'    ; sub rsp, 40
        4c 8d 44 24 30  ; lea r8, [rsp+48]  (arg 3, &params)
        49 b9 ..        ; mov r9, .. (arg 4, operand to follow)
        */
        p := NumPut(0x54894808244c8948, p+0)
        p := NumPut(0x4c182444894c1024, p+0)
        p := NumPut(0x28ec834820244c89, p+0)
        p := NumPut(  0xb9493024448d4c, p+0) - 1
        lParamPtr := p, p += 8

        p := NumPut(0xba, p+0, "char") ; mov edx, nmsg
        p := NumPut(sMsg, p+0, "int")
        p := NumPut(0xb9, p+0, "char") ; mov ecx, hwnd
        p := NumPut(sHwnd, p+0, "int")
        p := NumPut(0xb848, p+0, "short") ; mov rax, SendMessageW
        p := NumPut(sSendMessageW, p+0)
        /*
        ff d0        ; call rax
        48 83 c4 28  ; add rsp, 40
        c3           ; ret
        */
        p := NumPut(0x00c328c48348d0ff, p+0)
    }
    else ;(A_PtrSize = 4)
    {
        p := NumPut(0x68, p+0, "char")      ; push ... (lParam data)
        lParamPtr := p, p += 4
        p := NumPut(0x0824448d, p+0, "int") ; lea eax, [esp+8]
        p := NumPut(0x50, p+0, "char")      ; push eax
        p := NumPut(0x68, p+0, "char")      ; push nmsg
        p := NumPut(sMsg, p+0, "int")
        p := NumPut(0x68, p+0, "char")      ; push hwnd
        p := NumPut(sHwnd, p+0, "int")
        p := NumPut(0xb8, p+0, "char")      ; mov eax, &SendMessageW
        p := NumPut(sSendMessageW, p+0, "int")
        p := NumPut(0xd0ff, p+0, "short")   ; call eax
        p := NumPut(0xc2, p+0, "char")      ; ret argsize
        p := NumPut((InStr(Options, "C") ? 0 : ParamCount*4), p+0, "short")
    }
    NumPut(p, lParamPtr+0) ; To be passed as lParam.
    p := NumPut(&fn, p+0)
    p := NumPut(ParamCount, p+0, "int")
    return pcb
}

RegisterSyncCallback_Msg(wParam, lParam)
{
    if (A_Gui != "RegisterSyncCallback")
        return
    fn := Object(NumGet(lParam + 0))
    paramCount := NumGet(lParam + A_PtrSize, "int")
    params := []
    Loop % paramCount
        params.Push(NumGet(wParam + A_PtrSize * (A_Index-1)))
    return %fn%(params*)
}
@gwarble: The Escape key doesn't work for me!

@lexikos: Would you please look at IEnumString_Clone()? Which value should the reference counter be set to in this case?
Last edited by just me on 15 Aug 2016, 07:53, edited 1 time in total.
User avatar
gwarble
Posts: 524
Joined: 30 Sep 2013, 15:01

Re: AutoComplete() (using iAutoComplete2) crashes script/ahk sometimes

12 Aug 2016, 12:53

Nice, works well in Win7 and in XP without the "fault exception" i was getting on exit...
just me wrote:@gwarble: The Escape key doesn't work for me!
You're right, your sample doesn't work for me either... I had only tested it on a ComboBox (by using the following to get its edit control's hwnd):
hedit := DllCall("GetWindow", "Uint", hComboBox, "Uint", GW_CHILD:=5)

in which case the Escape key does work for some reason (ComboBoxes also close their drop down list on escape, so maybe its related)
EitherMouse - Multiple mice, individual settings . . . . www.EitherMouse.com . . . . forum . . . .
User avatar
kczx3
Posts: 1640
Joined: 06 Oct 2015, 21:39

Re: AutoComplete() (using iAutoComplete2) crashes script/ahk sometimes

12 Aug 2016, 14:34

This is rather awesome! Works pretty good on my end. It doesn't look like the SEARCH option works when looking at the description here. Also, would be good to implement these predefined sources as well for URLs and the Shell namespace - https://msdn.microsoft.com/en-us/librar ... s.85).aspx
User avatar
gwarble
Posts: 524
Joined: 30 Sep 2013, 15:01

Re: AutoComplete() (using iAutoComplete2) crashes script/ahk sometimes

12 Aug 2016, 14:52

do you mean ACO_SEARCH? if so it works fine, adds the "Search for "typedstring"" to the end of the AutoComplete list, and if selected sends "? typedstring" to the combo box, just like in a windows explorer window when you start typing in the location box
(EDIT: for some reason "just me"'s class doesn't define SEARCH=0x4 like the other options, but if you add it it will work fine:
Static ACO := {NONE: 0, AUTOSUGGEST: 1, AUTOAPPEND: 2, SEARCH: 4, FILTERPREFIXES: 8, USETAB: 16, UPDOWNKEYDROPSLIST: 32, RTLREADING: 64, WORD_FILTER: 128, NOPREFIXFILTERING: 256})


are you asking about SHAutoComplete? like in the windows "Run" dialog? If so it works fine with:

Code: Select all

SHAutoComplete(hEdit) {
	DllCall("ole32\CoInitialize", "Uint", 0)
	DllCall("shlwapi\SHAutoComplete", "Uint", hEdit, "Uint", 0)
	DllCall("ole32\CoUninitialize")
}
There's probably a ComObjCreate equiv for _L
Last edited by gwarble on 12 Aug 2016, 14:59, edited 3 times in total.
EitherMouse - Multiple mice, individual settings . . . . www.EitherMouse.com . . . . forum . . . .
User avatar
kczx3
Posts: 1640
Joined: 06 Oct 2015, 21:39

Re: AutoComplete() (using iAutoComplete2) crashes script/ahk sometimes

12 Aug 2016, 14:54

Yes, but in the class, the option is just "search". It does not work in just me's example on a standard edit control
User avatar
gwarble
Posts: 524
Joined: 30 Sep 2013, 15:01

Re: AutoComplete() (using iAutoComplete2) crashes script/ahk sometimes

12 Aug 2016, 15:03

I was using the function version, edited my response above to add it to the class version

also realized the predefined sources you are asking about aren't the same as the SHAutoComplete... i'll have to research these more
EitherMouse - Multiple mice, individual settings . . . . www.EitherMouse.com . . . . forum . . . .
lexikos
Posts: 9583
Joined: 30 Sep 2013, 04:07
Contact:

Re: AutoComplete() (using iAutoComplete2) crashes script/ahk sometimes

12 Aug 2016, 19:57

just me wrote:@lexikos: Would you please look at IEnumString_Clone()? Which value should the reference counter be set to in this case?
On objects with a reference count of zero – The Old New Thing
User avatar
kczx3
Posts: 1640
Joined: 06 Oct 2015, 21:39

Re: AutoComplete() (using iAutoComplete2) crashes script/ahk sometimes

12 Aug 2016, 21:01

gwarble wrote:do you mean ACO_SEARCH? if so it works fine, adds the "Search for "typedstring"" to the end of the AutoComplete list, and if selected sends "? typedstring" to the combo box, just like in a windows explorer window when you start typing in the location box
(EDIT: for some reason "just me"'s class doesn't define SEARCH=0x4 like the other options, but if you add it it will work fine:
Static ACO := {NONE: 0, AUTOSUGGEST: 1, AUTOAPPEND: 2, SEARCH: 4, FILTERPREFIXES: 8, USETAB: 16, UPDOWNKEYDROPSLIST: 32, RTLREADING: 64, WORD_FILTER: 128, NOPREFIXFILTERING: 256})


are you asking about SHAutoComplete? like in the windows "Run" dialog? If so it works fine with:

Code: Select all

SHAutoComplete(hEdit) {
	DllCall("ole32\CoInitialize", "Uint", 0)
	DllCall("shlwapi\SHAutoComplete", "Uint", hEdit, "Uint", 0)
	DllCall("ole32\CoUninitialize")
}
There's probably a ComObjCreate equiv for _L
Good catch!
just me
Posts: 9442
Joined: 02 Oct 2013, 08:51
Location: Germany

Re: AutoComplete() (using iAutoComplete2) crashes script/ahk sometimes

13 Aug 2016, 02:57

  • In autosuggest mode, autocompletion displays a drop-down list beneath the edit control with one or more suggested complete strings. The user can select one of the suggested strings, usually by clicking it, or continue typing. As typing progresses, the drop-down list may be modified, based on the current partial string. If you set the ACO_SEARCH flag in the dwFlag parameter of IAutoComplete2::SetOptions, a "Search for 'XXX'" item is added to the bottom of the drop-down list. It is displayed even if there are no suggested strings. "XXX" is set to the current partial string and is updated as the user continues to type. If the user selects "Search for '...'", your application should launch a search engine to assist the user.
Source
That's why I removed ACO_SEARCH from the array of options.

Also, the purpose of the class is to provide the option to implement own autocompletion lists. Other sources as used by the explorer aren't supported.
User avatar
gwarble
Posts: 524
Joined: 30 Sep 2013, 15:01

Re: AutoComplete() (using iAutoComplete2) crashes script/ahk sometimes

13 Aug 2016, 03:03

Yeah but why not? Its easy enough to implement "a search engine to assist the user" however you define that, might as well include the option
EitherMouse - Multiple mice, individual settings . . . . www.EitherMouse.com . . . . forum . . . .
just me
Posts: 9442
Joined: 02 Oct 2013, 08:51
Location: Germany

Re: AutoComplete() (using iAutoComplete2) crashes script/ahk sometimes

13 Aug 2016, 03:24

Well, would you provide an example? Otherwise I foresee questions like "I activated the SEARCH option, but nothing happens when I select it".
just me
Posts: 9442
Joined: 02 Oct 2013, 08:51
Location: Germany

Re: AutoComplete() (using iAutoComplete2) crashes script/ahk sometimes

13 Aug 2016, 03:58

lexikos wrote:
just me wrote:@lexikos: Would you please look at IEnumString_Clone()? Which value should the reference counter be set to in this case?
On objects with a reference count of zero – The Old New Thing
I'm still not sure what to do. In case of the IEnumString passed to IAutoComplete.Init() IEnumString.AddRef() is called once. And seemingly IEnumString.Release() is called once when the last reference of IAutoComplete is released. So it seems to be balanced if you initialize the reference counter with 0.

I don't know whether the same will happen with the interface pointer returned by IEnumString.Clone(). But luckily it doesn't matter for the class because this methods won't be called.
lexikos
Posts: 9583
Joined: 30 Sep 2013, 04:07
Contact:

Re: AutoComplete() (using iAutoComplete2) crashes script/ahk sometimes

13 Aug 2016, 06:37

Raymond Chen! wrote:If you construct your object with a reference count of zero, you are playing with matches. For starters, when the object is created, there reference count is not zero - the person who created the object has a reference.
...
Objects should be created with a reference count of one, not zero.
I think the message is basically: Creating an object with reference count of zero is wrong. Don't do it.

Anyway, it is not logically possible to return a reference to an object with reference count 0. Reference count 0 means there are no references. So how was one returned?

I should be able to call IEnumString.Clone() and then immediately Release() the pointer, deleting the new object. If the reference count is 0 after Clone returns, I would have to call AddRef and then Release for the object to be deleted. That's not how reference counting is supposed to work.
User avatar
gwarble
Posts: 524
Joined: 30 Sep 2013, 15:01

Re: AutoComplete() (using iAutoComplete2) crashes script/ahk sometimes

13 Aug 2016, 11:02

just me wrote:Well, would you provide an example? Otherwise I foresee questions like "I activated the SEARCH option, but nothing happens when I select it".
there are any number of things you could do with the search string (in fact in my app i would just replace the "? " at the beginning of the string because it already does a search for whats input whether what you type in comes from the AutoComplete list or not) you could launch google or search a listview for the closest match etc

but to see the effect:
Note: Windows 7 returns "? searchedstring" and XP returns "Search searchedstring"

Code: Select all

InputGui(Title, Prompt, Default := "", Suggestions := "", Owner := "") {
   If( Owner <> "" ) {
      Gui %Owner%:+Disabled
      Gui InputGui:+Owner%Owner%
   }
   Gui, InputGui:-AlwaysOnTop +LabelInputGui +LastFound -SysMenu +OwnDialogs
   Gui, InputGui:Margin, 0, 0
   Gui, InputGui:Color, White
   Gui, InputGui:Font, s12 w400, Segoe UI
   Gui, InputGui:Add, Text, x10 y10 w320 c002299, %Prompt%
   Gui, InputGui:Font, s9 Normal
   Gui, InputGui:Add, Edit, x10 y+30 w320 hwndhEdit gEditChanged, %Default%
   Gui, InputGui:Add, Text, x0 y+30 w340 h30 -Background hwndhTxt
   Gui, InputGui:Font, s12
   Gui, InputGui:Add, Button, x120 yp+10 w100 gInputGuiOK Default, OK
   Gui, InputGui:Add, Button, x+10 yp w100 gInputGuiClose hwndhBtn, Cancel
   GuiControlGet, P, InputGui:Pos, %hBtn%
   GuiControl, InputGui:Move, %hTxt%, % "h" . (PH + 20)
   Gui, InputGui:Show, AutoSize, %Title%

   If IsObject(Suggestions)
      ACInput := IAutoComplete_Create(hEdit, Suggestions, ["AutoSuggest", "UseTab", "Search"])

   WinWaitClose
   Return Result
   ; ------------------------------------------------------------------------------
   InputGuiOK:
   GuiControlGet, Result, , %hEdit%
   ; InputGuiEscape:
   InputGuiClose:
   If(Owner <> "")
      Gui %Owner%:-Disabled
   Gui, Destroy
   Return
 EditChanged:
  GuiControlGet, SearchString,, % hEdit
  If (SubStr(SearchString, 1, 2) = "? ") ;windows 7
   MsgBox, % "You searched for """ SubStr(SearchString, 3) """" "`n`nWhat are you gonna do about it?"
  Else If (SubStr(SearchString, 1, 7) = "Search ") ;windows xp
   MsgBox, % "You searched for """ SubStr(SearchString, 8) """" "`n`nWhat are you gonna do about it?"
 Return
}
on testing it would be better handled in the InputGuiOK: routine than when the edit box changes, but that was off the cuff and i gotta run
EitherMouse - Multiple mice, individual settings . . . . www.EitherMouse.com . . . . forum . . . .
User avatar
kczx3
Posts: 1640
Joined: 06 Oct 2015, 21:39

Re: AutoComplete() (using iAutoComplete2) crashes script/ahk sometimes

23 Sep 2016, 11:42

Do you know if there is a way to limit the number of results in the popup? The height of the pop-up is limited and it gains a scrollbar but i'd rather only show like 10 options and as the text in the edit changes, update the 10 shown as the closest matches or first matches.
User avatar
gwarble
Posts: 524
Joined: 30 Sep 2013, 15:01

Re: AutoComplete() (using iAutoComplete2) crashes script/ahk sometimes

26 Sep 2016, 12:14

Not that I've seen... unfortunately while its a nice end result, its kind of a black box as far as fine-tuning what you want (unfortunately i don't think there is any way to do custom ignored-prefixes, nor a way to return results mid-string (ie have "234" bring up item "12345") etc
EitherMouse - Multiple mice, individual settings . . . . www.EitherMouse.com . . . . forum . . . .
ahkForWork
Posts: 92
Joined: 28 Mar 2016, 07:59

Re: AutoComplete() (using iAutoComplete2) crashes script/ahk sometimes

28 Sep 2016, 09:52

Does anyone know if there's a way if the user uses the keyboard to make their selection via Up,Down that pressing Enter would do the same as if they clicked the selection?

also if the selection of the autocomplete could trigger a sub-routine?
just me
Posts: 9442
Joined: 02 Oct 2013, 08:51
Location: Germany

Re: AutoComplete() (using iAutoComplete2) crashes script/ahk sometimes

28 Sep 2016, 10:44

ahkForWork wrote:Does anyone know if there's a way if the user uses the keyboard to make their selection via Up,Down that pressing Enter would do the same as if they clicked the selection?
You might try the option ACO_UPDOWNKEYDROPSLIST (32).
ahkForWork wrote:also if the selection of the autocomplete could trigger a sub-routine?
Why not?
User avatar
kczx3
Posts: 1640
Joined: 06 Oct 2015, 21:39

Re: AutoComplete() (using iAutoComplete2) crashes script/ahk sometimes

28 Sep 2016, 12:52

just me wrote:
ahkForWork wrote:Does anyone know if there's a way if the user uses the keyboard to make their selection via Up,Down that pressing Enter would do the same as if they clicked the selection?
You might try the option ACO_UPDOWNKEYDROPSLIST (32).
ahkForWork wrote:also if the selection of the autocomplete could trigger a sub-routine?
Why not?
Using the UPDOWN option, hitting Enter doesn't do anything for me. But hitting TAB does at least close the options and moves focus to the next control.

Not sure about responding to the selection. Might have to monitor some kind of message.
ahkForWork
Posts: 92
Joined: 28 Mar 2016, 07:59

Re: AutoComplete() (using iAutoComplete2) crashes script/ahk sometimes

29 Sep 2016, 06:14

kczx3 wrote:
just me wrote:
ahkForWork wrote:Does anyone know if there's a way if the user uses the keyboard to make their selection via Up,Down that pressing Enter would do the same as if they clicked the selection?
You might try the option ACO_UPDOWNKEYDROPSLIST (32).
ahkForWork wrote:also if the selection of the autocomplete could trigger a sub-routine?
Why not?
Using the UPDOWN option, hitting Enter doesn't do anything for me. But hitting TAB does at least close the options and moves focus to the next control.

Not sure about responding to the selection. Might have to monitor some kind of message.
thanks. I've taken this and added it into my own application so its a little different than the example. i'm not sure where to define the "Option". i know which function it's used for but can't get it to work :think:

Return to “Ask for Help (v1)”

Who is online

Users browsing this forum: NullRefEx, ShatterCoder and 107 guests