2025-11-28 00:35:46 +09:00

397 lines
12 KiB
C++

// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
// PARTICULAR PURPOSE.
//
// Copyright (c) Microsoft Corporation. All rights reserved
#define STRICT_TYPED_ITEMIDS // in case you do IDList stuff you want this on for better type saftey
#pragma once
#pragma comment(lib, "comctl32.lib")
#pragma comment(lib, "shlwapi.lib")
#pragma comment(linker,"/manifestdependency:\"type='win32' name='Microsoft.Windows.Common-Controls' "\
"version='6.0.0.0' processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\"")
#define STRICT_TYPED_ITEMIDS
#include <windows.h>
#include <shlwapi.h>
#include <commctrl.h>
#include <shobjidl.h>
#include <strsafe.h>
#include <new>
#include "resource.h"
#define MAX_TREE_DEPTH 255
HINSTANCE g_hinst = NULL;
// Forward Declarations
INT_PTR CALLBACK MainPageDialogProc(HWND hwndDialog, UINT uMsg, WPARAM wParam, LPARAM lParam);
INT_PTR CALLBACK BackupDialogProc(HWND hwndDialog, UINT uMsg, WPARAM wParam, LPARAM lParam);
HRESULT DoBackup(HWND hwnd);
HRESULT ShowBackupFolderPicker(HWND hwnd, IShellItem **ppsi);
HRESULT WalkFolderContents(HWND hwndTreeView, IShellItem *psiItem, HTREEITEM hTreeParent);
HTREEITEM AddItemToTreeView(HWND hwndTreeView, IShellItem *psiItem, HTREEITEM hTreeParent);
// INamespaceWalkCB implementation
class CNameSpaceWalkCB : public INamespaceWalkCB2
{
public:
CNameSpaceWalkCB(HWND hwndTreeView, HTREEITEM hTreeParent): _cRef(1), _iCurTreeDepth(0), _hwndTreeView(hwndTreeView)
{
_rghTreeParentItemArray[_iCurTreeDepth] = hTreeParent;
}
// IUnknown
IFACEMETHODIMP QueryInterface(REFIID riid, void **ppv)
{
static const QITAB qit[] =
{
QITABENT(CNameSpaceWalkCB, INamespaceWalkCB),
QITABENT(CNameSpaceWalkCB, INamespaceWalkCB2),
{0},
};
return QISearch(this, qit, riid, ppv);
}
IFACEMETHODIMP_(DWORD) AddRef()
{
return InterlockedIncrement(&_cRef);
}
IFACEMETHODIMP_(DWORD) Release()
{
ULONG cRef = InterlockedDecrement(&_cRef);
if (0 == cRef)
{
delete this;
}
return cRef;
}
// INamespaceWalkCB
IFACEMETHODIMP FoundItem(IShellFolder *psf, PCUITEMID_CHILD pidl)
{
IShellItem *psi;
HRESULT hr = SHCreateItemWithParent(NULL, psf, pidl, IID_PPV_ARGS(&psi));
if (SUCCEEDED(hr))
{
hr = AddItemToTreeView(_hwndTreeView, psi, _rghTreeParentItemArray[_iCurTreeDepth]) ? S_OK : E_FAIL;
psi->Release();
}
return hr;
}
IFACEMETHODIMP EnterFolder(IShellFolder *psf, PCUITEMID_CHILD pidl)
{
HRESULT hr = (_iCurTreeDepth < ARRAYSIZE(_rghTreeParentItemArray) - 1) ? S_OK : E_FAIL;
if (SUCCEEDED(hr))
{
IShellItem *psi;
hr = SHCreateItemWithParent(NULL, psf, pidl, IID_PPV_ARGS(&psi));
if (SUCCEEDED(hr))
{
_rghTreeParentItemArray[_iCurTreeDepth + 1] = AddItemToTreeView(_hwndTreeView, psi, _rghTreeParentItemArray[_iCurTreeDepth]);
hr = _rghTreeParentItemArray[_iCurTreeDepth + 1] ? S_OK : E_FAIL;
_iCurTreeDepth++;
psi->Release();
}
}
return hr;
}
IFACEMETHODIMP LeaveFolder(IShellFolder * /*psf*/, PCUITEMID_CHILD /*pidl*/)
{
HRESULT hr = _iCurTreeDepth > 0 ? S_OK : E_FAIL;
if (SUCCEEDED(hr))
{
_rghTreeParentItemArray[_iCurTreeDepth--] = NULL;
}
return hr;
}
IFACEMETHODIMP InitializeProgressDialog(PWSTR *ppszTitle, PWSTR *ppszCancel)
{ *ppszTitle = NULL; *ppszCancel = NULL; return E_NOTIMPL; }
// INamespaceWalkCB2
IFACEMETHODIMP WalkComplete(HRESULT /*hr*/)
{ return S_OK; }
private:
~CNameSpaceWalkCB()
{ }
long _cRef;
int _iCurTreeDepth;
HWND _hwndTreeView;
HTREEITEM _rghTreeParentItemArray[MAX_TREE_DEPTH];
};
HRESULT DoBackup(HWND hwnd)
{
IShellItem *psi;
HRESULT hr = ShowBackupFolderPicker(hwnd, &psi);
if (SUCCEEDED(hr))
{
HWND hwndTreeView = GetDlgItem(hwnd, IDC_BACKUPTREE);
hr = hwndTreeView ? S_OK : E_FAIL;
if (SUCCEEDED(hr))
{
// Enumerate the locations if the folder is a library
IShellLibrary *psl;
if (SUCCEEDED(SHLoadLibraryFromItem(psi, STGM_READ, IID_PPV_ARGS(&psl))))
{
// Make sure the library is up-to-date
SHResolveLibrary(psi);
HTREEITEM htiLibrary = AddItemToTreeView(hwndTreeView, psi, TVI_ROOT);
hr = htiLibrary ? S_OK : E_FAIL;
if (htiLibrary)
{
IShellItemArray *psiaFolders;
hr = psl->GetFolders(LFF_FORCEFILESYSTEM, IID_PPV_ARGS(&psiaFolders));
if (SUCCEEDED(hr))
{
DWORD cFolders;
hr = psiaFolders->GetCount(&cFolders);
if (SUCCEEDED(hr))
{
UINT cFoldersAdded = 0;
for (UINT i = 0; SUCCEEDED(hr) && i < cFolders; i++)
{
IShellItem *psiTemp;
hr = psiaFolders->GetItemAt(i, &psiTemp);
if (SUCCEEDED(hr))
{
// Walk the contents of this location
hr = WalkFolderContents(hwndTreeView, psiTemp, htiLibrary);
if (SUCCEEDED(hr))
{
cFoldersAdded++;
}
psiTemp->Release();
}
}
if (cFoldersAdded)
{
TreeView_SortChildren(hwndTreeView, htiLibrary, FALSE);
}
}
psiaFolders->Release();
}
}
psl->Release();
}
else
{
hr = WalkFolderContents(hwndTreeView, psi, TVI_ROOT);
if (SUCCEEDED(hr))
{
TreeView_SortChildren(hwndTreeView, TVI_ROOT, FALSE);
}
}
}
psi->Release();
}
return hr;
}
// Folder picker
HRESULT ShowBackupFolderPicker(HWND hwnd, IShellItem **ppsi)
{
*ppsi = NULL;
IFileOpenDialog *pfod;
HRESULT hr = CoCreateInstance(CLSID_FileOpenDialog, NULL, CLSCTX_INPROC, IID_PPV_ARGS(&pfod));
if (SUCCEEDED(hr))
{
hr = pfod->SetOptions(FOS_PICKFOLDERS);
if (SUCCEEDED(hr))
{
hr = pfod->Show(hwnd);
if (SUCCEEDED(hr))
{
hr = pfod->GetResult(ppsi);
}
}
pfod->Release();
}
return hr;
}
HRESULT WalkFolderContents(HWND hwndTreeView, IShellItem *psiItem, HTREEITEM hTreeParent)
{
INamespaceWalkCB *pnswcb = new (std::nothrow) CNameSpaceWalkCB(hwndTreeView, hTreeParent);
HRESULT hr = pnswcb ? S_OK : E_OUTOFMEMORY;
if (SUCCEEDED(hr))
{
INamespaceWalk *pnsw;
hr = CoCreateInstance(CLSID_NamespaceWalker, NULL, CLSCTX_INPROC, IID_PPV_ARGS(&pnsw));
if (SUCCEEDED(hr))
{
hr = pnsw->Walk(psiItem, NSWF_ASYNC | NSWF_DONT_TRAVERSE_LINKS | NSWF_DONT_ACCUMULATE_RESULT, MAX_TREE_DEPTH, pnswcb);
pnsw->Release();
}
pnswcb->Release();
}
return hr;
}
HTREEITEM AddItemToTreeView(HWND hwndTreeView, IShellItem *psiItem, HTREEITEM hTreeParent)
{
HTREEITEM htiAdded = NULL;
PWSTR pszName;
if (SUCCEEDED(psiItem->GetDisplayName(SIGDN_NORMALDISPLAY, &pszName)))
{
TVINSERTSTRUCT tvins = {};
tvins.item.mask = TVIF_TEXT;
tvins.item.pszText = pszName;
tvins.hInsertAfter = TVI_LAST;
tvins.hParent = hTreeParent;
htiAdded = TreeView_InsertItem(hwndTreeView, &tvins);
CoTaskMemFree(pszName);
}
return htiAdded;
}
int APIENTRY wWinMain(HINSTANCE hInstance, HINSTANCE, PWSTR, int /*nCmdShow*/)
{
g_hinst = hInstance;
HRESULT hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE);
if (SUCCEEDED(hr))
{
UINT cPages = 0;
HPROPSHEETPAGE rhpsp[2] = {};
// Create the main page of the wizard
PROPSHEETPAGE psp = {sizeof(psp)};
psp.dwFlags = PSP_USEHEADERTITLE;
psp.hInstance = g_hinst;
psp.pszTemplate = MAKEINTRESOURCE(IDD_MainPage);
psp.pfnDlgProc = MainPageDialogProc;
psp.pszHeaderTitle = MAKEINTRESOURCE(IDS_MAINTITLE);
rhpsp[cPages] = CreatePropertySheetPage(&psp);
if (rhpsp[cPages])
{
cPages++;
// Create the Backup page of the wizard
psp.dwFlags = PSP_USEHEADERTITLE;
psp.pszTemplate = MAKEINTRESOURCE(IDD_BackupPage);
psp.pfnDlgProc = BackupDialogProc;
psp.pszHeaderTitle = MAKEINTRESOURCE(IDS_BACKUPTITLE);
rhpsp[cPages] = CreatePropertySheetPage(&psp);
if (rhpsp[cPages])
{
cPages++;
}
}
if (cPages == ARRAYSIZE(rhpsp))
{
PROPSHEETHEADER psh = {sizeof(psh)};
psh.dwFlags = PSH_AEROWIZARD;
psh.hInstance = g_hinst;
psh.nPages = ARRAYSIZE(rhpsp);
psh.phpage = rhpsp;
psh.pszCaption = MAKEINTRESOURCE(IDS_APP_TITLE);
PropertySheet(&psh);
}
else
{
for (UINT iPage = 0; iPage < cPages; iPage++)
{
DestroyPropertySheetPage(rhpsp[iPage]);
}
}
CoUninitialize();
}
return 0;
}
INT_PTR CALLBACK MainPageDialogProc(HWND hwndDialog, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg)
{
case WM_NOTIFY:
{
LPNMHDR pnmh = (LPNMHDR)lParam;
switch (pnmh->code)
{
case PSN_SETACTIVE:
PropSheet_ShowWizButtons(hwndDialog,
PSWIZB_SHOW,
PSWIZB_BACK | PSWIZB_CANCEL | PSWIZB_FINISH | PSWIZB_NEXT | PSWIZB_RESTORE);
break;
}
}
break;
case WM_COMMAND:
switch (LOWORD(wParam))
{
case IDC_BACKUP:
PropSheet_ShowWizButtons(hwndDialog, PSWIZB_NEXT, PSWIZB_NEXT);
PropSheet_PressButton(hwndDialog, PSBTN_NEXT);
break;
}
break;
}
return 0;
}
INT_PTR CALLBACK BackupDialogProc(HWND hwndDialog, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg)
{
case WM_NOTIFY:
{
LPNMHDR pnmh = (LPNMHDR)lParam;
switch (pnmh->code)
{
case PSN_SETACTIVE:
PropSheet_ShowWizButtons(hwndDialog,
PSWIZB_BACK | PSWIZB_FINISH,
PSWIZB_BACK | PSWIZB_CANCEL | PSWIZB_FINISH | PSWIZB_NEXT | PSWIZB_RESTORE);
break;
case PSN_WIZBACK:
TreeView_DeleteAllItems(GetDlgItem(hwndDialog, IDC_BACKUPTREE));
break;
}
}
break;
case WM_COMMAND:
switch (LOWORD(wParam))
{
case IDC_BACKUPADDDIR:
DoBackup(hwndDialog);
break;
case IDC_BACKUPREMOVEDIR:
{
HWND hwndTreeView = GetDlgItem(hwndDialog, IDC_BACKUPTREE);
if (hwndTreeView)
{
HTREEITEM hTreeItem = TreeView_GetSelection(hwndTreeView);
if (hTreeItem)
{
TreeView_DeleteItem(hwndTreeView, hTreeItem);
}
}
}
break;
}
break;
}
return 0;
}