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