windows-nt/Source/XPSP1/NT/shell/osshell/lmui/shareui/sfolder.cxx
2020-09-26 16:20:57 +08:00

887 lines
22 KiB
C++

//+---------------------------------------------------------------------------
//
// Microsoft Windows
// Copyright (C) Microsoft Corporation, 1995 - 1995.
//
// File: sfolder.cxx
//
// Contents: Implementation of IShellFolder
//
// History: 13-Dec-95 BruceFo Created
//
//----------------------------------------------------------------------------
#include "headers.hxx"
#pragma hdrstop
#include "dutil.hxx"
#include "enum.hxx"
#include "menuutil.hxx"
#include "menu.hxx"
#include "menusp.hxx"
#include "menubg.hxx"
#include "sdetails.hxx"
#include "sfolder.hxx"
#include "shares.h"
#include "shares.hxx"
#include "util.hxx"
#include "xicon.hxx"
#include "resource.h"
//////////////////////////////////////////////////////////////////////////////
void
FSSetStatusText(
HWND hwndOwner,
LPTSTR* ppszText,
int iStart,
int iEnd);
//////////////////////////////////////////////////////////////////////////////
STDMETHODIMP
CSharesSF::ParseDisplayName(
HWND hwndOwner,
LPBC pbc,
LPOLESTR lpszDisplayName,
ULONG* pchEaten,
LPITEMIDLIST* ppidlOutm,
ULONG* pdwAttributes
)
{
return E_NOTIMPL;
}
STDMETHODIMP
CSharesSF::GetAttributesOf(
UINT cidl,
LPCITEMIDLIST* apidl,
ULONG* pdwInOut
)
{
// There are four types of object: New object, View NetWare, View Mac,
// and regular share. If there is a single selection, then the operations
// possible are:
// New share: open, create shortcut
// View NetWare: open, create shortcut
// View Mac: open, create shortcut
// a share: delete, properties
// If there are different types of objects multiply selected, then
// the items must all be shares, or there are no allowed operations.
// For shares, the only multiple-select operation allowed is delete.
ULONG fMask = 0;
if (cidl == 0)
{
// What things in general can be done in the folder? Return a
// mask of everything possible.
fMask = SFGAO_CANDELETE | SFGAO_HASPROPSHEET | SFGAO_CANRENAME | SFGAO_LINK;
}
else if (cidl == 1)
{
LPIDSHARE pids = (LPIDSHARE)apidl[0];
if (Share_IsShare(pids))
{
fMask = SFGAO_CANDELETE | SFGAO_HASPROPSHEET;
if (!(Share_GetType(pids) & STYPE_SPECIAL))
{
fMask |= SFGAO_CANRENAME;
}
}
else
{
fMask = SFGAO_CANLINK;
}
}
else if (cidl > 1)
{
UINT i;
for (i = 0; i < cidl; i++)
{
LPIDSHARE pids = (LPIDSHARE)apidl[i];
if (!Share_IsShare(pids))
{
break;
}
}
if (i == cidl)
{
fMask |= SFGAO_CANDELETE;
}
}
*pdwInOut &= fMask;
return S_OK;
}
STDMETHODIMP
CSharesSF::GetUIObjectOf(
HWND hwndOwner,
UINT cidl,
LPCITEMIDLIST* apidl,
REFIID riid,
UINT* prgfInOut,
LPVOID* ppvOut
)
{
CShares* This = IMPL(CShares,m_ShellFolder,this);
HRESULT hr = E_NOINTERFACE;
*ppvOut = NULL;
if (cidl == 1 && IsEqualIID(riid, IID_IExtractIcon))
{
LPIDSHARE pids = (LPIDSHARE)apidl[0];
CSharesEI* pObj = new CSharesEI(Share_GetFlags(pids), Share_GetType(pids));
if (NULL == pObj)
{
return E_OUTOFMEMORY;
}
hr = pObj->QueryInterface(riid, ppvOut);
pObj->Release();
}
#ifdef UNICODE
else if (cidl == 1 && IsEqualIID(riid, IID_IExtractIconA))
{
LPIDSHARE pids = (LPIDSHARE)apidl[0];
CSharesEIA* pObj = new CSharesEIA(Share_GetFlags(pids), Share_GetType(pids));
if (NULL == pObj)
{
return E_OUTOFMEMORY;
}
hr = pObj->QueryInterface(riid, ppvOut);
pObj->Release();
}
#endif // UNICODE
else if (cidl > 0 && IsEqualIID(riid, IID_IContextMenu))
{
// Create a context menu for selected items. If there is only one
// item, then the context menu is based on that object and is
// CSharesCM for shares and CSharesCMSpecial for special objects.
// If there is a multiple selection, then the selection must all be
// shares, in which case the context-menu is CSharesCM, else there
// is no context menu!
if (This->m_level < 2)
{
// user has insufficient privilege to perform any operations.
return E_NOINTERFACE;
}
IUnknown* punk = NULL;
if (cidl == 1)
{
LPIDSHARE pids = (LPIDSHARE)apidl[0];
if (Share_IsShare(pids))
{
CSharesCM* pObj = new CSharesCM(hwndOwner);
if (NULL == pObj)
{
return E_OUTOFMEMORY;
}
hr = pObj->InitInstance(This->m_pszMachine, cidl, apidl, this);
if (FAILED(hr))
{
return hr;
}
punk = (IUnknown*)pObj;
}
#ifdef WIZARDS
else
{
CSharesCMSpecial* pObj = new CSharesCMSpecial(hwndOwner);
if (NULL == pObj)
{
return E_OUTOFMEMORY;
}
hr = pObj->InitInstance(This->m_pszMachine, apidl[0], this);
if (FAILED(hr))
{
return hr;
}
punk = (IUnknown*)pObj;
}
#endif // WIZARDS
}
else if (cidl > 1)
{
UINT i;
for (i = 0; i < cidl; i++)
{
LPIDSHARE pids = (LPIDSHARE)apidl[i];
if (!Share_IsShare(pids))
{
break;
}
}
if (i == cidl)
{
CSharesCM* pObj = new CSharesCM(hwndOwner);
if (NULL == pObj)
{
return E_OUTOFMEMORY;
}
hr = pObj->InitInstance(This->m_pszMachine, cidl, apidl, this);
if (FAILED(hr))
{
return hr;
}
punk = (IUnknown*)pObj;
}
else
{
return E_FAIL;
}
}
appAssert(NULL != punk);
hr = punk->QueryInterface(riid, ppvOut);
punk->Release();
}
else if (cidl > 0 && IsEqualIID(riid, IID_IDataObject))
{
hr = CIDLData_CreateFromIDArray(
This->m_pidl,
cidl,
apidl,
(LPDATAOBJECT *)ppvOut);
}
return hr;
}
STDMETHODIMP
CSharesSF::EnumObjects(
HWND hwndOwner,
DWORD grfFlags,
LPENUMIDLIST* ppenumUnknown
)
{
CShares* This = IMPL(CShares,m_ShellFolder,this);
HRESULT hr = E_FAIL;
*ppenumUnknown = NULL;
if (!(grfFlags & SHCONTF_NONFOLDERS))
{
return hr;
}
appAssert(0 != This->m_level);
CSharesEnum* pEnum = new CSharesEnum(This->m_pszMachine, This->m_level);
if (NULL == pEnum)
{
return E_OUTOFMEMORY;
}
hr = pEnum->Init(grfFlags);
if (FAILED(hr))
{
return hr;
}
hr = pEnum->QueryInterface(IID_IEnumIDList, (LPVOID*)ppenumUnknown);
pEnum->Release();
return hr;
}
STDMETHODIMP
CSharesSF::BindToObject(
LPCITEMIDLIST pidl,
LPBC pbc,
REFIID riid,
LPVOID* ppvOut
)
{
//
// Shares folder doesn't contain sub-folders
//
*ppvOut = NULL;
return E_FAIL;
}
// not used in Win95
STDMETHODIMP
CSharesSF::BindToStorage(
LPCITEMIDLIST pidl,
LPBC pbcReserved,
REFIID riid,
LPVOID* ppvOut
)
{
*ppvOut = NULL;
return E_NOTIMPL;
}
#define PlusMinus(x) (((x) < 0) ? -1 : ( ((x) > 0) ? 1 : 0 ))
int
CSharesSF::_CompareOne(
DWORD iCol,
LPIDSHARE pids1,
LPIDSHARE pids2
)
{
switch (iCol)
{
case ICOL2_NAME:
return lstrcmpi(Share_GetName(pids1), Share_GetName(pids2));
case ICOL2_COMMENT:
return lstrcmpi(Share_GetComment(pids1), Share_GetComment(pids2));
case ICOL2_PATH:
return lstrcmpi(Share_GetPath(pids1), Share_GetPath(pids2));
case ICOL2_MAXUSES:
{
DWORD max1 = Share_GetMaxUses(pids1);
DWORD max2 = Share_GetMaxUses(pids2);
if (max1 == SHI_USES_UNLIMITED && max2 == SHI_USES_UNLIMITED)
{
return 0;
}
else if (max1 == SHI_USES_UNLIMITED)
{
return 1;
}
else if (max2 == SHI_USES_UNLIMITED)
{
return -1;
}
else
{
return max1 - max2;
}
}
default: appAssert(!"Illegal column"); return 0;
}
}
STDMETHODIMP
CSharesSF::CompareIDs(
LPARAM iCol,
LPCITEMIDLIST pidl1,
LPCITEMIDLIST pidl2
)
{
CShares* This = IMPL(CShares,m_ShellFolder,this);
// If one item is a special item, then put it ahead of the other one.
// If they are both special items, sort on name.
LPIDSHARE pids1 = (LPIDSHARE)pidl1;
LPIDSHARE pids2 = (LPIDSHARE)pidl2;
int iCmp;
#ifdef WIZARDS
if (Share_IsSpecial(pids1))
{
if (Share_IsSpecial(pids2))
{
// both special; sort on name
return ResultFromShort(lstrcmpi(Share_GetName(pids1),
Share_GetName(pids2)));
}
else
{
return ResultFromShort(-1);
}
}
else if (Share_IsSpecial(pids2))
{
return ResultFromShort(1);
}
#endif // WIZARDS
// Documentation says iCol is always zero, but that is wrong! It will
// be non-zero in case the user has clicked on a column heading to sort
// the column. In general, we want the entire item to be equal before we
// return 0 for equality. To do this, we first check the chosen element.
// If it is not equal, return the value. Otherwise, check all elements in
// this standard order:
// name
// comment
// path
// max uses
// current uses
// Only after all these checks return 0 (equality) do we return 0 (equality)
iCmp = _CompareOne((ULONG)iCol, pids1, pids2);
if (iCmp != 0)
{
return ResultFromShort(PlusMinus(iCmp));
}
// now, check each in turn
iCmp = _CompareOne(ICOL2_NAME, pids1, pids2);
if (iCmp != 0)
{
return ResultFromShort(PlusMinus(iCmp));
}
iCmp = _CompareOne(ICOL2_COMMENT, pids1, pids2);
if (iCmp != 0)
{
return ResultFromShort(PlusMinus(iCmp));
}
if (This->m_level == 2)
{
iCmp = _CompareOne(ICOL2_PATH, pids1, pids2);
if (iCmp != 0)
{
return ResultFromShort(PlusMinus(iCmp));
}
iCmp = _CompareOne(ICOL2_MAXUSES, pids1, pids2);
if (iCmp != 0)
{
return ResultFromShort(PlusMinus(iCmp));
}
}
return 0; // the same!
}
STDMETHODIMP
CSharesSF::CreateViewObject(
HWND hwnd,
REFIID riid,
LPVOID* ppvOut
)
{
CShares* This = IMPL(CShares,m_ShellFolder,this);
HRESULT hr = E_NOINTERFACE;
*ppvOut = NULL;
if (IsEqualIID(riid, IID_IShellView))
{
CSFV csfv =
{
sizeof(CSFV), // cbSize
(IShellFolder*)this, // pshf
NULL, // psvOuter
NULL, // pidl to monitor (NULL == all)
SHCNE_NETSHARE | SHCNE_NETUNSHARE | SHCNE_UPDATEITEM, // events
_SFVCallBack, // pfnCallback
FVM_DETAILS
};
hr = SHCreateShellFolderViewEx(&csfv, (LPSHELLVIEW *)ppvOut);
}
else if (IsEqualIID(riid, IID_IShellDetails))
{
appAssert(This->m_level != 0);
CSharesSD* pObj = new CSharesSD(hwnd, This->m_level);
if (NULL == pObj)
{
return E_OUTOFMEMORY;
}
hr = pObj->QueryInterface(riid, ppvOut);
pObj->Release();
}
else if (IsEqualIID(riid, IID_IContextMenu))
{
// Create a context menu for the background
CSharesCMBG* pObj = new CSharesCMBG(hwnd, This->m_pszMachine, This->m_level);
if (NULL == pObj)
{
return E_OUTOFMEMORY;
}
hr = pObj->QueryInterface(riid, ppvOut);
pObj->Release();
}
return hr;
}
STDMETHODIMP
CSharesSF::GetDisplayNameOf(
LPCITEMIDLIST pidl,
DWORD uFlags,
LPSTRRET lpName
)
{
CShares* This = IMPL(CShares,m_ShellFolder,this);
LPIDSHARE pids = (LPIDSHARE)pidl;
if (uFlags == SHGDN_FORPARSING)
{
return E_NOTIMPL; // don't support parsing.
}
else if (uFlags == SHGDN_INFOLDER)
{
return STRRETCopy(Share_GetName(pids), lpName);
}
else if (uFlags == SHGDN_NORMAL)
{
if (NULL == This->m_pszMachine)
{
return STRRETCopy(Share_GetName(pids), lpName);
}
else
{
LPWSTR pszMachine = This->m_pszMachine;
if (pszMachine[0] == TEXT('\\') && pszMachine[1] == TEXT('\\'))
{
pszMachine += 2;
}
WCHAR szBuf[MAX_PATH];
szBuf[0] = L'\0';
MyFormatMessage(
MSG_TEMPLATE_WITH_ON,
szBuf,
ARRAYLEN(szBuf),
pszMachine,
Share_GetName(pids));
#ifdef UNICODE
LPTSTR pszCopy = (LPTSTR)SHAlloc((lstrlen(szBuf)+1) * sizeof(TCHAR));
if (pszCopy)
{
wcscpy(pszCopy, szBuf);
lpName->uType = STRRET_OLESTR;
lpName->pOleStr = pszCopy;
}
else
{
lpName->uType = STRRET_CSTR;
lpName->cStr[0] = '\0';
}
#else
lpName->uType = STRRET_CSTR;
lstrcpyn(lpName->cStr, szBuf, ARRAYSIZE(lpName->cStr));
SHFree(pszRet);
#endif
return S_OK;
}
}
else
{
return E_INVALIDARG;
}
}
STDMETHODIMP
CSharesSF::SetNameOf(
HWND hwndOwner,
LPCITEMIDLIST pidl,
LPCOLESTR lpszName,
DWORD uFlags,
LPITEMIDLIST* ppidlOut
)
{
CShares* This = IMPL(CShares,m_ShellFolder,this);
if (uFlags != SHGDN_INFOLDER)
{
return E_NOTIMPL;
}
if (NULL == lpszName || L'\0' == *lpszName)
{
// can't change name to nothing
MessageBeep(0);
return E_FAIL;
}
NET_API_STATUS ret;
WCHAR szBuf[MAX_PATH];
LPSHARE_INFO_502 pInfo;
LPIDSHARE pids = (LPIDSHARE)pidl;
// Get information about the existing share before deleting it.
ret = NetShareGetInfo(This->m_pszMachine, Share_GetName(pids), 502, (LPBYTE*)&pInfo);
if (ret != NERR_Success)
{
DisplayError(hwndOwner, IERR_CANT_DEL_SHARE, ret, Share_GetName(pids));
return HRESULT_FROM_WIN32(ret);
}
// Validate the new share name
// Trying to create a reserved share?
if ( (0 == _wcsicmp(g_szIpcShare, lpszName))
|| (0 == _wcsicmp(g_szAdminShare, lpszName)))
{
MyErrorDialog(hwndOwner, MSG_ADDSPECIAL2);
NetApiBufferFree(pInfo);
return E_FAIL;
}
HRESULT hrTemp;
if (!IsValidShareName(lpszName, &hrTemp))
{
MyErrorDialog(hwndOwner, hrTemp);
NetApiBufferFree(pInfo);
return E_FAIL;
}
// Check to see that the same share isn't already used, for either the
// same path or for another path.
SHARE_INFO_2* pInfo2;
ret = NetShareGetInfo(This->m_pszMachine, (LPWSTR)lpszName, 2, (LPBYTE*)&pInfo2);
if (ret == NERR_Success)
{
// Is it already shared for the same path?
if (0 == _wcsicmp(pInfo2->shi2_path, pInfo->shi502_path))
{
MyErrorDialog(hwndOwner, IERR_AlreadyExists, lpszName);
NetApiBufferFree(pInfo);
NetApiBufferFree(pInfo2);
return TRUE;
}
// Shared for a different path. Ask the user if they wish to delete
// the old share and create the new one using the name.
DWORD id = ConfirmReplaceShare(hwndOwner, lpszName, pInfo2->shi2_path, pInfo->shi502_path);
if (id == IDNO || id == IDCANCEL) // FEATURE: should be only yes/no
{
NetApiBufferFree(pInfo);
NetApiBufferFree(pInfo2);
return E_FAIL;
}
// User said to replace the old share. Do it.
ret = NetShareDel(This->m_pszMachine, (LPWSTR)lpszName, 0);
if (ret != NERR_Success)
{
DisplayError(hwndOwner, IERR_CANT_DEL_SHARE, ret, (LPWSTR)lpszName);
NetApiBufferFree(pInfo);
NetApiBufferFree(pInfo2);
return FALSE;
}
else
{
SHChangeNotify(SHCNE_NETUNSHARE, SHCNF_PATH, pInfo2->shi2_path, NULL);
}
NetApiBufferFree(pInfo2);
}
// Check for downlevel accessibility
ULONG nType;
if (NERR_Success != NetpPathType(NULL, (LPWSTR)lpszName, &nType, INPT_FLAGS_OLDPATHS))
{
DWORD id = MyConfirmationDialog(
hwndOwner,
IERR_InaccessibleByDos,
MB_YESNO | MB_ICONEXCLAMATION,
lpszName);
if (id == IDNO)
{
return TRUE;
}
}
// delete the existing share
ret = NetShareDel(This->m_pszMachine, Share_GetName(pids), 0);
if (ret != NERR_Success)
{
NetApiBufferFree(pInfo);
DisplayError(hwndOwner, IERR_CANT_DEL_SHARE, ret, Share_GetName(pids));
return HRESULT_FROM_WIN32(ret);
}
// Create a new share with the new name.
LPWSTR ptmp = pInfo->shi502_netname;
pInfo->shi502_netname = (LPWSTR)lpszName; // cast away const
ret = NetShareAdd(This->m_pszMachine, 502, (LPBYTE)pInfo, NULL);
if (ret != NERR_Success)
{
pInfo->shi502_netname = ptmp;
NetApiBufferFree(pInfo);
DisplayError(hwndOwner, IERR_CANT_ADD_SHARE, ret, (LPWSTR)lpszName); // cast away const
return HRESULT_FROM_WIN32(ret);
}
// Ok, now I've renamed it. So, fill an ID list with the new guy, and
// return it in *ppidlOut.
HRESULT hr = S_OK;
if (NULL != ppidlOut)
{
IDSHARE ids;
FillID2(&ids, (LPSHARE_INFO_2)pInfo); // ignore security at end of level 502
*ppidlOut = ILClone((LPCITEMIDLIST)(&ids));
if (NULL == *ppidlOut)
{
hr = E_OUTOFMEMORY;
}
}
// force a view refresh
SHChangeNotify(SHCNE_UPDATEITEM, SHCNF_IDLIST, NULL, 0);
pInfo->shi502_netname = ptmp;
NetApiBufferFree(pInfo);
return hr;
}
//
// Callback from SHCreateShellFolderViewEx
//
HRESULT CALLBACK
CSharesSF::_SFVCallBack(
LPSHELLVIEW psvOuter,
LPSHELLFOLDER psf,
HWND hwndOwner,
UINT uMsg,
WPARAM wParam,
LPARAM lParam
)
{
CShares* This = IMPL(CShares,m_ShellFolder,psf);
HRESULT hr = S_OK; // assume no error
switch (uMsg)
{
case DVM_UPDATESTATUSBAR:
{
IShellBrowser* psb = FileCabinet_GetIShellBrowser(hwndOwner);
UINT cidl = ShellFolderView_GetSelectedCount(hwndOwner);
if (cidl == 1)
{
LPITEMIDLIST *apidl;
LPIDSHARE pids;
LPTSTR lpsz = TEXT("");
ShellFolderView_GetSelectedObjects(hwndOwner, &apidl);
if (apidl)
{
pids = (LPIDSHARE)apidl[0];
if (Share_IsShare(pids))
{
lpsz = Share_GetComment(pids);
}
FSSetStatusText(hwndOwner, &lpsz, 0, 0);
LocalFree(apidl);
}
}
else
{
hr = E_FAIL;
}
break;
}
case DVM_MERGEMENU:
{
appDebugOut((DEB_TRACE, "DVM_MERGEMENU\n"));
appAssert(This->m_pMenuBg == NULL);
hr = psf->CreateViewObject(hwndOwner, IID_IContextMenu, (LPVOID*)&This->m_pMenuBg);
if (SUCCEEDED(hr))
{
LPQCMINFO pqcm = (LPQCMINFO)lParam;
hr = This->m_pMenuBg->QueryContextMenu(
pqcm->hmenu,
pqcm->indexMenu,
pqcm->idCmdFirst,
pqcm->idCmdLast,
CMF_DVFILE);
}
break;
}
case DVM_UNMERGEMENU:
{
appDebugOut((DEB_TRACE, "DVM_UNMERGEMENU\n"));
if (NULL != This->m_pMenuBg)
{
This->m_pMenuBg->Release();
This->m_pMenuBg = NULL;
}
break;
}
case DVM_INVOKECOMMAND:
{
appDebugOut((DEB_TRACE, "DVM_INVOKECOMMAND\n"));
appAssert(This->m_pMenuBg != NULL);
CMINVOKECOMMANDINFO ici =
{
sizeof(ici),
0, // mask
hwndOwner,
(LPCSTR)wParam,
NULL,
NULL,
0,
0,
NULL
};
hr = This->m_pMenuBg->InvokeCommand(&ici);
break;
}
case DVM_GETHELPTEXT:
{
appDebugOut((DEB_TRACE, "DVM_GETHELPTEXT\n"));
hr = This->m_pMenuBg->GetCommandString(LOWORD(wParam), GCS_HELPTEXT, NULL, (LPSTR)lParam, HIWORD(wParam));
break;
}
case DVM_DEFITEMCOUNT:
//
// If DefView times out enumerating items, let it know we probably only
// have about 20 items
//
*(int *)lParam = 20;
break;
case DVM_FSNOTIFY:
{
LPCITEMIDLIST* ppidl = (LPCITEMIDLIST*)wParam;
switch (lParam)
{
case SHCNE_NETSHARE:
case SHCNE_NETUNSHARE:
// a share was added, removed, or changed. Force a view refresh.
// SHChangeNotify(SHCNE_UPDATEITEM, SHCNF_IDLIST, NULL, 0);
return S_OK;
}
break;
}
default:
hr = E_FAIL;
}
return hr;
}