It is currently Wed Feb 26, 2020 12:25 pm

All times are UTC + 1 hour




Post new topic Reply to topic  [ 7 posts ] 
Author Message
 Post subject: IAutoComplete for the COM freaks
PostPosted: Thu Jun 16, 2005 5:52 pm 
Offline
Enthusiast
Enthusiast

Joined: Sat Apr 26, 2003 2:49 pm
Posts: 659
This will show you how to use the IAutoComplete object, the one that expands an edit control with strings like in the IE URL.

The 1st code chunk are list functions of my own, it's just a small set of an include i use. the names an behaviour is inspired by the C++ CObList class, there aren't current elements, nor indexs.. But you could probably use a PB list.

The 2nd code is the example itself.

I haven't implemented the Skip() and Clone() methods because i think they are not used but this is just an assumption you should check it.

You are supposed to type something in the edit box if there are similar strings in the string list it will be expanded with string candidates.
Code:
;- Linked list
Structure _LL_ELEMENT_
   *pNext._LL_ELEMENT_
   *pPrev._LL_ELEMENT_
   
   *elData.l
EndStructure

Structure _LL_DATA_
   *pFirst._LL_ELEMENT_   
   *pLast._LL_ELEMENT_
   size.l         ;number of elements in list
   elSize.l      ;element size (not including extra pointers pNext, pPrev)
EndStructure

Procedure liGetNext(*ppEl.LONG)
   *pEl._LL_ELEMENT_ = *ppEl\l - OffsetOf(_LL_ELEMENT_\elData)
   
   If *pEl\pNext
      *ppEl\l = *pEl\pNext + OffsetOf(_LL_ELEMENT_\elData)
   Else
      *ppEl\l = #NULL
   EndIf
EndProcedure

Procedure liGetHead(*pList._LL_DATA_)
   If *pList\pFirst
      ProcedureReturn @*pList\pFirst\elData
   Else
      ProcedureReturn #NULL
   EndIf
EndProcedure

Procedure liNew(elSize)
   *pList._LL_DATA_ = AllocateMemory(SizeOf(_LL_DATA_))

   *pList\size = 0
   *pList\elSize = elSize
   ProcedureReturn *pList
EndProcedure

Procedure liAddTail(*pList._LL_DATA_)
   *pNewEl._LL_ELEMENT_ = AllocateMemory(OffsetOf(_LL_ELEMENT_\elData) + *pList\elSize)

   If *pList\size=0 ;no items, add as first
      *pList\pFirst = *pNewEl
      *pList\pLast = *pNewEl
      
   Else ;Items, add after last
      *pNewEl\pPrev = *pList\pLast
      *pList\pLast\pNext = *pNewEl
      
      *pList\pLast = *pNewEl
   EndIf
   
   *pList\size + 1
   ProcedureReturn @*pNewEl\elData
EndProcedure


Code:
;- IAutoComplete
#CLSCTX_INPROC_SERVER        = $1

#ACO_NONE   = 0
#ACO_AUTOSUGGEST   = $1
#ACO_AUTOAPPEND   = $2
#ACO_SEARCH   = $4
#ACO_FILTERPREFIXES   = $8
#ACO_USETAB   = $10
#ACO_UPDOWNKEYDROPSLIST   = $20
#ACO_RTLREADING   = $40

Interface _IEnumString Extends IEnumString
   put_List(*hList._LL_DATA_)
   get_List()
   _ClearList()
   DestroyList()
EndInterface

;Creates an unicode string from an ansi string, returns pointer to unicode string
;String memory needs to be freed by the caller
Procedure cWstr(st$)
   blen = (lstrlen_(st$) * 2) + 2
   wbuf = AllocateMemory(blen)
   
   MultiByteToWideChar_(#CP_ACP, 0, st$, -1, wbuf, blen)
   ProcedureReturn wbuf
EndProcedure

Structure IEnumString_VT
   ;IEnumString
  QueryInterface.l
  AddRef.l
  Release.l
  _Next.l
  Skip.l
  Reset.l
  Clone.l
 
  ;Obj own functions
  put_List.l
  get_List.l
  _ClearList.l
  DestroyList.l
EndStructure

Structure IEnumString_OBJ
  *vt.IEnumString_VT

   objCount.l
   *hstList._LL_DATA_   ;string list handle (list of pointers to unicode strings)
   *strCurr.LONG            ;pointer to current list element
EndStructure

;- IUnknown
Procedure est_QueryInterface(*THIS.IEnumString_OBJ, *iid.GUID, *Object.LONG)
  If CompareMemory(*iid, ?IID_IUnknown, SizeOf(GUID)) Or CompareMemory(*iid, ?IID_IEnumString, SizeOf(GUID))
    *Object\l = *THIS
    *THIS\objCount + 1
    ProcedureReturn #S_OK
  Else   
    *Object\l = 0
    ProcedureReturn #E_NOINTERFACE
  EndIf
EndProcedure

Procedure.l est_AddRef(*THIS.IEnumString_OBJ)
  *THIS\objCount + 1
  ProcedureReturn *THIS\objCount
EndProcedure

Procedure.l est_Release(*THIS.IEnumString_OBJ)
  *THIS\objCount - 1
  ProcedureReturn *THIS\objCount
EndProcedure

;- IEnumString
Procedure est_Next(*THIS.IEnumString_OBJ, celt.l, *rgelt.LONG, *pceltFetched.LONG)   
   If *THIS\strCurr ;not last   
      *rgelt\l = *THIS\strCurr\l
      *pceltFetched\l = 1      
      liGetNext(@*THIS\strCurr)
      ProcedureReturn #S_OK
   Else
      *pceltFetched\l = 0
      ProcedureReturn #S_FALSE
   EndIf
EndProcedure

Procedure est_Skip(*THIS.IEnumString_OBJ, celt.l)
   ProcedureReturn #S_FALSE
EndProcedure

Procedure est_Reset(*THIS.IEnumString_OBJ)
   *THIS\strCurr = liGetHead(*THIS\hstList)
   ProcedureReturn #S_OK
EndProcedure

Procedure est_Clone(*THIS.IEnumString_OBJ, *ppenum.l)
   ProcedureReturn #E_UNEXPECTED
EndProcedure

;Sets the object string list
Procedure est_put_List(*THIS.IEnumString_OBJ, hstList.l) : *THIS\hstList = hstList : EndProcedure

;Gets the object string list
Procedure est_get_List(*THIS.IEnumString_OBJ) : ProcedureReturn *THIS\hstList : EndProcedure

;Frees the object string list elements and sets the current string element to null,
;leaves the string list handle valid.
Procedure est_ClearList(*THIS.IEnumString_OBJ)
   *pELdat.LONG ;pointer to element data
   *pEl.l ;pointer to list element
   
   If *THIS\hstList
      *pELdat = liGetHead(*THIS\hstList)
      While *pELdat
         *pEl = *pEldat - OffsetOf(_LL_ELEMENT_\elData) ;save pointer for deletion
         
         FreeMemory(*pELdat\l) ;free unicode string
         
         liGetNext(@*pELdat)

         FreeMemory(*pEl) ;free element
      Wend
      
      *THIS\hstList\size = 0
      *THIS\hstList\pFirst = #NULL
      *THIS\hstList\pLast = #NULL
      
      *THIS\strCurr = #NULL
   EndIf
EndProcedure

;Destroys the string list invalidating the handle
Procedure est_DestroyList(*THIS.IEnumString_OBJ)
   oest._IEnumString = *THIS
   
   If *THIS
      oest\_ClearList()
      FreeMemory(*THIS\hstList)
   EndIf
EndProcedure

;EnumString Object constructor
Procedure CEnumString()
   *obj.IEnumString_OBJ = allocatememory(SizeOf(IEnumString_OBJ))
   *obj\vt = AllocateMemory(SizeOf(IEnumString_VT))
      
   *obj\vt\QueryInterface = @est_QueryInterface()
   *obj\vt\AddRef = @est_AddRef()
   *obj\vt\Release = @est_Release()
   
   *obj\vt\_Next = @est_Next()
   *obj\vt\Skip = @est_Skip()
   *obj\vt\Reset = @est_Reset()
   *obj\vt\Clone = @est_Clone()
   
   *obj\vt\put_List = @est_put_List()
   *obj\vt\get_List = @est_get_List()
   *obj\vt\_ClearList = @est_ClearList()
   *obj\vt\DestroyList = @est_DestroyList()

   ProcedureReturn *obj
EndProcedure

;EnumString Object destructor
;Frees object memory. It does not free string list memory.
Procedure DEnumString(*obj.IEnumString_OBJ)
   If *obj
      If *obj\vt : FreeMemory(*obj\vt) : EndIf
      FreeMemory(*obj) 
   EndIf
EndProcedure

;- #CODE

;Init COM
CoInitialize_(0)

;Create window
OpenWindow(0,0,0,322,275,#PB_Window_SystemMenu|#PB_Window_ScreenCentered,"IAutoComplete") And CreateGadgetList(WindowID(0))
hwED = StringGadget(0, 8, 10, 306, 20, "")

;- Create object that exposes EnumString
oest._IEnumString = CEnumString()

;Make a list of pointers to unicode strings
*pl.LONG ;List element

hstl = liNew(SizeOf(LONG))
*pl = liAddTail(hstl) : *pl\l = cwstr("Red")
*pl = liAddTail(hstl) : *pl\l = cwstr("Green")
*pl = liAddTail(hstl) : *pl\l = cwstr("Blue")
*pl = liAddTail(hstl) : *pl\l = cwstr("White")
*pl = liAddTail(hstl) : *pl\l = cwstr("Yellow")
*pl = liAddTail(hstl) : *pl\l = cwstr("Black")

;Set the object list
oest\put_List(hstl)

;- Create AutoComplete object
r = CoCreateInstance_(?CLSID_AutoComplete, #NULL, #CLSCTX_INPROC_SERVER, ?IID_IAutoComplete, @oac.IAutoComplete2)
If r<>#S_OK ;error
   End
EndIf

;Setup AutoComplete
oac\SetOptions(#ACO_AUTOSUGGEST)
oac\Init(hwED, oest, 0, 0)

;Msg loop
fQuit = 0
Repeat
   EvID = WaitWindowEvent()
   If EvID=#PB_Event_CloseWindow
      fQuit=1
   EndIf
Until fQuit

;Free
oac\Release()
oest\DestroyList()
DEnumString(oest)

CoUnInitialize_()
End

;- #DATA
DataSection
   IID_IEnumString:
   Data.l $00000101
   Data.w $0000, $0000
   Data.b $C0, $00, $00, $00, $00, $00, $00, $46
   
   IID_IAutoComplete:
   Data.l $00bb2762
   Data.w $6a77, $11d0
   Data.b $a5, $35, $00, $c0, $4f, $d7, $d0, $62
   
   CLSID_AutoComplete:
   Data.l $00BB2763
   Data.w $6a77, $11d0
   Data.b $a5, $35, $00, $c0, $4f, $d7, $d0, $62
   
   IID_IUnknown: 
   Data.l $00000000
   Data.w $0000, $0000
   Data.b $C0, $00, $00, $00, $00, $00, $00, $46
EndDataSection 


Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: Thu Jun 16, 2005 10:14 pm 
Offline
Enthusiast
Enthusiast

Joined: Sun Jan 11, 2004 11:34 am
Posts: 274
Location: France
PB don't know this structure:_LL_DATA_ :?:


Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: Thu Jun 16, 2005 10:23 pm 
Offline
Addict
Addict

Joined: Mon Apr 28, 2003 2:22 pm
Posts: 942
Location: Europe
Code crashes on quit on my win 2000 system.

_________________
Tranquil


Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: Fri Jun 17, 2005 7:14 am 
Offline
Enthusiast
Enthusiast
User avatar

Joined: Fri Apr 25, 2003 7:44 pm
Posts: 465
Location: end of www
Here is an autocomplete mechanism for paths by hm (german forum).

To test it, just type e.g. "C:\" into the textfield ... to see what it does.

Code:

; SHAutoComplete Beispiel
; hm
; 031127
;  Die SHAutoComplete Function befindet sich in shlwapi.dll
;  LWSTDAPI SHAutoComplete(hwnd hwndedit, DWORD dwFlags);

; ab IE5:
#SHACF_DEFAULT           = $00000000  ;// Currently (SHACF_FILESYSTEM | SHACF_URLALL)
#SHACF_FILESYSTEM        = $00000001  ;// This includes the File System as well as the rest of the shell (Desktop\My Computer\Control Panel\)
#SHACF_URLALL            = ( $00000002 | $00000004 )    ; (#SHACF_URLHISTORY | #SHACF_URLMRU)
#SHACF_URLHISTORY        = $00000002  ;// URLs in the User's History
#SHACF_URLMRU            = $00000004  ;// URLs in the User's Recently Used list.
#SHACF_USETAB            = $00000008  ;// Use the tab To move thru the autocomplete possibilities instead of To the Next dialog/window Control.
#SHACF_FILESYS_ONLY      = $00000010  ;// This includes the File System

#SHACF_AUTOSUGGEST_FORCE_ON  = $10000000  ;// Ignore the registry Default And force the feature on.
#SHACF_AUTOSUGGEST_FORCE_OFF = $20000000  ;// Ignore the registry Default And force the feature off.
#SHACF_AUTOAPPEND_FORCE_ON   = $40000000  ;// Ignore the registry Default And force the feature on. (Also know as autocomplete)
#SHACF_AUTOAPPEND_FORCE_OFF  = $80000000  ;// Ignore the registry Default And force the feature off. (Also know as autocomplete)
; ab IE6:
#SHACF_FILESYS_DIRS      = $00000020  ;// Same as SHACF_FILESYS_ONLY except it only includes directories, UNC servers, And UNC server shares.
 
 
Enumeration
  #Window_Main
EndEnumeration

Enumeration
  #String_AutoComplete
EndEnumeration


If OpenWindow(#Window_Main, 217, 150, 292, 40,  #PB_Window_SystemMenu | #PB_Window_TitleBar , "SHAutoComplete Test")
  If CreateGadgetList(WindowID())
    StringGadget(#String_AutoComplete, 10, 10, 270, 20, "")

    CoInitialize_(#NULL)
    If OpenLibrary(0,"shlwapi.dll")
      func_shautocomplete.l = IsFunction(0,"SHAutoComplete")
      If func_shautocomplete <> #NULL
        If CallFunctionFast(func_shautocomplete, GadgetID(#String_AutoComplete), ( #SHACF_AUTOAPPEND_FORCE_ON | #SHACF_AUTOSUGGEST_FORCE_ON | #SHACF_FILESYSTEM ) ) = #S_OK
        Else
          MessageRequester("error","Fehler bei SHAutoComplete()")
        EndIf
      Else
        MessageRequester("error","Funktion SHAutoComplete nicht gefunden in shlwapi.dll");
      EndIf
      CloseLibrary(0)
    Else
      MessageRequester("error","Fehler beim Öffnen von shlwapi.dll")
    EndIf

  EndIf
EndIf

Repeat : Until WaitWindowEvent() = #PB_EventCloseWindow

CoUninitialize_()

End

_________________
regards,
benny!
-
pe0ple ar3 str4nge!!!


Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: Fri Jun 17, 2005 10:27 am 
Offline
Enthusiast
Enthusiast

Joined: Sat Apr 26, 2003 2:49 pm
Posts: 659
_LL_DATA_ is defined in the 1st code part

i'm interested in the crash, can you try invalidating DEnumString(oest) in the free part? i suspect it comes from here althoug it works on XP


Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: Fri Jun 17, 2005 11:41 am 
Offline
Enthusiast
Enthusiast

Joined: Sat Apr 26, 2003 2:49 pm
Posts: 659
i think i know what happened, in the constructor you have to set the object reference count to 1

Code:
Procedure CEnumString()
   *obj.IEnumString_OBJ = AllocateMemory(SizeOf(IEnumString_OBJ))
   *obj\vt = AllocateMemory(SizeOf(IEnumString_VT))
      
   *obj\vt\QueryInterface = @est_QueryInterface()
   *obj\vt\AddRef = @est_AddRef()
   *obj\vt\Release = @est_Release()
   
   *obj\vt\_Next = @est_Next()
   *obj\vt\Skip = @est_Skip()
   *obj\vt\Reset = @est_Reset()
   *obj\vt\Clone = @est_Clone()
   
   *obj\vt\put_List = @est_put_List()
   *obj\vt\get_List = @est_get_List()
   *obj\vt\_ClearList = @est_ClearList()
   *obj\vt\DestroyList = @est_DestroyList()
   
   *obj\objCount = 1

   ProcedureReturn *obj
EndProcedure


then you destroy the object in the Release() method when the count reaches 0, you'll need to declare DEnumString() first
Code:
Procedure.l est_Release(*THIS.IEnumString_OBJ)
  *THIS\objCount - 1
   If *THIS\objCount = 0
     DEnumString(*THIS)
     Debug "IEnumString Released"
  EndIf
  ProcedureReturn *THIS\objCount
EndProcedure


and you free the objects like this at the end
Code:
;Free
oac\Release()
oest\DestroyList()
oest\Release()


Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: Fri Jun 17, 2005 11:51 am 
Offline
Enthusiast
Enthusiast

Joined: Sat Apr 26, 2003 2:49 pm
Posts: 659
continues to crash when creating an exe :D

you have to use closewindow() before freeing or probably free in the WM_DESTROY message because i guess the edit control has to exist before freeing IAutocomplete

with the previuos change and freeing like this works,
Code:
;Free
CloseWindow(0)
oac\Release() ;Release IAutoComplete
oest\DestroyList()
oest\Release()


Top
 Profile  
Reply with quote  
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 7 posts ] 

All times are UTC + 1 hour


Who is online

Users browsing this forum: kinglestat and 5 guests


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

Search for:
Jump to:  

 


Powered by phpBB © 2008 phpBB Group
subSilver+ theme by Canver Software, sponsor Sanal Modifiye