[SHELLFIND] Use DeferWindowPos for resizing child windows
[reactos.git] / dll / win32 / browseui / shellfind / CSearchBar.cpp
1 /*
2 * PROJECT: ReactOS Search Shell Extension
3 * LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later)
4 * PURPOSE: Search UI
5 * COPYRIGHT: Copyright 2019 Brock Mammen
6 */
7
8 #include "CSearchBar.h"
9 #include <psdk/wingdi.h>
10 #include <commoncontrols.h>
11 #include <../browseui.h>
12 #include <shellapi.h>
13 #include <exdispid.h>
14
15 WINE_DEFAULT_DEBUG_CHANNEL(shellfind);
16
17 #if 1
18 #undef UNIMPLEMENTED
19
20 #define UNIMPLEMENTED DbgPrint("%s is UNIMPLEMENTED!\n", __FUNCTION__)
21 #endif
22
23 CSearchBar::CSearchBar() :
24 pSite(NULL),
25 fVisible(FALSE)
26 {
27 }
28
29 CSearchBar::~CSearchBar()
30 {
31 }
32
33 LRESULT CSearchBar::OnInitDialog(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled)
34 {
35 SetSearchInProgress(FALSE);
36
37 HWND hCombobox = GetDlgItem(IDC_SEARCH_COMBOBOX);
38 IImageList *pImageList;
39 HRESULT hResult = SHGetImageList(SHIL_SMALL, IID_PPV_ARG(IImageList, &pImageList));
40 SendMessage(hCombobox, CBEM_SETIMAGELIST, 0, FAILED_UNEXPECTEDLY(hResult) ? 0 : reinterpret_cast<LPARAM>(pImageList));
41
42 SendMessage(hCombobox, CBEM_SETEXTENDEDSTYLE,
43 CBES_EX_CASESENSITIVE | CBES_EX_NOSIZELIMIT, CBES_EX_CASESENSITIVE | CBES_EX_NOSIZELIMIT);
44 HWND hEditControl = reinterpret_cast<HWND>(SendMessage(hCombobox, CBEM_GETEDITCONTROL, 0, 0));
45 hResult = CAddressEditBox_CreateInstance(IID_PPV_ARG(IAddressEditBox, &m_AddressEditBox));
46 if (FAILED_UNEXPECTEDLY(hResult))
47 return hResult;
48
49 CComPtr<IShellService> pShellService;
50 hResult = fAddressEditBox->QueryInterface(IID_PPV_ARG(IShellService, &pShellService));
51 if (FAILED_UNEXPECTEDLY(hResult))
52 return hResult;
53 hResult = fAddressEditBox->Init(hCombobox, fEditControl, 0, pSite);
54 if (FAILED_UNEXPECTEDLY(hResult))
55 return hResult;
56
57 CComPtr<IDispatch> pDispatch;
58 hResult = fAddressEditBox->QueryInterface(IID_PPV_ARG(IDispatch, &pDispatch));
59 if (FAILED_UNEXPECTEDLY(hResult))
60 return hResult;
61 DISPPARAMS params = {0};
62 hResult = pDispatch->Invoke(DISPID_NAVIGATECOMPLETE2, GUID_NULL, 0, DISPATCH_METHOD, &params, NULL, NULL, NULL);
63
64 hResult = pShellService->SetOwner(NULL);
65 if (FAILED_UNEXPECTEDLY(hResult))
66 return hResult;
67
68 return 0;
69 }
70
71 HRESULT CSearchBar::ExecuteCommand(CComPtr<IContextMenu>& menu, UINT nCmd)
72 {
73 CComPtr<IOleWindow> pBrowserOleWnd;
74 CMINVOKECOMMANDINFO cmi;
75 HWND browserWnd;
76 HRESULT hr;
77
78 hr = IUnknown_QueryService(pSite, SID_SShellBrowser, IID_PPV_ARG(IOleWindow, &pBrowserOleWnd));
79 if (FAILED_UNEXPECTEDLY(hr))
80 return hr;
81
82 hr = pBrowserOleWnd->GetWindow(&browserWnd);
83 if (FAILED_UNEXPECTEDLY(hr))
84 return hr;
85
86 ZeroMemory(&cmi, sizeof(cmi));
87 cmi.cbSize = sizeof(cmi);
88 cmi.lpVerb = MAKEINTRESOURCEA(nCmd);
89 cmi.hwnd = browserWnd;
90 if (GetKeyState(VK_SHIFT) & 0x8000)
91 cmi.fMask |= CMIC_MASK_SHIFT_DOWN;
92 if (GetKeyState(VK_CONTROL) & 0x8000)
93 cmi.fMask |= CMIC_MASK_CONTROL_DOWN;
94
95 return menu->InvokeCommand(&cmi);
96 }
97
98
99 // *** ATL event handlers ***
100 LRESULT CSearchBar::OnSetFocus(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled)
101 {
102 IUnknown_OnFocusChangeIS(pSite, reinterpret_cast<IUnknown*>(this), TRUE);
103 bHandled = FALSE;
104 return TRUE;
105 }
106
107 HRESULT CSearchBar::GetSearchResultsFolder(IShellBrowser **ppShellBrowser, HWND *pHwnd, IShellFolder **ppShellFolder)
108 {
109 HRESULT hr;
110 CComPtr<IShellBrowser> pShellBrowser;
111 if (!ppShellBrowser)
112 ppShellBrowser = &pShellBrowser;
113 if (!*ppShellBrowser)
114 {
115 hr = IUnknown_QueryService(m_pSite, SID_SShellBrowser, IID_PPV_ARG(IShellBrowser, ppShellBrowser));
116 if (FAILED_UNEXPECTEDLY(hr))
117 return hr;
118 }
119
120 CComPtr<IShellView> pShellView;
121 hr = (*ppShellBrowser)->QueryActiveShellView(&pShellView);
122 if (FAILED(hr) || !pShellView)
123 return hr;
124
125 CComPtr<IFolderView> pFolderView;
126 hr = pShellView->QueryInterface(IID_PPV_ARG(IFolderView, &pFolderView));
127 if (FAILED(hr) || !pFolderView)
128 return hr;
129
130 CComPtr<IShellFolder> pShellFolder;
131 if (!ppShellFolder)
132 ppShellFolder = &pShellFolder;
133 hr = pFolderView->GetFolder(IID_PPV_ARG(IShellFolder, ppShellFolder));
134 if (FAILED(hr) || !pShellFolder)
135 return hr;
136
137 CLSID clsid;
138 hr = IUnknown_GetClassID(*ppShellFolder, &clsid);
139 if (FAILED(hr))
140 return hr;
141 if (clsid != CLSID_FindFolder)
142 return E_FAIL;
143
144 if (pHwnd)
145 {
146 hr = pShellView->GetWindow(pHwnd);
147 if (FAILED_UNEXPECTEDLY(hr))
148 return hr;
149 }
150
151 return S_OK;
152 }
153
154 LRESULT CSearchBar::OnSearchButtonClicked(WORD wNotifyCode, WORD wID, HWND hWndCtl, BOOL& bHandled)
155 {
156 CComHeapPtr<SearchStart> pSearchStart((SearchStart *)SHAlloc(sizeof(SearchStart)));
157 GetDlgItemText(IDC_SEARCH_FILENAME, pSearchStart->szFileName, _countof(pSearchStart->szFileName));
158 GetDlgItemText(IDC_SEARCH_QUERY, pSearchStart->szQuery, _countof(pSearchStart->szQuery));
159 if (!GetAddressEditBoxPath(pSearchStart->szPath))
160 {
161 ShellMessageBoxW(_AtlBaseModule.GetResourceInstance(), m_hWnd, MAKEINTRESOURCEW(IDS_SEARCHINVALID), MAKEINTRESOURCEW(IDS_SEARCHLABEL), MB_OK | MB_ICONERROR, pSearchStart->szPath);
162 return TRUE;
163 }
164
165 CComPtr<IShellBrowser> pShellBrowser;
166 HRESULT hr = IUnknown_QueryService(pSite, SID_SShellBrowser, IID_PPV_ARG(IShellBrowser, &pShellBrowser));
167 if (FAILED_UNEXPECTEDLY(hr))
168 return hr;
169
170 HWND hwnd;
171 if (FAILED(GetSearchResultsFolder(&pShellBrowser, &hwnd, NULL)))
172 {
173 // Open a new search results folder
174 WCHAR szShellGuid[MAX_PATH];
175 const WCHAR shellGuidPrefix[] = L"shell:::";
176 memcpy(szShellGuid, shellGuidPrefix, sizeof(shellGuidPrefix));
177 hr = StringFromGUID2(CLSID_FindFolder, szShellGuid + _countof(shellGuidPrefix) - 1,
178 _countof(szShellGuid) - _countof(shellGuidPrefix));
179 if (FAILED_UNEXPECTEDLY(hr))
180 return hr;
181
182 CComHeapPtr<ITEMIDLIST> findFolderPidl;
183 hr = SHParseDisplayName(szShellGuid, NULL, &findFolderPidl, 0, NULL);
184 if (FAILED_UNEXPECTEDLY(hr))
185 return hr;
186
187 hr = pShellBrowser->BrowseObject(findFolderPidl, 0);
188 if (FAILED_UNEXPECTEDLY(hr))
189 return hr;
190
191 CComPtr<IShellFolder> pShellFolder;
192 hr = GetSearchResultsFolder(*pShellBrowser, &hwnd, &pShellFolder);
193 if (FAILED_UNEXPECTEDLY(hr))
194 return hr;
195
196 // Subscribe to search events
197 DWORD fAdviseCookie;
198 hr = AtlAdvise(pShellFolder, static_cast<IDispatch *>(this), DIID_DSearchCommandEvents, &fAdviseCookie);
199 if (FAILED_UNEXPECTEDLY(hr))
200 return hr;
201 }
202
203 ::PostMessageW(hwnd, WM_SEARCH_START, 0, (LPARAM) pSearchStart.Detach());
204
205 SetSearchInProgress(TRUE);
206
207 return TRUE;
208 }
209
210 LRESULT CSearchBar::OnStopButtonClicked(WORD wNotifyCode, WORD wID, HWND hWndCtl, BOOL& bHandled)
211 {
212 HWND hwnd;
213 HRESULT hr = GetSearchResultsFolder(NULL, &hwnd, NULL);
214 if (SUCCEEDED(hr))
215 ::PostMessageW(hwnd, WM_SEARCH_STOP, 0, 0);
216
217 return TRUE;
218 }
219
220 BOOL CSearchBar::GetAddressEditBoxPath(WCHAR (&szPath)[MAX_PATH])
221 {
222 HWND hComboboxEx = GetDlgItem(IDC_SEARCH_COMBOBOX);
223 ::GetWindowTextW(hComboboxEx, szPath, _countof(szPath));
224 INT iSelectedIndex = SendMessageW(hComboboxEx, CB_GETCURSEL, 0, 0);
225 if (iSelectedIndex != CB_ERR)
226 {
227 WCHAR szItemText[MAX_PATH];
228 COMBOBOXEXITEMW item = {0};
229 item.mask = CBEIF_LPARAM | CBEIF_TEXT;
230 item.iItem = iSelectedIndex;
231 item.pszText = szItemText;
232 item.cchTextMax = _countof(szItemText);
233 SendMessageW(hComboboxEx, CBEM_GETITEMW, 0, (LPARAM)&item);
234
235 if (!wcscmp(szItemText, szPath) && SHGetPathFromIDListW((LPCITEMIDLIST)item.lParam, szPath))
236 {
237 return TRUE;
238 }
239 }
240
241 DWORD dwAttributes = GetFileAttributesW(szPath);
242 return dwAttributes != INVALID_FILE_ATTRIBUTES
243 && (dwAttributes & FILE_ATTRIBUTE_DIRECTORY);
244 }
245
246 LRESULT CSearchBar::OnSize(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled)
247 {
248 INT iWidth = LOWORD(lParam);
249 INT iPadding = 10;
250
251 ((CWindow)GetDlgItem(IDC_SEARCH_LABEL)).SetWindowPos(NULL, 0, 0, iWidth - iPadding, 40, SWP_NOACTIVATE | SWP_NOCOPYBITS | SWP_NOMOVE | SWP_NOOWNERZORDER | SWP_NOZORDER);
252
253 int inputs[] = { IDC_SEARCH_FILENAME, IDC_SEARCH_QUERY, IDC_SEARCH_COMBOBOX, IDC_SEARCH_BUTTON, IDC_SEARCH_STOP_BUTTON, IDC_PROGRESS_BAR };
254 HDWP hdwp = BeginDeferWindowPos(_countof(inputs));
255 for (SIZE_T i = 0; i < _countof(inputs); i++)
256 {
257 CWindow wnd = (CWindow) GetDlgItem(inputs[i]);
258 RECT rect;
259 wnd.GetWindowRect(&rect);
260 POINT pt = { rect.left, rect.top };
261 ScreenToClient(&pt);
262 hdwp = wnd.DeferWindowPos(hdwp,
263 HWND_TOP,
264 iPadding,
265 pt.y,
266 iWidth - iPadding * 2,
267 rect.bottom - rect.top,
268 SWP_NOZORDER | SWP_NOACTIVATE);
269 }
270 EndDeferWindowPos(hdwp);
271
272 return 0;
273 }
274
275
276 // *** IOleWindow methods ***
277 HRESULT STDMETHODCALLTYPE CSearchBar::GetWindow(HWND *lphwnd)
278 {
279 if (!lphwnd)
280 return E_INVALIDARG;
281 *lphwnd = m_hWnd;
282 return S_OK;
283 }
284
285 HRESULT STDMETHODCALLTYPE CSearchBar::ContextSensitiveHelp(BOOL fEnterMode)
286 {
287 UNIMPLEMENTED;
288 return E_NOTIMPL;
289 }
290
291
292 // *** IDockingWindow methods ***
293 HRESULT STDMETHODCALLTYPE CSearchBar::CloseDW(DWORD dwReserved)
294 {
295 // We do nothing, we don't have anything to save yet
296 TRACE("CloseDW called\n");
297 return S_OK;
298 }
299
300 HRESULT STDMETHODCALLTYPE CSearchBar::ResizeBorderDW(const RECT *prcBorder, IUnknown *punkToolbarSite, BOOL fReserved)
301 {
302 /* Must return E_NOTIMPL according to MSDN */
303 return E_NOTIMPL;
304 }
305
306 HRESULT STDMETHODCALLTYPE CSearchBar::ShowDW(BOOL fShow)
307 {
308 fVisible = fShow;
309 ShowWindow(fShow);
310 return S_OK;
311 }
312
313
314 // *** IDeskBand methods ***
315 HRESULT STDMETHODCALLTYPE CSearchBar::GetBandInfo(DWORD dwBandID, DWORD dwViewMode, DESKBANDINFO *pdbi)
316 {
317 if (!pdbi)
318 {
319 return E_INVALIDARG;
320 }
321
322 if (pdbi->dwMask & DBIM_MINSIZE)
323 {
324 pdbi->ptMinSize.x = 200;
325 pdbi->ptMinSize.y = 30;
326 }
327
328 if (pdbi->dwMask & DBIM_MAXSIZE)
329 {
330 pdbi->ptMaxSize.y = -1;
331 }
332
333 if (pdbi->dwMask & DBIM_INTEGRAL)
334 {
335 pdbi->ptIntegral.y = 1;
336 }
337
338 if (pdbi->dwMask & DBIM_ACTUAL)
339 {
340 pdbi->ptActual.x = 200;
341 pdbi->ptActual.y = 30;
342 }
343
344 if (pdbi->dwMask & DBIM_TITLE)
345 {
346 if (!LoadStringW(_AtlBaseModule.GetResourceInstance(), IDS_SEARCHLABEL, pdbi->wszTitle, _countof(pdbi->wszTitle)))
347 return HRESULT_FROM_WIN32(GetLastError());
348 }
349
350 if (pdbi->dwMask & DBIM_MODEFLAGS)
351 {
352 pdbi->dwModeFlags = DBIMF_NORMAL | DBIMF_VARIABLEHEIGHT;
353 }
354
355 if (pdbi->dwMask & DBIM_BKCOLOR)
356 {
357 pdbi->dwMask &= ~DBIM_BKCOLOR;
358 }
359 return S_OK;
360 }
361
362
363 // *** IObjectWithSite methods ***
364 HRESULT STDMETHODCALLTYPE CSearchBar::SetSite(IUnknown *pUnkSite)
365 {
366 HRESULT hr;
367 HWND parentWnd;
368
369 if (pUnkSite == pSite)
370 return S_OK;
371
372 TRACE("SetSite called \n");
373 if (!pUnkSite)
374 {
375 DestroyWindow();
376 m_hWnd = NULL;
377 }
378
379 if (pUnkSite != pSite)
380 {
381 pSite = NULL;
382 }
383
384 if(!pUnkSite)
385 return S_OK;
386
387 hr = IUnknown_GetWindow(pUnkSite, &parentWnd);
388 if (!SUCCEEDED(hr))
389 {
390 ERR("Could not get parent's window ! Status: %08lx\n", hr);
391 return E_INVALIDARG;
392 }
393
394 pSite = pUnkSite;
395
396 if (m_hWnd)
397 {
398 // Change its parent
399 SetParent(parentWnd);
400 }
401 else
402 {
403 CDialogImpl::Create(parentWnd);
404
405 }
406 return S_OK;
407 }
408
409 HRESULT STDMETHODCALLTYPE CSearchBar::GetSite(REFIID riid, void **ppvSite)
410 {
411 if (!ppvSite)
412 return E_POINTER;
413 *ppvSite = pSite;
414 return S_OK;
415 }
416
417
418 // *** IOleCommandTarget methods ***
419 HRESULT STDMETHODCALLTYPE CSearchBar::QueryStatus(const GUID *pguidCmdGroup, ULONG cCmds, OLECMD prgCmds [], OLECMDTEXT *pCmdText)
420 {
421 UNIMPLEMENTED;
422 return E_NOTIMPL;
423 }
424
425 HRESULT STDMETHODCALLTYPE CSearchBar::Exec(const GUID *pguidCmdGroup, DWORD nCmdID, DWORD nCmdexecopt, VARIANT *pvaIn, VARIANT *pvaOut)
426 {
427 UNIMPLEMENTED;
428 return E_NOTIMPL;
429 }
430
431
432 // *** IServiceProvider methods ***
433 HRESULT STDMETHODCALLTYPE CSearchBar::QueryService(REFGUID guidService, REFIID riid, void **ppvObject)
434 {
435 /* FIXME: we probably want to handle more services here */
436 return IUnknown_QueryService(pSite, SID_SShellBrowser, riid, ppvObject);
437 }
438
439
440 // *** IInputObject methods ***
441 HRESULT STDMETHODCALLTYPE CSearchBar::UIActivateIO(BOOL fActivate, LPMSG lpMsg)
442 {
443 if (fActivate)
444 {
445 //SetFocus();
446 SetActiveWindow();
447 }
448 // TODO: handle message
449 if(lpMsg)
450 {
451 TranslateMessage(lpMsg);
452 DispatchMessage(lpMsg);
453 }
454 return S_OK;
455 }
456
457 HRESULT STDMETHODCALLTYPE CSearchBar::HasFocusIO()
458 {
459 return S_OK;
460 }
461
462 HRESULT STDMETHODCALLTYPE CSearchBar::TranslateAcceleratorIO(LPMSG lpMsg)
463 {
464 if (IsDialogMessage(lpMsg))
465 return S_OK;
466
467 if ((lpMsg->hwnd == m_hWnd || IsChild(lpMsg->hwnd)))
468 {
469 TranslateMessage(lpMsg);
470 DispatchMessage(lpMsg);
471 return S_OK;
472 }
473
474 return S_FALSE;
475 }
476
477 // *** IPersist methods ***
478 HRESULT STDMETHODCALLTYPE CSearchBar::GetClassID(CLSID *pClassID)
479 {
480 if (!pClassID)
481 return E_POINTER;
482 *pClassID = CLSID_FileSearchBand;
483 return S_OK;
484 }
485
486
487 // *** IPersistStream methods ***
488 HRESULT STDMETHODCALLTYPE CSearchBar::IsDirty()
489 {
490 UNIMPLEMENTED;
491 return E_NOTIMPL;
492 }
493
494 HRESULT STDMETHODCALLTYPE CSearchBar::Load(IStream *pStm)
495 {
496 UNIMPLEMENTED;
497 return E_NOTIMPL;
498 }
499
500 HRESULT STDMETHODCALLTYPE CSearchBar::Save(IStream *pStm, BOOL fClearDirty)
501 {
502 UNIMPLEMENTED;
503 return E_NOTIMPL;
504 }
505
506 HRESULT STDMETHODCALLTYPE CSearchBar::GetSizeMax(ULARGE_INTEGER *pcbSize)
507 {
508 // TODO: calculate max size
509 UNIMPLEMENTED;
510 return E_NOTIMPL;
511 }
512
513
514 // *** IWinEventHandler methods ***
515 HRESULT STDMETHODCALLTYPE CSearchBar::OnWinEvent(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT *theResult)
516 {
517 return S_OK;
518 }
519
520 HRESULT STDMETHODCALLTYPE CSearchBar::IsWindowOwner(HWND hWnd)
521 {
522 return (hWnd == m_hWnd) ? S_OK : S_FALSE;
523 }
524
525 // *** IBandNavigate methods ***
526 HRESULT STDMETHODCALLTYPE CSearchBar::Select(long paramC)
527 {
528 UNIMPLEMENTED;
529 return E_NOTIMPL;
530 }
531
532 // *** INamespaceProxy ***
533 HRESULT STDMETHODCALLTYPE CSearchBar::GetNavigateTarget(long paramC, long param10, long param14)
534 {
535 UNIMPLEMENTED;
536 return E_NOTIMPL;
537 }
538
539 HRESULT STDMETHODCALLTYPE CSearchBar::Invoke(long paramC)
540 {
541 UNIMPLEMENTED;
542 return E_NOTIMPL;
543 }
544
545 HRESULT STDMETHODCALLTYPE CSearchBar::OnSelectionChanged(long paramC)
546 {
547 UNIMPLEMENTED;
548 return E_NOTIMPL;
549 }
550
551 HRESULT STDMETHODCALLTYPE CSearchBar::RefreshFlags(long paramC, long param10, long param14)
552 {
553 UNIMPLEMENTED;
554 return E_NOTIMPL;
555 }
556
557 HRESULT STDMETHODCALLTYPE CSearchBar::CacheItem(long paramC)
558 {
559 UNIMPLEMENTED;
560 return E_NOTIMPL;
561 }
562
563 // *** IDispatch methods ***
564 HRESULT STDMETHODCALLTYPE CSearchBar::GetTypeInfoCount(UINT *pctinfo)
565 {
566 UNIMPLEMENTED;
567 return E_NOTIMPL;
568 }
569
570 HRESULT STDMETHODCALLTYPE CSearchBar::GetTypeInfo(UINT iTInfo, LCID lcid, ITypeInfo **ppTInfo)
571 {
572 UNIMPLEMENTED;
573 return E_NOTIMPL;
574 }
575
576 HRESULT STDMETHODCALLTYPE CSearchBar::GetIDsOfNames(REFIID riid, LPOLESTR *rgszNames, UINT cNames, LCID lcid, DISPID *rgDispId)
577 {
578 UNIMPLEMENTED;
579 return E_NOTIMPL;
580 }
581
582 void CSearchBar::SetSearchInProgress(BOOL bInProgress)
583 {
584 ::ShowWindow(GetDlgItem(IDC_SEARCH_BUTTON), bInProgress ? SW_HIDE : SW_SHOW);
585 ::ShowWindow(GetDlgItem(IDC_SEARCH_STOP_BUTTON), bInProgress ? SW_SHOW : SW_HIDE);
586 HWND hProgressBar = GetDlgItem(IDC_PROGRESS_BAR);
587 ::ShowWindow(hProgressBar, bInProgress ? SW_SHOW : SW_HIDE);
588 ::PostMessage(hProgressBar, PBM_SETMARQUEE, bInProgress, 0);
589 }
590
591 HRESULT STDMETHODCALLTYPE CSearchBar::Invoke(DISPID dispIdMember, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS *pDispParams, VARIANT *pVarResult, EXCEPINFO *pExcepInfo, UINT *puArgErr)
592 {
593 if (dispIdMember == DISPID_SEARCHCOMPLETE || dispIdMember == DISPID_SEARCHABORT)
594 {
595 SetSearchInProgress(FALSE);
596 return S_OK;
597 }
598 return E_INVALIDARG;
599 }