153d6484fb49965dce302f3abde4268c28617728
[reactos.git] / dll / shellext / shellbtrfs / propsheet.cpp
1 /* Copyright (c) Mark Harmstone 2016-17
2 *
3 * This file is part of WinBtrfs.
4 *
5 * WinBtrfs is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU Lesser General Public Licence as published by
7 * the Free Software Foundation, either version 3 of the Licence, or
8 * (at your option) any later version.
9 *
10 * WinBtrfs is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU Lesser General Public Licence for more details.
14 *
15 * You should have received a copy of the GNU Lesser General Public Licence
16 * along with WinBtrfs. If not, see <http://www.gnu.org/licenses/>. */
17
18 #define ISOLATION_AWARE_ENABLED 1
19 #define STRSAFE_NO_DEPRECATE
20
21 #include "shellext.h"
22 #ifndef __REACTOS__
23 #include <windows.h>
24 #include <strsafe.h>
25 #include <winternl.h>
26 #else
27 #define WIN32_NO_STATUS
28 #include <windef.h>
29 #include <winbase.h>
30 #include <strsafe.h>
31 #include <ndk/iofuncs.h>
32 #include <ndk/iotypes.h>
33 #endif
34
35 #define NO_SHLWAPI_STRFCNS
36 #include <shlwapi.h>
37 #include <uxtheme.h>
38
39 #include "propsheet.h"
40 #include "resource.h"
41
42 #define SUBVOL_ROOT_INODE 0x100
43
44 #ifndef __REACTOS__
45 #ifndef __MINGW32__ // in winternl.h in mingw
46
47 typedef struct _FILE_ACCESS_INFORMATION {
48 ACCESS_MASK AccessFlags;
49 } FILE_ACCESS_INFORMATION, *PFILE_ACCESS_INFORMATION;
50
51 #define FileAccessInformation (FILE_INFORMATION_CLASS)8
52
53 typedef struct _FILE_STANDARD_INFORMATION {
54 LARGE_INTEGER AllocationSize;
55 LARGE_INTEGER EndOfFile;
56 ULONG NumberOfLinks;
57 BOOLEAN DeletePending;
58 BOOLEAN Directory;
59 } FILE_STANDARD_INFORMATION, *PFILE_STANDARD_INFORMATION;
60
61 #define FileStandardInformation (FILE_INFORMATION_CLASS)5
62
63 #endif
64 #endif
65
66 HRESULT __stdcall BtrfsPropSheet::QueryInterface(REFIID riid, void **ppObj) {
67 if (riid == IID_IUnknown || riid == IID_IShellPropSheetExt) {
68 *ppObj = static_cast<IShellPropSheetExt*>(this);
69 AddRef();
70 return S_OK;
71 } else if (riid == IID_IShellExtInit) {
72 *ppObj = static_cast<IShellExtInit*>(this);
73 AddRef();
74 return S_OK;
75 }
76
77 *ppObj = nullptr;
78 return E_NOINTERFACE;
79 }
80
81 void BtrfsPropSheet::do_search(const wstring& fn) {
82 wstring ss;
83 WIN32_FIND_DATAW ffd;
84
85 #ifndef __REACTOS__
86 ss = fn + L"\\*"s;
87 #else
88 ss = fn + wstring(L"\\*");
89 #endif
90
91 fff_handle h = FindFirstFileW(ss.c_str(), &ffd);
92 if (h == INVALID_HANDLE_VALUE)
93 return;
94
95 do {
96 if (ffd.cFileName[0] != '.' || ((ffd.cFileName[1] != 0) && (ffd.cFileName[1] != '.' || ffd.cFileName[2] != 0))) {
97 wstring fn2;
98
99 #ifndef __REACTOS__
100 fn2 = fn + L"\\"s + ffd.cFileName;
101 #else
102 fn2 = fn + wstring(L"\\") + ffd.cFileName;
103 #endif
104
105 if (ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
106 search_list.push_back(fn2);
107 else {
108 win_handle fh = CreateFileW(fn2.c_str(), FILE_TRAVERSE | FILE_READ_ATTRIBUTES, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, nullptr,
109 OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT, nullptr);
110
111 if (fh != INVALID_HANDLE_VALUE) {
112 NTSTATUS Status;
113 IO_STATUS_BLOCK iosb;
114 btrfs_inode_info bii2;
115
116 memset(&bii2, 0, sizeof(bii2));
117
118 Status = NtFsControlFile(fh, nullptr, nullptr, nullptr, &iosb, FSCTL_BTRFS_GET_INODE_INFO, nullptr, 0, &bii2, sizeof(btrfs_inode_info));
119
120 if (NT_SUCCESS(Status)) {
121 sizes[0] += bii2.inline_length;
122 sizes[1] += bii2.disk_size_uncompressed;
123 sizes[2] += bii2.disk_size_zlib;
124 sizes[3] += bii2.disk_size_lzo;
125 sizes[4] += bii2.disk_size_zstd;
126 totalsize += bii2.inline_length + bii2.disk_size_uncompressed + bii2.disk_size_zlib + bii2.disk_size_lzo + bii2.disk_size_zstd;
127 sparsesize += bii2.sparse_size;
128 }
129
130 FILE_STANDARD_INFORMATION fsi;
131
132 Status = NtQueryInformationFile(fh, &iosb, &fsi, sizeof(fsi), FileStandardInformation);
133
134 if (NT_SUCCESS(Status)) {
135 if (bii2.inline_length > 0)
136 allocsize += fsi.EndOfFile.QuadPart;
137 else
138 allocsize += fsi.AllocationSize.QuadPart;
139 }
140 }
141 }
142 }
143 } while (FindNextFileW(h, &ffd));
144 }
145
146 DWORD BtrfsPropSheet::search_list_thread() {
147 while (!search_list.empty()) {
148 do_search(search_list.front());
149
150 search_list.pop_front();
151 }
152
153 thread = nullptr;
154
155 return 0;
156 }
157
158 static DWORD WINAPI global_search_list_thread(LPVOID lpParameter) {
159 BtrfsPropSheet* bps = (BtrfsPropSheet*)lpParameter;
160
161 return bps->search_list_thread();
162 }
163
164 HRESULT BtrfsPropSheet::check_file(const wstring& fn, UINT i, UINT num_files, UINT* sv) {
165 win_handle h;
166 IO_STATUS_BLOCK iosb;
167 NTSTATUS Status;
168 FILE_ACCESS_INFORMATION fai;
169 BY_HANDLE_FILE_INFORMATION bhfi;
170 btrfs_inode_info bii2;
171
172 h = CreateFileW(fn.c_str(), MAXIMUM_ALLOWED, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, nullptr,
173 OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT, nullptr);
174
175 if (h == INVALID_HANDLE_VALUE)
176 return E_FAIL;
177
178 Status = NtQueryInformationFile(h, &iosb, &fai, sizeof(FILE_ACCESS_INFORMATION), FileAccessInformation);
179 if (!NT_SUCCESS(Status))
180 return E_FAIL;
181
182 if (fai.AccessFlags & FILE_READ_ATTRIBUTES)
183 can_change_perms = fai.AccessFlags & WRITE_DAC;
184
185 readonly = !(fai.AccessFlags & FILE_WRITE_ATTRIBUTES);
186
187 if (!readonly && num_files == 1 && !can_change_perms)
188 show_admin_button = true;
189
190 if (GetFileInformationByHandle(h, &bhfi) && bhfi.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
191 search_list.push_back(fn);
192
193 memset(&bii2, 0, sizeof(bii2));
194
195 Status = NtFsControlFile(h, nullptr, nullptr, nullptr, &iosb, FSCTL_BTRFS_GET_INODE_INFO, nullptr, 0, &bii2, sizeof(btrfs_inode_info));
196
197 if (NT_SUCCESS(Status) && !bii2.top) {
198 LARGE_INTEGER filesize;
199
200 if (i == 0) {
201 subvol = bii2.subvol;
202 inode = bii2.inode;
203 type = bii2.type;
204 uid = bii2.st_uid;
205 gid = bii2.st_gid;
206 rdev = bii2.st_rdev;
207 } else {
208 if (subvol != bii2.subvol)
209 various_subvols = true;
210
211 if (inode != bii2.inode)
212 various_inodes = true;
213
214 if (type != bii2.type)
215 various_types = true;
216
217 if (uid != bii2.st_uid)
218 various_uids = true;
219
220 if (gid != bii2.st_gid)
221 various_gids = true;
222 }
223
224 if (bii2.inline_length > 0) {
225 totalsize += bii2.inline_length;
226 sizes[0] += bii2.inline_length;
227 }
228
229 if (bii2.disk_size_uncompressed > 0) {
230 totalsize += bii2.disk_size_uncompressed;
231 sizes[1] += bii2.disk_size_uncompressed;
232 }
233
234 if (bii2.disk_size_zlib > 0) {
235 totalsize += bii2.disk_size_zlib;
236 sizes[2] += bii2.disk_size_zlib;
237 }
238
239 if (bii2.disk_size_lzo > 0) {
240 totalsize += bii2.disk_size_lzo;
241 sizes[3] += bii2.disk_size_lzo;
242 }
243
244 if (bii2.disk_size_zstd > 0) {
245 totalsize += bii2.disk_size_zstd;
246 sizes[4] += bii2.disk_size_zstd;
247 }
248
249 sparsesize += bii2.sparse_size;
250
251 FILE_STANDARD_INFORMATION fsi;
252
253 Status = NtQueryInformationFile(h, &iosb, &fsi, sizeof(fsi), FileStandardInformation);
254
255 if (!NT_SUCCESS(Status))
256 throw ntstatus_error(Status);
257
258 if (bii2.inline_length > 0)
259 allocsize += fsi.EndOfFile.QuadPart;
260 else
261 allocsize += fsi.AllocationSize.QuadPart;
262
263 min_mode |= ~bii2.st_mode;
264 max_mode |= bii2.st_mode;
265 min_flags |= ~bii2.flags;
266 max_flags |= bii2.flags;
267 min_compression_type = bii2.compression_type < min_compression_type ? bii2.compression_type : min_compression_type;
268 max_compression_type = bii2.compression_type > max_compression_type ? bii2.compression_type : max_compression_type;
269
270 if (bii2.inode == SUBVOL_ROOT_INODE) {
271 bool ro = bhfi.dwFileAttributes & FILE_ATTRIBUTE_READONLY;
272
273 has_subvols = true;
274
275 if (*sv == 0)
276 ro_subvol = ro;
277 else {
278 if (ro_subvol != ro)
279 various_ro = true;
280 }
281
282 (*sv)++;
283 }
284
285 ignore = false;
286
287 if (bii2.type != BTRFS_TYPE_DIRECTORY && GetFileSizeEx(h, &filesize)) {
288 if (filesize.QuadPart != 0)
289 can_change_nocow = false;
290 }
291 } else
292 return E_FAIL;
293
294 return S_OK;
295 }
296
297 HRESULT BtrfsPropSheet::load_file_list() {
298 UINT num_files, i, sv = 0;
299 WCHAR fn[MAX_PATH];
300
301 num_files = DragQueryFileW((HDROP)stgm.hGlobal, 0xFFFFFFFF, nullptr, 0);
302
303 min_mode = 0;
304 max_mode = 0;
305 min_flags = 0;
306 max_flags = 0;
307 min_compression_type = 0xff;
308 max_compression_type = 0;
309 various_subvols = various_inodes = various_types = various_uids = various_gids = various_ro = false;
310
311 can_change_perms = true;
312 can_change_nocow = true;
313
314 sizes[0] = sizes[1] = sizes[2] = sizes[3] = sizes[4] = 0;
315 totalsize = allocsize = sparsesize = 0;
316
317 for (i = 0; i < num_files; i++) {
318 if (DragQueryFileW((HDROP)stgm.hGlobal, i, fn, sizeof(fn) / sizeof(MAX_PATH))) {
319 HRESULT hr;
320
321 hr = check_file(fn, i, num_files, &sv);
322 if (FAILED(hr))
323 return hr;
324 } else
325 return E_FAIL;
326 }
327
328 min_mode = ~min_mode;
329 min_flags = ~min_flags;
330
331 mode = min_mode;
332 mode_set = ~(min_mode ^ max_mode);
333
334 flags = min_flags;
335 flags_set = ~(min_flags ^ max_flags);
336
337 return S_OK;
338 }
339
340 HRESULT __stdcall BtrfsPropSheet::Initialize(PCIDLIST_ABSOLUTE pidlFolder, IDataObject* pdtobj, HKEY hkeyProgID) {
341 try {
342 FORMATETC format = { CF_HDROP, nullptr, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
343 HDROP hdrop;
344 HRESULT hr;
345
346 if (pidlFolder)
347 return E_FAIL;
348
349 if (!pdtobj)
350 return E_FAIL;
351
352 stgm.tymed = TYMED_HGLOBAL;
353
354 if (FAILED(pdtobj->GetData(&format, &stgm)))
355 return E_INVALIDARG;
356
357 stgm_set = true;
358
359 hdrop = (HDROP)GlobalLock(stgm.hGlobal);
360
361 if (!hdrop) {
362 ReleaseStgMedium(&stgm);
363 stgm_set = false;
364 return E_INVALIDARG;
365 }
366
367 try {
368 hr = load_file_list();
369 if (FAILED(hr))
370 return hr;
371
372 if (search_list.size() > 0) {
373 thread = CreateThread(nullptr, 0, global_search_list_thread, this, 0, nullptr);
374
375 if (!thread)
376 throw last_error(GetLastError());
377 }
378 } catch (...) {
379 GlobalUnlock(hdrop);
380 throw;
381 }
382
383 GlobalUnlock(hdrop);
384 } catch (const exception& e) {
385 error_message(nullptr, e.what());
386
387 return E_FAIL;
388 }
389
390 return S_OK;
391 }
392
393 void BtrfsPropSheet::set_cmdline(const wstring& cmdline) {
394 win_handle h;
395 IO_STATUS_BLOCK iosb;
396 NTSTATUS Status;
397 UINT sv = 0;
398 BY_HANDLE_FILE_INFORMATION bhfi;
399 btrfs_inode_info bii2;
400 FILE_ACCESS_INFORMATION fai;
401
402 min_mode = 0;
403 max_mode = 0;
404 min_flags = 0;
405 max_flags = 0;
406 min_compression_type = 0xff;
407 max_compression_type = 0;
408 various_subvols = various_inodes = various_types = various_uids = various_gids = various_ro = false;
409
410 can_change_perms = true;
411 can_change_nocow = true;
412
413 h = CreateFileW(cmdline.c_str(), MAXIMUM_ALLOWED, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, nullptr,
414 OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT, nullptr);
415
416 if (h == INVALID_HANDLE_VALUE)
417 return;
418
419 Status = NtQueryInformationFile(h, &iosb, &fai, sizeof(FILE_ACCESS_INFORMATION), FileAccessInformation);
420 if (!NT_SUCCESS(Status))
421 return;
422
423 if (fai.AccessFlags & FILE_READ_ATTRIBUTES)
424 can_change_perms = fai.AccessFlags & WRITE_DAC;
425
426 readonly = !(fai.AccessFlags & FILE_WRITE_ATTRIBUTES);
427
428 if (GetFileInformationByHandle(h, &bhfi) && bhfi.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
429 search_list.push_back(cmdline);
430
431 memset(&bii2, 0, sizeof(bii2));
432
433 Status = NtFsControlFile(h, nullptr, nullptr, nullptr, &iosb, FSCTL_BTRFS_GET_INODE_INFO, nullptr, 0, &bii2, sizeof(btrfs_inode_info));
434
435 if (NT_SUCCESS(Status) && !bii2.top) {
436 LARGE_INTEGER filesize;
437
438 subvol = bii2.subvol;
439 inode = bii2.inode;
440 type = bii2.type;
441 uid = bii2.st_uid;
442 gid = bii2.st_gid;
443 rdev = bii2.st_rdev;
444
445 if (bii2.inline_length > 0) {
446 totalsize += bii2.inline_length;
447 sizes[0] += bii2.inline_length;
448 }
449
450 if (bii2.disk_size_uncompressed > 0) {
451 totalsize += bii2.disk_size_uncompressed;
452 sizes[1] += bii2.disk_size_uncompressed;
453 }
454
455 if (bii2.disk_size_zlib > 0) {
456 totalsize += bii2.disk_size_zlib;
457 sizes[2] += bii2.disk_size_zlib;
458 }
459
460 if (bii2.disk_size_lzo > 0) {
461 totalsize += bii2.disk_size_lzo;
462 sizes[3] += bii2.disk_size_lzo;
463 }
464
465 if (bii2.disk_size_zstd > 0) {
466 totalsize += bii2.disk_size_zstd;
467 sizes[4] += bii2.disk_size_zstd;
468 }
469
470 sparsesize += bii2.sparse_size;
471
472 FILE_STANDARD_INFORMATION fsi;
473
474 Status = NtQueryInformationFile(h, &iosb, &fsi, sizeof(fsi), FileStandardInformation);
475
476 if (!NT_SUCCESS(Status))
477 throw ntstatus_error(Status);
478
479 if (bii2.inline_length > 0)
480 allocsize += fsi.EndOfFile.QuadPart;
481 else
482 allocsize += fsi.AllocationSize.QuadPart;
483
484 min_mode |= ~bii2.st_mode;
485 max_mode |= bii2.st_mode;
486 min_flags |= ~bii2.flags;
487 max_flags |= bii2.flags;
488 min_compression_type = bii2.compression_type < min_compression_type ? bii2.compression_type : min_compression_type;
489 max_compression_type = bii2.compression_type > max_compression_type ? bii2.compression_type : max_compression_type;
490
491 if (bii2.inode == SUBVOL_ROOT_INODE) {
492 bool ro = bhfi.dwFileAttributes & FILE_ATTRIBUTE_READONLY;
493
494 has_subvols = true;
495
496 if (sv == 0)
497 ro_subvol = ro;
498 else {
499 if (ro_subvol != ro)
500 various_ro = true;
501 }
502
503 sv++;
504 }
505
506 ignore = false;
507
508 if (bii2.type != BTRFS_TYPE_DIRECTORY && GetFileSizeEx(h, &filesize)) {
509 if (filesize.QuadPart != 0)
510 can_change_nocow = false;
511 }
512 } else
513 return;
514
515 min_mode = ~min_mode;
516 min_flags = ~min_flags;
517
518 mode = min_mode;
519 mode_set = ~(min_mode ^ max_mode);
520
521 flags = min_flags;
522 flags_set = ~(min_flags ^ max_flags);
523
524 if (search_list.size() > 0) {
525 thread = CreateThread(nullptr, 0, global_search_list_thread, this, 0, nullptr);
526
527 if (!thread)
528 throw last_error(GetLastError());
529 }
530
531 this->filename = cmdline;
532 }
533
534 static ULONG inode_type_to_string_ref(uint8_t type) {
535 switch (type) {
536 case BTRFS_TYPE_FILE:
537 return IDS_INODE_FILE;
538
539 case BTRFS_TYPE_DIRECTORY:
540 return IDS_INODE_DIR;
541
542 case BTRFS_TYPE_CHARDEV:
543 return IDS_INODE_CHAR;
544
545 case BTRFS_TYPE_BLOCKDEV:
546 return IDS_INODE_BLOCK;
547
548 case BTRFS_TYPE_FIFO:
549 return IDS_INODE_FIFO;
550
551 case BTRFS_TYPE_SOCKET:
552 return IDS_INODE_SOCKET;
553
554 case BTRFS_TYPE_SYMLINK:
555 return IDS_INODE_SYMLINK;
556
557 default:
558 return IDS_INODE_UNKNOWN;
559 }
560 }
561
562 void BtrfsPropSheet::change_inode_flag(HWND hDlg, uint64_t flag, UINT state) {
563 if (flag & BTRFS_INODE_NODATACOW)
564 flag |= BTRFS_INODE_NODATASUM;
565
566 if (state == BST_CHECKED) {
567 flags |= flag;
568 flags_set |= flag;
569 } else if (state == BST_UNCHECKED) {
570 flags &= ~flag;
571 flags_set |= flag;
572 } else if (state == BST_INDETERMINATE) {
573 flags_set = ~flag;
574 }
575
576 flags_changed = true;
577
578 SendMessageW(GetParent(hDlg), PSM_CHANGED, (WPARAM)hDlg, 0);
579 }
580
581 void BtrfsPropSheet::apply_changes_file(HWND hDlg, const wstring& fn) {
582 win_handle h;
583 IO_STATUS_BLOCK iosb;
584 NTSTATUS Status;
585 btrfs_set_inode_info bsii;
586 btrfs_inode_info bii2;
587 ULONG perms = FILE_TRAVERSE | FILE_READ_ATTRIBUTES;
588
589 if (flags_changed || ro_changed)
590 perms |= FILE_WRITE_ATTRIBUTES;
591
592 if (perms_changed || gid_changed || uid_changed)
593 perms |= WRITE_DAC;
594
595 if (mode_set & S_ISUID && (((min_mode & S_ISUID) != (max_mode & S_ISUID)) || ((min_mode & S_ISUID) != (mode & S_ISUID))))
596 perms |= WRITE_OWNER;
597
598 h = CreateFileW(fn.c_str(), perms, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, nullptr,
599 OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT, nullptr);
600
601 if (h == INVALID_HANDLE_VALUE)
602 throw last_error(GetLastError());
603
604 ZeroMemory(&bsii, sizeof(btrfs_set_inode_info));
605
606 Status = NtFsControlFile(h, nullptr, nullptr, nullptr, &iosb, FSCTL_BTRFS_GET_INODE_INFO, nullptr, 0, &bii2, sizeof(btrfs_inode_info));
607
608 if (!NT_SUCCESS(Status))
609 throw ntstatus_error(Status);
610
611 if (bii2.inode == SUBVOL_ROOT_INODE && ro_changed) {
612 BY_HANDLE_FILE_INFORMATION bhfi;
613 FILE_BASIC_INFO fbi;
614
615 if (!GetFileInformationByHandle(h, &bhfi))
616 throw last_error(GetLastError());
617
618 memset(&fbi, 0, sizeof(fbi));
619 fbi.FileAttributes = bhfi.dwFileAttributes;
620
621 if (ro_subvol)
622 fbi.FileAttributes |= FILE_ATTRIBUTE_READONLY;
623 else
624 fbi.FileAttributes &= ~FILE_ATTRIBUTE_READONLY;
625
626 if (!SetFileInformationByHandle(h, FileBasicInfo, &fbi, sizeof(fbi)))
627 throw last_error(GetLastError());
628 }
629
630 if (flags_changed || perms_changed || uid_changed || gid_changed || compress_type_changed) {
631 if (flags_changed) {
632 bsii.flags_changed = true;
633 bsii.flags = (bii2.flags & ~flags_set) | (flags & flags_set);
634 }
635
636 if (perms_changed) {
637 bsii.mode_changed = true;
638 bsii.st_mode = (bii2.st_mode & ~mode_set) | (mode & mode_set);
639 }
640
641 if (uid_changed) {
642 bsii.uid_changed = true;
643 bsii.st_uid = uid;
644 }
645
646 if (gid_changed) {
647 bsii.gid_changed = true;
648 bsii.st_gid = gid;
649 }
650
651 if (compress_type_changed) {
652 bsii.compression_type_changed = true;
653 bsii.compression_type = compress_type;
654 }
655
656 Status = NtFsControlFile(h, nullptr, nullptr, nullptr, &iosb, FSCTL_BTRFS_SET_INODE_INFO, &bsii, sizeof(btrfs_set_inode_info), nullptr, 0);
657
658 if (!NT_SUCCESS(Status))
659 throw ntstatus_error(Status);
660 }
661 }
662
663 void BtrfsPropSheet::apply_changes(HWND hDlg) {
664 UINT num_files, i;
665 WCHAR fn[MAX_PATH]; // FIXME - is this long enough?
666
667 if (various_uids)
668 uid_changed = false;
669
670 if (various_gids)
671 gid_changed = false;
672
673 if (!flags_changed && !perms_changed && !uid_changed && !gid_changed && !compress_type_changed && !ro_changed)
674 return;
675
676 if (filename[0] != 0)
677 apply_changes_file(hDlg, filename);
678 else {
679 num_files = DragQueryFileW((HDROP)stgm.hGlobal, 0xFFFFFFFF, nullptr, 0);
680
681 for (i = 0; i < num_files; i++) {
682 if (DragQueryFileW((HDROP)stgm.hGlobal, i, fn, sizeof(fn) / sizeof(MAX_PATH))) {
683 apply_changes_file(hDlg, fn);
684 }
685 }
686 }
687
688 flags_changed = false;
689 perms_changed = false;
690 uid_changed = false;
691 gid_changed = false;
692 ro_changed = false;
693 }
694
695 void BtrfsPropSheet::set_size_on_disk(HWND hwndDlg) {
696 wstring s, size_on_disk, cr;
697 WCHAR old_text[1024];
698 float ratio;
699
700 format_size(totalsize, size_on_disk, true);
701
702 wstring_sprintf(s, size_format, size_on_disk.c_str());
703
704 if (allocsize == sparsesize || totalsize == 0)
705 ratio = 0.0f;
706 else
707 ratio = 100.0f * (1.0f - ((float)totalsize / (float)(allocsize - sparsesize)));
708
709 wstring_sprintf(cr, cr_format, ratio);
710
711 GetDlgItemTextW(hwndDlg, IDC_SIZE_ON_DISK, old_text, sizeof(old_text) / sizeof(WCHAR));
712
713 if (s != old_text)
714 SetDlgItemTextW(hwndDlg, IDC_SIZE_ON_DISK, s.c_str());
715
716 GetDlgItemTextW(hwndDlg, IDC_COMPRESSION_RATIO, old_text, sizeof(old_text) / sizeof(WCHAR));
717
718 if (cr != old_text)
719 SetDlgItemTextW(hwndDlg, IDC_COMPRESSION_RATIO, cr.c_str());
720 }
721
722 void BtrfsPropSheet::change_perm_flag(HWND hDlg, ULONG flag, UINT state) {
723 if (state == BST_CHECKED) {
724 mode |= flag;
725 mode_set |= flag;
726 } else if (state == BST_UNCHECKED) {
727 mode &= ~flag;
728 mode_set |= flag;
729 } else if (state == BST_INDETERMINATE) {
730 mode_set = ~flag;
731 }
732
733 perms_changed = true;
734
735 SendMessageW(GetParent(hDlg), PSM_CHANGED, (WPARAM)hDlg, 0);
736 }
737
738 void BtrfsPropSheet::change_uid(HWND hDlg, uint32_t uid) {
739 if (this->uid != uid) {
740 this->uid = uid;
741 uid_changed = true;
742
743 SendMessageW(GetParent(hDlg), PSM_CHANGED, (WPARAM)hDlg, 0);
744 }
745 }
746
747 void BtrfsPropSheet::change_gid(HWND hDlg, uint32_t gid) {
748 if (this->gid != gid) {
749 this->gid = gid;
750 gid_changed = true;
751
752 SendMessageW(GetParent(hDlg), PSM_CHANGED, (WPARAM)hDlg, 0);
753 }
754 }
755
756 void BtrfsPropSheet::update_size_details_dialog(HWND hDlg) {
757 wstring size;
758 WCHAR old_text[1024];
759 int i;
760 ULONG items[] = { IDC_SIZE_INLINE, IDC_SIZE_UNCOMPRESSED, IDC_SIZE_ZLIB, IDC_SIZE_LZO, IDC_SIZE_ZSTD };
761
762 for (i = 0; i < 5; i++) {
763 format_size(sizes[i], size, true);
764
765 GetDlgItemTextW(hDlg, items[i], old_text, sizeof(old_text) / sizeof(WCHAR));
766
767 if (size != old_text)
768 SetDlgItemTextW(hDlg, items[i], size.c_str());
769 }
770 }
771
772 static INT_PTR CALLBACK SizeDetailsDlgProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) {
773 try {
774 switch (uMsg) {
775 case WM_INITDIALOG:
776 {
777 BtrfsPropSheet* bps = (BtrfsPropSheet*)lParam;
778
779 SetWindowLongPtr(hwndDlg, GWLP_USERDATA, (LONG_PTR)bps);
780
781 bps->update_size_details_dialog(hwndDlg);
782
783 if (bps->thread)
784 SetTimer(hwndDlg, 1, 250, nullptr);
785
786 return true;
787 }
788
789 case WM_COMMAND:
790 if (HIWORD(wParam) == BN_CLICKED && (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL)) {
791 EndDialog(hwndDlg, 0);
792 return true;
793 }
794 break;
795
796 case WM_TIMER:
797 {
798 BtrfsPropSheet* bps = (BtrfsPropSheet*)GetWindowLongPtr(hwndDlg, GWLP_USERDATA);
799
800 if (bps) {
801 bps->update_size_details_dialog(hwndDlg);
802
803 if (!bps->thread)
804 KillTimer(hwndDlg, 1);
805 }
806
807 break;
808 }
809 }
810 } catch (const exception& e) {
811 error_message(hwndDlg, e.what());
812 }
813
814 return false;
815 }
816
817 static void set_check_box(HWND hwndDlg, ULONG id, uint64_t min, uint64_t max) {
818 if (min && max) {
819 SendDlgItemMessage(hwndDlg, id, BM_SETCHECK, BST_CHECKED, 0);
820 } else if (!min && !max) {
821 SendDlgItemMessage(hwndDlg, id, BM_SETCHECK, BST_UNCHECKED, 0);
822 } else {
823 LONG_PTR style;
824
825 style = GetWindowLongPtr(GetDlgItem(hwndDlg, id), GWL_STYLE);
826 style &= ~BS_AUTOCHECKBOX;
827 style |= BS_AUTO3STATE;
828 SetWindowLongPtr(GetDlgItem(hwndDlg, id), GWL_STYLE, style);
829
830 SendDlgItemMessage(hwndDlg, id, BM_SETCHECK, BST_INDETERMINATE, 0);
831 }
832 }
833
834 void BtrfsPropSheet::open_as_admin(HWND hwndDlg) {
835 ULONG num_files, i;
836 WCHAR fn[MAX_PATH], modfn[MAX_PATH];
837
838 num_files = DragQueryFileW((HDROP)stgm.hGlobal, 0xFFFFFFFF, nullptr, 0);
839
840 if (num_files == 0)
841 return;
842
843 GetModuleFileNameW(module, modfn, sizeof(modfn) / sizeof(WCHAR));
844
845 for (i = 0; i < num_files; i++) {
846 if (DragQueryFileW((HDROP)stgm.hGlobal, i, fn, sizeof(fn) / sizeof(MAX_PATH))) {
847 wstring t;
848 SHELLEXECUTEINFOW sei;
849
850 #ifndef __REACTOS__
851 t = L"\""s + modfn + L"\",ShowPropSheet "s + fn;
852 #else
853 t = wstring(L"\"") + modfn + wstring(L"\",ShowPropSheet ") + fn;
854 #endif
855
856 RtlZeroMemory(&sei, sizeof(sei));
857
858 sei.cbSize = sizeof(sei);
859 sei.hwnd = hwndDlg;
860 sei.lpVerb = L"runas";
861 sei.lpFile = L"rundll32.exe";
862 sei.lpParameters = t.c_str();
863 sei.nShow = SW_SHOW;
864 sei.fMask = SEE_MASK_NOCLOSEPROCESS;
865
866 if (!ShellExecuteExW(&sei))
867 throw last_error(GetLastError());
868
869 WaitForSingleObject(sei.hProcess, INFINITE);
870 CloseHandle(sei.hProcess);
871
872 load_file_list();
873 init_propsheet(hwndDlg);
874 }
875 }
876 }
877
878 // based on functions in sys/sysmacros.h
879 #define major(rdev) ((((rdev) >> 8) & 0xFFF) | ((uint32_t)((rdev) >> 32) & ~0xFFF))
880 #define minor(rdev) (((rdev) & 0xFF) | ((uint32_t)((rdev) >> 12) & ~0xFF))
881
882 void BtrfsPropSheet::init_propsheet(HWND hwndDlg) {
883 wstring s;
884 ULONG sr;
885 int i;
886 HWND comptype;
887
888 static ULONG perm_controls[] = { IDC_USERR, IDC_USERW, IDC_USERX, IDC_GROUPR, IDC_GROUPW, IDC_GROUPX, IDC_OTHERR, IDC_OTHERW, IDC_OTHERX,
889 IDC_SETUID, IDC_SETGID, IDC_STICKY, 0 };
890 static ULONG perms[] = { S_IRUSR, S_IWUSR, S_IXUSR, S_IRGRP, S_IWGRP, S_IXGRP, S_IROTH, S_IWOTH, S_IXOTH, S_ISUID, S_ISGID, S_ISVTX, 0 };
891 static ULONG comp_types[] = { IDS_COMPRESS_ANY, IDS_COMPRESS_ZLIB, IDS_COMPRESS_LZO, IDS_COMPRESS_ZSTD, 0 };
892
893 if (various_subvols) {
894 if (!load_string(module, IDS_VARIOUS, s))
895 throw last_error(GetLastError());
896 } else
897 wstring_sprintf(s, L"%llx", subvol);
898
899 SetDlgItemTextW(hwndDlg, IDC_SUBVOL, s.c_str());
900
901 if (various_inodes) {
902 if (!load_string(module, IDS_VARIOUS, s))
903 throw last_error(GetLastError());
904 } else
905 wstring_sprintf(s, L"%llx", inode);
906
907 SetDlgItemTextW(hwndDlg, IDC_INODE, s.c_str());
908
909 if (various_types)
910 sr = IDS_VARIOUS;
911 else
912 sr = inode_type_to_string_ref(type);
913
914 if (various_inodes) {
915 if (sr == IDS_INODE_CHAR)
916 sr = IDS_INODE_CHAR_SIMPLE;
917 else if (sr == IDS_INODE_BLOCK)
918 sr = IDS_INODE_BLOCK_SIMPLE;
919 }
920
921 if (sr == IDS_INODE_UNKNOWN) {
922 wstring t;
923
924 if (!load_string(module, sr, t))
925 throw last_error(GetLastError());
926
927 wstring_sprintf(s, t, type);
928 } else if (sr == IDS_INODE_CHAR || sr == IDS_INODE_BLOCK) {
929 wstring t;
930
931 if (!load_string(module, sr, t))
932 throw last_error(GetLastError());
933
934 wstring_sprintf(s, t, major(rdev), minor(rdev));
935 } else {
936 if (!load_string(module, sr, s))
937 throw last_error(GetLastError());
938 }
939
940 SetDlgItemTextW(hwndDlg, IDC_TYPE, s.c_str());
941
942 if (size_format[0] == 0)
943 GetDlgItemTextW(hwndDlg, IDC_SIZE_ON_DISK, size_format, sizeof(size_format) / sizeof(WCHAR));
944
945 if (cr_format[0] == 0)
946 GetDlgItemTextW(hwndDlg, IDC_COMPRESSION_RATIO, cr_format, sizeof(cr_format) / sizeof(WCHAR));
947
948 set_size_on_disk(hwndDlg);
949
950 if (thread)
951 SetTimer(hwndDlg, 1, 250, nullptr);
952
953 set_check_box(hwndDlg, IDC_NODATACOW, min_flags & BTRFS_INODE_NODATACOW, max_flags & BTRFS_INODE_NODATACOW);
954 set_check_box(hwndDlg, IDC_COMPRESS, min_flags & BTRFS_INODE_COMPRESS, max_flags & BTRFS_INODE_COMPRESS);
955
956 comptype = GetDlgItem(hwndDlg, IDC_COMPRESS_TYPE);
957
958 while (SendMessage(comptype, CB_GETCOUNT, 0, 0) > 0) {
959 SendMessage(comptype, CB_DELETESTRING, 0, 0);
960 }
961
962 if (min_compression_type != max_compression_type) {
963 SendMessage(comptype, CB_ADDSTRING, 0, (LPARAM)L"");
964 SendMessage(comptype, CB_SETCURSEL, 0, 0);
965 }
966
967 i = 0;
968 while (comp_types[i] != 0) {
969 wstring t;
970
971 if (!load_string(module, comp_types[i], t))
972 throw last_error(GetLastError());
973
974 SendMessage(comptype, CB_ADDSTRING, 0, (LPARAM)t.c_str());
975
976 i++;
977 }
978
979 if (min_compression_type == max_compression_type) {
980 SendMessage(comptype, CB_SETCURSEL, min_compression_type, 0);
981 compress_type = min_compression_type;
982 }
983
984 EnableWindow(comptype, max_flags & BTRFS_INODE_COMPRESS);
985
986 i = 0;
987 while (perm_controls[i] != 0) {
988 set_check_box(hwndDlg, perm_controls[i], min_mode & perms[i], max_mode & perms[i]);
989 i++;
990 }
991
992 if (various_uids) {
993 if (!load_string(module, IDS_VARIOUS, s))
994 throw last_error(GetLastError());
995
996 EnableWindow(GetDlgItem(hwndDlg, IDC_UID), 0);
997 } else
998 s = to_wstring(uid);
999
1000 SetDlgItemTextW(hwndDlg, IDC_UID, s.c_str());
1001
1002 if (various_gids) {
1003 if (!load_string(module, IDS_VARIOUS, s))
1004 throw last_error(GetLastError());
1005
1006 EnableWindow(GetDlgItem(hwndDlg, IDC_GID), 0);
1007 } else
1008 s = to_wstring(gid);
1009
1010 SetDlgItemTextW(hwndDlg, IDC_GID, s.c_str());
1011
1012 ShowWindow(GetDlgItem(hwndDlg, IDC_SUBVOL_RO), has_subvols);
1013
1014 if (has_subvols)
1015 set_check_box(hwndDlg, IDC_SUBVOL_RO, ro_subvol, various_ro ? (!ro_subvol) : ro_subvol);
1016
1017 if (!can_change_nocow)
1018 EnableWindow(GetDlgItem(hwndDlg, IDC_NODATACOW), 0);
1019
1020 if (!can_change_perms) {
1021 i = 0;
1022 while (perm_controls[i] != 0) {
1023 EnableWindow(GetDlgItem(hwndDlg, perm_controls[i]), 0);
1024 i++;
1025 }
1026
1027 EnableWindow(GetDlgItem(hwndDlg, IDC_UID), 0);
1028 EnableWindow(GetDlgItem(hwndDlg, IDC_GID), 0);
1029 EnableWindow(GetDlgItem(hwndDlg, IDC_SETUID), 0);
1030 }
1031
1032 if (readonly) {
1033 EnableWindow(GetDlgItem(hwndDlg, IDC_NODATACOW), 0);
1034 EnableWindow(GetDlgItem(hwndDlg, IDC_COMPRESS), 0);
1035 EnableWindow(GetDlgItem(hwndDlg, IDC_COMPRESS_TYPE), 0);
1036 }
1037
1038 if (show_admin_button) {
1039 SendMessageW(GetDlgItem(hwndDlg, IDC_OPEN_ADMIN), BCM_SETSHIELD, 0, true);
1040 ShowWindow(GetDlgItem(hwndDlg, IDC_OPEN_ADMIN), SW_SHOW);
1041 } else
1042 ShowWindow(GetDlgItem(hwndDlg, IDC_OPEN_ADMIN), SW_HIDE);
1043 }
1044
1045 static INT_PTR CALLBACK PropSheetDlgProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) {
1046 try {
1047 switch (uMsg) {
1048 case WM_INITDIALOG:
1049 {
1050 PROPSHEETPAGE* psp = (PROPSHEETPAGE*)lParam;
1051 BtrfsPropSheet* bps = (BtrfsPropSheet*)psp->lParam;
1052
1053 EnableThemeDialogTexture(hwndDlg, ETDT_ENABLETAB);
1054
1055 SetWindowLongPtr(hwndDlg, GWLP_USERDATA, (LONG_PTR)bps);
1056
1057 bps->init_propsheet(hwndDlg);
1058
1059 return false;
1060 }
1061
1062 case WM_COMMAND:
1063 {
1064 BtrfsPropSheet* bps = (BtrfsPropSheet*)GetWindowLongPtr(hwndDlg, GWLP_USERDATA);
1065
1066 if (bps && !bps->readonly) {
1067 switch (HIWORD(wParam)) {
1068 case BN_CLICKED: {
1069 switch (LOWORD(wParam)) {
1070 case IDC_NODATACOW:
1071 bps->change_inode_flag(hwndDlg, BTRFS_INODE_NODATACOW, IsDlgButtonChecked(hwndDlg, LOWORD(wParam)));
1072 break;
1073
1074 case IDC_COMPRESS:
1075 bps->change_inode_flag(hwndDlg, BTRFS_INODE_COMPRESS, IsDlgButtonChecked(hwndDlg, LOWORD(wParam)));
1076
1077 EnableWindow(GetDlgItem(hwndDlg, IDC_COMPRESS_TYPE), IsDlgButtonChecked(hwndDlg, LOWORD(wParam)) != BST_UNCHECKED);
1078 break;
1079
1080 case IDC_USERR:
1081 bps->change_perm_flag(hwndDlg, S_IRUSR, IsDlgButtonChecked(hwndDlg, LOWORD(wParam)));
1082 break;
1083
1084 case IDC_USERW:
1085 bps->change_perm_flag(hwndDlg, S_IWUSR, IsDlgButtonChecked(hwndDlg, LOWORD(wParam)));
1086 break;
1087
1088 case IDC_USERX:
1089 bps->change_perm_flag(hwndDlg, S_IXUSR, IsDlgButtonChecked(hwndDlg, LOWORD(wParam)));
1090 break;
1091
1092 case IDC_GROUPR:
1093 bps->change_perm_flag(hwndDlg, S_IRGRP, IsDlgButtonChecked(hwndDlg, LOWORD(wParam)));
1094 break;
1095
1096 case IDC_GROUPW:
1097 bps->change_perm_flag(hwndDlg, S_IWGRP, IsDlgButtonChecked(hwndDlg, LOWORD(wParam)));
1098 break;
1099
1100 case IDC_GROUPX:
1101 bps->change_perm_flag(hwndDlg, S_IXGRP, IsDlgButtonChecked(hwndDlg, LOWORD(wParam)));
1102 break;
1103
1104 case IDC_OTHERR:
1105 bps->change_perm_flag(hwndDlg, S_IROTH, IsDlgButtonChecked(hwndDlg, LOWORD(wParam)));
1106 break;
1107
1108 case IDC_OTHERW:
1109 bps->change_perm_flag(hwndDlg, S_IWOTH, IsDlgButtonChecked(hwndDlg, LOWORD(wParam)));
1110 break;
1111
1112 case IDC_OTHERX:
1113 bps->change_perm_flag(hwndDlg, S_IXOTH, IsDlgButtonChecked(hwndDlg, LOWORD(wParam)));
1114 break;
1115
1116 case IDC_SETUID:
1117 bps->change_perm_flag(hwndDlg, S_ISUID, IsDlgButtonChecked(hwndDlg, LOWORD(wParam)));
1118 break;
1119
1120 case IDC_SETGID:
1121 bps->change_perm_flag(hwndDlg, S_ISGID, IsDlgButtonChecked(hwndDlg, LOWORD(wParam)));
1122 break;
1123
1124 case IDC_STICKY:
1125 bps->change_perm_flag(hwndDlg, S_ISVTX, IsDlgButtonChecked(hwndDlg, LOWORD(wParam)));
1126 break;
1127
1128 case IDC_SUBVOL_RO:
1129 switch (IsDlgButtonChecked(hwndDlg, LOWORD(wParam))) {
1130 case BST_CHECKED:
1131 bps->ro_subvol = true;
1132 bps->ro_changed = true;
1133 break;
1134
1135 case BST_UNCHECKED:
1136 bps->ro_subvol = false;
1137 bps->ro_changed = true;
1138 break;
1139
1140 case BST_INDETERMINATE:
1141 bps->ro_changed = false;
1142 break;
1143 }
1144
1145 SendMessageW(GetParent(hwndDlg), PSM_CHANGED, (WPARAM)hwndDlg, 0);
1146 break;
1147
1148 case IDC_OPEN_ADMIN:
1149 bps->open_as_admin(hwndDlg);
1150 break;
1151 }
1152
1153 break;
1154 }
1155
1156 case EN_CHANGE: {
1157 switch (LOWORD(wParam)) {
1158 case IDC_UID: {
1159 WCHAR s[255];
1160
1161 GetDlgItemTextW(hwndDlg, LOWORD(wParam), s, sizeof(s) / sizeof(WCHAR));
1162
1163 bps->change_uid(hwndDlg, _wtoi(s));
1164 break;
1165 }
1166
1167 case IDC_GID: {
1168 WCHAR s[255];
1169
1170 GetDlgItemTextW(hwndDlg, LOWORD(wParam), s, sizeof(s) / sizeof(WCHAR));
1171
1172 bps->change_gid(hwndDlg, _wtoi(s));
1173 break;
1174 }
1175 }
1176
1177 break;
1178 }
1179
1180 case CBN_SELCHANGE: {
1181 switch (LOWORD(wParam)) {
1182 case IDC_COMPRESS_TYPE: {
1183 int sel = SendMessageW(GetDlgItem(hwndDlg, LOWORD(wParam)), CB_GETCURSEL, 0, 0);
1184
1185 if (bps->min_compression_type != bps->max_compression_type) {
1186 if (sel == 0)
1187 bps->compress_type_changed = false;
1188 else {
1189 bps->compress_type = (uint8_t)(sel - 1);
1190 bps->compress_type_changed = true;
1191 }
1192 } else {
1193 bps->compress_type = (uint8_t)sel;
1194 bps->compress_type_changed = true;
1195 }
1196
1197 SendMessageW(GetParent(hwndDlg), PSM_CHANGED, (WPARAM)hwndDlg, 0);
1198
1199 break;
1200 }
1201 }
1202
1203 break;
1204 }
1205 }
1206 }
1207
1208 break;
1209 }
1210
1211 case WM_NOTIFY:
1212 {
1213 switch (((LPNMHDR)lParam)->code) {
1214 case PSN_KILLACTIVE:
1215 SetWindowLongPtrW(hwndDlg, DWLP_MSGRESULT, false);
1216 break;
1217
1218 case PSN_APPLY: {
1219 BtrfsPropSheet* bps = (BtrfsPropSheet*)GetWindowLongPtr(hwndDlg, GWLP_USERDATA);
1220
1221 bps->apply_changes(hwndDlg);
1222 SetWindowLongPtrW(hwndDlg, DWLP_MSGRESULT, PSNRET_NOERROR);
1223 break;
1224 }
1225
1226 case NM_CLICK:
1227 case NM_RETURN: {
1228 if (((LPNMHDR)lParam)->hwndFrom == GetDlgItem(hwndDlg, IDC_SIZE_ON_DISK)) {
1229 PNMLINK pNMLink = (PNMLINK)lParam;
1230
1231 if (pNMLink->item.iLink == 0)
1232 DialogBoxParamW(module, MAKEINTRESOURCEW(IDD_SIZE_DETAILS), hwndDlg, SizeDetailsDlgProc, GetWindowLongPtr(hwndDlg, GWLP_USERDATA));
1233 }
1234 break;
1235 }
1236 }
1237 }
1238
1239 case WM_TIMER:
1240 {
1241 BtrfsPropSheet* bps = (BtrfsPropSheet*)GetWindowLongPtr(hwndDlg, GWLP_USERDATA);
1242
1243 if (bps) {
1244 bps->set_size_on_disk(hwndDlg);
1245
1246 if (!bps->thread)
1247 KillTimer(hwndDlg, 1);
1248 }
1249
1250 break;
1251 }
1252 }
1253 } catch (const exception& e) {
1254 error_message(hwndDlg, e.what());
1255 }
1256
1257 return false;
1258 }
1259
1260 HRESULT __stdcall BtrfsPropSheet::AddPages(LPFNADDPROPSHEETPAGE pfnAddPage, LPARAM lParam) {
1261 try {
1262 PROPSHEETPAGE psp;
1263 HPROPSHEETPAGE hPage;
1264 INITCOMMONCONTROLSEX icex;
1265
1266 if (ignore)
1267 return S_OK;
1268
1269 icex.dwSize = sizeof(icex);
1270 icex.dwICC = ICC_LINK_CLASS;
1271
1272 if (!InitCommonControlsEx(&icex))
1273 throw string_error(IDS_INITCOMMONCONTROLSEX_FAILED);
1274
1275 psp.dwSize = sizeof(psp);
1276 psp.dwFlags = PSP_USEREFPARENT | PSP_USETITLE;
1277 psp.hInstance = module;
1278 psp.pszTemplate = MAKEINTRESOURCE(IDD_PROP_SHEET);
1279 psp.hIcon = 0;
1280 psp.pszTitle = MAKEINTRESOURCE(IDS_PROP_SHEET_TITLE);
1281 psp.pfnDlgProc = (DLGPROC)PropSheetDlgProc;
1282 psp.pcRefParent = (UINT*)&objs_loaded;
1283 psp.pfnCallback = nullptr;
1284 psp.lParam = (LPARAM)this;
1285
1286 hPage = CreatePropertySheetPage(&psp);
1287
1288 if (hPage) {
1289 if (pfnAddPage(hPage, lParam)) {
1290 this->AddRef();
1291 return S_OK;
1292 } else
1293 DestroyPropertySheetPage(hPage);
1294 } else
1295 return E_OUTOFMEMORY;
1296 } catch (const exception& e) {
1297 error_message(nullptr, e.what());
1298 }
1299
1300 return E_FAIL;
1301 }
1302
1303 HRESULT __stdcall BtrfsPropSheet::ReplacePage(UINT uPageID, LPFNADDPROPSHEETPAGE pfnReplacePage, LPARAM lParam) {
1304 return S_OK;
1305 }
1306
1307 #ifdef __cplusplus
1308 extern "C" {
1309 #endif
1310
1311 void CALLBACK ShowPropSheetW(HWND hwnd, HINSTANCE hinst, LPWSTR lpszCmdLine, int nCmdShow) {
1312 try {
1313 BtrfsPropSheet bps;
1314 PROPSHEETPAGEW psp;
1315 PROPSHEETHEADERW psh;
1316 wstring title;
1317
1318 set_dpi_aware();
1319
1320 load_string(module, IDS_STANDALONE_PROPSHEET_TITLE, title);
1321
1322 bps.set_cmdline(lpszCmdLine);
1323
1324 psp.dwSize = sizeof(psp);
1325 psp.dwFlags = PSP_USETITLE;
1326 psp.hInstance = module;
1327 psp.pszTemplate = MAKEINTRESOURCEW(IDD_PROP_SHEET);
1328 psp.hIcon = 0;
1329 psp.pszTitle = MAKEINTRESOURCEW(IDS_PROP_SHEET_TITLE);
1330 psp.pfnDlgProc = (DLGPROC)PropSheetDlgProc;
1331 psp.pfnCallback = nullptr;
1332 psp.lParam = (LPARAM)&bps;
1333
1334 memset(&psh, 0, sizeof(PROPSHEETHEADERW));
1335
1336 psh.dwSize = sizeof(PROPSHEETHEADERW);
1337 psh.dwFlags = PSH_PROPSHEETPAGE;
1338 psh.hwndParent = hwnd;
1339 psh.hInstance = psp.hInstance;
1340 psh.pszCaption = title.c_str();
1341 psh.nPages = 1;
1342 psh.ppsp = &psp;
1343
1344 PropertySheetW(&psh);
1345 } catch (const exception& e) {
1346 error_message(hwnd, e.what());
1347 }
1348 }
1349
1350 #ifdef __cplusplus
1351 }
1352 #endif