1 /*
2  * ReactOS Explorer
3  *
4  * Copyright 2009 Andrew Hill <ash77 at domain reactos.org>
5  * Copyright 2023 Katayama Hirofumi MZ <katayama.hirofumi.mz@gmail.com>
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2.1 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with this library; if not, write to the Free Software
19  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
20  */
21 
22 /*
23 This class handles the combo box of the address band.
24 */
25 
26 #include "precomp.h"
27 
28 /*
29 TODO:
30     Add drag and drop of icon in edit box
31     Handle change notifies to update appropriately
32 */
33 
34 CAddressEditBox::CAddressEditBox() :
35     fCombobox(WC_COMBOBOXEXW, this),
36     fEditWindow(WC_EDITW, this),
37     fSite(NULL),
38     pidlLastParsed(NULL)
39 {
40 }
41 
42 CAddressEditBox::~CAddressEditBox()
43 {
44     if (pidlLastParsed)
45         ILFree(pidlLastParsed);
46 }
47 
48 HRESULT STDMETHODCALLTYPE CAddressEditBox::SetOwner(IUnknown *pOwner)
49 {
50     if (!pOwner)
51     {
52         CComPtr<IBrowserService> browserService;
53         HRESULT hResult = IUnknown_QueryService(fSite, SID_SShellBrowser, IID_PPV_ARG(IBrowserService, &browserService));
54         if (SUCCEEDED(hResult))
55             AtlUnadvise(browserService, DIID_DWebBrowserEvents, fAdviseCookie);
56         fSite = NULL;
57     }
58     // connect to browser connection point
59     return 0;
60 }
61 
62 HRESULT STDMETHODCALLTYPE CAddressEditBox::FileSysChange(long param8, long paramC)
63 {
64     return E_NOTIMPL;
65 }
66 
67 HRESULT STDMETHODCALLTYPE CAddressEditBox::Refresh(long param8)
68 {
69     return E_NOTIMPL;
70 }
71 
72 HRESULT STDMETHODCALLTYPE CAddressEditBox::Init(HWND comboboxEx, HWND editControl, long param14, IUnknown *param18)
73 {
74     CComPtr<IBrowserService> browserService;
75 
76     fCombobox.SubclassWindow(comboboxEx);
77     fEditWindow.SubclassWindow(editControl);
78     fSite = param18;
79     hComboBoxEx = comboboxEx;
80 
81     SHAutoComplete(fEditWindow.m_hWnd, SHACF_FILESYSTEM | SHACF_URLALL | SHACF_USETAB);
82 
83     // take advice to watch events
84     HRESULT hResult = IUnknown_QueryService(param18, SID_SShellBrowser, IID_PPV_ARG(IBrowserService, &browserService));
85     if (SUCCEEDED(hResult))
86     {
87         hResult = AtlAdvise(browserService, static_cast<IDispatch *>(this), DIID_DWebBrowserEvents, &fAdviseCookie);
88     }
89 
90     return hResult;
91 }
92 
93 HRESULT STDMETHODCALLTYPE CAddressEditBox::SetCurrentDir(long paramC)
94 {
95     return E_NOTIMPL;
96 }
97 
98 BOOL CAddressEditBox::GetComboBoxText(CComHeapPtr<WCHAR>& pszText)
99 {
100     pszText.Free();
101     INT cchMax = fCombobox.GetWindowTextLength() + sizeof(UNICODE_NULL);
102     if (!pszText.Allocate(cchMax))
103         return FALSE;
104     return fCombobox.GetWindowText(pszText, cchMax);
105 }
106 
107 HRESULT CAddressEditBox::RefreshAddress()
108 {
109     /* Get the current pidl of the browser */
110     CComHeapPtr<ITEMIDLIST> absolutePIDL;
111     HRESULT hr = GetAbsolutePidl(&absolutePIDL);
112     if (FAILED_UNEXPECTEDLY(hr))
113         return hr;
114 
115     /* Fill the combobox */
116     ATLASSERT(absolutePIDL != NULL);
117     PopulateComboBox(absolutePIDL);
118 
119     /* Get pShellFolder and pidlChild */
120     CComPtr<IShellFolder> pShellFolder;
121     PCITEMID_CHILD pidlChild;
122     hr = SHBindToParent(absolutePIDL, IID_PPV_ARG(IShellFolder, &pShellFolder), &pidlChild);
123     if (FAILED_UNEXPECTEDLY(hr))
124         return hr;
125 
126     /* Get ready to set the displayed item */
127     COMBOBOXEXITEMW item = { CBEIF_IMAGE | CBEIF_SELECTEDIMAGE | CBEIF_TEXT | CBEIF_LPARAM };
128     item.iItem = -1; /* -1 to specify the displayed item */
129     item.iImage = SHMapPIDLToSystemImageListIndex(pShellFolder, pidlChild, &item.iSelectedImage);
130 
131     /* Set the path if filesystem; otherwise use the name */
132     WCHAR szPathOrName[MAX_PATH];
133     SHGDNF flags = SHGDN_FORADDRESSBAR;
134     if (gCabinetState.fFullPathAddress)
135         flags |= SHGDN_FORPARSING;
136 
137     if (SUCCEEDED(IEGetNameAndFlags(absolutePIDL, flags, szPathOrName, _countof(szPathOrName), NULL)))
138         item.pszText = szPathOrName;
139 
140     /* Ownership of absolutePIDL will be moved to fCombobox. See CBEN_DELETEITEM */
141     item.lParam = reinterpret_cast<LPARAM>(absolutePIDL.Detach());
142 
143     fCombobox.SendMessage(CBEM_SETITEM, 0, reinterpret_cast<LPARAM>(&item)); /* Set it! */
144     return S_OK;
145 }
146 
147 HRESULT CAddressEditBox::GetAbsolutePidl(PIDLIST_ABSOLUTE *pAbsolutePIDL)
148 {
149     CComPtr<IBrowserService> isb;
150     HRESULT hr = IUnknown_QueryService(fSite, SID_STopLevelBrowser, IID_PPV_ARG(IBrowserService, &isb));
151     if (FAILED_UNEXPECTEDLY(hr))
152         return hr;
153 
154     hr = isb->GetPidl(pAbsolutePIDL);
155     if (FAILED_UNEXPECTEDLY(hr))
156         return hr;
157 
158     return S_OK;
159 }
160 
161 /* Execute command line from address bar */
162 BOOL CAddressEditBox::ExecuteCommandLine()
163 {
164     /* Get command line */
165     CComHeapPtr<WCHAR> pszCmdLine;
166     if (!GetComboBoxText(pszCmdLine))
167         return FALSE;
168 
169     /* Split 1st parameter from trailing arguments */
170     PWCHAR args = PathGetArgsW(pszCmdLine);
171     PathRemoveArgsW(pszCmdLine);
172 
173     PathUnquoteSpacesW(pszCmdLine); /* Unquote the 1st parameter */
174 
175     /* Get ready for execution */
176     SHELLEXECUTEINFOW info = { sizeof(info), SEE_MASK_FLAG_NO_UI, m_hWnd };
177     info.lpFile = pszCmdLine;
178     info.lpParameters = args;
179     info.nShow = SW_SHOWNORMAL;
180 
181     /* Set current directory */
182     WCHAR dir[MAX_PATH] = L"";
183     CComHeapPtr<ITEMIDLIST> pidl;
184     if (SUCCEEDED(GetAbsolutePidl(&pidl)))
185     {
186         if (SHGetPathFromIDListW(pidl, dir) && PathIsDirectoryW(dir))
187             info.lpDirectory = dir;
188     }
189 
190     if (!::ShellExecuteExW(&info)) /* Execute! */
191         return FALSE;
192 
193     RefreshAddress();
194     return TRUE;
195 }
196 
197 HRESULT STDMETHODCALLTYPE CAddressEditBox::ParseNow(long paramC)
198 {
199     ULONG eaten;
200     ULONG attributes;
201     HRESULT hr;
202     HWND topLevelWindow;
203     PIDLIST_ABSOLUTE pidlCurrent= NULL;
204     PIDLIST_RELATIVE pidlRelative = NULL;
205     CComPtr<IShellFolder> psfCurrent;
206 
207     CComPtr<IBrowserService> pbs;
208     hr = IUnknown_QueryService(fSite, SID_SShellBrowser, IID_PPV_ARG(IBrowserService, &pbs));
209     if (FAILED_UNEXPECTEDLY(hr))
210         return hr;
211 
212     hr = IUnknown_GetWindow(pbs, &topLevelWindow);
213     if (FAILED_UNEXPECTEDLY(hr))
214         return hr;
215 
216     /* Get the path to browse and expand it if needed */
217     CComHeapPtr<WCHAR> input, address;
218     if (!GetComboBoxText(input))
219         return E_FAIL;
220 
221     INT addressLength = (wcschr(input, L'%') ? ::SHExpandEnvironmentStringsW(input, NULL, 0) : 0);
222     if (addressLength <= 0 ||
223         !address.Allocate(addressLength + 1) ||
224         !::SHExpandEnvironmentStringsW(input, address, addressLength))
225     {
226         address.Free();
227         address.Attach(input.Detach());
228     }
229 
230     /* Try to parse a relative path and if it fails, try to browse an absolute path */
231     CComPtr<IShellFolder> psfDesktop;
232     hr = SHGetDesktopFolder(&psfDesktop);
233     if (FAILED_UNEXPECTEDLY(hr))
234         goto cleanup;
235 
236     hr = pbs->GetPidl(&pidlCurrent);
237     if (FAILED_UNEXPECTEDLY(hr))
238         goto parseabsolute;
239 
240     hr = psfDesktop->BindToObject(pidlCurrent, NULL, IID_PPV_ARG(IShellFolder, &psfCurrent));
241     if (FAILED_UNEXPECTEDLY(hr))
242         goto parseabsolute;
243 
244     hr = psfCurrent->ParseDisplayName(topLevelWindow, NULL, address, &eaten,  &pidlRelative, &attributes);
245     if (SUCCEEDED(hr))
246     {
247         pidlLastParsed = ILCombine(pidlCurrent, pidlRelative);
248         ILFree(pidlRelative);
249         goto cleanup;
250     }
251 
252 parseabsolute:
253     /* We couldn't parse a relative path, attempt to parse an absolute path */
254     hr = psfDesktop->ParseDisplayName(topLevelWindow, NULL, address, &eaten, &pidlLastParsed, &attributes);
255 
256 cleanup:
257     if (pidlCurrent)
258         ILFree(pidlCurrent);
259     return hr;
260 }
261 
262 HRESULT STDMETHODCALLTYPE CAddressEditBox::ShowFileNotFoundError(HRESULT hRet)
263 {
264     CComHeapPtr<WCHAR> input;
265     if (!GetComboBoxText(input))
266         return E_FAIL;
267 
268     ShellMessageBoxW(_AtlBaseModule.GetResourceInstance(), fCombobox.m_hWnd, MAKEINTRESOURCEW(IDS_PARSE_ADDR_ERR_TEXT), MAKEINTRESOURCEW(IDS_PARSE_ADDR_ERR_TITLE), MB_OK | MB_ICONERROR, input.m_pData);
269 
270     return hRet;
271 }
272 
273 HRESULT STDMETHODCALLTYPE CAddressEditBox::Execute(long paramC)
274 {
275     HRESULT hr;
276 
277     /*
278      * Parse the path if it wasn't parsed
279      */
280     if (!pidlLastParsed)
281     {
282         hr = ParseNow(0);
283 
284         /* If the destination path doesn't exist then display an error message */
285         if (hr == HRESULT_FROM_WIN32(ERROR_INVALID_DRIVE) || hr == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND))
286         {
287             if (ExecuteCommandLine())
288                 return S_OK;
289 
290             return ShowFileNotFoundError(hr);
291         }
292 
293         if (!pidlLastParsed)
294             return E_FAIL;
295     }
296 
297     /*
298      * Get the IShellBrowser and IBrowserService interfaces of the shell browser
299      */
300     CComPtr<IShellBrowser> pisb;
301     hr = IUnknown_QueryService(fSite, SID_SShellBrowser, IID_PPV_ARG(IShellBrowser, &pisb));
302     if (FAILED(hr))
303         return hr;
304 
305     /*
306      * Get the current pidl of the shellbrowser and check if it is the same with the parsed one
307      */
308     PIDLIST_ABSOLUTE pidl;
309     hr = GetAbsolutePidl(&pidl);
310     if (FAILED(hr))
311         return hr;
312 
313     CComPtr<IShellFolder> psf;
314     hr = SHGetDesktopFolder(&psf);
315     if (FAILED(hr))
316         return hr;
317 
318     hr = psf->CompareIDs(0, pidl, pidlLastParsed);
319 
320     SHFree(pidl);
321     if (hr == 0)
322         return S_OK;
323 
324     /*
325      * Attempt to browse to the parsed pidl
326      */
327     hr = pisb->BrowseObject(pidlLastParsed, 0);
328     if (SUCCEEDED(hr))
329         return hr;
330 
331     /*
332      * Browsing to the pidl failed so it's not a folder. So invoke its defaule command.
333      */
334     HWND topLevelWindow;
335     hr = IUnknown_GetWindow(pisb, &topLevelWindow);
336     if (FAILED(hr))
337         return hr;
338 
339     LPCITEMIDLIST pidlChild;
340     CComPtr<IShellFolder> sf;
341     hr = SHBindToParent(pidlLastParsed, IID_PPV_ARG(IShellFolder, &sf), &pidlChild);
342     if (FAILED(hr))
343         return hr;
344 
345     hr = SHInvokeDefaultCommand(topLevelWindow, sf, pidlChild);
346     if (FAILED(hr))
347         return hr;
348 
349     return hr;
350 }
351 
352 HRESULT STDMETHODCALLTYPE CAddressEditBox::Save(long paramC)
353 {
354     return E_NOTIMPL;
355 }
356 
357 HRESULT STDMETHODCALLTYPE CAddressEditBox::OnWinEvent(
358     HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT *theResult)
359 {
360     LPNMHDR hdr;
361 
362     if (theResult)
363         *theResult = 0;
364 
365     switch (uMsg)
366     {
367         case WM_COMMAND:
368         {
369             if (HIWORD(wParam) == CBN_SELCHANGE)
370             {
371                 UINT selectedIndex = SendMessageW((HWND)lParam, CB_GETCURSEL, 0, 0);
372                 pidlLastParsed = ILClone((LPITEMIDLIST)SendMessageW((HWND)lParam, CB_GETITEMDATA, selectedIndex, 0));
373                 Execute(0);
374             }
375             break;
376         }
377         case WM_NOTIFY:
378         {
379             hdr = (LPNMHDR) lParam;
380             if (hdr->code == CBEN_ENDEDIT)
381             {
382                 NMCBEENDEDITW *endEdit = (NMCBEENDEDITW*) lParam;
383                 if (endEdit->iWhy == CBENF_RETURN)
384                 {
385                     Execute(0);
386                 }
387                 else if (endEdit->iWhy == CBENF_ESCAPE)
388                 {
389                     /* Reset the contents of the combo box */
390                     RefreshAddress();
391                 }
392             }
393             else if (hdr->code == CBEN_DELETEITEM)
394             {
395                 PNMCOMBOBOXEX pCBEx = (PNMCOMBOBOXEX) lParam;
396                 LPITEMIDLIST itemPidl = (LPITEMIDLIST)pCBEx->ceItem.lParam;
397                 if (itemPidl)
398                 {
399                     ILFree(itemPidl);
400                 }
401             }
402             break;
403         }
404     }
405     return S_OK;
406 }
407 
408 HRESULT STDMETHODCALLTYPE CAddressEditBox::IsWindowOwner(HWND hWnd)
409 {
410     if (fCombobox.m_hWnd == hWnd)
411         return S_OK;
412     if (fEditWindow.m_hWnd == hWnd)
413         return S_OK;
414     return S_FALSE;
415 }
416 
417 HRESULT STDMETHODCALLTYPE CAddressEditBox::QueryStatus(
418     const GUID *pguidCmdGroup, ULONG cCmds, OLECMD prgCmds[  ], OLECMDTEXT *pCmdText)
419 {
420     return E_NOTIMPL;
421 }
422 
423 HRESULT STDMETHODCALLTYPE CAddressEditBox::Exec(const GUID *pguidCmdGroup, DWORD nCmdID,
424     DWORD nCmdexecopt, VARIANT *pvaIn, VARIANT *pvaOut)
425 {
426     return E_NOTIMPL;
427 }
428 
429 HRESULT STDMETHODCALLTYPE CAddressEditBox::GetTypeInfoCount(UINT *pctinfo)
430 {
431     return E_NOTIMPL;
432 }
433 
434 HRESULT STDMETHODCALLTYPE CAddressEditBox::GetTypeInfo(UINT iTInfo, LCID lcid, ITypeInfo **ppTInfo)
435 {
436     return E_NOTIMPL;
437 }
438 
439 HRESULT STDMETHODCALLTYPE CAddressEditBox::GetIDsOfNames(
440     REFIID riid, LPOLESTR *rgszNames, UINT cNames, LCID lcid, DISPID *rgDispId)
441 {
442     return E_NOTIMPL;
443 }
444 
445 HRESULT STDMETHODCALLTYPE CAddressEditBox::Invoke(DISPID dispIdMember, REFIID riid, LCID lcid,
446     WORD wFlags, DISPPARAMS *pDispParams, VARIANT *pVarResult, EXCEPINFO *pExcepInfo, UINT *puArgErr)
447 {
448     if (pDispParams == NULL)
449         return E_INVALIDARG;
450 
451     switch (dispIdMember)
452     {
453     case DISPID_NAVIGATECOMPLETE2:
454     case DISPID_DOCUMENTCOMPLETE:
455         if (pidlLastParsed)
456         {
457             ILFree(pidlLastParsed);
458             pidlLastParsed = NULL;
459         }
460 
461         RefreshAddress();
462         break;
463     }
464     return S_OK;
465 }
466 
467 HRESULT STDMETHODCALLTYPE CAddressEditBox::GetClassID(CLSID *pClassID)
468 {
469     if (pClassID == NULL)
470         return E_POINTER;
471     *pClassID = CLSID_AddressEditBox;
472     return S_OK;
473 }
474 
475 HRESULT STDMETHODCALLTYPE CAddressEditBox::IsDirty()
476 {
477     return E_NOTIMPL;
478 }
479 
480 HRESULT STDMETHODCALLTYPE CAddressEditBox::Load(IStream *pStm)
481 {
482     return E_NOTIMPL;
483 }
484 
485 HRESULT STDMETHODCALLTYPE CAddressEditBox::Save(IStream *pStm, BOOL fClearDirty)
486 {
487     return E_NOTIMPL;
488 }
489 
490 HRESULT STDMETHODCALLTYPE CAddressEditBox::GetSizeMax(ULARGE_INTEGER *pcbSize)
491 {
492     return E_NOTIMPL;
493 }
494 
495 void CAddressEditBox::PopulateComboBox(LPITEMIDLIST pidlCurrent)
496 {
497     HRESULT hr;
498     LPITEMIDLIST pidl;
499     int indent = 0;
500     int index;
501 
502     index = SendMessageW(hComboBoxEx, CB_GETCOUNT, 0, 0);
503     for (int i = 0; i < index; i++)
504         SendMessageW(hComboBoxEx, CBEM_DELETEITEM, i, 0);
505     SendMessageW(hComboBoxEx, CB_RESETCONTENT, 0, 0);
506 
507     /* Calculate the indent level. No need to clone the pidl */
508     pidl = pidlCurrent;
509     do
510     {
511         if(!pidl->mkid.cb)
512             break;
513         pidl = ILGetNext(pidl);
514         indent++;
515     } while (pidl);
516     index = indent;
517 
518     /* Add every id from the pidl in the combo box */
519     pidl = ILClone(pidlCurrent);
520     do
521     {
522         AddComboBoxItem(pidl, 0, index);
523         ILRemoveLastID(pidl);
524         index--;
525     } while (index >= 0);
526     ILFree(pidl);
527 
528     /* Add the items of the desktop */
529     FillOneLevel(0, 1, indent);
530 
531     /* Add the items of My Computer */
532     hr = SHGetSpecialFolderLocation(0, CSIDL_DRIVES, &pidl);
533     if (FAILED_UNEXPECTEDLY(hr))
534         return;
535 
536     for(LPITEMIDLIST i = GetItemData(0); i; i = GetItemData(index))
537     {
538         if (ILIsEqual(i, pidl))
539         {
540             FillOneLevel(index, 2, indent);
541             break;
542         }
543         index++;
544     }
545     ILFree(pidl);
546 }
547 
548 void CAddressEditBox::AddComboBoxItem(LPITEMIDLIST pidl, int index, int indent)
549 {
550     HRESULT hr;
551     WCHAR buf[4096];
552 
553     LPCITEMIDLIST pidlChild;
554     CComPtr<IShellFolder> sf;
555     hr = SHBindToParent(pidl, IID_PPV_ARG(IShellFolder, &sf), &pidlChild);
556     if (FAILED_UNEXPECTEDLY(hr))
557         return;
558 
559     STRRET strret;
560     hr = sf->GetDisplayNameOf(pidlChild, SHGDN_FORADDRESSBAR, &strret);
561     if (FAILED_UNEXPECTEDLY(hr))
562         return;
563 
564     hr = StrRetToBufW(&strret, pidlChild, buf, 4095);
565     if (FAILED_UNEXPECTEDLY(hr))
566         return;
567 
568     COMBOBOXEXITEMW item = {0};
569     item.mask = CBEIF_LPARAM | CBEIF_INDENT | CBEIF_SELECTEDIMAGE | CBEIF_IMAGE | CBEIF_TEXT;
570     item.iImage = SHMapPIDLToSystemImageListIndex(sf, pidlChild, &item.iSelectedImage);
571     item.pszText = buf;
572     item.lParam = (LPARAM)(ILClone(pidl));
573     item.iIndent = indent;
574     item.iItem = index;
575     SendMessageW(hComboBoxEx, CBEM_INSERTITEMW, 0, (LPARAM)&item);
576 }
577 
578 void CAddressEditBox::FillOneLevel(int index, int levelIndent, int indent)
579 {
580     HRESULT hr;
581     ULONG numObj;
582     int count;
583     LPITEMIDLIST pidl, pidl2, pidl3, pidl4;
584 
585     count = index + 1;
586     pidl = GetItemData(index);
587     pidl2 = GetItemData(count);
588     if(pidl)
589     {
590         CComPtr<IShellFolder> psfDesktop;
591         CComPtr<IShellFolder> psfItem;
592 
593         hr = SHGetDesktopFolder(&psfDesktop);
594         if (FAILED_UNEXPECTEDLY(hr))
595             return;
596 
597         if (!pidl->mkid.cb)
598         {
599             psfItem = psfDesktop;
600         }
601         else
602         {
603             hr = psfDesktop->BindToObject(pidl, NULL, IID_PPV_ARG(IShellFolder, &psfItem));
604             if (FAILED_UNEXPECTEDLY(hr))
605                 return;
606         }
607 
608         CComPtr<IEnumIDList> pEnumIDList;
609         hr = psfItem->EnumObjects(0, SHCONTF_FOLDERS | SHCONTF_INCLUDEHIDDEN, &pEnumIDList);
610         if (FAILED_UNEXPECTEDLY(hr))
611             return;
612 
613         do
614         {
615             hr = pEnumIDList->Next(1, &pidl3, &numObj);
616             if(hr != S_OK || !numObj)
617                 break;
618 
619             pidl4 = ILCombine(pidl, pidl3);
620             if (pidl2 && ILIsEqual(pidl4, pidl2))
621                 count += (indent - levelIndent);
622             else
623                 AddComboBoxItem(pidl4, count, levelIndent);
624             count++;
625             ILFree(pidl3);
626             ILFree(pidl4);
627         } while (true);
628     }
629 }
630 
631 LPITEMIDLIST CAddressEditBox::GetItemData(int index)
632 {
633     COMBOBOXEXITEMW item;
634 
635     memset(&item, 0, sizeof(COMBOBOXEXITEMW));
636     item.mask = CBEIF_LPARAM;
637     item.iItem = index;
638     SendMessageW(hComboBoxEx, CBEM_GETITEMW, 0, (LPARAM)&item);
639     return (LPITEMIDLIST)item.lParam;
640 }
641 
642 LRESULT CAddressEditBox::OnSettingChange(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled)
643 {
644     RefreshAddress();
645     return NO_ERROR;
646 }
647