windows-nt/Source/XPSP1/NT/shell/ext/brfcase/filesync/syncui/ibrfext.c
2020-09-26 16:20:57 +08:00

1438 lines
37 KiB
C

//---------------------------------------------------------------------------
//
// Copyright (c) Microsoft Corporation 1993-1994
//
// File: ibrfext.c
//
// This files contains the IShellExtInit, IShellPropSheetExt and
// IContextMenu interfaces.
//
// History:
// 02-02-94 ScottH Moved from iface.c; added new shell interface support
//
//---------------------------------------------------------------------------
#include "brfprv.h" // common headers
#include <brfcasep.h>
#include "res.h"
#include "recact.h"
// Briefcase extension structure. This is used for IContextMenu
// and PropertySheet binding.
//
typedef struct _BriefExt
{
// We use the sxi also as our IUnknown interface
IShellExtInit sxi; // 1st base class
IContextMenu ctm; // 2nd base class
IShellPropSheetExt spx; // 3rd base class
UINT cRef; // reference count
LPDATAOBJECT pdtobj; // data object
HKEY hkeyProgID; // reg. database key to ProgID
} BriefExt, * PBRIEFEXT;
//---------------------------------------------------------------------------
// IDataObject extraction functions
//---------------------------------------------------------------------------
/*----------------------------------------------------------
Purpose: Return TRUE if the IDataObject knows the special
briefcase file-system object format
Returns: see above
Cond: --
*/
BOOL PUBLIC DataObj_KnowsBriefObj(
LPDATAOBJECT pdtobj)
{
HRESULT hres;
FORMATETC fmte = {(CLIPFORMAT)g_cfBriefObj, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
// Does this dataobject support briefcase object format?
//
hres = pdtobj->lpVtbl->QueryGetData(pdtobj, &fmte);
return (hres == ResultFromScode(S_OK));
}
/*----------------------------------------------------------
Purpose: Gets the briefcase path from an IDataObject.
Returns: standard
Cond: --
*/
HRESULT PUBLIC DataObj_QueryBriefPath(
LPDATAOBJECT pdtobj,
LPTSTR pszBriefPath) // Must be size MAX_PATH
{
HRESULT hres = ResultFromScode(E_FAIL);
FORMATETC fmte = {(CLIPFORMAT)g_cfBriefObj, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
STGMEDIUM medium;
ASSERT(pdtobj);
ASSERT(pszBriefPath);
// Does this dataobject support briefcase object format?
//
hres = pdtobj->lpVtbl->GetData(pdtobj, &fmte, &medium);
if (SUCCEEDED(hres))
{
PBRIEFOBJ pbo = (PBRIEFOBJ)GlobalLock(medium.hGlobal);
LPTSTR psz = BOBriefcasePath(pbo);
lstrcpy(pszBriefPath, psz);
GlobalUnlock(medium.hGlobal);
MyReleaseStgMedium(&medium);
}
return hres;
}
/*----------------------------------------------------------
Purpose: Gets a single path from an IDataObject.
Returns: standard
S_OK if the object is inside a briefcase
S_FALSE if not
Cond: --
*/
HRESULT PUBLIC DataObj_QueryPath(
LPDATAOBJECT pdtobj,
LPTSTR pszPath) // Must be size MAX_PATH
{
HRESULT hres = E_FAIL;
FORMATETC fmteBrief = {(CLIPFORMAT)g_cfBriefObj, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
FORMATETC fmteHdrop = {(CLIPFORMAT)CF_HDROP, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
STGMEDIUM medium;
ASSERT(pdtobj);
ASSERT(pszPath);
// Does this dataobject support briefcase object format?
//
hres = pdtobj->lpVtbl->GetData(pdtobj, &fmteBrief, &medium);
if (SUCCEEDED(hres))
{
// Yup
PBRIEFOBJ pbo = (PBRIEFOBJ)GlobalLock(medium.hGlobal);
LPTSTR psz = BOFileList(pbo);
// Only get first path in list
lstrcpy(pszPath, psz);
GlobalUnlock(medium.hGlobal);
MyReleaseStgMedium(&medium);
hres = S_OK;
}
else
{
// Or does it support hdrops?
hres = pdtobj->lpVtbl->GetData(pdtobj, &fmteHdrop, &medium);
if (SUCCEEDED(hres))
{
// Yup
HDROP hdrop = medium.hGlobal;
// Only get first path in the file list
DragQueryFile(hdrop, 0, pszPath, MAX_PATH);
MyReleaseStgMedium(&medium);
hres = S_FALSE;
}
}
return hres;
}
/*----------------------------------------------------------
Purpose: Gets a file list from an IDataObject. Allocates
ppszList to appropriate size and fills it with
a null-terminated list of paths. It is double-null
terminated.
If ppszList is NULL, then simply get the count of files.
Call DataObj_FreeList to free the ppszList.
Returns: standard
S_OK if the objects are inside a briefcase
S_FALSE if not
Cond: --
*/
HRESULT PUBLIC DataObj_QueryFileList(
LPDATAOBJECT pdtobj,
LPTSTR * ppszList, // List of files (may be NULL)
LPUINT puCount) // Count of files
{
HRESULT hres = ResultFromScode(E_FAIL);
FORMATETC fmteBrief = {(CLIPFORMAT)g_cfBriefObj, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
FORMATETC fmteHdrop = {(CLIPFORMAT)CF_HDROP, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
STGMEDIUM medium;
ASSERT(pdtobj);
ASSERT(puCount);
// Does this dataobject support briefcase object format?
//
hres = pdtobj->lpVtbl->GetData(pdtobj, &fmteBrief, &medium);
if (SUCCEEDED(hres))
{
// Yup
PBRIEFOBJ pbo = (PBRIEFOBJ)GlobalLock(medium.hGlobal);
*puCount = BOFileCount(pbo);
hres = ResultFromScode(S_OK);
if (ppszList)
{
*ppszList = GAlloc(BOFileListSize(pbo));
if (*ppszList)
{
BltByte(*ppszList, BOFileList(pbo), BOFileListSize(pbo));
}
else
{
hres = ResultFromScode(E_OUTOFMEMORY);
}
}
GlobalUnlock(medium.hGlobal);
MyReleaseStgMedium(&medium);
goto Leave;
}
// Or does it support hdrops?
//
hres = pdtobj->lpVtbl->GetData(pdtobj, &fmteHdrop, &medium);
if (SUCCEEDED(hres))
{
// Yup
HDROP hdrop = medium.hGlobal;
UINT cFiles = DragQueryFile(hdrop, (UINT)-1, NULL, 0);
UINT cchSize = 0;
UINT i;
*puCount = cFiles;
hres = ResultFromScode(S_FALSE);
if (ppszList)
{
// Determine size we need to allocate
for (i = 0; i < cFiles; i++)
{
cchSize += DragQueryFile(hdrop, i, NULL, 0) + 1;
}
cchSize++; // for extra null
*ppszList = GAlloc(CbFromCch(cchSize));
if (*ppszList)
{
LPTSTR psz = *ppszList;
UINT cch;
// Translate the hdrop into our file list format.
// We know that they really are the same format,
// but to maintain the abstraction layer, we
// pretend we don't.
for (i = 0; i < cFiles; i++)
{
cch = DragQueryFile(hdrop, i, psz, cchSize) + 1;
psz += cch;
cchSize -= cch;
}
*psz = TEXT('\0'); // extra null
}
else
{
hres = ResultFromScode(E_OUTOFMEMORY);
}
}
MyReleaseStgMedium(&medium);
goto Leave;
}
// FEATURE: do we need to query for CF_TEXT?
Leave:
return hres;
}
/*----------------------------------------------------------
Purpose: Frees a file list that was allocated by DataObj_QueryFileList.
Returns: --
Cond: --
*/
void PUBLIC DataObj_FreeList(
LPTSTR pszList)
{
GFree(pszList);
}
//---------------------------------------------------------------------------
// BriefExt IUnknown base member functions
//---------------------------------------------------------------------------
/*----------------------------------------------------------
Purpose: IUnknown::QueryInterface
Returns: standard
Cond: --
*/
STDMETHODIMP BriefExt_QueryInterface(
LPUNKNOWN punk,
REFIID riid,
LPVOID * ppvOut)
{
PBRIEFEXT this = IToClass(BriefExt, sxi, punk);
HRESULT hres;
if (IsEqualIID(riid, &IID_IUnknown) ||
IsEqualIID(riid, &IID_IShellExtInit))
{
// We use the sxi field as our IUnknown as well
*ppvOut = &this->sxi;
this->cRef++;
hres = NOERROR;
}
else if (IsEqualIID(riid, &IID_IContextMenu))
{
(LPCONTEXTMENU)*ppvOut = &this->ctm;
this->cRef++;
hres = NOERROR;
}
else if (IsEqualIID(riid, &IID_IShellPropSheetExt))
{
(LPSHELLPROPSHEETEXT)*ppvOut = &this->spx;
this->cRef++;
hres = NOERROR;
}
else
{
*ppvOut = NULL;
hres = ResultFromScode(E_NOINTERFACE);
}
return hres;
}
/*----------------------------------------------------------
Purpose: IUnknown::AddRef
Returns: new reference count
Cond: --
*/
STDMETHODIMP_(UINT) BriefExt_AddRef(
LPUNKNOWN punk)
{
PBRIEFEXT this = IToClass(BriefExt, sxi, punk);
return ++this->cRef;
}
/*----------------------------------------------------------
Purpose: IUnknown::Release
Returns: new reference count
Cond: --
*/
STDMETHODIMP_(UINT) BriefExt_Release(
LPUNKNOWN punk)
{
PBRIEFEXT this = IToClass(BriefExt, sxi, punk);
if (--this->cRef)
{
return this->cRef;
}
if (this->pdtobj)
{
this->pdtobj->lpVtbl->Release(this->pdtobj);
}
if (this->hkeyProgID)
{
RegCloseKey(this->hkeyProgID);
}
GFree(this);
ENTEREXCLUSIVE();
{
DecBusySemaphore(); // Decrement the reference count to the DLL
}
LEAVEEXCLUSIVE();
return 0;
}
//---------------------------------------------------------------------------
// BriefExt IShellExtInit member functions
//---------------------------------------------------------------------------
/*----------------------------------------------------------
Purpose: IShellExtInit::QueryInterface
Returns: standard
Cond: --
*/
STDMETHODIMP BriefExt_SXI_QueryInterface(
LPSHELLEXTINIT psxi,
REFIID riid,
LPVOID * ppvOut)
{
return BriefExt_QueryInterface((LPUNKNOWN)psxi, riid, ppvOut);
}
/*----------------------------------------------------------
Purpose: IShellExtInit::AddRef
Returns: new reference count
Cond: --
*/
STDMETHODIMP_(UINT) BriefExt_SXI_AddRef(
LPSHELLEXTINIT psxi)
{
return BriefExt_AddRef((LPUNKNOWN)psxi);
}
/*----------------------------------------------------------
Purpose: IShellExtInit::Release
Returns: new reference count
Cond: --
*/
STDMETHODIMP_(UINT) BriefExt_SXI_Release(
LPSHELLEXTINIT psxi)
{
return BriefExt_Release((LPUNKNOWN)psxi);
}
/*----------------------------------------------------------
Purpose: IShellExtInit::Initialize
Returns: standard
Cond: --
*/
STDMETHODIMP BriefExt_SXI_Initialize(
LPSHELLEXTINIT psxi,
LPCITEMIDLIST pidlFolder,
LPDATAOBJECT pdtobj,
HKEY hkeyProgID)
{
PBRIEFEXT this = IToClass(BriefExt, sxi, psxi);
// Initialize can be called more than once.
//
if (this->pdtobj)
{
this->pdtobj->lpVtbl->Release(this->pdtobj);
}
if (this->hkeyProgID)
{
RegCloseKey(this->hkeyProgID);
}
// Duplicate the pdtobj pointer
if (pdtobj)
{
this->pdtobj = pdtobj;
pdtobj->lpVtbl->AddRef(pdtobj);
}
// Duplicate the handle
if (hkeyProgID)
{
RegOpenKeyEx(hkeyProgID, NULL, 0L, MAXIMUM_ALLOWED, &this->hkeyProgID);
}
return NOERROR;
}
//---------------------------------------------------------------------------
// BriefExt IContextMenu member functions
//---------------------------------------------------------------------------
/*----------------------------------------------------------
Purpose: IContextMenu::QueryInterface
Returns: standard
Cond: --
*/
STDMETHODIMP BriefExt_CM_QueryInterface(
LPCONTEXTMENU pctm,
REFIID riid,
LPVOID * ppvOut)
{
PBRIEFEXT this = IToClass(BriefExt, ctm, pctm);
return BriefExt_QueryInterface((LPUNKNOWN)&this->sxi, riid, ppvOut);
}
/*----------------------------------------------------------
Purpose: IContextMenu::AddRef
Returns: new reference count
Cond: --
*/
STDMETHODIMP_(UINT) BriefExt_CM_AddRef(
LPCONTEXTMENU pctm)
{
PBRIEFEXT this = IToClass(BriefExt, ctm, pctm);
return BriefExt_AddRef((LPUNKNOWN)&this->sxi);
}
/*----------------------------------------------------------
Purpose: IContextMenu::Release
Returns: new reference count
Cond: --
*/
STDMETHODIMP_(UINT) BriefExt_CM_Release(
LPCONTEXTMENU pctm)
{
PBRIEFEXT this = IToClass(BriefExt, ctm, pctm);
return BriefExt_Release((LPUNKNOWN)&this->sxi);
}
/*----------------------------------------------------------
Purpose: IContextMenu::QueryContextMenu
Returns: standard
Cond: --
*/
#define IDCM_UPDATEALL 0
#define IDCM_UPDATE 1
STDMETHODIMP BriefExt_CM_QueryContextMenu(
LPCONTEXTMENU pctm,
HMENU hmenu,
UINT indexMenu,
UINT idCmdFirst,
UINT idCmdLast,
UINT uFlags)
{
PBRIEFEXT this = IToClass(BriefExt, ctm, pctm);
USHORT cItems = 0;
// We only want to add items to the context menu if:
// 1) That's what the caller is asking for; and
// 2) The object is a briefcase or an object inside
// a briefcase
//
if (IsFlagClear(uFlags, CMF_DEFAULTONLY)) // check for (1)
{
TCHAR szIDS[MAXSHORTLEN];
// Is the object inside a briefcase? We know it is if
// the object understands our special format.
//
if (DataObj_KnowsBriefObj(this->pdtobj))
{
// Yes
InsertMenu(hmenu, indexMenu++, MF_BYPOSITION | MF_STRING,
idCmdFirst+IDCM_UPDATE, SzFromIDS(IDS_MENU_UPDATE, szIDS, ARRAYSIZE(szIDS)));
// NOTE: We should actually be using idCmdFirst+0 above since we are only adding
// one item to the menu. But since this code relies on using idCmdFirst+1 then
// we need to lie and say that we added two items to the menu. Otherwise the next
// context menu handler to get called might use the same menu ID that we are using.
cItems = 2;
}
else
{
// No
TCHAR szPath[MAX_PATH];
// Is the object a briefcase root?
if (SUCCEEDED(DataObj_QueryPath(this->pdtobj, szPath)) &&
PathIsBriefcase(szPath))
{
// Yup
InsertMenu(hmenu, indexMenu++, MF_BYPOSITION | MF_STRING,
idCmdFirst+IDCM_UPDATEALL, SzFromIDS(IDS_MENU_UPDATEALL, szIDS, ARRAYSIZE(szIDS)));
cItems++;
}
}
}
return ResultFromScode(MAKE_SCODE(SEVERITY_SUCCESS, FACILITY_NULL, (USHORT)cItems));
}
/*----------------------------------------------------------
Purpose: IContextMenu::InvokeCommand
Returns: standard
Cond: --
*/
STDMETHODIMP BriefExt_CM_InvokeCommand(
LPCONTEXTMENU pctm,
LPCMINVOKECOMMANDINFO pici)
{
HWND hwnd = pici->hwnd;
//LPCSTR pszWorkingDir = pici->lpDirectory;
//LPCSTR pszCmd = pici->lpVerb;
//LPCSTR pszParam = pici->lpParameters;
//int iShowCmd = pici->nShow;
PBRIEFEXT this = IToClass(BriefExt, ctm, pctm);
LPBRIEFCASESTG pbrfstg;
HRESULT hres;
// The only command we have is to update the selection(s). Create
// an instance of IBriefcaseStg so we can call its Update
// member function.
//
hres = BriefStg_CreateInstance(NULL, &IID_IBriefcaseStg, &pbrfstg);
if (SUCCEEDED(hres))
{
TCHAR szPath[MAX_PATH];
hres = DataObj_QueryPath(this->pdtobj, szPath);
if (SUCCEEDED(hres))
{
hres = pbrfstg->lpVtbl->Initialize(pbrfstg, szPath, hwnd);
if (SUCCEEDED(hres))
{
hres = pbrfstg->lpVtbl->UpdateObject(pbrfstg, this->pdtobj, hwnd);
}
pbrfstg->lpVtbl->Release(pbrfstg);
}
}
return hres;
}
/*----------------------------------------------------------
Purpose: IContextMenu::GetCommandString
Returns: standard
Cond: --
*/
STDMETHODIMP BriefExt_CM_GetCommandString(
LPCONTEXTMENU pctm,
UINT_PTR idCmd,
UINT wReserved,
UINT * pwReserved,
LPSTR pszName,
UINT cchMax)
{
switch (wReserved)
{
case GCS_VERB:
switch (idCmd)
{
case IDCM_UPDATE:
lstrcpyn((LPTSTR)pszName, TEXT("update"), cchMax);
return NOERROR;
case IDCM_UPDATEALL:
lstrcpyn((LPTSTR)pszName, TEXT("update all"), cchMax);
return NOERROR;
}
}
return E_NOTIMPL;
}
//---------------------------------------------------------------------------
// PageData functions
//---------------------------------------------------------------------------
/*----------------------------------------------------------
Purpose: Allocates a pagedata.
Returns: TRUE if the allocation/increment was successful
Cond: --
*/
BOOL PRIVATE PageData_Alloc(
PPAGEDATA * pppd,
int atomPath)
{
PPAGEDATA this;
ASSERT(pppd);
this = GAlloc(sizeof(*this));
if (this)
{
HRESULT hres;
LPCTSTR pszPath = Atom_GetName(atomPath);
int atomBrf;
// Create an instance of IBriefcaseStg.
hres = BriefStg_CreateInstance(NULL, &IID_IBriefcaseStg, &this->pbrfstg);
if (SUCCEEDED(hres))
{
hres = this->pbrfstg->lpVtbl->Initialize(this->pbrfstg, pszPath, NULL);
if (SUCCEEDED(hres))
{
TCHAR szBrfPath[MAX_PATH];
// Request the root path of the briefcase storage
this->pbrfstg->lpVtbl->GetExtraInfo(this->pbrfstg, NULL, GEI_ROOT,
(WPARAM)ARRAYSIZE(szBrfPath), (LPARAM)szBrfPath);
atomBrf = Atom_Add(szBrfPath);
hres = (ATOM_ERR != atomBrf) ? NOERROR : E_OUTOFMEMORY;
}
}
if (SUCCEEDED(hres))
{
this->pcbs = CBS_Get(atomBrf);
ASSERT(this->pcbs);
Atom_AddRef(atomPath);
this->atomPath = atomPath;
this->cRef = 1;
this->bFolder = (FALSE != PathIsDirectory(pszPath));
Atom_Delete(atomBrf);
}
else
{
// Failed
if (this->pbrfstg)
this->pbrfstg->lpVtbl->Release(this->pbrfstg);
GFree(this);
}
}
*pppd = this;
return NULL != this;
}
/*----------------------------------------------------------
Purpose: Increments the reference count of a pagedata
Returns: Current count
Cond: --
*/
UINT PRIVATE PageData_AddRef(
PPAGEDATA this)
{
ASSERT(this);
return ++(this->cRef);
}
/*----------------------------------------------------------
Purpose: Releases a pagedata struct
Returns: the next reference count
0 if the struct was freed
Cond: --
*/
UINT PRIVATE PageData_Release(
PPAGEDATA this)
{
UINT cRef;
ASSERT(this);
ASSERT(0 < this->cRef);
cRef = this->cRef;
if (0 < this->cRef)
{
this->cRef--;
if (0 == this->cRef)
{
if (this->pftl)
{
Sync_DestroyFolderList(this->pftl);
}
if (this->prl)
{
Sync_DestroyRecList(this->prl);
}
CBS_Delete(this->pcbs->atomBrf, NULL);
Atom_Delete(this->atomPath);
this->pbrfstg->lpVtbl->Release(this->pbrfstg);
GFree(this);
return 0;
}
}
return this->cRef;
}
/*----------------------------------------------------------
Purpose: Sets the data in the pagedata struct to indicate this
is an orphan. This function makes no change to the
database--the caller must do that.
Returns: --
Cond: --
*/
void PUBLIC PageData_Orphanize(
PPAGEDATA this)
{
this->bOrphan = TRUE;
if (this->pftl)
{
Sync_DestroyFolderList(this->pftl);
this->pftl = NULL;
}
if (this->prl)
{
Sync_DestroyRecList(this->prl);
this->prl = NULL;
}
}
/*----------------------------------------------------------
Purpose: Initializes the common page data struct shared between
the property pages. Keep in mind that this function may
be called multiple times, so it must behave properly
under these conditions (ie, don't blow anything away).
This function will return S_OK if it is. S_FALSE means
the data in question has been invalidated. This means
the twin has become an orphan.
Returns: standard result
Cond: --
*/
HRESULT PUBLIC PageData_Init(
PPAGEDATA this,
HWND hwndOwner)
{
HRESULT hres;
HBRFCASE hbrf = PageData_GetHbrf(this);
LPCTSTR pszPath = Atom_GetName(this->atomPath);
// ** Note: this structure is not serialized because it is
// assumed that of the pages that are sharing it, only one
// can access it at a time.
ASSERT(pszPath);
// Has this been explicitly marked as an orphan?
if (FALSE == this->bOrphan)
{
// No; is it (still) a twin?
if (S_OK == Sync_IsTwin(hbrf, pszPath, 0))
{
// Yes; has the folder twinlist or reclist been created yet?
if (NULL == this->prl ||
(this->bFolder && NULL == this->pftl))
{
// No; create it/them
HTWINLIST htl;
PFOLDERTWINLIST pftl = NULL;
PRECLIST prl = NULL;
HWND hwndProgress;
TWINRESULT tr;
ASSERT(NULL == this->prl);
ASSERT( !this->bFolder || NULL == this->pftl);
hwndProgress = UpdBar_Show(hwndOwner, UB_CHECKING | UB_NOCANCEL, DELAY_UPDBAR);
tr = Sync_CreateTwinList(hbrf, &htl);
hres = HRESULT_FROM_TR(tr);
if (SUCCEEDED(hres))
{
// Add to the twinlist. Create folder twinlist if
// necessary.
if (Sync_AddPathToTwinList(hbrf, htl, pszPath, &pftl))
{
// Does the reclist need creating?
if (NULL == this->prl)
{
// Yes
hres = Sync_CreateRecListEx(htl, UpdBar_GetAbortEvt(hwndProgress), &prl);
if (SUCCEEDED(hres))
{
// The object may have been implicitly
// deleted in CreateRecList. Check again.
hres = Sync_IsTwin(hbrf, pszPath, 0);
}
}
}
else
hres = E_FAIL;
// Fill in proper fields
if (NULL == this->prl && prl)
{
this->prl = prl;
}
if (NULL == this->pftl && pftl)
{
this->pftl = pftl;
}
// Clean up twinlist
Sync_DestroyTwinList(htl);
}
UpdBar_Kill(hwndProgress);
// Did the above succeed?
if (FAILED(hres) || S_FALSE == hres)
{
// No
PageData_Orphanize(this);
}
}
else
{
// Yes; do nothing
hres = S_OK;
}
}
else
{
// No; say the thing is an orphan
PageData_Orphanize(this);
hres = S_FALSE;
}
}
else
{
// Yes
hres = S_FALSE;
}
#ifdef DEBUG
if (S_OK == hres)
{
ASSERT( !this->bFolder || this->pftl );
ASSERT(this->prl);
}
else
{
ASSERT(NULL == this->pftl);
ASSERT(NULL == this->prl);
}
#endif
return hres;
}
/*----------------------------------------------------------
Purpose: Verifies whether the page data shared by the property
pages is still valid. This function will return S_OK if
it is. S_FALSE means the data in question has been
invalidated. This means the twin has become an orphan.
This function assumes PageData_Init has been previously
called.
Returns: standard result
Cond: --
*/
HRESULT PUBLIC PageData_Query(
PPAGEDATA this,
HWND hwndOwner,
PRECLIST * pprl, // May be NULL
PFOLDERTWINLIST * ppftl) // May be NULL
{
HRESULT hres;
LPCTSTR pszPath = Atom_GetName(this->atomPath);
// ** Note: this structure is not serialized because it is
// assumed that of the pages that are sharing it, only one
// can access it at a time.
ASSERT(pszPath);
// Is a recalc called for?
if (this->bRecalc)
{
// Yes; clear the fields and do again
PageData_Orphanize(this); // only temporary
this->bOrphan = FALSE; // undo the orphan state
this->bRecalc = FALSE;
// Reinit
hres = PageData_Init(this, hwndOwner);
if (pprl)
*pprl = this->prl;
if (ppftl)
*ppftl = this->pftl;
}
// Are the fields valid?
else if ( this->prl && (!this->bFolder || this->pftl) )
{
// Yes; is it (still) a twin?
ASSERT(FALSE == this->bOrphan);
hres = Sync_IsTwin(this->pcbs->hbrf, pszPath, 0);
if (S_OK == hres)
{
// Yes
if (pprl)
*pprl = this->prl;
if (ppftl)
*ppftl = this->pftl;
}
else if (S_FALSE == hres)
{
// No; update struct fields
PageData_Orphanize(this);
goto OrphanTime;
}
}
else
{
// No; say it is an orphan
OrphanTime:
ASSERT(this->bOrphan);
if (pprl)
*pprl = NULL;
if (ppftl)
*ppftl = NULL;
hres = S_FALSE;
}
return hres;
}
//---------------------------------------------------------------------------
// BriefExt IShellPropSheetExt member functions
//---------------------------------------------------------------------------
/*----------------------------------------------------------
Purpose: IShellPropSheetExt::QueryInterface
Returns: standard
Cond: --
*/
STDMETHODIMP BriefExt_SPX_QueryInterface(
LPSHELLPROPSHEETEXT pspx,
REFIID riid,
LPVOID * ppvOut)
{
PBRIEFEXT this = IToClass(BriefExt, spx, pspx);
return BriefExt_QueryInterface((LPUNKNOWN)&this->sxi, riid, ppvOut);
}
/*----------------------------------------------------------
Purpose: IShellPropSheetExt::AddRef
Returns: new reference count
Cond: --
*/
STDMETHODIMP_(UINT) BriefExt_SPX_AddRef(
LPSHELLPROPSHEETEXT pspx)
{
PBRIEFEXT this = IToClass(BriefExt, spx, pspx);
return BriefExt_AddRef((LPUNKNOWN)&this->sxi);
}
/*----------------------------------------------------------
Purpose: IShellPropSheetExt::Release
Returns: new reference count
Cond: --
*/
STDMETHODIMP_(UINT) BriefExt_SPX_Release(
LPSHELLPROPSHEETEXT pspx)
{
PBRIEFEXT this = IToClass(BriefExt, spx, pspx);
return BriefExt_Release((LPUNKNOWN)&this->sxi);
}
/*----------------------------------------------------------
Purpose: Callback when Status property page is done
Returns: --
Cond: --
*/
UINT CALLBACK StatusPageCallback(
HWND hwnd,
UINT uMsg,
LPPROPSHEETPAGE ppsp)
{
if (PSPCB_RELEASE == uMsg)
{
PPAGEDATA ppagedata = (PPAGEDATA)ppsp->lParam;
DEBUG_CODE( TRACE_MSG(TF_GENERAL, TEXT("Releasing status page")); )
PageData_Release(ppagedata);
}
return TRUE;
}
/*----------------------------------------------------------
Purpose: Callback when Info property sheet is done
Returns: --
Cond: --
*/
UINT CALLBACK InfoPageCallback(
HWND hwnd,
UINT uMsg,
LPPROPSHEETPAGE ppsp)
{
if (PSPCB_RELEASE == uMsg)
{
PPAGEDATA ppagedata = (PPAGEDATA)ppsp->lParam;
PINFODATA pinfodata = (PINFODATA)ppagedata->lParam;
DEBUG_CODE( TRACE_MSG(TF_GENERAL, TEXT("Releasing info page")); )
if (pinfodata->hdpaTwins)
{
int iItem;
int cItems = DPA_GetPtrCount(pinfodata->hdpaTwins);
HTWIN htwin;
for (iItem = 0; iItem < cItems; iItem++)
{
htwin = DPA_FastGetPtr(pinfodata->hdpaTwins, iItem);
Sync_ReleaseTwin(htwin);
}
DPA_Destroy(pinfodata->hdpaTwins);
}
GFree(pinfodata);
PageData_Release(ppagedata);
}
return TRUE;
}
/*----------------------------------------------------------
Purpose: Add the status property page
Returns: TRUE on success
FALSE if out of memory
Cond: --
*/
BOOL PRIVATE AddStatusPage(
PPAGEDATA ppd,
LPFNADDPROPSHEETPAGE pfnAddPage,
LPARAM lParam)
{
BOOL bRet = FALSE;
HPROPSHEETPAGE hpsp;
PROPSHEETPAGE psp = {
sizeof(PROPSHEETPAGE), // size
PSP_USECALLBACK, // PSP_ flags
g_hinst, // hinstance
MAKEINTRESOURCE(IDD_STATUS), // pszTemplate
NULL, // icon
NULL, // pszTitle
Stat_WrapperProc, // pfnDlgProc
(LPARAM)ppd, // lParam
StatusPageCallback, // pfnCallback
0 }; // ref count
ASSERT(ppd);
ASSERT(pfnAddPage);
DEBUG_CODE( TRACE_MSG(TF_GENERAL, TEXT("Adding status page")); )
// Add the status property sheet
hpsp = CreatePropertySheetPage(&psp);
if (hpsp)
{
bRet = (*pfnAddPage)(hpsp, lParam);
if (FALSE == bRet)
{
// Cleanup on failure
DestroyPropertySheetPage(hpsp);
}
}
return bRet;
}
/*----------------------------------------------------------
Purpose: Add the info property page.
Returns: TRUE on success
FALSE if out of memory
Cond: --
*/
BOOL PRIVATE AddInfoPage(
PPAGEDATA ppd,
LPFNADDPROPSHEETPAGE lpfnAddPage,
LPARAM lParam)
{
BOOL bRet = FALSE;
HPROPSHEETPAGE hpsp;
PINFODATA pinfodata;
ASSERT(lpfnAddPage);
DEBUG_CODE( TRACE_MSG(TF_GENERAL, TEXT("Adding info page")); )
pinfodata = GAlloc(sizeof(*pinfodata));
if (pinfodata)
{
PROPSHEETPAGE psp = {
sizeof(PROPSHEETPAGE), // size
PSP_USECALLBACK, // PSP_ flags
g_hinst, // hinstance
MAKEINTRESOURCE(IDD_INFO), // pszTemplate
NULL, // icon
NULL, // pszTitle
Info_WrapperProc, // pfnDlgProc
(LPARAM)ppd, // lParam
InfoPageCallback, // pfnCallback
0 }; // ref count
ppd->lParam = (LPARAM)pinfodata;
pinfodata->atomTo = ATOM_ERR; // Not needed for page
pinfodata->bStandAlone = FALSE;
if (NULL != (pinfodata->hdpaTwins = DPA_Create(8)))
{
hpsp = CreatePropertySheetPage(&psp);
if (hpsp)
{
bRet = (*lpfnAddPage)(hpsp, lParam);
if (FALSE == bRet)
{
// Cleanup on failure
DestroyPropertySheetPage(hpsp);
}
}
if (FALSE == bRet)
{
// Cleanup on failure
DPA_Destroy(pinfodata->hdpaTwins);
}
}
if (FALSE == bRet)
{
// Cleanup on failure
GFree(pinfodata);
}
}
return bRet;
}
/*----------------------------------------------------------
Purpose: Does the real work to add the briefcase pages to
the property sheet.
Returns: --
Cond: --
*/
void PRIVATE BriefExt_AddPagesPrivate(
LPSHELLPROPSHEETEXT pspx,
LPCTSTR pszPath,
LPFNADDPROPSHEETPAGE lpfnAddPage,
LPARAM lParam)
{
PBRIEFEXT this = IToClass(BriefExt, spx, pspx);
HRESULT hres = NOERROR;
TCHAR szCanonPath[MAX_PATH];
int atomPath;
BrfPathCanonicalize(pszPath, szCanonPath);
atomPath = Atom_Add(szCanonPath);
if (atomPath != ATOM_ERR)
{
PPAGEDATA ppagedata;
BOOL bVal;
// Allocate the pagedata
if (PageData_Alloc(&ppagedata, atomPath))
{
// Always add the status page (even for orphans).
// Add the info page if the object is a folder.
bVal = AddStatusPage(ppagedata, lpfnAddPage, lParam);
if (bVal && ppagedata->bFolder)
{
PageData_AddRef(ppagedata);
AddInfoPage(ppagedata, lpfnAddPage, lParam);
}
else if (FALSE == bVal)
{
// (Cleanup on failure)
PageData_Release(ppagedata);
}
}
Atom_Delete(atomPath);
}
}
/*----------------------------------------------------------
Purpose: IShellPropSheetExt::AddPages
The shell calls this member function when it is
time to add pages to a property sheet.
As the briefcase storage, we only add pages for
entities inside a briefcase. Anything outside
a briefcase is not touched.
We can quickly determine if the object is inside
the briefcase by querying the data object that
we have. If it knows our special "briefcase object"
format, then it must be inside a briefcase. We
purposely do not add pages for the root folder itself.
Returns: standard hresult
Cond: --
*/
STDMETHODIMP BriefExt_SPX_AddPages(
LPSHELLPROPSHEETEXT pspx,
LPFNADDPROPSHEETPAGE lpfnAddPage,
LPARAM lParam)
{
PBRIEFEXT this = IToClass(BriefExt, spx, pspx);
LPTSTR pszList;
UINT cFiles;
// Questions:
// 1) Does this know the briefcase object format?
// 2) Is there only a single object selected?
//
if (DataObj_KnowsBriefObj(this->pdtobj) && /* (1) */
SUCCEEDED(DataObj_QueryFileList(this->pdtobj, &pszList, &cFiles)) &&
cFiles == 1) /* (2) */
{
// Yes; add the pages
BriefExt_AddPagesPrivate(pspx, pszList, lpfnAddPage, lParam);
DataObj_FreeList(pszList);
}
return NOERROR; // Always allow property sheet to appear
}
//---------------------------------------------------------------------------
// BriefExtMenu class : Vtables
//---------------------------------------------------------------------------
IShellExtInitVtbl c_BriefExt_SXIVtbl =
{
BriefExt_SXI_QueryInterface,
BriefExt_SXI_AddRef,
BriefExt_SXI_Release,
BriefExt_SXI_Initialize
};
IContextMenuVtbl c_BriefExt_CTMVtbl =
{
BriefExt_CM_QueryInterface,
BriefExt_CM_AddRef,
BriefExt_CM_Release,
BriefExt_CM_QueryContextMenu,
BriefExt_CM_InvokeCommand,
BriefExt_CM_GetCommandString,
};
IShellPropSheetExtVtbl c_BriefExt_SPXVtbl = {
BriefExt_SPX_QueryInterface,
BriefExt_SPX_AddRef,
BriefExt_SPX_Release,
BriefExt_SPX_AddPages
};
/*----------------------------------------------------------
Purpose: This function is called back from within
IClassFactory::CreateInstance() of the default class
factory object, which is created by SHCreateClassObject.
Returns: standard
Cond: --
*/
HRESULT CALLBACK BriefExt_CreateInstance(
LPUNKNOWN punkOuter,
REFIID riid,
LPVOID * ppvOut)
{
HRESULT hres;
PBRIEFEXT this;
DBG_ENTER_RIID(TEXT("BriefExt_CreateInstance"), riid);
// Shell extentions typically do not support aggregation.
//
if (punkOuter)
{
hres = ResultFromScode(CLASS_E_NOAGGREGATION);
*ppvOut = NULL;
goto Leave;
}
this = GAlloc(sizeof(*this));
if (!this)
{
hres = ResultFromScode(E_OUTOFMEMORY);
*ppvOut = NULL;
goto Leave;
}
this->sxi.lpVtbl = &c_BriefExt_SXIVtbl;
this->ctm.lpVtbl = &c_BriefExt_CTMVtbl;
this->spx.lpVtbl = &c_BriefExt_SPXVtbl;
this->cRef = 1;
this->pdtobj = NULL;
this->hkeyProgID = NULL;
ENTEREXCLUSIVE();
{
// The decrement is in BriefExt_Release()
IncBusySemaphore();
}
LEAVEEXCLUSIVE();
// Note that the Release member will free the object, if QueryInterface
// failed.
//
hres = c_BriefExt_SXIVtbl.QueryInterface(&this->sxi, riid, ppvOut);
c_BriefExt_SXIVtbl.Release(&this->sxi);
Leave:
DBG_EXIT_HRES(TEXT("BriefExt_CreateInstance"), hres);
return hres; // S_OK or E_NOINTERFACE
}