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

1053 lines
32 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
#include <windows.h>
#include <windowsx.h>
#include <strsafe.h>
#include "ShellHelpers.h"
#include "DragDropHelpers.h"
#include "RegisterExtension.h"
#include "ApplicationVerb.h"
#include "ResizeableDialog.h"
#include <new> // std::nothrow
#include "resource.h"
WCHAR const c_szApplicationName[] = L"Player Verb Sample";
// request an IShellItem or related interface
HRESULT GetSelectedItemFromSite(IUnknown *punkSite, REFIID riid, void **ppv)
{
*ppv = NULL;
IFolderView2 *pfv;
HRESULT hr = IUnknown_QueryService(punkSite, SID_SFolderView, IID_PPV_ARGS(&pfv));
if (SUCCEEDED(hr))
{
hr = GetItemFromView(pfv, -1, riid, ppv);
pfv->Release();
}
return hr;
}
// Return IShellItemArray object from the selection in the view, accessed via the site pointer
HRESULT GetSelectionFromSite(IUnknown *punkSite, BOOL fNoneImpliesFolder, IShellItemArray **ppsia)
{
*ppsia = NULL;
IFolderView2 *pfv;
HRESULT hr = IUnknown_QueryService(punkSite, SID_SFolderView, IID_PPV_ARGS(&pfv));
if (SUCCEEDED(hr))
{
hr = pfv->GetSelection(fNoneImpliesFolder, ppsia);
pfv->Release();
}
return hr;
}
// CFileOpenBasketPicker is a helper class to use the file dialog in a modal way such that
// users can easily pick multiple files. it does this by overriding the normal "Open" button
// with an "Add" button that passes the selection back to this app
//
// one case this class does not support is selecting folders this way. this has
// the issue of the "Open" button being overloaded for "nav into the folder" and "add the folder"
// one way to deal with this is to add a new button, "Add Folder", the
// PickFolderAndItem samples demonstrate this
// to use this provide an application object the implements the method "AddItems"
// as a template parameter passed to the constructor
//
// CFileOpenBasketPicker<CPlayerApplication> fileOpenBasketPicker(this);
// fileOpenBasketPicker.PickItems(_hdlg, L"Player Sample");
DWORD const c_idAdd = 601;
DWORD const c_idDone = 602;
template <class TApplication> class CFileOpenBasketPicker : public IFileDialogEvents, public IFileDialogControlEvents
{
public:
// note FOS_PICKFOLDERS implies picking files and folders
CFileOpenBasketPicker(TApplication *pApp, FILEOPENDIALOGOPTIONS options) : _pApp(pApp), _options(options)
{
}
// psiInitialFolder and pszTitle are optional
HRESULT PickItems(HWND hwnd, IShellItem *psiInitialFolder, PCWSTR pszTitle)
{
IFileOpenDialog *pfd;
HRESULT hr = CoCreateInstance(CLSID_FileOpenDialog, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&pfd));
if (SUCCEEDED(hr))
{
DWORD dwCookie;
hr = pfd->Advise(this, &dwCookie);
if (SUCCEEDED(hr))
{
DWORD dwOptions;
if (SUCCEEDED(pfd->GetOptions(&dwOptions)))
{
// don't put the file dialog in FOS_PICKFOLDERS mode
// as we support this implictly in the basket mode
pfd->SetOptions(dwOptions | (_options & ~FOS_PICKFOLDERS));
}
// this flag lets the client pick folder mode that uses the
// Add/Open/Done button model
if (FOS_PICKFOLDERS & _options)
{
IFileDialogCustomize *pfdc;
if (SUCCEEDED(pfd->QueryInterface(&pfdc)))
{
pfdc->AddPushButton(c_idAdd, L"Add current folder ");
pfdc->Release();
}
}
if (psiInitialFolder)
{
pfd->AddPlace(psiInitialFolder, FDAP_TOP);
pfd->SetDefaultFolder(psiInitialFolder);
}
// Win7 has a way to override the "Cancel" button text, use that
// here when on Win7
IFileDialog2 *pfd2;
if (SUCCEEDED(pfd->QueryInterface(&pfd2)))
{
pfd2->SetCancelButtonLabel(L"Done");
pfd2->Release();
}
else
{
IFileDialogCustomize *pfdc;
if (SUCCEEDED(pfd->QueryInterface(&pfdc)))
{
pfdc->AddPushButton(c_idDone, L"Done");
pfdc->Release();
}
}
if (pszTitle)
{
pfd->SetTitle(pszTitle);
}
// the items selected are passed back via OnFileOk()
// so we don't process the results of the dialog
hr = pfd->Show(hwnd);
pfd->Unadvise(dwCookie);
}
pfd->Release();
}
return hr;
}
private:
// Keep the COM identity of this object private so it does not get in the way
// of the auto-complete of the methods we expect client to use.
// IUnknown
IFACEMETHODIMP QueryInterface(REFIID riid, void **ppv)
{
static const QITAB qit[] = {
QITABENT(CFileOpenBasketPicker, IFileDialogEvents),
QITABENT(CFileOpenBasketPicker, IFileDialogControlEvents),
{ 0 },
};
return QISearch(this, qit, riid, ppv);
}
IFACEMETHODIMP_(ULONG) AddRef() { return 3; }
IFACEMETHODIMP_(ULONG) Release() { return 2; }
// IFileDialogEvents
IFACEMETHODIMP OnFileOk(IFileDialog *pfd)
{
IShellItemArray *psia;
HRESULT hr = GetSelectionFromSite(pfd, FOS_PICKFOLDERS & _options, &psia);
if (SUCCEEDED(hr))
{
_DoAdd(psia);
psia->Release();
}
return S_FALSE; // S_FALSE keeps the dialog up, return S_OK to allows it to dismiss
}
IFACEMETHODIMP OnFolderChanging(IFileDialog * /*pfd*/, IShellItem * /*psi*/)
{
return E_NOTIMPL;
}
IFACEMETHODIMP OnFolderChange(IFileDialog * /*pfd*/)
{
// this event happens after a navigation is complete
// if selecting folders is supported update the
// text of the Open/Add button here based on the selection
return E_NOTIMPL;
}
IFACEMETHODIMP OnSelectionChange(IFileDialog *pfd)
{
if (FOS_PICKFOLDERS & _options)
{
// here is the design for the text of the "Add" button
// Single select item "Add file"
// Single select folder "Add folder"
// Multiselect "Add items"
// Null select "Add current folder"
IFileDialogCustomize *pfdc;
if (SUCCEEDED(pfd->QueryInterface(&pfdc)))
{
// GetSelectionFromSite() fails on no selection, that => the current folder
// so default to that value
PCWSTR pszLabel = L"Add current folder";
IShellItemArray *psia;
if (SUCCEEDED(GetSelectionFromSite(pfd, FALSE, &psia)))
{
DWORD count;
if (SUCCEEDED(psia->GetCount(&count)))
{
if (count == 1)
{
IShellItem *psi;
if (SUCCEEDED(psia->GetItemAt(0, &psi)))
{
SFGAOF attributes;
if (S_OK == psi->GetAttributes(SFGAO_FOLDER, &attributes))
{
pszLabel = L"Add folder";
}
else
{
pszLabel = L"Add file";
}
psi->Release();
}
}
else if (count > 1)
{
pszLabel = L"Add items";
}
}
psia->Release();
}
pfdc->SetControlLabel(c_idAdd, pszLabel);
pfdc->Release();
}
}
else
{
IFileDialogCustomize *pfdc;
HRESULT hr = pfd->QueryInterface(&pfdc);
if (SUCCEEDED(hr))
{
// update the text of the Open/Add button here based on the selection
// we can't use pfd->GetCurrentSelection() as that does not provide
// access to the true selection state of t he items in the view
IShellItem *psi;
hr = GetSelectedItemFromSite(pfd, IID_PPV_ARGS(&psi));
if (SUCCEEDED(hr))
{
SFGAOF attr;
hr = psi->GetAttributes(SFGAO_FOLDER | SFGAO_STREAM, &attr);
if (SUCCEEDED(hr) && (SFGAO_FOLDER == attr))
{
pfd->SetOkButtonLabel(L"Open");
}
else
{
// non folder selected
pfd->SetOkButtonLabel(L"Add");
}
psi->Release();
}
pfdc->Release();
}
}
return S_OK;
}
IFACEMETHODIMP OnShareViolation(IFileDialog * /*pfd*/, IShellItem * /*psi*/, FDE_SHAREVIOLATION_RESPONSE * /*pResponse*/) { return E_NOTIMPL; }
IFACEMETHODIMP OnTypeChange(IFileDialog * /*pfd*/) { return E_NOTIMPL; }
IFACEMETHODIMP OnOverwrite(IFileDialog * /*pfd*/, IShellItem * /*psi*/, FDE_OVERWRITE_RESPONSE * /*pResponse*/) { return E_NOTIMPL;}
// IFileDialogControlEvents
IFACEMETHODIMP OnItemSelected(IFileDialogCustomize * /*pfdc*/, DWORD /*dwIDCtl*/, DWORD /*dwIDItem*/) { return E_NOTIMPL; }
IFACEMETHODIMP OnButtonClicked(IFileDialogCustomize *pfdc, DWORD dwIDCtl)
{
switch (dwIDCtl)
{
case c_idAdd:
{
// instead of using IFileDialog::GetCurrentSelection() we need to get the
// selection from the view as that handles the "none implies folder" case
IShellItemArray *psia;
HRESULT hr = GetSelectionFromSite(pfdc, TRUE, &psia);
if (SUCCEEDED(hr))
{
_DoAdd(psia);
psia->Release();
}
}
break;
case c_idDone:
IFileDialog *pfd;
if (SUCCEEDED(pfdc->QueryInterface(&pfd)))
{
pfd->Close(S_OK);
pfd->Release();
}
break;
default:
break;
}
return S_OK;
}
IFACEMETHODIMP OnCheckButtonToggled(IFileDialogCustomize * /*pfdc*/, DWORD /*dwIDCtl*/, BOOL /*bChecked*/) { return E_NOTIMPL; }
IFACEMETHODIMP OnControlActivating(IFileDialogCustomize * /*pfdc*/, DWORD /*dwIDCtl*/) { return E_NOTIMPL; }
void _DoAdd(IShellItemArray *psia)
{
// Callers taht specify FOS_ALLOWMULTISELECT must provide TApplication::AddItems()
// to take the array of items. apps that do not must provide TApplication::AddItem()
// that takes the single selection.
__if_exists(TApplication::AddItems)
{
_pApp->AddItems(psia); // callback to the app with the items
}
__if_not_exists(TApplication::AddItems)
{
__if_exists(TApplication::AddItem)
{
IShellItem2 *psi;
if (SUCCEEDED(GetItemAt(psia, 0, IID_PPV_ARGS(&psi))))
{
_pApp->AddItem(psi); // callback to the app with only one item
psi->Release();
}
}
}
}
// This method is disabled.
// User-defined assignment operator is necessary for /W4 /WX since a default
// one cannot be created by the compiler because this class contains const members.
CFileOpenBasketPicker & operator=(const CFileOpenBasketPicker &);
FILEOPENDIALOGOPTIONS const _options;
TApplication *_pApp;
};
// class to wrap CLSID_ExplorerBrowser to make it easier to program
class CExplorerBrowser
{
public:
CExplorerBrowser() : _hwndHost(NULL), _peb(NULL)
{
}
~CExplorerBrowser()
{
SafeRelease(&_peb);
}
// hwndHost is used to position the explorer browser control. resize it and
// call OnSize() to resize the browser window. the parent of this window
// is used to init the explorer browser
HRESULT Initialize(HWND hwndHost, IUnknown *punkSite)
{
_hwndHost = hwndHost;
HRESULT hr = CoCreateInstance(CLSID_ExplorerBrowser, NULL, CLSCTX_INPROC, IID_PPV_ARGS(&_peb));
if (SUCCEEDED(hr))
{
IUnknown_SetSite(_peb, punkSite);
FOLDERSETTINGS fs = {};
fs.ViewMode = static_cast<UINT>(FVM_AUTO);
fs.fFlags = FWF_SHOWSELALWAYS;
RECT rc;
GetWindowRectInClient(_hwndHost, &rc);
hr = _peb->Initialize(GetParent(_hwndHost), &rc, &fs);
if (SUCCEEDED(hr))
{
// init the exporer browser so that we can use the results folder
// as the data source. that enables us to program the contents of
// the view via IResultsFolder
hr = _peb->FillFromObject(NULL, EBF_NODROPTARGET);
}
}
return hr;
}
void Destroy()
{
if (_peb)
{
IUnknown_SetSite(_peb, NULL);
_peb->Destroy();
_peb->Release();
_peb = NULL;
}
}
void OnSize() const
{
if (_peb)
{
RECT rc;
GetWindowRectInClient(_hwndHost, &rc);
_peb->SetRect(NULL, rc);
}
}
HRESULT GetView(REFIID riid, void **ppv) const
{
*ppv = NULL;
return _peb ? _peb->GetCurrentView(riid, ppv) : E_FAIL;
}
HRESULT GetFolder(REFIID riid, void **ppv) const
{
*ppv = NULL;
IFolderView *pfv;
HRESULT hr = GetView(IID_PPV_ARGS(&pfv));
if (SUCCEEDED(hr))
{
hr = pfv->GetFolder(riid, ppv);
pfv->Release();
}
return hr;
}
HRESULT GetItemFromView(int iItem, REFIID riid, void **ppv) const
{
*ppv = NULL;
IFolderView2* pfv;
HRESULT hr = GetView(IID_PPV_ARGS(&pfv));
if (SUCCEEDED(hr))
{
hr = ::GetItemFromView(pfv, iItem, riid, ppv);
pfv->Release();
}
return hr;
}
int GetItemCount() const
{
int cItems = 0;
IFolderView2* pfv;
HRESULT hr = GetView(IID_PPV_ARGS(&pfv));
if (SUCCEEDED(hr))
{
hr = pfv->ItemCount(SVGIO_ALLVIEW, &cItems);
pfv->Release();
}
return cItems;
}
private:
IExplorerBrowser *_peb;
HWND _hwndHost;
};
__inline DWORD GetPerfTime()
{
static __int64 freq;
__int64 curtime;
if (!freq)
QueryPerformanceFrequency((LARGE_INTEGER *)&freq);
QueryPerformanceCounter((LARGE_INTEGER *)&curtime);
return (DWORD)((curtime * 1000) / freq);
}
class CPlayerApplication : public CDragDropHelper, public ICommDlgBrowser2, public IServiceProvider
{
public:
CPlayerApplication() : _cRef(1), _hdlg(NULL), _pViewToTrack(NULL)
{
_verbPlay.SetApplication(this);
_verbAddToQueue.SetApplication(this);
}
HRESULT ClearQueue() const
{
IResultsFolder *prf;
HRESULT hr = _explorerBrowser.GetFolder(IID_PPV_ARGS(&prf));
if (SUCCEEDED(hr))
{
prf->RemoveAll();
prf->Release();
}
return S_OK;
}
HRESULT AddToQueue(IShellItem *psi) const
{
IResultsFolder *prf;
HRESULT hr = _explorerBrowser.GetFolder(IID_PPV_ARGS(&prf));
if (SUCCEEDED(hr))
{
hr = prf->AddItem(psi);
prf->Release();
}
return hr;
}
HRESULT AddItems(IShellItemArray *psia) const
{
IResultsFolder *prf;
HRESULT hr = _explorerBrowser.GetFolder(IID_PPV_ARGS(&prf));
if (SUCCEEDED(hr))
{
DWORD iCount;
hr = psia->GetCount(&iCount);
for (DWORD i = 0; SUCCEEDED(hr) && (i < iCount); i++)
{
IShellItem *psi;
hr = GetItemAt(psia, i, IID_PPV_ARGS(&psi));
if (SUCCEEDED(hr))
{
prf->AddItem(psi);
psi->Release();
}
}
prf->Release();
}
return S_OK;
}
void ActivateWithKeyboardOverride(DWORD grfKeyState) const
{
// key modifiers leave the player in the background
if (!((MK_CONTROL | MK_SHIFT) & grfKeyState))
{
SetForegroundWindow(_hdlg);
}
}
HRESULT DoModal(HWND hwnd)
{
DialogBoxParam(GetModuleHINSTANCE(), MAKEINTRESOURCE(IDD_DIALOG), hwnd, s_DlgProc, (LPARAM)this);
return S_OK;
}
// CApplicationVerb provides us access to the site via this callback
// when the verb is given the site, use that to discover the view
// that invoked teh verb so it can be tracked
void SetSite(IUnknown *punkSite)
{
if (punkSite && !_pViewToTrack)
{
// capture the folder view on first play
IUnknown_QueryService(punkSite, SID_SFolderView, IID_PPV_ARGS(&_pViewToTrack));
}
}
void ReportResults(PCWSTR psz)
{
SetDlgItemText(_hdlg, IDC_STATUS, psz);
}
// IUnknown
IFACEMETHODIMP QueryInterface(REFIID riid, void **ppv)
{
static QITAB const qit[] =
{
QITABENT(CPlayerApplication, IDropTarget),
QITABENT(CPlayerApplication, IServiceProvider),
QITABENT(CPlayerApplication, ICommDlgBrowser),
QITABENT(CPlayerApplication, ICommDlgBrowser2),
{ 0 },
};
return QISearch(this, qit, riid, ppv);
}
IFACEMETHODIMP_(ULONG) AddRef()
{
return InterlockedIncrement(&_cRef);
}
IFACEMETHODIMP_(ULONG) Release()
{
long cRef = InterlockedDecrement(&_cRef);
if (!cRef)
delete this;
return cRef;
}
// IServiceProvider
IFACEMETHODIMP QueryService(REFGUID guidService, REFIID riid, void **ppv)
{
*ppv = NULL;
HRESULT hr = E_NOINTERFACE;
if (guidService == SID_SExplorerBrowserFrame)
{
hr = QueryInterface(riid, ppv);
}
return hr;
}
// ICommDlgBrowser
IFACEMETHODIMP OnDefaultCommand(IShellView * /* psv */)
{
_OpenSelectedItem();
return S_OK;
}
IFACEMETHODIMP OnStateChange(IShellView * /* psv */, ULONG uChange)
{
if (uChange == CDBOSC_SELCHANGE && _pViewToTrack)
{
IShellItem *psi;
HRESULT hr = _explorerBrowser.GetItemFromView(-1, IID_PPV_ARGS(&psi));
if (SUCCEEDED(hr))
{
IParentAndItem *ppai;
hr = psi->QueryInterface(&ppai);
if (SUCCEEDED(hr))
{
PITEMID_CHILD pidlChild;
hr = ppai->GetParentAndItem(NULL, NULL, &pidlChild);
if (SUCCEEDED(hr))
{
hr = _pViewToTrack->SelectItem(pidlChild, SVSI_SELECT | SVSI_ENSUREVISIBLE | SVSI_DESELECTOTHERS);
if (FAILED(hr))
{
SafeRelease(&_pViewToTrack);
}
CoTaskMemFree(pidlChild);
}
ppai->Release();
}
psi->Release();
}
}
return S_OK;
}
IFACEMETHODIMP IncludeObject(IShellView * /*psv*/, PCUITEMID_CHILD /*pidl*/)
{
return S_OK;
}
// ICommDlgBrowser2
IFACEMETHODIMP Notify(IShellView * /*ppshv*/, DWORD /*dwNotifyType*/)
{
return S_OK;
}
IFACEMETHODIMP GetDefaultMenuText(IShellView * /*ppshv*/, PWSTR /*pszText*/, int /*cchMax*/)
{
return E_NOTIMPL;
}
IFACEMETHODIMP GetViewFlags(DWORD *pdwFlags)
{
// setting this flag is needed to avoid the poor perf of having
// ICommDlgBrowser::IncludeObject() for every item when the result
// set is large.
*pdwFlags = CDB2GVF_NOINCLUDEITEM;
return S_OK;
}
private:
~CPlayerApplication()
{
SafeRelease(&_pViewToTrack);
}
// async verb example
class __declspec(uuid("4a4f70f8-0f4d-46dc-a4ee-3611308d885f"))
CPlayVerb : public CApplicationVerb<CPlayerApplication, CPlayVerb>
{
DWORD _startTimeMS;
DWORD _countItems;
public:
CPlayVerb() : CApplicationVerb(AVF_ASYNC | AVF_ONE_IMPLIES_ALL) {}
void StartVerb()
{
_countItems = 0;
_startTimeMS = GetPerfTime();
CPlayerApplication *pApp = GetApp();
if (pApp)
{
pApp->ActivateWithKeyboardOverride(GetKeyState());
pApp->ClearQueue(); // clear the queue first
}
}
void OnItem(IShellItem *psi)
{
_countItems++;
CPlayerApplication *pApp = GetApp();
if (pApp)
{
pApp->AddToQueue(psi);
}
}
void EndVerb()
{
CPlayerApplication *pApp = GetApp();
if (pApp)
{
DWORD const totalTimeMS = GetPerfTime() - _startTimeMS;
DWORD const itemPerSecond = _countItems * 1000 / totalTimeMS;
WCHAR sz[128];
StringCchPrintf(sz, ARRAYSIZE(sz), L"%d.%d seconds, %d items/second", totalTimeMS / 1000, totalTimeMS % 1000, itemPerSecond);
pApp->ReportResults(sz);
}
}
};
// async verb example
class __declspec(uuid("2b987da6-2a4b-4ab2-b11a-bd5bff6b3759"))
CAddToQueueVerb : public CApplicationVerb<CPlayerApplication, CAddToQueueVerb>
{
DWORD _startTimeMS;
DWORD _countItems;
public:
CAddToQueueVerb() : CApplicationVerb(AVF_ASYNC) {}
void StartVerb()
{
_countItems = 0;
_startTimeMS = GetPerfTime();
CPlayerApplication *pApp = GetApp();
if (pApp)
{
pApp->ActivateWithKeyboardOverride(GetKeyState());
}
}
void OnItem(IShellItem *psi)
{
_countItems++;
CPlayerApplication *pApp = GetApp();
if (pApp)
{
pApp->AddToQueue(psi);
}
}
void EndVerb()
{
CPlayerApplication *pApp = GetApp();
if (pApp)
{
DWORD const totalTimeMS = GetPerfTime() - _startTimeMS;
DWORD const itemPerSecond = _countItems * 1000 / totalTimeMS;
WCHAR sz[128];
StringCchPrintf(sz, ARRAYSIZE(sz), L"%d.%d seconds, %d items/second", totalTimeMS / 1000, totalTimeMS % 1000, itemPerSecond);
pApp->ReportResults(sz);
}
}
};
// CDragDropHelper override
virtual HRESULT OnDrop(IShellItemArray *psia, DWORD /*grfKeyState*/)
{
// invoke the verb implementation for drag and drop, this gets all of the
// same features including container support
_verbAddToQueue.SetSelection(psia);
_verbAddToQueue.Execute();
return S_OK;
}
void _AddItems()
{
CFileOpenBasketPicker<CPlayerApplication> fileOpenBasketPicker(this, FOS_PICKFOLDERS | FOS_ALLOWMULTISELECT | FOS_ALLNONSTORAGEITEMS);
fileOpenBasketPicker.PickItems(_hdlg, NULL, c_szApplicationName);
}
void _OpenSelectedItem()
{
IShellItem *psi;
HRESULT hr = _explorerBrowser.GetItemFromView(-1, IID_PPV_ARGS(&psi));
if (SUCCEEDED(hr))
{
hr = ShellExecuteItem(_hdlg, NULL, psi);
psi->Release();
}
}
static INT_PTR CALLBACK s_DlgProc(HWND hdlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
CPlayerApplication *pcd = (CPlayerApplication*) (LONG_PTR) GetWindowLongPtr(hdlg, DWLP_USER);
if (uMsg == WM_INITDIALOG)
{
pcd = (CPlayerApplication *) lParam;
pcd->_hdlg = hdlg;
SetWindowLongPtr(hdlg, DWLP_USER, (LONG_PTR)pcd);
}
return pcd ? pcd->_DlgProc(uMsg, wParam, lParam) : FALSE;
}
INT_PTR _DlgProc(UINT uMsg, WPARAM wParam, LPARAM lParam);
void _OnInitDlg();
void _OnDestroyDlg();
long _cRef;
HWND _hdlg;
CExplorerBrowser _explorerBrowser;
IShellView *_pViewToTrack;
CPlayVerb _verbPlay;
CAddToQueueVerb _verbAddToQueue;
static ANCHOR const c_rgAnchors[6];
RECT _rgAnchorOffsets[ARRAYSIZE(c_rgAnchors)];
};
ANCHOR const CPlayerApplication::c_rgAnchors[] =
{
{ IDC_EXPLORER_BROWSER, AF_LEFT | AF_RIGHT | AF_TOP | AF_BOTTOM },
{ IDC_OPEN, AF_LEFT | AF_BOTTOM },
{ IDC_CLEAR, AF_LEFT | AF_BOTTOM },
{ IDC_ADD, AF_LEFT | AF_BOTTOM },
{ IDC_STATUS, AF_LEFT | AF_BOTTOM },
{ IDC_GRIPPER, AF_RIGHT | AF_BOTTOM },
};
void CPlayerApplication::_OnInitDlg()
{
InitResizeData(_hdlg, c_rgAnchors, ARRAYSIZE(c_rgAnchors), _rgAnchorOffsets);
InitializeDragDropHelper(_hdlg, DROPIMAGE_COPY, L"Add %1 to Queue");
_verbPlay.Register();
_verbAddToQueue.Register();
SetDialogIcon(_hdlg, SIID_APPLICATION);
HRESULT hr = _explorerBrowser.Initialize(GetDlgItem(_hdlg, IDC_EXPLORER_BROWSER), static_cast<IDropTarget *>(this));
if (SUCCEEDED(hr))
{
IColumnManager *pcm;
hr = _explorerBrowser.GetView(IID_PPV_ARGS(&pcm));
if (SUCCEEDED(hr))
{
PROPERTYKEY const rgkeys[] = { PKEY_ItemNameDisplay, PKEY_Music_TrackNumber, PKEY_Title, PKEY_Music_AlbumArtist, PKEY_Music_AlbumTitle, PKEY_ItemPathDisplayNarrow };
hr = pcm->SetColumns(rgkeys, ARRAYSIZE(rgkeys));
pcm->Release();
}
}
}
void CPlayerApplication::_OnDestroyDlg()
{
TerminateDragDropHelper();
_verbPlay.UnRegister();
_verbAddToQueue.UnRegister();
_explorerBrowser.Destroy();
}
INT_PTR CPlayerApplication::_DlgProc(UINT uMsg, WPARAM wParam, LPARAM /*lParam*/)
{
switch (uMsg)
{
case WM_INITDIALOG:
_OnInitDlg();
break;
case WM_SIZE:
OnSize(_hdlg, c_rgAnchors, ARRAYSIZE(c_rgAnchors), _rgAnchorOffsets);
_explorerBrowser.OnSize();
break;
case WM_COMMAND:
{
int const idCmd = GET_WM_COMMAND_ID(wParam, lParam);
switch (idCmd)
{
case IDOK:
case IDCANCEL:
return EndDialog(_hdlg, idCmd);
case IDC_OPEN:
_OpenSelectedItem();
break;
case IDC_CLEAR:
ClearQueue();
break;
case IDC_ADD:
_AddItems();
break;
}
}
break;
case WM_DESTROY:
_OnDestroyDlg();
break;
default:
return FALSE;
}
return TRUE;
}
WCHAR const c_szRegisterCmdLineOption[] = L"-register";
WCHAR const c_szUnRegisterCmdLineOption[] = L"-unRegister";
WCHAR const c_szNoRegisterCmdLineOption[] = L"-noRegister";
WCHAR const c_szEmbeddingCmdLineOption[] = L"-Embedding"; // COM passes this when activating local servers
WCHAR const c_szPlayVerb[] = L"PlayerSample.Play";
WCHAR const c_szPlayVerbName[] = L"Play (Sample App)";
WCHAR const c_szAddToQueueVerb[] = L"PlayerSample.AddToQueue";
WCHAR const c_szAddToQUeueVerbName[] = L"Add to Queue (Sample App)";
PCWSTR const rgAssociationElementsMusic[] =
{
L"SystemFileAssociations\\Directory.Audio", // music folders
L"Stack.System.Music", // music stacks anywhere
L"Stack.Audio", // stacks in music library
L"SystemFileAssociations\\Audio", // music items
};
PCWSTR const rgAssociationElementsPhotos[] =
{
L"SystemFileAssociations\\Directory.Image", // picture folders
L"Stack.System.Photo", // photo stacks anywhere
L"Stack.Image", // stacks in photo library
L"SystemFileAssociations\\Image", // photo items
};
HRESULT RegisterApp()
{
CRegisterExtension rePlay(__uuidof(CPlayerApplication::CPlayVerb));
CRegisterExtension reAddToQueue(__uuidof(CPlayerApplication::CAddToQueueVerb));
HRESULT hr = rePlay.RegisterPlayerVerbs(rgAssociationElementsPhotos, ARRAYSIZE(rgAssociationElementsPhotos),
// HRESULT hr = rePlay.RegisterPlayerVerbs(rgAssociationElementsMusic, ARRAYSIZE(rgAssociationElementsMusic),
c_szPlayVerb, c_szPlayVerbName);
if (SUCCEEDED(hr))
{
hr = reAddToQueue.RegisterPlayerVerbs(rgAssociationElementsPhotos, ARRAYSIZE(rgAssociationElementsPhotos),
// hr = reAddToQueue.RegisterPlayerVerbs(rgAssociationElementsMusic, ARRAYSIZE(rgAssociationElementsMusic),
c_szAddToQueueVerb, c_szAddToQUeueVerbName);
}
return hr;
}
HRESULT UnRegisterApp()
{
// note these default to per user registration
CRegisterExtension rePlay(__uuidof(CPlayerApplication::CPlayVerb));
CRegisterExtension reAddToQueue(__uuidof(CPlayerApplication::CAddToQueueVerb));
HRESULT hr; // best effort, don't track failure
hr = rePlay.UnRegisterVerbs(rgAssociationElementsPhotos, ARRAYSIZE(rgAssociationElementsPhotos), c_szPlayVerb);
hr = rePlay.UnRegisterVerbs(rgAssociationElementsMusic, ARRAYSIZE(rgAssociationElementsMusic), c_szPlayVerb);
hr = reAddToQueue.UnRegisterVerbs(rgAssociationElementsPhotos, ARRAYSIZE(rgAssociationElementsPhotos), c_szAddToQueueVerb);
hr = reAddToQueue.UnRegisterVerbs(rgAssociationElementsMusic, ARRAYSIZE(rgAssociationElementsMusic), c_szAddToQueueVerb);
return hr;
}
int WINAPI wWinMain(HINSTANCE, HINSTANCE, PWSTR pszCmdLine, int)
{
HRESULT hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE);
if (SUCCEEDED(hr))
{
DisableComExceptionHandling();
if (StrStrI(pszCmdLine, c_szRegisterCmdLineOption))
{
hr = RegisterApp();
}
else if (StrStrI(pszCmdLine, c_szUnRegisterCmdLineOption))
{
hr = UnRegisterApp();
}
else
{
if (!StrStrI(pszCmdLine, c_szEmbeddingCmdLineOption) &&
!StrStrI(pszCmdLine, c_szNoRegisterCmdLineOption))
{
TASKDIALOGCONFIG taskDialogParams = { sizeof(taskDialogParams) };
taskDialogParams.dwFlags = TDF_USE_COMMAND_LINKS | TDF_ALLOW_DIALOG_CANCELLATION;
TASKDIALOG_BUTTON buttons[] =
{
{ 100, L"Register Verbs" },
{ 101, L"UnRegister Verbs" },
{ 102, L"Run the sample" },
};
taskDialogParams.pButtons = buttons;
taskDialogParams.cButtons = ARRAYSIZE(buttons);
taskDialogParams.pszMainInstruction =
L"Player App SDK Sample. This sample demonstrates how to impelment verbs and drag and drop " \
L"including verbs on shell containers (folders, stacks, librarys) and support for OpenSearch items";
taskDialogParams.pszWindowTitle = c_szApplicationName;
int selectedId;
hr = TaskDialogIndirect(&taskDialogParams, &selectedId, NULL, NULL);
if (SUCCEEDED(hr))
{
if (100 == selectedId)
{
hr = RegisterApp();
}
else if (101 == selectedId)
{
hr = UnRegisterApp();
}
else
{
hr = HRESULT_FROM_WIN32(ERROR_CANCELLED);
}
}
}
CPlayerApplication *pdlg = new (std::nothrow) CPlayerApplication();
if (pdlg)
{
pdlg->DoModal(NULL);
pdlg->Release();
}
}
CoUninitialize();
}
return 0;
}