//--------------------------------------------------------------------------- // // 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 #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 = {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 = {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 = {g_cfBriefObj, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL}; FORMATETC fmteHdrop = {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 = {g_cfBriefObj, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL}; FORMATETC fmteHdrop = {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; } // BUGBUG: 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: -- */ 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+cItems, SzFromIDS(IDS_MENU_UPDATE, szIDS, ARRAYSIZE(szIDS))); cItems++; } 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+cItems, 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. // if (SUCCEEDED(BriefStg_CreateInstance(NULL, &IID_IBriefcaseStg, &pbrfstg))) { TCHAR szPath[MAX_PATH]; if (SUCCEEDED(DataObj_QueryPath(this->pdtobj, szPath))) { 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 idCmd, UINT wReserved, UINT * pwReserved, LPSTR pszName, UINT cchMax) { return NOERROR; } //--------------------------------------------------------------------------- // 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; PathCanonicalize(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 //--------------------------------------------------------------------------- #pragma data_seg(DATASEG_READONLY) 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 }; #pragma data_seg() /*---------------------------------------------------------- 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 }