[NETAPI32]
[reactos.git] / reactos / dll / shellext / acppage / CLayerUIPropPage.cpp
1 /*
2 * Copyright 2015-2017 Mark Jansen
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
17 */
18
19 #include "precomp.h"
20
21 #include <shlwapi.h>
22 #include <shellapi.h>
23 #include <shellutils.h>
24 #include <strsafe.h>
25 #include <apphelp.h>
26 #include <windowsx.h>
27 #include <sfc.h>
28
29 const GUID CLSID_CLayerUIPropPage = { 0x513D916F, 0x2A8E, 0x4F51, { 0xAE, 0xAB, 0x0C, 0xBC, 0x76, 0xFB, 0x1A, 0xF8 } };
30 #define ACP_WNDPROP L"{513D916F-2A8E-4F51-AEAB-0CBC76FB1AF8}.Prop"
31
32 #define GPLK_USER 1
33 #define GPLK_MACHINE 2
34 #define MAX_LAYER_LENGTH 256
35
36 static struct {
37 const PCWSTR Display;
38 const PCWSTR Name;
39 } g_CompatModes[] = {
40 { L"Windows 95", L"WIN95" },
41 { L"Windows 98/ME", L"WIN98" },
42 { L"Windows NT 4.0 (SP5)", L"NT4SP5" },
43 { L"Windows 2000", L"WIN2000" },
44 { L"Windows XP (SP2)", L"WINXPSP2" },
45 { L"Windows XP (SP3)", L"WINXPSP3" },
46 { L"Windows Server 2003 (SP1)", L"WINSRV03SP1" },
47 #if 0
48 { L"Windows Server 2008 (SP1)", L"WINSRV08SP1" },
49 { L"Windows Vista", L"VISTARTM" },
50 { L"Windows Vista (SP1)", L"VISTASP1" },
51 { L"Windows Vista (SP2)", L"VISTASP2" },
52 { L"Windows 7", L"WIN7RTM" },
53 #endif
54 { NULL, NULL }
55 };
56
57 static struct {
58 const PCWSTR Name;
59 DWORD Id;
60 } g_Layers[] = {
61 { L"256COLOR", IDC_CHKRUNIN256COLORS },
62 { L"640X480", IDC_CHKRUNIN640480RES },
63 { L"DISABLETHEMES", IDC_CHKDISABLEVISUALTHEMES },
64 #if 0
65 { L"DISABLEDWM", IDC_??, TRUE },
66 { L"HIGHDPIAWARE", IDC_??, TRUE },
67 { L"RUNASADMIN", IDC_??, TRUE },
68 #endif
69 { NULL, 0 }
70 };
71
72 static const WCHAR* g_AllowedExtensions[] = {
73 L".exe",
74 L".msi",
75 L".pif",
76 L".bat",
77 L".cmd",
78 0
79 };
80
81
82 void ACDBG_FN(PCSTR FunctionName, PCWSTR Format, ...)
83 {
84 WCHAR Buffer[512];
85 WCHAR* Current = Buffer;
86 size_t Length = _countof(Buffer);
87
88 StringCchPrintfExW(Current, Length, &Current, &Length, STRSAFE_NULL_ON_FAILURE, L"[%-20S] ", FunctionName);
89 va_list ArgList;
90 va_start(ArgList, Format);
91 StringCchVPrintfExW(Current, Length, &Current, &Length, STRSAFE_NULL_ON_FAILURE, Format, ArgList);
92 va_end(ArgList);
93 OutputDebugStringW(Buffer);
94 }
95
96 #define ACDBG(fmt, ...) ACDBG_FN(__FUNCTION__, fmt, ##__VA_ARGS__ )
97
98
99
100 CLayerUIPropPage::CLayerUIPropPage()
101 : m_IsSfcProtected(FALSE)
102 , m_AllowPermLayer(FALSE)
103 , m_LayerQueryFlags(GPLK_USER) /* TODO: When do we read from HKLM? */
104 , m_RegistryOSMode(0)
105 , m_OSMode(0)
106 , m_RegistryEnabledLayers(0)
107 , m_EnabledLayers(0)
108 {
109 }
110
111 CLayerUIPropPage::~CLayerUIPropPage()
112 {
113 }
114
115 HRESULT CLayerUIPropPage::InitFile(PCWSTR Filename)
116 {
117 CString ExpandedFilename;
118 DWORD dwRequired = ExpandEnvironmentStringsW(Filename, NULL, 0);
119 if (dwRequired > 0)
120 {
121 LPWSTR Buffer = ExpandedFilename.GetBuffer(dwRequired);
122 DWORD dwReturned = ExpandEnvironmentStringsW(Filename, Buffer, dwRequired);
123 if (dwRequired == dwReturned)
124 {
125 ExpandedFilename.ReleaseBufferSetLength(dwReturned - 1);
126 ACDBG(L"Expanded '%s' => '%s'\r\n", Filename, (PCWSTR)ExpandedFilename);
127 }
128 else
129 {
130 ExpandedFilename.ReleaseBufferSetLength(0);
131 ExpandedFilename = Filename;
132 ACDBG(L"Failed during expansion '%s'\r\n", Filename);
133 }
134 }
135 else
136 {
137 ACDBG(L"Failed to expand '%s'\r\n", Filename);
138 ExpandedFilename = Filename;
139 }
140 PCWSTR pwszExt = PathFindExtensionW(ExpandedFilename);
141 if (!pwszExt)
142 {
143 ACDBG(L"Failed to find an extension: '%s'\r\n", (PCWSTR)ExpandedFilename);
144 return E_FAIL;
145 }
146 if (!wcsicmp(pwszExt, L".lnk"))
147 {
148 WCHAR Buffer[MAX_PATH];
149 if (!GetExeFromLnk(ExpandedFilename, Buffer, _countof(Buffer)))
150 {
151 ACDBG(L"Failed to read link target from: '%s'\r\n", (PCWSTR)ExpandedFilename);
152 return E_FAIL;
153 }
154 if (!wcsicmp(Buffer, ExpandedFilename))
155 {
156 ACDBG(L"Link redirects to itself: '%s'\r\n", (PCWSTR)ExpandedFilename);
157 return E_FAIL;
158 }
159 return InitFile(Buffer);
160 }
161 for (size_t n = 0; g_AllowedExtensions[n]; ++n)
162 {
163 if (!wcsicmp(g_AllowedExtensions[n], pwszExt))
164 {
165 m_Filename = ExpandedFilename;
166 ACDBG(L"Got: %s\r\n", (PCWSTR)ExpandedFilename);
167 m_IsSfcProtected = SfcIsFileProtected(NULL, m_Filename);
168 m_AllowPermLayer = AllowPermLayer(ExpandedFilename);
169 return S_OK;
170 }
171 }
172 ACDBG(L"Extension not included: '%s'\r\n", pwszExt);
173 return E_FAIL;
174 }
175
176 static BOOL GetLayerInfo(PCWSTR Filename, DWORD QueryFlags, PDWORD OSMode, PDWORD Enabledlayers, CSimpleArray<CString>& customLayers)
177 {
178 WCHAR wszLayers[MAX_LAYER_LENGTH] = { 0 };
179 DWORD dwBytes = sizeof(wszLayers);
180
181 *OSMode = *Enabledlayers = 0;
182 customLayers.RemoveAll();
183 if (!SdbGetPermLayerKeys(Filename, wszLayers, &dwBytes, QueryFlags))
184 return FALSE;
185
186 for (PWCHAR Layer = wcstok(wszLayers, L" "); Layer; Layer = wcstok(NULL, L" "))
187 {
188 size_t n;
189 for (n = 0; g_Layers[n].Name; ++n)
190 {
191 if (!wcsicmp(g_Layers[n].Name, Layer))
192 {
193 *Enabledlayers |= (1<<n);
194 break;
195 }
196 }
197 /* Did we find it? */
198 if (g_Layers[n].Name)
199 continue;
200
201 for (n = 0; g_CompatModes[n].Name; ++n)
202 {
203 if (!wcsicmp(g_CompatModes[n].Name, Layer))
204 {
205 *OSMode = n+1;
206 break;
207 }
208 }
209 /* Did we find it? */
210 if (g_CompatModes[n].Name)
211 continue;
212
213 /* Must be a 'custom' layer */
214 customLayers.Add(Layer);
215 }
216 return TRUE;
217 }
218
219 void CLayerUIPropPage::OnRefresh(HWND hWnd)
220 {
221 if (!GetLayerInfo(m_Filename, m_LayerQueryFlags, &m_RegistryOSMode, &m_RegistryEnabledLayers, m_RegistryCustomLayers))
222 m_RegistryOSMode = m_RegistryEnabledLayers = 0;
223
224 for (size_t n = 0; g_Layers[n].Name; ++n)
225 CheckDlgButton(hWnd, g_Layers[n].Id, (m_RegistryEnabledLayers & (1<<n)) ? BST_CHECKED : BST_UNCHECKED);
226
227 CheckDlgButton(hWnd, IDC_CHKRUNCOMPATIBILITY, m_RegistryOSMode ? BST_CHECKED : BST_UNCHECKED);
228
229 if (m_RegistryOSMode)
230 ComboBox_SetCurSel(GetDlgItem(hWnd, IDC_COMPATIBILITYMODE), m_RegistryOSMode-1);
231
232 m_CustomLayers = m_RegistryCustomLayers;
233
234 /* TODO: visualize 'custom' layers! */
235
236 UpdateControls(hWnd);
237 }
238
239
240 static BOOL ArrayEquals(const CSimpleArray<CString>& lhs, const CSimpleArray<CString>& rhs)
241 {
242 if (lhs.GetSize() != rhs.GetSize())
243 return FALSE;
244
245 for (int n = 0; n < lhs.GetSize(); ++n)
246 {
247 if (lhs[n] != rhs[n])
248 return FALSE;
249 }
250 return TRUE;
251 }
252
253 BOOL CLayerUIPropPage::HasChanges() const
254 {
255 if (m_RegistryEnabledLayers != m_EnabledLayers)
256 return TRUE;
257
258 if (m_RegistryOSMode != m_OSMode)
259 return TRUE;
260
261 if (!ArrayEquals(m_RegistryCustomLayers, m_CustomLayers))
262 return TRUE;
263
264 return FALSE;
265 }
266
267 void CLayerUIPropPage::OnApply(HWND hWnd)
268 {
269 if (HasChanges())
270 {
271 BOOL bMachine = m_LayerQueryFlags == GPLK_MACHINE;
272
273 for (size_t n = 0; g_CompatModes[n].Name; ++n)
274 SetPermLayerState(m_Filename, g_CompatModes[n].Name, 0, bMachine, (n+1) == m_OSMode);
275
276 for (size_t n = 0; g_Layers[n].Name; ++n)
277 {
278 SetPermLayerState(m_Filename, g_Layers[n].Name, 0, bMachine, ((1<<n) & m_EnabledLayers) != 0);
279 }
280
281 /* Disable all old values */
282 for (int j = 0; j < m_RegistryCustomLayers.GetSize(); j++)
283 {
284 SetPermLayerState(m_Filename, m_RegistryCustomLayers[j].GetString(), 0, bMachine, FALSE);
285 }
286
287 /* Enable all new values */
288 for (int j = 0; j < m_CustomLayers.GetSize(); j++)
289 {
290 SetPermLayerState(m_Filename, m_CustomLayers[j].GetString(), 0, bMachine, TRUE);
291 }
292
293 SHChangeNotify(SHCNE_UPDATEITEM, SHCNF_PATHW, (PCWSTR)m_Filename, NULL);
294 }
295 }
296
297 INT_PTR CLayerUIPropPage::InitDialog(HWND hWnd)
298 {
299 HWND cboMode = GetDlgItem(hWnd, IDC_COMPATIBILITYMODE);
300 for (size_t n = 0; g_CompatModes[n].Display; ++n)
301 ComboBox_AddString(cboMode, g_CompatModes[n].Display);
302 ComboBox_SetCurSel(cboMode, 5);
303
304 CComBSTR explanation;
305 if (!m_AllowPermLayer)
306 {
307 explanation.LoadString(g_hModule, IDS_FAILED_NETWORK);
308 DisableControls(hWnd);
309 ACDBG(L"AllowPermLayer returned FALSE\r\n");
310 }
311 else if (m_IsSfcProtected)
312 {
313 explanation.LoadString(g_hModule, IDS_FAILED_PROTECTED);
314 DisableControls(hWnd);
315 ACDBG(L"Protected OS file\r\n");
316 }
317 else
318 {
319 return TRUE;
320 }
321 SetDlgItemTextW(hWnd, IDC_EXPLANATION, explanation);
322 return TRUE;
323 }
324
325 INT_PTR CLayerUIPropPage::DisableControls(HWND hWnd)
326 {
327 EnableWindow(GetDlgItem(hWnd, IDC_COMPATIBILITYMODE), 0);
328 EnableWindow(GetDlgItem(hWnd, IDC_CHKRUNCOMPATIBILITY), 0);
329 for (size_t n = 0; g_Layers[n].Name; ++n)
330 EnableWindow(GetDlgItem(hWnd, g_Layers[n].Id), 0);
331 EnableWindow(GetDlgItem(hWnd, IDC_EDITCOMPATIBILITYMODES), 0);
332 return TRUE;
333 }
334
335 void CLayerUIPropPage::UpdateControls(HWND hWnd)
336 {
337 m_OSMode = 0, m_EnabledLayers = 0;
338 BOOL ModeEnabled = IsDlgButtonChecked(hWnd, IDC_CHKRUNCOMPATIBILITY);
339 if (ModeEnabled)
340 m_OSMode = ComboBox_GetCurSel(GetDlgItem(hWnd, IDC_COMPATIBILITYMODE))+1;
341 EnableWindow(GetDlgItem(hWnd, IDC_COMPATIBILITYMODE), ModeEnabled);
342
343 for (size_t n = 0; g_Layers[n].Name; ++n)
344 {
345 m_EnabledLayers |= IsDlgButtonChecked(hWnd, g_Layers[n].Id) ? (1<<n) : 0;
346 ShowWindow(GetDlgItem(hWnd, g_Layers[n].Id), SW_SHOW);
347 }
348
349 if (HasChanges())
350 {
351 PropSheet_Changed(GetParent(hWnd), hWnd);
352 }
353 else
354 {
355 PropSheet_UnChanged(GetParent(hWnd), hWnd);
356 }
357 }
358
359 INT_PTR CLayerUIPropPage::OnCommand(HWND hWnd, WORD id)
360 {
361 switch (id)
362 {
363 case IDC_CHKRUNCOMPATIBILITY:
364 UpdateControls(hWnd);
365 break;
366 case IDC_COMPATIBILITYMODE:
367 UpdateControls(hWnd);
368 break;
369 case IDC_CHKRUNIN256COLORS:
370 case IDC_CHKRUNIN640480RES:
371 case IDC_CHKDISABLEVISUALTHEMES:
372 UpdateControls(hWnd);
373 break;
374 case IDC_EDITCOMPATIBILITYMODES:
375 if (DialogBoxParam(g_hModule, MAKEINTRESOURCE(IDD_EDITCOMPATIBILITYMODES), hWnd, EditModesProc, (LPARAM)this) == IDOK)
376 {
377 UpdateControls(hWnd);
378 }
379 break;
380 }
381 return FALSE;
382 }
383
384 INT_PTR CALLBACK CLayerUIPropPage::PropDlgProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
385 {
386 CLayerUIPropPage* page = NULL;
387
388 switch (uMsg)
389 {
390 case WM_INITDIALOG:
391 page = (CLayerUIPropPage*)((LPPROPSHEETPAGE)lParam)->lParam;
392 SetProp(hWnd, ACP_WNDPROP, page);
393 return page->InitDialog(hWnd);
394
395 case WM_ENDSESSION:
396 case WM_DESTROY:
397 page = (CLayerUIPropPage*)GetProp(hWnd, ACP_WNDPROP);
398 RemoveProp(hWnd, ACP_WNDPROP);
399 page->Release();
400 break;
401
402 case WM_COMMAND:
403 page = (CLayerUIPropPage*)GetProp(hWnd, ACP_WNDPROP);
404 return page->OnCommand(hWnd, LOWORD(wParam));
405 case WM_NOTIFY:
406 switch (((LPNMHDR)lParam)->code)
407 {
408 case PSN_SETACTIVE:
409 if (((LPNMHDR)lParam)->hwndFrom == GetParent(hWnd))
410 {
411 page = (CLayerUIPropPage*)GetProp(hWnd, ACP_WNDPROP);
412 page->OnRefresh(hWnd);
413 }
414 break;
415 case PSN_APPLY:
416 if (((LPNMHDR)lParam)->hwndFrom == GetParent(hWnd))
417 {
418 page = (CLayerUIPropPage*)GetProp(hWnd, ACP_WNDPROP);
419 page->OnApply(hWnd);
420 }
421 break;
422 case NM_CLICK:
423 case NM_RETURN:
424 if (((LPNMHDR)lParam)->idFrom == IDC_INFOLINK)
425 {
426 ShellExecute(NULL, L"open", L"https://www.reactos.org/forum/viewforum.php?f=4", NULL, NULL, SW_SHOW);
427 }
428 break;
429 default:
430 break;
431 }
432 break;
433 }
434
435 return FALSE;
436 }
437
438 static void ListboxChanged(HWND hWnd)
439 {
440 int Sel = ListBox_GetCurSel(GetDlgItem(hWnd, IDC_COMPATIBILITYMODE));
441 EnableWindow(GetDlgItem(hWnd, IDC_EDIT), Sel >= 0);
442 EnableWindow(GetDlgItem(hWnd, IDC_DELETE), Sel >= 0);
443 }
444
445 INT_PTR CALLBACK CLayerUIPropPage::EditModesProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
446 {
447 CLayerUIPropPage* page = NULL;
448 switch (uMsg)
449 {
450 case WM_INITDIALOG:
451 page = (CLayerUIPropPage*)lParam;
452 page->AddRef();
453 SetProp(hWnd, ACP_WNDPROP, page);
454 {
455 CComPtr<IAutoComplete> autoComplete;
456 HRESULT hr = CoCreateInstance(CLSID_AutoComplete, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARG(IAutoComplete, &autoComplete));
457 if (SUCCEEDED(hr))
458 {
459 CComPtr<IAutoComplete2> autoComplete2;
460 hr = autoComplete->QueryInterface(IID_PPV_ARG(IAutoComplete2, &autoComplete2));
461 if (SUCCEEDED(hr))
462 {
463 autoComplete2->SetOptions(ACO_AUTOSUGGEST | ACO_UPDOWNKEYDROPSLIST);
464 HWND Edit = GetDlgItem(hWnd, IDC_NEWCOMPATIBILITYMODE);
465 CComObject<CLayerStringList>* pList = new CComObject<CLayerStringList>();
466 hr = autoComplete2->Init(Edit, pList, NULL, NULL);
467 }
468 }
469
470 HWND List = GetDlgItem(hWnd, IDC_COMPATIBILITYMODE);
471 for (int n = 0; n < page->m_CustomLayers.GetSize(); ++n)
472 {
473 const WCHAR* Str = page->m_CustomLayers[n].GetString();
474 int Index = ListBox_FindStringExact(List, -1, Str);
475 if (Index == LB_ERR)
476 Index = ListBox_AddString(List, Str);
477 }
478 }
479 break;
480 case WM_ENDSESSION:
481 case WM_DESTROY:
482 page = (CLayerUIPropPage*)GetProp(hWnd, ACP_WNDPROP);
483 RemoveProp(hWnd, ACP_WNDPROP);
484 page->Release();
485 break;
486 case WM_COMMAND:
487 switch(LOWORD(wParam))
488 {
489 case IDC_ADD:
490 {
491 HWND Edit = GetDlgItem(hWnd, IDC_NEWCOMPATIBILITYMODE);
492 int Length = GetWindowTextLengthW(Edit);
493 CComBSTR Str(Length);
494 GetWindowTextW(Edit, Str, Length+1);
495 HWND List = GetDlgItem(hWnd, IDC_COMPATIBILITYMODE);
496 int Index = ListBox_FindStringExact(List, -1, Str);
497 if (Index == LB_ERR)
498 Index = ListBox_AddString(List, Str);
499 ListBox_SetCurSel(List, Index);
500 ListboxChanged(hWnd);
501 Edit_SetText(Edit, TEXT(""));
502 SetFocus(Edit);
503 }
504 break;
505 case IDC_EDIT:
506 {
507 HWND List = GetDlgItem(hWnd, IDC_COMPATIBILITYMODE);
508 int Cur = ListBox_GetCurSel(List);
509 int Length = ListBox_GetTextLen(List, Cur);
510 CComBSTR Str(Length);
511 ListBox_GetText(List, Cur, Str);
512 ListBox_DeleteString(List, Cur);
513 HWND Edit = GetDlgItem(hWnd, IDC_NEWCOMPATIBILITYMODE);
514 Edit_SetText(Edit, Str);
515 ListboxChanged(hWnd);
516 Edit_SetSel(Edit, 30000, 30000);
517 SetFocus(Edit);
518 }
519 break;
520 case IDC_DELETE:
521 {
522 HWND List = GetDlgItem(hWnd, IDC_COMPATIBILITYMODE);
523 ListBox_DeleteString(List, ListBox_GetCurSel(List));
524 ListboxChanged(hWnd);
525 }
526 break;
527 case IDC_COMPATIBILITYMODE:
528 ListboxChanged(hWnd);
529 break;
530 case IDC_NEWCOMPATIBILITYMODE:
531 EnableWindow(GetDlgItem(hWnd, IDC_ADD), Edit_GetTextLength(GetDlgItem(hWnd, IDC_NEWCOMPATIBILITYMODE)) > 0);
532 break;
533 case IDOK:
534 /* Copy from list! */
535 {
536 page = (CLayerUIPropPage*)GetProp(hWnd, ACP_WNDPROP);
537
538 HWND List = GetDlgItem(hWnd, IDC_COMPATIBILITYMODE);
539 int Count = ListBox_GetCount(List);
540 page->m_CustomLayers.RemoveAll();
541 for (int Cur = 0; Cur < Count; ++Cur)
542 {
543 int Length = ListBox_GetTextLen(List, Cur);
544 CString Str;
545 LPWSTR Buffer = Str.GetBuffer(Length + 1);
546 ListBox_GetText(List, Cur, Buffer);
547 Str.ReleaseBuffer(Length);
548 page->m_CustomLayers.Add(Str);
549 }
550 }
551 /* Fall trough */
552 case IDCANCEL:
553 EndDialog(hWnd, LOWORD(wParam));
554 break;
555 }
556 break;
557 case WM_CLOSE:
558 EndDialog(hWnd, IDCANCEL);
559 break;
560 }
561 return FALSE;
562 }
563
564
565 STDMETHODIMP CLayerUIPropPage::Initialize(LPCITEMIDLIST pidlFolder, LPDATAOBJECT pDataObj, HKEY hkeyProgID)
566 {
567 FORMATETC etc = { CF_HDROP, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
568 STGMEDIUM stg;
569 HRESULT hr = pDataObj->GetData(&etc, &stg);
570 if (FAILED(hr))
571 {
572 ACDBG(L"Failed to retrieve Data from pDataObj.\r\n");
573 return E_INVALIDARG;
574 }
575 hr = E_FAIL;
576 HDROP hdrop = (HDROP)GlobalLock(stg.hGlobal);
577 if (hdrop)
578 {
579 UINT uNumFiles = DragQueryFileW(hdrop, 0xFFFFFFFF, NULL, 0);
580 if (uNumFiles == 1)
581 {
582 WCHAR szFile[MAX_PATH * 2];
583 if (DragQueryFileW(hdrop, 0, szFile, _countof(szFile)))
584 {
585 this->AddRef();
586 hr = InitFile(szFile);
587 }
588 else
589 {
590 ACDBG(L"Failed to query the file.\r\n");
591 }
592 }
593 else
594 {
595 ACDBG(L"Invalid number of files: %d\r\n", uNumFiles);
596 }
597 GlobalUnlock(stg.hGlobal);
598 }
599 else
600 {
601 ACDBG(L"Could not lock stg.hGlobal\r\n");
602 }
603 ReleaseStgMedium(&stg);
604 return hr;
605 }
606
607 STDMETHODIMP CLayerUIPropPage::AddPages(LPFNADDPROPSHEETPAGE pfnAddPage, LPARAM lParam)
608 {
609 PROPSHEETPAGEW psp = { 0 };
610 psp.dwSize = sizeof(psp);
611 psp.dwFlags = PSP_USEREFPARENT | PSP_USETITLE;
612 psp.hInstance = g_hModule;
613 psp.pszTemplate = MAKEINTRESOURCE(IDD_ACPPAGESHEET);
614 psp.pszTitle = MAKEINTRESOURCE(IDS_TABTITLE);
615 psp.pfnDlgProc = PropDlgProc;
616 psp.lParam = (LPARAM)this;
617 psp.pcRefParent = (PUINT)&g_ModuleRefCnt;
618 HPROPSHEETPAGE hPage = CreatePropertySheetPageW(&psp);
619 if (hPage && !pfnAddPage(hPage, lParam))
620 DestroyPropertySheetPage(hPage);
621
622 return S_OK;
623 }