aboutsummaryrefslogtreecommitdiff
path: root/src/samples/thmviewer/thmviewer.cpp
diff options
context:
space:
mode:
authorRob Mensching <rob@firegiant.com>2022-07-14 15:19:53 -0700
committerRob Mensching <rob@firegiant.com>2022-07-14 16:02:24 -0700
commit229242cf7c328b89b5aa65ed7a04e33c8b93b393 (patch)
treede0a9547e73e46490b0946d6850228d5b30258b8 /src/samples/thmviewer/thmviewer.cpp
parentf46ca6a9dce91607ffc9855270dd6998216e1a8b (diff)
downloadwix-229242cf7c328b89b5aa65ed7a04e33c8b93b393.tar.gz
wix-229242cf7c328b89b5aa65ed7a04e33c8b93b393.tar.bz2
wix-229242cf7c328b89b5aa65ed7a04e33c8b93b393.zip
Rename "samples" segment to "tools"
This segment is a bit of a "miscellaneous section" in the WiX repo. As such it has been difficult to name. I originally eschewed the name "tools" because what is in the "wix" segment was once called "tools". However, now that wix.exe is firmly established as the entry point for WiX operations, I've become comfortable with its segment being named "wix". That meant "tools" was again available and "tools" better describes the content of this section.
Diffstat (limited to 'src/samples/thmviewer/thmviewer.cpp')
-rw-r--r--src/samples/thmviewer/thmviewer.cpp564
1 files changed, 0 insertions, 564 deletions
diff --git a/src/samples/thmviewer/thmviewer.cpp b/src/samples/thmviewer/thmviewer.cpp
deleted file mode 100644
index 38f3c4dc..00000000
--- a/src/samples/thmviewer/thmviewer.cpp
+++ /dev/null
@@ -1,564 +0,0 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3#include "precomp.h"
4
5static const LPCWSTR THMVWR_WINDOW_CLASS_MAIN = L"ThmViewerMain";
6
7static THEME* vpTheme = NULL;
8static DWORD vdwDisplayThreadId = 0;
9static LPWSTR vsczThemeLoadErrors = NULL;
10
11enum THMVWR_CONTROL
12{
13 // Non-paged controls
14 THMVWR_CONTROL_TREE = THEME_FIRST_ASSIGN_CONTROL_ID,
15};
16
17// Internal functions
18
19static HRESULT ProcessCommandLine(
20 __in_z_opt LPCWSTR wzCommandLine,
21 __out_z LPWSTR* psczThemeFile,
22 __out_z LPWSTR* psczWxlFile
23 );
24static HRESULT CreateTheme(
25 __in HINSTANCE hInstance,
26 __out THEME** ppTheme
27 );
28static HRESULT CreateMainWindowClass(
29 __in HINSTANCE hInstance,
30 __in THEME* pTheme,
31 __out ATOM* pAtom
32 );
33static LRESULT CALLBACK MainWndProc(
34 __in HWND hWnd,
35 __in UINT uMsg,
36 __in WPARAM wParam,
37 __in LPARAM lParam
38 );
39static void OnThemeLoadBegin(
40 __in_z_opt LPWSTR sczThemeLoadErrors
41 );
42static void OnThemeLoadError(
43 __in THEME* pTheme,
44 __in HRESULT hrFailure
45 );
46static void OnNewTheme(
47 __in THEME* pTheme,
48 __in HWND hWnd,
49 __in HANDLE_THEME* pHandle
50 );
51static BOOL OnThemeLoadingControl(
52 __in const THEME_LOADINGCONTROL_ARGS* pArgs,
53 __in THEME_LOADINGCONTROL_RESULTS* pResults
54 );
55static BOOL OnThemeControlWmNotify(
56 __in const THEME_CONTROLWMNOTIFY_ARGS* pArgs,
57 __in THEME_CONTROLWMNOTIFY_RESULTS* pResults
58 );
59static void CALLBACK ThmviewerTraceError(
60 __in_z LPCSTR szFile,
61 __in int iLine,
62 __in REPORT_LEVEL rl,
63 __in UINT source,
64 __in HRESULT hrError,
65 __in_z __format_string LPCSTR szFormat,
66 __in va_list args
67 );
68
69
70int WINAPI wWinMain(
71 __in HINSTANCE hInstance,
72 __in_opt HINSTANCE /* hPrevInstance */,
73 __in_z LPWSTR lpCmdLine,
74 __in int /*nCmdShow*/
75 )
76{
77 ::HeapSetInformation(NULL, HeapEnableTerminationOnCorruption, NULL, 0);
78
79 HRESULT hr = S_OK;
80 BOOL fComInitialized = FALSE;
81 LPWSTR sczThemeFile = NULL;
82 LPWSTR sczWxlFile = NULL;
83 ATOM atom = 0;
84 HWND hWnd = NULL;
85
86 HANDLE hDisplayThread = NULL;
87 HANDLE hLoadThread = NULL;
88
89 BOOL fRet = FALSE;
90 MSG msg = { };
91
92 hr = ::CoInitialize(NULL);
93 ExitOnFailure(hr, "Failed to initialize COM.");
94 fComInitialized = TRUE;
95
96 DutilInitialize(&ThmviewerTraceError);
97
98 hr = ProcessCommandLine(lpCmdLine, &sczThemeFile, &sczWxlFile);
99 ExitOnFailure(hr, "Failed to process command line.");
100
101 hr = CreateTheme(hInstance, &vpTheme);
102 ExitOnFailure(hr, "Failed to create theme.");
103
104 hr = CreateMainWindowClass(hInstance, vpTheme, &atom);
105 ExitOnFailure(hr, "Failed to create main window.");
106
107 hr = ThemeCreateParentWindow(vpTheme, 0, reinterpret_cast<LPCWSTR>(atom), vpTheme->sczCaption, vpTheme->dwStyle, CW_USEDEFAULT, CW_USEDEFAULT, HWND_DESKTOP, hInstance, NULL, THEME_WINDOW_INITIAL_POSITION_DEFAULT, &hWnd);
108 ExitOnFailure(hr, "Failed to create window.");
109
110 if (!sczThemeFile)
111 {
112 // Prompt for a path to the theme file.
113 OPENFILENAMEW ofn = { };
114 WCHAR wzFile[MAX_PATH] = { };
115
116 ofn.lStructSize = sizeof(ofn);
117 ofn.hwndOwner = hWnd;
118 ofn.lpstrFile = wzFile;
119 ofn.nMaxFile = countof(wzFile);
120 ofn.lpstrFilter = L"Theme Files (*.thm)\0*.thm\0XML Files (*.xml)\0*.xml\0All Files (*.*)\0*.*\0";
121 ofn.nFilterIndex = 1;
122 ofn.Flags = OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST;
123 ofn.lpstrTitle = vpTheme->sczCaption;
124
125 if (::GetOpenFileNameW(&ofn))
126 {
127 hr = StrAllocString(&sczThemeFile, wzFile, 0);
128 ExitOnFailure(hr, "Failed to copy opened file to theme file.");
129 }
130 else
131 {
132 ::MessageBoxW(hWnd, L"Must specify a path to theme file.", vpTheme->sczCaption, MB_OK | MB_ICONERROR);
133 ExitFunction1(hr = E_INVALIDARG);
134 }
135 }
136
137 hr = DisplayStart(hInstance, hWnd, &hDisplayThread, &vdwDisplayThreadId);
138 ExitOnFailure(hr, "Failed to start display.");
139
140 hr = LoadStart(sczThemeFile, sczWxlFile, hWnd, &hLoadThread);
141 ExitOnFailure(hr, "Failed to start load.");
142
143 // message pump
144 while (0 != (fRet = ::GetMessageW(&msg, NULL, 0, 0)))
145 {
146 if (-1 == fRet)
147 {
148 hr = E_UNEXPECTED;
149 ExitOnFailure(hr, "Unexpected return value from message pump.");
150 }
151 else if (!ThemeHandleKeyboardMessage(vpTheme, msg.hwnd, &msg))
152 {
153 ::TranslateMessage(&msg);
154 ::DispatchMessageW(&msg);
155 }
156 }
157
158LExit:
159 if (::IsWindow(hWnd))
160 {
161 ::DestroyWindow(hWnd);
162 }
163
164 if (hDisplayThread)
165 {
166 ::PostThreadMessageW(vdwDisplayThreadId, WM_QUIT, 0, 0);
167 ::WaitForSingleObject(hDisplayThread, 10000);
168 ::CloseHandle(hDisplayThread);
169 }
170
171 // TODO: come up with a good way to kill the load thread, probably need to switch
172 // the ReadDirectoryW() to overlapped mode.
173 ReleaseHandle(hLoadThread);
174
175 if (atom && !::UnregisterClassW(reinterpret_cast<LPCWSTR>(atom), hInstance))
176 {
177 DWORD er = ::GetLastError();
178 er = er;
179 }
180
181 ThemeFree(vpTheme);
182 ThemeUninitialize();
183 DutilUninitialize();
184
185 // uninitialize COM
186 if (fComInitialized)
187 {
188 ::CoUninitialize();
189 }
190
191 ReleaseNullStr(vsczThemeLoadErrors);
192 ReleaseStr(sczThemeFile);
193 ReleaseStr(sczWxlFile);
194 return hr;
195}
196
197static void CALLBACK ThmviewerTraceError(
198 __in_z LPCSTR /*szFile*/,
199 __in int /*iLine*/,
200 __in REPORT_LEVEL /*rl*/,
201 __in UINT source,
202 __in HRESULT hrError,
203 __in_z __format_string LPCSTR szFormat,
204 __in va_list args
205 )
206{
207 HRESULT hr = S_OK;
208 LPSTR sczFormattedAnsi = NULL;
209 LPWSTR sczMessage = NULL;
210
211 if (DUTIL_SOURCE_THMUTIL != source)
212 {
213 ExitFunction();
214 }
215
216 hr = StrAnsiAllocFormattedArgs(&sczFormattedAnsi, szFormat, args);
217 ExitOnFailure(hr, "Failed to format error log string.");
218
219 hr = StrAllocFormatted(&sczMessage, L"Error 0x%08x: %S\r\n", hrError, sczFormattedAnsi);
220 ExitOnFailure(hr, "Failed to prepend error number to error log string.");
221
222 hr = StrAllocConcat(&vsczThemeLoadErrors, sczMessage, 0);
223 ExitOnFailure(hr, "Failed to append theme load error.");
224
225LExit:
226 ReleaseStr(sczFormattedAnsi);
227 ReleaseStr(sczMessage);
228}
229
230
231//
232// ProcessCommandLine - process the provided command line arguments.
233//
234static HRESULT ProcessCommandLine(
235 __in_z_opt LPCWSTR wzCommandLine,
236 __out_z LPWSTR* psczThemeFile,
237 __out_z LPWSTR* psczWxlFile
238 )
239{
240 HRESULT hr = S_OK;
241 int argc = 0;
242 LPWSTR* argv = NULL;
243
244 if (wzCommandLine && *wzCommandLine)
245 {
246 hr = AppParseCommandLine(wzCommandLine, &argc, &argv);
247 ExitOnFailure(hr, "Failed to parse command line.");
248
249 for (int i = 0; i < argc; ++i)
250 {
251 if (argv[i][0] == L'-' || argv[i][0] == L'/')
252 {
253 if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, L"lang", -1))
254 {
255 if (i + 1 >= argc)
256 {
257 ExitOnRootFailure(hr = E_INVALIDARG, "Must specify a language.");
258 }
259
260 ++i;
261 }
262 }
263 else
264 {
265 LPCWSTR wzExtension = PathExtension(argv[i]);
266 if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, wzExtension, -1, L".wxl", -1))
267 {
268 hr = StrAllocString(psczWxlFile, argv[i], 0);
269 }
270 else
271 {
272 hr = StrAllocString(psczThemeFile, argv[i], 0);
273 }
274 ExitOnFailure(hr, "Failed to copy path to file.");
275 }
276 }
277 }
278
279LExit:
280 if (argv)
281 {
282 AppFreeCommandLineArgs(argv);
283 }
284
285 return hr;
286}
287
288static HRESULT CreateTheme(
289 __in HINSTANCE hInstance,
290 __out THEME** ppTheme
291 )
292{
293 HRESULT hr = S_OK;
294
295 hr = ThemeInitialize(hInstance);
296 ExitOnFailure(hr, "Failed to initialize theme manager.");
297
298 hr = ThemeLoadFromResource(hInstance, MAKEINTRESOURCEA(THMVWR_RES_THEME_FILE), ppTheme);
299 ExitOnFailure(hr, "Failed to load theme from thmviewer.thm.");
300
301LExit:
302 return hr;
303}
304
305static HRESULT CreateMainWindowClass(
306 __in HINSTANCE hInstance,
307 __in THEME* pTheme,
308 __out ATOM* pAtom
309 )
310{
311 HRESULT hr = S_OK;
312 ATOM atom = 0;
313 WNDCLASSW wc = { };
314
315 ThemeInitializeWindowClass(pTheme, &wc, MainWndProc, hInstance, THMVWR_WINDOW_CLASS_MAIN);
316
317 atom = ::RegisterClassW(&wc);
318 if (!atom)
319 {
320 ExitWithLastError(hr, "Failed to register main windowclass .");
321 }
322
323 *pAtom = atom;
324
325LExit:
326 return hr;
327}
328
329static LRESULT CALLBACK MainWndProc(
330 __in HWND hWnd,
331 __in UINT uMsg,
332 __in WPARAM wParam,
333 __in LPARAM lParam
334 )
335{
336 HANDLE_THEME* pHandleTheme = reinterpret_cast<HANDLE_THEME*>(::GetWindowLongPtrW(hWnd, GWLP_USERDATA));
337
338 switch (uMsg)
339 {
340 case WM_NCCREATE:
341 {
342 //LPCREATESTRUCT lpcs = reinterpret_cast<LPCREATESTRUCT>(lParam);
343 //pBA = reinterpret_cast<CWixStandardBootstrapperApplication*>(lpcs->lpCreateParams);
344 //::SetWindowLongPtrW(hWnd, GWLP_USERDATA, reinterpret_cast<LONG_PTR>(pBA));
345 }
346 break;
347
348 case WM_NCDESTROY:
349 DecrementHandleTheme(pHandleTheme);
350 ::SetWindowLongPtrW(hWnd, GWLP_USERDATA, 0);
351 ::PostQuitMessage(0);
352 break;
353
354 case WM_THMVWR_THEME_LOAD_BEGIN:
355 OnThemeLoadBegin(vsczThemeLoadErrors);
356 return 0;
357
358 case WM_THMVWR_THEME_LOAD_ERROR:
359 OnThemeLoadError(vpTheme, lParam);
360 return 0;
361
362 case WM_THMVWR_NEW_THEME:
363 OnNewTheme(vpTheme, hWnd, reinterpret_cast<HANDLE_THEME*>(lParam));
364 return 0;
365
366 case WM_THMUTIL_LOADING_CONTROL:
367 return OnThemeLoadingControl(reinterpret_cast<THEME_LOADINGCONTROL_ARGS*>(wParam), reinterpret_cast<THEME_LOADINGCONTROL_RESULTS*>(lParam));
368
369 case WM_THMUTIL_CONTROL_WM_NOTIFY:
370 return OnThemeControlWmNotify(reinterpret_cast<THEME_CONTROLWMNOTIFY_ARGS*>(wParam), reinterpret_cast<THEME_CONTROLWMNOTIFY_RESULTS*>(lParam));
371 }
372
373 return ThemeDefWindowProc(vpTheme, hWnd, uMsg, wParam, lParam);
374}
375
376static void OnThemeLoadBegin(
377 __in_z_opt LPWSTR sczThemeLoadErrors
378 )
379{
380 ReleaseNullStr(sczThemeLoadErrors);
381}
382
383static void OnThemeLoadError(
384 __in THEME* pTheme,
385 __in HRESULT hrFailure
386 )
387{
388 HRESULT hr = S_OK;
389 LPWSTR sczMessage = NULL;
390 LPWSTR* psczErrors = NULL;
391 UINT cErrors = 0;
392 TVINSERTSTRUCTW tvi = { };
393 const THEME_CONTROL* pTreeControl = NULL;
394
395 if (!ThemeControlExistsById(pTheme, THMVWR_CONTROL_TREE, &pTreeControl))
396 {
397 ExitWithRootFailure(hr, E_INVALIDSTATE, "THMVWR_CONTROL_TREE control doesn't exist.");
398 }
399
400 // Add the application node.
401 tvi.hParent = NULL;
402 tvi.hInsertAfter = TVI_ROOT;
403 tvi.item.mask = TVIF_TEXT | TVIF_PARAM;
404 tvi.item.lParam = 0;
405 tvi.item.pszText = L"Failed to load theme.";
406 tvi.hParent = reinterpret_cast<HTREEITEM>(::SendMessage(pTreeControl->hWnd, TVM_INSERTITEMW, 0, reinterpret_cast<LPARAM>(&tvi)));
407
408 if (!vsczThemeLoadErrors)
409 {
410 hr = StrAllocFormatted(&sczMessage, L"Error 0x%08x.", hrFailure);
411 ExitOnFailure(hr, "Failed to format error message.");
412
413 tvi.item.pszText = sczMessage;
414 ::SendMessage(pTreeControl->hWnd, TVM_INSERTITEMW, 0, reinterpret_cast<LPARAM>(&tvi));
415
416 hr = StrAllocFromError(&sczMessage, hrFailure, NULL);
417 ExitOnFailure(hr, "Failed to format error message text.");
418
419 tvi.item.pszText = sczMessage;
420 ::SendMessage(pTreeControl->hWnd, TVM_INSERTITEMW, 0, reinterpret_cast<LPARAM>(&tvi));
421 }
422 else
423 {
424 hr = StrSplitAllocArray(&psczErrors, &cErrors, vsczThemeLoadErrors, L"\r\n");
425 ExitOnFailure(hr, "Failed to split theme load errors.");
426
427 for (DWORD i = 0; i < cErrors; ++i)
428 {
429 tvi.item.pszText = psczErrors[i];
430 ::SendMessage(pTreeControl->hWnd, TVM_INSERTITEMW, 0, reinterpret_cast<LPARAM>(&tvi));
431 }
432 }
433
434 ::SendMessage(pTreeControl->hWnd, TVM_EXPAND, TVE_EXPAND, reinterpret_cast<LPARAM>(tvi.hParent));
435
436LExit:
437 ReleaseStr(sczMessage);
438 ReleaseMem(psczErrors);
439}
440
441
442static void OnNewTheme(
443 __in THEME* pTheme,
444 __in HWND hWnd,
445 __in HANDLE_THEME* pHandle
446 )
447{
448 const THEME_CONTROL* pTreeControl = NULL;
449 HANDLE_THEME* pOldHandle = reinterpret_cast<HANDLE_THEME*>(::GetWindowLongPtrW(hWnd, GWLP_USERDATA));
450 THEME* pNewTheme = pHandle->pTheme;
451
452 WCHAR wzSelectedPage[MAX_PATH] = { };
453 HTREEITEM htiSelected = NULL;
454 TVINSERTSTRUCTW tvi = { };
455 TVITEMW item = { };
456
457 if (pOldHandle)
458 {
459 DecrementHandleTheme(pOldHandle);
460 pOldHandle = NULL;
461 }
462
463 // Pass the new theme handle to the display thread so it can get the display window prepared
464 // to show the new theme.
465 IncrementHandleTheme(pHandle);
466 ::PostThreadMessageW(vdwDisplayThreadId, WM_THMVWR_NEW_THEME, 0, reinterpret_cast<LPARAM>(pHandle));
467
468 ::SetWindowLongPtrW(hWnd, GWLP_USERDATA, reinterpret_cast<LONG_PTR>(pHandle));
469
470 if (!ThemeControlExistsById(pTheme, THMVWR_CONTROL_TREE, &pTreeControl))
471 {
472 TraceError(E_INVALIDSTATE, "Tree control doesn't exist.");
473 return;
474 }
475
476 // Remember the currently selected item by name so we can try to automatically select it later.
477 // Otherwise, the user would see their window destroyed after every save of their theme file and
478 // have to click to get the window back.
479 item.mask = TVIF_TEXT;
480 item.pszText = wzSelectedPage;
481 item.cchTextMax = countof(wzSelectedPage);
482 item.hItem = reinterpret_cast<HTREEITEM>(::SendMessage(pTreeControl->hWnd, TVM_GETNEXTITEM, TVGN_CARET, NULL));
483 ::SendMessage(pTreeControl->hWnd, TVM_GETITEM, 0, reinterpret_cast<LPARAM>(&item));
484
485 // Remove the previous items in the tree.
486 ::SendMessage(pTreeControl->hWnd, TVM_DELETEITEM, 0, reinterpret_cast<LPARAM>(TVI_ROOT));
487
488 // Add the application node.
489 tvi.hParent = NULL;
490 tvi.hInsertAfter = TVI_ROOT;
491 tvi.item.mask = TVIF_TEXT | TVIF_PARAM;
492 tvi.item.lParam = 0;
493 tvi.item.pszText = pHandle && pHandle->pTheme && pHandle->pTheme->sczCaption ? pHandle->pTheme->sczCaption : L"Window";
494
495 // Add the pages.
496 tvi.hParent = reinterpret_cast<HTREEITEM>(::SendMessage(pTreeControl->hWnd, TVM_INSERTITEMW, 0, reinterpret_cast<LPARAM>(&tvi)));
497 tvi.hInsertAfter = TVI_SORT;
498 for (DWORD i = 0; i < pNewTheme->cPages; ++i)
499 {
500 THEME_PAGE* pPage = pNewTheme->rgPages + i;
501 if (pPage->sczName && *pPage->sczName)
502 {
503 tvi.item.pszText = pPage->sczName;
504 tvi.item.lParam = i + 1; //prgdwPageIds[i]; - TODO: do the right thing here by calling ThemeGetPageIds(), should not assume we know how the page ids will be calculated.
505
506 HTREEITEM hti = reinterpret_cast<HTREEITEM>(::SendMessage(pTreeControl->hWnd, TVM_INSERTITEMW, 0, reinterpret_cast<LPARAM>(&tvi)));
507 if (*wzSelectedPage && CSTR_EQUAL == ::CompareStringW(LOCALE_NEUTRAL, 0, pPage->sczName, -1, wzSelectedPage, -1))
508 {
509 htiSelected = hti;
510 }
511 }
512 }
513
514 if (*wzSelectedPage && CSTR_EQUAL == ::CompareStringW(LOCALE_NEUTRAL, 0, L"Application", -1, wzSelectedPage, -1))
515 {
516 htiSelected = tvi.hParent;
517 }
518
519 ::SendMessage(pTreeControl->hWnd, TVM_EXPAND, TVE_EXPAND, reinterpret_cast<LPARAM>(tvi.hParent));
520 if (htiSelected)
521 {
522 ::SendMessage(pTreeControl->hWnd, TVM_SELECTITEM, TVGN_CARET, reinterpret_cast<LPARAM>(htiSelected));
523 }
524}
525
526static BOOL OnThemeLoadingControl(
527 __in const THEME_LOADINGCONTROL_ARGS* pArgs,
528 __in THEME_LOADINGCONTROL_RESULTS* pResults
529 )
530{
531 if (CSTR_EQUAL == ::CompareStringW(LOCALE_NEUTRAL, 0, pArgs->pThemeControl->sczName, -1, L"Tree", -1))
532 {
533 pResults->wId = THMVWR_CONTROL_TREE;
534 }
535
536 pResults->hr = S_OK;
537 return TRUE;
538}
539
540static BOOL OnThemeControlWmNotify(
541 __in const THEME_CONTROLWMNOTIFY_ARGS* pArgs,
542 __in THEME_CONTROLWMNOTIFY_RESULTS* /*pResults*/
543 )
544{
545 BOOL fProcessed = FALSE;
546
547 switch (pArgs->lParam->code)
548 {
549 case TVN_SELCHANGEDW:
550 switch (pArgs->pThemeControl->wId)
551 {
552 case THMVWR_CONTROL_TREE:
553 NMTREEVIEWW* ptv = reinterpret_cast<NMTREEVIEWW*>(pArgs->lParam);
554 ::PostThreadMessageW(vdwDisplayThreadId, WM_THMVWR_SHOWPAGE, SW_HIDE, ptv->itemOld.lParam);
555 ::PostThreadMessageW(vdwDisplayThreadId, WM_THMVWR_SHOWPAGE, SW_SHOW, ptv->itemNew.lParam);
556
557 fProcessed = TRUE;
558 break;
559 }
560 break;
561 }
562
563 return fProcessed;
564}