shell32: Avoid crash on NULL pointer.
[wine] / dlls / shell32 / autocomplete.c
1 /*
2  *      AutoComplete interfaces implementation.
3  *
4  *      Copyright 2004  Maxime Bellengé <maxime.bellenge@laposte.net>
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19  */
20
21 /*
22   Implemented:
23   - ACO_AUTOAPPEND style
24   - ACO_AUTOSUGGEST style
25   - ACO_UPDOWNKEYDROPSLIST style
26
27   - Handle pwzsRegKeyPath and pwszQuickComplete in Init
28
29   TODO:
30   - implement ACO_SEARCH style
31   - implement ACO_FILTERPREFIXES style
32   - implement ACO_USETAB style
33   - implement ACO_RTLREADING style
34   - implement ResetEnumerator
35   - string compares should be case-insensitive, the content of the list should be sorted
36   
37  */
38 #include "config.h"
39
40 #include <stdarg.h>
41 #include <stdlib.h>
42 #include <string.h>
43
44 #define COBJMACROS
45
46 #include "wine/debug.h"
47 #include "windef.h"
48 #include "winbase.h"
49 #include "winreg.h"
50 #include "undocshell.h"
51 #include "shlwapi.h"
52 #include "winerror.h"
53 #include "objbase.h"
54
55 #include "pidl.h"
56 #include "shlobj.h"
57 #include "shldisp.h"
58 #include "debughlp.h"
59 #include "shell32_main.h"
60
61 #include "wine/unicode.h"
62
63 WINE_DEFAULT_DEBUG_CHANNEL(shell);
64
65 typedef struct
66 {
67     IAutoComplete2 IAutoComplete2_iface;
68     IAutoCompleteDropDown IAutoCompleteDropDown_iface;
69     LONG ref;
70     BOOL initialized;
71     BOOL enabled;
72     HWND hwndEdit;
73     HWND hwndListBox;
74     WNDPROC wpOrigEditProc;
75     WNDPROC wpOrigLBoxProc;
76     WCHAR *txtbackup;
77     WCHAR *quickComplete;
78     IEnumString *enumstr;
79     AUTOCOMPLETEOPTIONS options;
80 } IAutoCompleteImpl;
81
82 static const WCHAR autocomplete_propertyW[] = {'W','i','n','e',' ','A','u','t','o',
83                                                'c','o','m','p','l','e','t','e',' ',
84                                                'c','o','n','t','r','o','l',0};
85
86 static inline IAutoCompleteImpl *impl_from_IAutoComplete2(IAutoComplete2 *iface)
87 {
88     return CONTAINING_RECORD(iface, IAutoCompleteImpl, IAutoComplete2_iface);
89 }
90
91 static inline IAutoCompleteImpl *impl_from_IAutoCompleteDropDown(IAutoCompleteDropDown *iface)
92 {
93     return CONTAINING_RECORD(iface, IAutoCompleteImpl, IAutoCompleteDropDown_iface);
94 }
95
96 /*
97   Window procedure for autocompletion
98  */
99 static LRESULT APIENTRY ACEditSubclassProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
100 {
101     IAutoCompleteImpl *This = GetPropW(hwnd, autocomplete_propertyW);
102     LPOLESTR strs;
103     HRESULT hr;
104     WCHAR hwndText[255];
105     WCHAR *hwndQCText;
106     RECT r;
107     BOOL control, filled, displayall = FALSE;
108     int cpt, height, sel;
109
110     if (!This->enabled) return CallWindowProcW(This->wpOrigEditProc, hwnd, uMsg, wParam, lParam);
111
112     switch (uMsg)
113     {
114         case CB_SHOWDROPDOWN:
115             ShowWindow(This->hwndListBox, SW_HIDE);
116             break;
117         case WM_KILLFOCUS:
118             if ((This->options & ACO_AUTOSUGGEST) && ((HWND)wParam != This->hwndListBox))
119             {
120                 ShowWindow(This->hwndListBox, SW_HIDE);
121             }
122             return CallWindowProcW(This->wpOrigEditProc, hwnd, uMsg, wParam, lParam);
123         case WM_KEYUP:
124             GetWindowTextW( hwnd, hwndText, sizeof(hwndText)/sizeof(WCHAR));
125
126             switch(wParam) {
127                 case VK_RETURN:
128                     /* If quickComplete is set and control is pressed, replace the string */
129                     control = GetKeyState(VK_CONTROL) & 0x8000;
130                     if (control && This->quickComplete) {
131                         hwndQCText = HeapAlloc(GetProcessHeap(), 0,
132                                               (lstrlenW(This->quickComplete)+lstrlenW(hwndText))*sizeof(WCHAR));
133                         sel = sprintfW(hwndQCText, This->quickComplete, hwndText);
134                         SendMessageW(hwnd, WM_SETTEXT, 0, (LPARAM)hwndQCText);
135                         SendMessageW(hwnd, EM_SETSEL, 0, sel);
136                         HeapFree(GetProcessHeap(), 0, hwndQCText);
137                     }
138
139                     ShowWindow(This->hwndListBox, SW_HIDE);
140                     return 0;
141                 case VK_LEFT:
142                 case VK_RIGHT:
143                     return 0;
144                 case VK_UP:
145                 case VK_DOWN:
146                     /* Two cases here :
147                        - if the listbox is not visible, displays it
148                        with all the entries if the style ACO_UPDOWNKEYDROPSLIST
149                        is present but does not select anything.
150                        - if the listbox is visible, change the selection
151                     */
152                     if ( (This->options & (ACO_AUTOSUGGEST | ACO_UPDOWNKEYDROPSLIST))
153                          && (!IsWindowVisible(This->hwndListBox) && (! *hwndText)) )
154                     {
155                          /* We must display all the entries */
156                          displayall = TRUE;
157                     } else {
158                         if (IsWindowVisible(This->hwndListBox)) {
159                             int count;
160
161                             count = SendMessageW(This->hwndListBox, LB_GETCOUNT, 0, 0);
162                             /* Change the selection */
163                             sel = SendMessageW(This->hwndListBox, LB_GETCURSEL, 0, 0);
164                             if (wParam == VK_UP)
165                                 sel = ((sel-1) < 0) ? count-1 : sel-1;
166                             else
167                                 sel = ((sel+1) >= count) ? -1 : sel+1;
168                             SendMessageW(This->hwndListBox, LB_SETCURSEL, sel, 0);
169                             if (sel != -1) {
170                                 WCHAR *msg;
171                                 int len;
172
173                                 len = SendMessageW(This->hwndListBox, LB_GETTEXTLEN, sel, 0);
174                                 msg = HeapAlloc(GetProcessHeap(), 0, (len+1)*sizeof(WCHAR));
175                                 SendMessageW(This->hwndListBox, LB_GETTEXT, sel, (LPARAM)msg);
176                                 SendMessageW(hwnd, WM_SETTEXT, 0, (LPARAM)msg);
177                                 SendMessageW(hwnd, EM_SETSEL, lstrlenW(msg), lstrlenW(msg));
178                                 HeapFree(GetProcessHeap(), 0, msg);
179                             } else {
180                                 SendMessageW(hwnd, WM_SETTEXT, 0, (LPARAM)This->txtbackup);
181                                 SendMessageW(hwnd, EM_SETSEL, lstrlenW(This->txtbackup), lstrlenW(This->txtbackup));
182                             }
183                         }
184                         return 0;
185                     }
186                     break;
187                 case VK_BACK:
188                 case VK_DELETE:
189                     if ((! *hwndText) && (This->options & ACO_AUTOSUGGEST)) {
190                         ShowWindow(This->hwndListBox, SW_HIDE);
191                         return CallWindowProcW(This->wpOrigEditProc, hwnd, uMsg, wParam, lParam);
192                     }
193                     if (This->options & ACO_AUTOAPPEND) {
194                         DWORD b;
195                         SendMessageW(hwnd, EM_GETSEL, (WPARAM)&b, 0);
196                         if (b>1) {
197                             hwndText[b-1] = '\0';
198                         } else {
199                             hwndText[0] = '\0';
200                             SetWindowTextW(hwnd, hwndText);
201                         }
202                     }
203                     break;
204                 default:
205                     ;
206             }
207
208             SendMessageW(This->hwndListBox, LB_RESETCONTENT, 0, 0);
209
210             HeapFree(GetProcessHeap(), 0, This->txtbackup);
211             This->txtbackup = HeapAlloc(GetProcessHeap(), 0, (lstrlenW(hwndText)+1)*sizeof(WCHAR));
212             lstrcpyW(This->txtbackup, hwndText);
213
214             /* Returns if there is no text to search and we doesn't want to display all the entries */
215             if ((!displayall) && (! *hwndText) )
216                 break;
217
218             IEnumString_Reset(This->enumstr);
219             filled = FALSE;
220             for(cpt = 0;;) {
221                 ULONG fetched;
222                 hr = IEnumString_Next(This->enumstr, 1, &strs, &fetched);
223                 if (hr != S_OK)
224                     break;
225
226                 if (StrStrIW(strs, hwndText) == strs) {
227                     if (!filled && (This->options & ACO_AUTOAPPEND)) {
228                         INT typed_length = strlenW(hwndText);
229                         WCHAR buffW[255];
230
231                         strcpyW(buffW, hwndText);
232                         strcatW(buffW, &strs[typed_length]);
233                         SetWindowTextW(hwnd, buffW);
234                         SendMessageW(hwnd, EM_SETSEL, typed_length, strlenW(strs));
235                         if (!(This->options & ACO_AUTOSUGGEST))
236                             break;
237                     }
238
239                     if (This->options & ACO_AUTOSUGGEST) {
240                         SendMessageW(This->hwndListBox, LB_ADDSTRING, 0, (LPARAM)strs);
241                         cpt++;
242                     }
243
244                     filled = TRUE;
245                 }
246             }
247
248             if (This->options & ACO_AUTOSUGGEST) {
249                 if (filled) {
250                     height = SendMessageW(This->hwndListBox, LB_GETITEMHEIGHT, 0, 0);
251                     SendMessageW(This->hwndListBox, LB_CARETOFF, 0, 0);
252                     GetWindowRect(hwnd, &r);
253                     SetParent(This->hwndListBox, HWND_DESKTOP);
254                     /* It seems that Windows XP displays 7 lines at most
255                        and otherwise displays a vertical scroll bar */
256                     SetWindowPos(This->hwndListBox, HWND_TOP,
257                                  r.left, r.bottom + 1, r.right - r.left, min(height * 7, height*(cpt+1)),
258                                  SWP_SHOWWINDOW );
259                 } else {
260                     ShowWindow(This->hwndListBox, SW_HIDE);
261                 }
262             }
263
264             break;
265         case WM_DESTROY:
266         {
267             WNDPROC proc = This->wpOrigEditProc;
268
269             RemovePropW(hwnd, autocomplete_propertyW);
270             SetWindowLongPtrW(hwnd, GWLP_WNDPROC, (LONG_PTR)proc);
271             This->hwndEdit = NULL;
272             if (This->hwndListBox)
273                     DestroyWindow(This->hwndListBox);
274             IAutoComplete2_Release(&This->IAutoComplete2_iface);
275             return CallWindowProcW(proc, hwnd, uMsg, wParam, lParam);
276         }
277         default:
278             return CallWindowProcW(This->wpOrigEditProc, hwnd, uMsg, wParam, lParam);
279
280     }
281
282     return 0;
283 }
284
285 static LRESULT APIENTRY ACLBoxSubclassProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
286 {
287     IAutoCompleteImpl *This = (IAutoCompleteImpl *)GetWindowLongPtrW(hwnd, GWLP_USERDATA);
288     WCHAR *msg;
289     int sel, len;
290
291     switch (uMsg) {
292         case WM_MOUSEMOVE:
293             sel = SendMessageW(hwnd, LB_ITEMFROMPOINT, 0, lParam);
294             SendMessageW(hwnd, LB_SETCURSEL, sel, 0);
295             break;
296         case WM_LBUTTONDOWN:
297             sel = SendMessageW(hwnd, LB_GETCURSEL, 0, 0);
298             if (sel < 0)
299                 break;
300             len = SendMessageW(This->hwndListBox, LB_GETTEXTLEN, sel, 0);
301             msg = HeapAlloc(GetProcessHeap(), 0, (len+1)*sizeof(WCHAR));
302             SendMessageW(hwnd, LB_GETTEXT, sel, (LPARAM)msg);
303             SendMessageW(This->hwndEdit, WM_SETTEXT, 0, (LPARAM)msg);
304             SendMessageW(This->hwndEdit, EM_SETSEL, 0, lstrlenW(msg));
305             ShowWindow(hwnd, SW_HIDE);
306             HeapFree(GetProcessHeap(), 0, msg);
307             break;
308         default:
309             return CallWindowProcW(This->wpOrigLBoxProc, hwnd, uMsg, wParam, lParam);
310     }
311     return 0;
312 }
313
314 static void create_listbox(IAutoCompleteImpl *This)
315 {
316     HWND hwndParent;
317
318     hwndParent = GetParent(This->hwndEdit);
319
320     /* FIXME : The listbox should be resizable with the mouse. WS_THICKFRAME looks ugly */
321     This->hwndListBox = CreateWindowExW(0, WC_LISTBOXW, NULL,
322                                     WS_BORDER | WS_CHILD | WS_VSCROLL | LBS_HASSTRINGS | LBS_NOTIFY | LBS_NOINTEGRALHEIGHT,
323                                     CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
324                                     hwndParent, NULL, shell32_hInstance, NULL );
325
326     if (This->hwndListBox) {
327         This->wpOrigLBoxProc = (WNDPROC) SetWindowLongPtrW( This->hwndListBox, GWLP_WNDPROC, (LONG_PTR) ACLBoxSubclassProc);
328         SetWindowLongPtrW( This->hwndListBox, GWLP_USERDATA, (LONG_PTR)This);
329     }
330 }
331
332 /**************************************************************************
333  *  AutoComplete_QueryInterface
334  */
335 static HRESULT WINAPI IAutoComplete2_fnQueryInterface(
336     IAutoComplete2 * iface,
337     REFIID riid,
338     LPVOID *ppvObj)
339 {
340     IAutoCompleteImpl *This = impl_from_IAutoComplete2(iface);
341
342     TRACE("(%p)->(IID:%s,%p)\n", This, shdebugstr_guid(riid), ppvObj);
343     *ppvObj = NULL;
344
345     if (IsEqualIID(riid, &IID_IUnknown) ||
346         IsEqualIID(riid, &IID_IAutoComplete) ||
347         IsEqualIID(riid, &IID_IAutoComplete2))
348     {
349         *ppvObj = This;
350     }
351     else if (IsEqualIID(riid, &IID_IAutoCompleteDropDown))
352     {
353         *ppvObj = &This->IAutoCompleteDropDown_iface;
354     }
355
356     if (*ppvObj)
357     {
358         IUnknown_AddRef((IUnknown*)*ppvObj);
359         TRACE("-- Interface: (%p)->(%p)\n", ppvObj, *ppvObj);
360         return S_OK;
361     }
362     WARN("unsupported interface: %s\n", debugstr_guid(riid));
363     return E_NOINTERFACE;
364 }
365
366 /******************************************************************************
367  * IAutoComplete2_fnAddRef
368  */
369 static ULONG WINAPI IAutoComplete2_fnAddRef(
370         IAutoComplete2 * iface)
371 {
372     IAutoCompleteImpl *This = impl_from_IAutoComplete2(iface);
373     ULONG refCount = InterlockedIncrement(&This->ref);
374
375     TRACE("(%p)->(%u)\n", This, refCount - 1);
376
377     return refCount;
378 }
379
380 /******************************************************************************
381  * IAutoComplete2_fnRelease
382  */
383 static ULONG WINAPI IAutoComplete2_fnRelease(
384         IAutoComplete2 * iface)
385 {
386     IAutoCompleteImpl *This = impl_from_IAutoComplete2(iface);
387     ULONG refCount = InterlockedDecrement(&This->ref);
388
389     TRACE("(%p)->(%u)\n", This, refCount + 1);
390
391     if (!refCount) {
392         TRACE("destroying IAutoComplete(%p)\n", This);
393         HeapFree(GetProcessHeap(), 0, This->quickComplete);
394         HeapFree(GetProcessHeap(), 0, This->txtbackup);
395         if (This->enumstr)
396             IEnumString_Release(This->enumstr);
397         HeapFree(GetProcessHeap(), 0, This);
398     }
399     return refCount;
400 }
401
402 /******************************************************************************
403  * IAutoComplete2_fnEnable
404  */
405 static HRESULT WINAPI IAutoComplete2_fnEnable(
406     IAutoComplete2 * iface,
407     BOOL fEnable)
408 {
409     IAutoCompleteImpl *This = impl_from_IAutoComplete2(iface);
410     HRESULT hr = S_OK;
411
412     TRACE("(%p)->(%s)\n", This, (fEnable)?"true":"false");
413
414     This->enabled = fEnable;
415
416     return hr;
417 }
418
419 /******************************************************************************
420  * IAutoComplete2_fnInit
421  */
422 static HRESULT WINAPI IAutoComplete2_fnInit(
423     IAutoComplete2 * iface,
424     HWND hwndEdit,
425     IUnknown *punkACL,
426     LPCOLESTR pwzsRegKeyPath,
427     LPCOLESTR pwszQuickComplete)
428 {
429     IAutoCompleteImpl *This = impl_from_IAutoComplete2(iface);
430
431     TRACE("(%p)->(%p, %p, %s, %s)\n",
432           This, hwndEdit, punkACL, debugstr_w(pwzsRegKeyPath), debugstr_w(pwszQuickComplete));
433
434     if (This->options & ACO_SEARCH) FIXME(" ACO_SEARCH not supported\n");
435     if (This->options & ACO_FILTERPREFIXES) FIXME(" ACO_FILTERPREFIXES not supported\n");
436     if (This->options & ACO_USETAB) FIXME(" ACO_USETAB not supported\n");
437     if (This->options & ACO_RTLREADING) FIXME(" ACO_RTLREADING not supported\n");
438
439     if (!hwndEdit || !punkACL)
440         return E_INVALIDARG;
441
442     if (This->initialized)
443     {
444         WARN("Autocompletion object is already initialized\n");
445         /* This->hwndEdit is set to NULL when the edit window is destroyed. */
446         return This->hwndEdit ? E_FAIL : E_UNEXPECTED;
447     }
448
449     if (FAILED (IUnknown_QueryInterface (punkACL, &IID_IEnumString, (LPVOID*)&This->enumstr))) {
450         WARN("No IEnumString interface\n");
451         return E_NOINTERFACE;
452     }
453
454     This->initialized = TRUE;
455     This->hwndEdit = hwndEdit;
456     This->wpOrigEditProc = (WNDPROC) SetWindowLongPtrW( hwndEdit, GWLP_WNDPROC, (LONG_PTR) ACEditSubclassProc);
457     /* Keep at least one reference to the object until the edit window is destroyed. */
458     IAutoComplete2_AddRef(&This->IAutoComplete2_iface);
459     SetPropW( hwndEdit, autocomplete_propertyW, This );
460
461     if (This->options & ACO_AUTOSUGGEST)
462         create_listbox(This);
463
464     if (pwzsRegKeyPath) {
465         WCHAR *key;
466         WCHAR result[MAX_PATH];
467         WCHAR *value;
468         HKEY hKey = 0;
469         LONG res;
470         LONG len;
471
472         /* pwszRegKeyPath contains the key as well as the value, so we split */
473         key = HeapAlloc(GetProcessHeap(), 0, (lstrlenW(pwzsRegKeyPath)+1)*sizeof(WCHAR));
474         strcpyW(key, pwzsRegKeyPath);
475         value = strrchrW(key, '\\');
476         *value = 0;
477         value++;
478         /* Now value contains the value and buffer the key */
479         res = RegOpenKeyExW(HKEY_CURRENT_USER, key, 0, KEY_READ, &hKey);
480         if (res != ERROR_SUCCESS) {
481             /* if the key is not found, MSDN states we must seek in HKEY_LOCAL_MACHINE */
482             res = RegOpenKeyExW(HKEY_LOCAL_MACHINE, key, 0, KEY_READ, &hKey);  
483         }
484         if (res == ERROR_SUCCESS) {
485             res = RegQueryValueW(hKey, value, result, &len);
486             if (res == ERROR_SUCCESS) {
487                 This->quickComplete = HeapAlloc(GetProcessHeap(), 0, len*sizeof(WCHAR));
488                 strcpyW(This->quickComplete, result);
489             }
490             RegCloseKey(hKey);
491         }
492         HeapFree(GetProcessHeap(), 0, key);
493     }
494
495     if ((pwszQuickComplete) && (!This->quickComplete)) {
496         This->quickComplete = HeapAlloc(GetProcessHeap(), 0, (lstrlenW(pwszQuickComplete)+1)*sizeof(WCHAR));
497         lstrcpyW(This->quickComplete, pwszQuickComplete);
498     }
499
500     return S_OK;
501 }
502
503 /**************************************************************************
504  *  IAutoComplete2_fnGetOptions
505  */
506 static HRESULT WINAPI IAutoComplete2_fnGetOptions(
507     IAutoComplete2 * iface,
508     DWORD *pdwFlag)
509 {
510     IAutoCompleteImpl *This = impl_from_IAutoComplete2(iface);
511     HRESULT hr = S_OK;
512
513     TRACE("(%p) -> (%p)\n", This, pdwFlag);
514
515     *pdwFlag = This->options;
516
517     return hr;
518 }
519
520 /**************************************************************************
521  *  IAutoComplete2_fnSetOptions
522  */
523 static HRESULT WINAPI IAutoComplete2_fnSetOptions(
524     IAutoComplete2 * iface,
525     DWORD dwFlag)
526 {
527     IAutoCompleteImpl *This = impl_from_IAutoComplete2(iface);
528     HRESULT hr = S_OK;
529
530     TRACE("(%p) -> (0x%x)\n", This, dwFlag);
531
532     This->options = dwFlag;
533
534     if ((This->options & ACO_AUTOSUGGEST) && This->hwndEdit && !This->hwndListBox)
535         create_listbox(This);
536
537     return hr;
538 }
539
540 /**************************************************************************
541  *  IAutoComplete2 VTable
542  */
543 static const IAutoComplete2Vtbl acvt =
544 {
545     IAutoComplete2_fnQueryInterface,
546     IAutoComplete2_fnAddRef,
547     IAutoComplete2_fnRelease,
548     IAutoComplete2_fnInit,
549     IAutoComplete2_fnEnable,
550     /* IAutoComplete2 */
551     IAutoComplete2_fnSetOptions,
552     IAutoComplete2_fnGetOptions,
553 };
554
555
556 static HRESULT WINAPI IAutoCompleteDropDown_fnQueryInterface(IAutoCompleteDropDown *iface,
557             REFIID riid, LPVOID *ppvObj)
558 {
559     IAutoCompleteImpl *This = impl_from_IAutoCompleteDropDown(iface);
560     return IAutoComplete2_fnQueryInterface(&This->IAutoComplete2_iface, riid, ppvObj);
561 }
562
563 static ULONG WINAPI IAutoCompleteDropDown_fnAddRef(IAutoCompleteDropDown *iface)
564 {
565     IAutoCompleteImpl *This = impl_from_IAutoCompleteDropDown(iface);
566     return IAutoComplete2_fnAddRef(&This->IAutoComplete2_iface);
567 }
568
569 static ULONG WINAPI IAutoCompleteDropDown_fnRelease(IAutoCompleteDropDown *iface)
570 {
571     IAutoCompleteImpl *This = impl_from_IAutoCompleteDropDown(iface);
572     return IAutoComplete2_fnRelease(&This->IAutoComplete2_iface);
573 }
574
575 /**************************************************************************
576  *  IAutoCompleteDropDown_fnGetDropDownStatus
577  */
578 static HRESULT WINAPI IAutoCompleteDropDown_fnGetDropDownStatus(
579     IAutoCompleteDropDown *iface,
580     DWORD *pdwFlags,
581     LPWSTR *ppwszString)
582 {
583     IAutoCompleteImpl *This = impl_from_IAutoCompleteDropDown(iface);
584     BOOL dropped;
585
586     TRACE("(%p) -> (%p, %p)\n", This, pdwFlags, ppwszString);
587
588     dropped = IsWindowVisible(This->hwndListBox);
589
590     if (pdwFlags)
591         *pdwFlags = (dropped ? ACDD_VISIBLE : 0);
592
593     if (ppwszString) {
594         if (dropped) {
595             int sel;
596
597             sel = SendMessageW(This->hwndListBox, LB_GETCURSEL, 0, 0);
598             if (sel >= 0)
599             {
600                 DWORD len;
601
602                 len = SendMessageW(This->hwndListBox, LB_GETTEXTLEN, sel, 0);
603                 *ppwszString = CoTaskMemAlloc((len+1)*sizeof(WCHAR));
604                 SendMessageW(This->hwndListBox, LB_GETTEXT, sel, (LPARAM)*ppwszString);
605             }
606             else
607                 *ppwszString = NULL;
608         }
609         else
610             *ppwszString = NULL;
611     }
612
613     return S_OK;
614 }
615
616 /**************************************************************************
617  *  IAutoCompleteDropDown_fnResetEnumarator
618  */
619 static HRESULT WINAPI IAutoCompleteDropDown_fnResetEnumerator(
620     IAutoCompleteDropDown *iface)
621 {
622     IAutoCompleteImpl *This = impl_from_IAutoCompleteDropDown(iface);
623
624     FIXME("(%p): stub\n", This);
625
626     return E_NOTIMPL;
627 }
628
629 /**************************************************************************
630  *  IAutoCompleteDropDown VTable
631  */
632 static const IAutoCompleteDropDownVtbl acdropdownvt =
633 {
634     IAutoCompleteDropDown_fnQueryInterface,
635     IAutoCompleteDropDown_fnAddRef,
636     IAutoCompleteDropDown_fnRelease,
637     IAutoCompleteDropDown_fnGetDropDownStatus,
638     IAutoCompleteDropDown_fnResetEnumerator,
639 };
640
641 /**************************************************************************
642  *  IAutoComplete_Constructor
643  */
644 HRESULT WINAPI IAutoComplete_Constructor(IUnknown * pUnkOuter, REFIID riid, LPVOID * ppv)
645 {
646     IAutoCompleteImpl *lpac;
647     HRESULT hr;
648
649     if (pUnkOuter && !IsEqualIID (riid, &IID_IUnknown))
650         return CLASS_E_NOAGGREGATION;
651
652     lpac = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(IAutoCompleteImpl));
653     if (!lpac)
654         return E_OUTOFMEMORY;
655
656     lpac->ref = 1;
657     lpac->IAutoComplete2_iface.lpVtbl = &acvt;
658     lpac->IAutoCompleteDropDown_iface.lpVtbl = &acdropdownvt;
659     lpac->enabled = TRUE;
660     lpac->options = ACO_AUTOAPPEND;
661
662     hr = IAutoComplete2_QueryInterface(&lpac->IAutoComplete2_iface, riid, ppv);
663     IAutoComplete2_Release(&lpac->IAutoComplete2_iface);
664
665     TRACE("-- (%p)->\n",lpac);
666
667     return hr;
668 }