4876 lines
155 KiB
C++
4876 lines
155 KiB
C++
#include "shellprv.h"
|
|
#include "caggunk.h"
|
|
#include "views.h"
|
|
#include "ids.h"
|
|
#include "shitemid.h"
|
|
#include "fstreex.h"
|
|
#include "clsobj.h"
|
|
#include "datautil.h"
|
|
#include "winnetp.h" // RESOURCE_SHAREABLE
|
|
#include "prop.h"
|
|
#include "infotip.h"
|
|
#include "basefvcb.h"
|
|
#include "netview.h"
|
|
#include "printer.h"
|
|
#include "fsdata.h"
|
|
#include "idldrop.h"
|
|
#include "enumidlist.h"
|
|
#include "util.h"
|
|
#include <webvw.h>
|
|
|
|
|
|
#define WNNC_NET_LARGEST WNNC_NET_SYMFONET
|
|
|
|
|
|
HRESULT CNetRootDropTarget_CreateInstance(HWND hwnd, LPCITEMIDLIST pidl, IDropTarget **ppdropt);
|
|
|
|
class CNetData : public CFSIDLData
|
|
{
|
|
public:
|
|
CNetData(LPCITEMIDLIST pidlFolder, UINT cidl, LPCITEMIDLIST apidl[]): CFSIDLData(pidlFolder, cidl, apidl, NULL) { };
|
|
|
|
// IDataObject methods overwrite
|
|
STDMETHODIMP GetData(FORMATETC *pFmtEtc, STGMEDIUM *pstm);
|
|
STDMETHODIMP QueryGetData(FORMATETC *pFmtEtc);
|
|
|
|
protected:
|
|
STDMETHODIMP GetHDrop(FORMATETC *pformatetcIn, STGMEDIUM *pmedium);
|
|
};
|
|
|
|
|
|
// {22BEB58B-0794-11d2-A4AA-00C04F8EEB3E}
|
|
const GUID CLSID_CNetFldr = { 0x22beb58b, 0x794, 0x11d2, 0xa4, 0xaa, 0x0, 0xc0, 0x4f, 0x8e, 0xeb, 0x3e };
|
|
|
|
// idlist.c
|
|
STDAPI_(void) StrRetFormat(STRRET *pStrRet, LPCITEMIDLIST pidlRel, LPCTSTR pszTemplate, LPCTSTR pszAppend);
|
|
|
|
// in stdenum.cpp
|
|
STDAPI_(void *) CStandardEnum_CreateInstance(REFIID riid, BOOL bInterfaces, int cElement, int cbElement, void *rgElements,
|
|
void (WINAPI * pfnCopyElement)(void *, const void *, DWORD));
|
|
|
|
// is a \\server\printer object
|
|
BOOL _IsPrintShare(LPCIDNETRESOURCE pidn)
|
|
{
|
|
return NET_GetDisplayType(pidn) == RESOURCEDISPLAYTYPE_SHARE &&
|
|
NET_GetType(pidn) == RESOURCETYPE_PRINT;
|
|
}
|
|
|
|
|
|
// column information
|
|
|
|
enum
|
|
{
|
|
ICOL_NAME = 0,
|
|
ICOL_COMMENT,
|
|
ICOL_COMPUTERNAME,
|
|
ICOL_NETWORKLOCATION
|
|
};
|
|
|
|
const COLUMN_INFO s_net_cols[] =
|
|
{
|
|
DEFINE_COL_STR_ENTRY(SCID_NAME, 30, IDS_NAME_COL),
|
|
DEFINE_COL_STR_ENTRY(SCID_Comment, 30, IDS_EXCOL_COMMENT),
|
|
DEFINE_COL_STR_ENTRY(SCID_COMPUTERNAME, 30, IDS_EXCOL_COMPUTER),
|
|
DEFINE_COL_STR_ENTRY(SCID_NETWORKLOCATION, 30, IDS_NETWORKLOCATION),
|
|
};
|
|
|
|
#define MAX_ICOL_NETFOLDER (ICOL_COMMENT+1)
|
|
#define MAX_ICOL_NETROOT (ICOL_NETWORKLOCATION+1)
|
|
|
|
STDAPI CNetwork_DFMCallBackBG(IShellFolder *psf, HWND hwnd, IDataObject *pdtobj, UINT uMsg, WPARAM wParam, LPARAM lParam);
|
|
|
|
class CNetFolderViewCB;
|
|
class CNetFolderEnum;
|
|
|
|
class CNetFolder : public CAggregatedUnknown,
|
|
public IShellFolder2,
|
|
public IPersistFolder3,
|
|
public IShellIconOverlay
|
|
{
|
|
friend CNetFolderViewCB;
|
|
friend CNetFolderEnum;
|
|
|
|
public:
|
|
// IUnknown
|
|
STDMETHODIMP QueryInterface(REFIID riid, void ** ppvObj)
|
|
{ return CAggregatedUnknown::QueryInterface(riid, ppvObj); };
|
|
STDMETHODIMP_(ULONG) AddRef(void)
|
|
{ return CAggregatedUnknown::AddRef(); };
|
|
STDMETHODIMP_(ULONG) Release(void)
|
|
{ return CAggregatedUnknown::Release(); };
|
|
|
|
// IShellFolder
|
|
STDMETHODIMP ParseDisplayName(HWND hwnd, LPBC pbc, LPOLESTR lpszDisplayName,
|
|
ULONG* pchEaten, LPITEMIDLIST* ppidl, ULONG* pdwAttributes);
|
|
STDMETHODIMP EnumObjects(HWND hwnd, DWORD grfFlags, IEnumIDList ** ppenumIDList);
|
|
STDMETHODIMP BindToObject(LPCITEMIDLIST pidl, LPBC pbc, REFIID riid, void **ppv);
|
|
STDMETHODIMP BindToStorage(LPCITEMIDLIST pidl, LPBC pbc, REFIID riid, void **ppvObj);
|
|
STDMETHODIMP CompareIDs(LPARAM lParam, LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2);
|
|
STDMETHODIMP CreateViewObject(HWND hwndOwner, REFIID riid, void **ppv);
|
|
STDMETHODIMP GetAttributesOf(UINT cidl, LPCITEMIDLIST* apidl, ULONG* rgfInOut);
|
|
STDMETHODIMP GetUIObjectOf(HWND hwndOwner, UINT cidl, LPCITEMIDLIST* apidl,
|
|
REFIID riid, UINT* prgfInOut, void **ppv);
|
|
STDMETHODIMP GetDisplayNameOf(LPCITEMIDLIST pidl, DWORD uFlags, LPSTRRET lpName);
|
|
STDMETHODIMP SetNameOf(HWND hwnd, LPCITEMIDLIST pidl, LPCOLESTR lpszName, DWORD uFlags,
|
|
LPITEMIDLIST* ppidlOut);
|
|
|
|
// IShellFolder2
|
|
STDMETHODIMP GetDefaultSearchGUID(GUID *pGuid);
|
|
STDMETHODIMP EnumSearches(IEnumExtraSearch **ppenum);
|
|
STDMETHODIMP GetDefaultColumn(DWORD dwRes, ULONG* pSort, ULONG* pDisplay);
|
|
STDMETHODIMP GetDefaultColumnState(UINT iColumn, DWORD* pbState)
|
|
{ return _GetDefaultColumnState(MAX_ICOL_NETFOLDER, iColumn, pbState); }
|
|
STDMETHODIMP GetDetailsEx(LPCITEMIDLIST pidl, const SHCOLUMNID* pscid, VARIANT* pv);
|
|
STDMETHODIMP GetDetailsOf(LPCITEMIDLIST pidl, UINT iColumn, SHELLDETAILS* pDetails)
|
|
{ return _GetDetailsOf(MAX_ICOL_NETFOLDER, pidl, iColumn, pDetails); }
|
|
STDMETHODIMP MapColumnToSCID(UINT iColumn, SHCOLUMNID* pscid)
|
|
{ return _MapColumnToSCID(MAX_ICOL_NETFOLDER, iColumn, pscid); }
|
|
|
|
// IPersist
|
|
STDMETHODIMP GetClassID(CLSID* pClassID);
|
|
// IPersistFolder
|
|
STDMETHODIMP Initialize(LPCITEMIDLIST pidl);
|
|
// IPersistFolder2
|
|
STDMETHODIMP GetCurFolder(LPITEMIDLIST* ppidl);
|
|
// IPersistFolder3
|
|
STDMETHOD(InitializeEx)(IBindCtx *pbc, LPCITEMIDLIST pidlRoot, const PERSIST_FOLDER_TARGET_INFO *ppfai);
|
|
STDMETHOD(GetFolderTargetInfo)(PERSIST_FOLDER_TARGET_INFO *ppfai);
|
|
|
|
// *** IShellIconOverlay methods***
|
|
STDMETHOD(GetOverlayIndex)(LPCITEMIDLIST pidl, int * pIndex);
|
|
STDMETHOD(GetOverlayIconIndex)(LPCITEMIDLIST pidl, int * pIconIndex);
|
|
|
|
protected:
|
|
CNetFolder(IUnknown* punkOuter);
|
|
~CNetFolder();
|
|
|
|
virtual HRESULT v_GetFileFolder(IShellFolder2 **ppsfFiles)
|
|
{ *ppsfFiles = NULL; return E_NOTIMPL; };
|
|
|
|
// used by the CAggregatedUnknown stuff
|
|
HRESULT v_InternalQueryInterface(REFIID riid, void **ppvObj);
|
|
|
|
HRESULT _OpenKeys(LPCIDNETRESOURCE pidn, HKEY ahkeys[]);
|
|
LPCTSTR _GetProvider(LPCIDNETRESOURCE pidn, IBindCtx *pbc, LPTSTR pszProvider, UINT cchProvider);
|
|
DWORD _OpenEnum(HWND hwnd, DWORD grfFlags, LPNETRESOURCE pnr, HANDLE *phEnum);
|
|
|
|
static HRESULT _CreateNetIDList(LPIDNETRESOURCE pidnIn,
|
|
LPCTSTR pszName, LPCTSTR pszProvider, LPCTSTR pszComment,
|
|
LPITEMIDLIST *ppidl);
|
|
|
|
static HRESULT _NetResToIDList(NETRESOURCE *pnr,
|
|
BOOL fKeepNullRemoteName,
|
|
BOOL fKeepProviderName,
|
|
BOOL fKeepComment,
|
|
LPITEMIDLIST *ppidl);
|
|
|
|
static HRESULT _CreateEntireNetwork(LPITEMIDLIST *ppidl);
|
|
|
|
static HRESULT _CreateEntireNetworkFullIDList(LPITEMIDLIST *ppidl);
|
|
|
|
LPTSTR _GetNameForParsing(LPCWSTR pwszName, LPTSTR pszBuffer, INT cchBuffer, LPTSTR *ppszRegItem);
|
|
HRESULT _ParseRest(LPBC pbc, LPCWSTR pszRest, LPITEMIDLIST* ppidl, DWORD* pdwAttributes);
|
|
HRESULT _AddUnknownIDList(DWORD dwDisplayType, LPITEMIDLIST *ppidl);
|
|
HRESULT _ParseSimple(LPBC pbc, LPWSTR pszName, LPITEMIDLIST* ppidl, DWORD* pdwAttributes);
|
|
HRESULT _NetResToIDLists(NETRESOURCE *pnr, DWORD dwbuf, LPITEMIDLIST *ppidl);
|
|
|
|
HRESULT _ParseNetName(HWND hwnd, LPBC pbc, LPCWSTR pwszName, ULONG* pchEaten,
|
|
LPITEMIDLIST* ppidl, DWORD* pdwAttributes);
|
|
LONG _GetFilePIDLType(LPCITEMIDLIST pidl);
|
|
BOOL _MakeStripToLikeKinds(UINT *pcidl, LPCITEMIDLIST **papidl, BOOL fNetObjects);
|
|
HRESULT _GetDefaultColumnState(UINT cColumns, UINT iColumn, DWORD* pdwState);
|
|
HRESULT _GetDetailsOf(UINT cColumns, LPCITEMIDLIST pidl, UINT iColumn, SHELLDETAILS *pDetails);
|
|
HRESULT _MapColumnToSCID(UINT cColumns, UINT iColumn, SHCOLUMNID* pscid);
|
|
|
|
LPFNDFMCALLBACK _GetCallbackType(LPCIDNETRESOURCE pidn)
|
|
{ return _IsPrintShare(pidn) ? &PrinterDFMCallBack : &DFMCallBack; };
|
|
|
|
static HRESULT CALLBACK _AttributesCallbackRoot(IShellFolder2* psf, LPCITEMIDLIST pidl, ULONG* prgfInOut);
|
|
|
|
LPITEMIDLIST _pidl;
|
|
LPITEMIDLIST _pidlTarget; // pidl of where the folder is in the namespace
|
|
LPCIDNETRESOURCE _pidnForProvider; // optional provider for this container...
|
|
LPTSTR _pszResName; // optional resource name of this container
|
|
UINT _uDisplayType; // display type of the folder
|
|
IShellFolder2* _psfFiles;
|
|
IUnknown* _punkReg;
|
|
|
|
private:
|
|
HRESULT _CreateInstance(LPCITEMIDLIST pidlAbs, LPCITEMIDLIST pidlTarget,
|
|
UINT uDisplayType,
|
|
LPCIDNETRESOURCE pidnForProvider, LPCTSTR pszResName,
|
|
REFIID riid, void **ppv);
|
|
friend HRESULT CNetwork_DFMCallBackBG(IShellFolder *psf, HWND hwnd,
|
|
IDataObject *pdtobj, UINT uMsg,
|
|
WPARAM wParam, LPARAM lParam);
|
|
static DWORD CALLBACK _PropertiesThreadProc(void *pv);
|
|
static HRESULT DFMCallBack(IShellFolder* psf, HWND hwnd,
|
|
IDataObject* pdtobj, UINT uMsg,
|
|
WPARAM wParam, LPARAM lParam);
|
|
static HRESULT PrinterDFMCallBack(IShellFolder* psf, HWND hwnd,
|
|
IDataObject* pdtobj, UINT uMsg,
|
|
WPARAM wParam, LPARAM lParam);
|
|
static HRESULT CALLBACK _AttributesCallback(IShellFolder2* psf, LPCITEMIDLIST pidl, ULONG* prgfInOut);
|
|
|
|
BOOL _GetPathForShare(LPCIDNETRESOURCE pidn, LPTSTR pszPath, int cchPath);
|
|
HRESULT _GetPathForItem(LPCIDNETRESOURCE pidn, LPTSTR pszPath, int cchPath);
|
|
HRESULT _CreateFolderForItem(LPBC pbc, LPCITEMIDLIST pidl, LPCITEMIDLIST pidlTarget, LPCIDNETRESOURCE pidnForProvider, REFIID riid, void **ppv);
|
|
HRESULT _GetFormatName(LPCIDNETRESOURCE pidn, STRRET* pStrRet);
|
|
HRESULT _GetIconOverlayInfo(LPCIDNETRESOURCE pidn, int *pIndex, DWORD dwFlags);
|
|
HKEY _OpenProviderTypeKey(LPCIDNETRESOURCE pidn);
|
|
HKEY _OpenProviderKey(LPCIDNETRESOURCE pidn);
|
|
static void WINAPI _CopyEnumElement(void* pDest, const void* pSource, DWORD dwSize);
|
|
HRESULT _GetNetResource(LPCIDNETRESOURCE pidn, NETRESOURCEW* pnr, int cb);
|
|
};
|
|
|
|
|
|
class CNetRootFolder : public CNetFolder
|
|
{
|
|
public:
|
|
// IUnknown
|
|
STDMETHODIMP QueryInterface(REFIID riid, void ** ppvObj)
|
|
{ return CNetFolder::QueryInterface(riid, ppvObj); };
|
|
STDMETHODIMP_(ULONG) AddRef(void)
|
|
{ return CNetFolder::AddRef(); };
|
|
STDMETHODIMP_(ULONG) Release(void)
|
|
{ return CNetFolder::Release(); };
|
|
|
|
// IShellFolder
|
|
STDMETHODIMP ParseDisplayName(HWND hwnd, LPBC pbc, LPOLESTR lpszDisplayName,
|
|
ULONG* pchEaten, LPITEMIDLIST* ppidl, ULONG* pdwAttributes);
|
|
STDMETHODIMP EnumObjects(HWND hwnd, DWORD grfFlags, IEnumIDList ** ppenumIDList);
|
|
STDMETHODIMP BindToObject(LPCITEMIDLIST pidl, LPBC pbc, REFIID riid, void **ppv);
|
|
STDMETHODIMP BindToStorage(LPCITEMIDLIST pidl, LPBC pbc, REFIID riid, void **ppvObj)
|
|
{ return CNetFolder::BindToStorage(pidl, pbc, riid, ppvObj); };
|
|
STDMETHODIMP CompareIDs(LPARAM lParam, LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2);
|
|
STDMETHODIMP CreateViewObject(HWND hwndOwner, REFIID riid, void **ppv);
|
|
STDMETHODIMP GetAttributesOf(UINT cidl, LPCITEMIDLIST* apidl, ULONG* rgfInOut);
|
|
STDMETHODIMP GetUIObjectOf(HWND hwndOwner, UINT cidl, LPCITEMIDLIST* apidl,
|
|
REFIID riid, UINT* prgfInOut, void **ppv);
|
|
STDMETHODIMP GetDisplayNameOf(LPCITEMIDLIST pidl, DWORD uFlags, LPSTRRET lpName);
|
|
STDMETHODIMP SetNameOf(HWND hwnd, LPCITEMIDLIST pidl, LPCOLESTR lpszName, DWORD uFlags,
|
|
LPITEMIDLIST* ppidlOut);
|
|
|
|
// IShellFolder2
|
|
STDMETHODIMP GetDefaultSearchGUID(GUID *pGuid)
|
|
{ return CNetFolder::GetDefaultSearchGUID(pGuid); };
|
|
STDMETHODIMP EnumSearches(IEnumExtraSearch **ppenum)
|
|
{ return CNetFolder::EnumSearches(ppenum); };
|
|
STDMETHODIMP GetDefaultColumn(DWORD dwRes, ULONG* pSort, ULONG* pDisplay)
|
|
{ return CNetFolder::GetDefaultColumn(dwRes, pSort, pDisplay); };
|
|
STDMETHODIMP GetDefaultColumnState(UINT iColumn, DWORD* pbState)
|
|
{ return _GetDefaultColumnState(MAX_ICOL_NETROOT, iColumn, pbState); } // +1 for <= check
|
|
STDMETHODIMP GetDetailsEx(LPCITEMIDLIST pidl, const SHCOLUMNID* pscid, VARIANT* pv)
|
|
{ return CNetFolder::GetDetailsEx(pidl, pscid, pv); };
|
|
STDMETHODIMP GetDetailsOf(LPCITEMIDLIST pidl, UINT iColumn, SHELLDETAILS* pDetails)
|
|
{ return _GetDetailsOf(MAX_ICOL_NETROOT, pidl, iColumn, pDetails); };
|
|
STDMETHODIMP MapColumnToSCID(UINT iColumn, SHCOLUMNID* pscid)
|
|
{ return _MapColumnToSCID(MAX_ICOL_NETROOT, iColumn, pscid); }
|
|
|
|
// IPersist
|
|
STDMETHODIMP GetClassID(CLSID* pClassID);
|
|
|
|
// IPersistFolder
|
|
STDMETHODIMP Initialize(LPCITEMIDLIST pidl);
|
|
|
|
// IPersistFolder2
|
|
STDMETHODIMP GetCurFolder(LPITEMIDLIST* ppidl) { return CNetFolder::GetCurFolder(ppidl); };
|
|
|
|
// IPersistFolder3
|
|
STDMETHOD(InitializeEx)(IBindCtx *pbc, LPCITEMIDLIST pidlRoot, const PERSIST_FOLDER_TARGET_INFO *ppfai)
|
|
{ return CNetFolder::InitializeEx(pbc, pidlRoot, ppfai); };
|
|
STDMETHOD(GetFolderTargetInfo)(PERSIST_FOLDER_TARGET_INFO *ppfai)
|
|
{ return CNetFolder::GetFolderTargetInfo(ppfai); };
|
|
|
|
protected:
|
|
CNetRootFolder(IUnknown* punkOuter) : CNetFolder(punkOuter) { };
|
|
~CNetRootFolder() { ASSERT(NULL != _spThis); _spThis = NULL; };
|
|
|
|
BOOL v_HandleDelete(PLONG pcRef);
|
|
HRESULT v_GetFileFolder(IShellFolder2 **ppsfFiles);
|
|
|
|
private:
|
|
HRESULT _TryParseEntireNet(HWND hwnd, LPBC pbc, WCHAR *pwszName, LPITEMIDLIST *ppidl, DWORD *pdwAttributes);
|
|
|
|
friend HRESULT CNetwork_CreateInstance(IUnknown* punkOuter, REFIID riid, void **ppv);
|
|
static CNetRootFolder* _spThis;
|
|
};
|
|
|
|
class CNetFolderViewCB : public CBaseShellFolderViewCB
|
|
{
|
|
public:
|
|
CNetFolderViewCB(CNetFolder *pFolder);
|
|
|
|
// IShellFolderViewCB
|
|
STDMETHODIMP RealMessage(UINT uMsg, WPARAM wParam, LPARAM lParam);
|
|
|
|
private:
|
|
~CNetFolderViewCB();
|
|
HRESULT OnINVOKECOMMAND(DWORD pv, UINT wP);
|
|
HRESULT OnGETHELPTEXT(DWORD pv, UINT wPl, UINT wPh, LPTSTR lP);
|
|
HRESULT OnREFRESH(DWORD pv, BOOL fPreRefresh);
|
|
HRESULT OnDELAYWINDOWCREATE(DWORD pv, HWND hwnd);
|
|
HRESULT OnGETCOLSAVESTREAM(DWORD pv, WPARAM wP, IStream **pps);
|
|
HRESULT OnDEFITEMCOUNT(DWORD pv, UINT *pnItems);
|
|
HRESULT OnGetZone(DWORD pv, DWORD * pdwZone);
|
|
HRESULT OnEnumeratedItems(DWORD pv, UINT celt, LPCITEMIDLIST *rgpidl);
|
|
HRESULT OnDefViewMode(DWORD pv, FOLDERVIEWMODE* pvm);
|
|
HRESULT OnGetDeferredViewSettings(DWORD pv, SFVM_DEFERRED_VIEW_SETTINGS* pSettings);
|
|
|
|
BOOL _EntireNetworkAvailable();
|
|
|
|
CNetFolder *_pFolder;
|
|
UINT _cItems;
|
|
|
|
// Web View implementation
|
|
HRESULT OnGetWebViewLayout(DWORD pv, UINT uViewMode, SFVM_WEBVIEW_LAYOUT_DATA* pData);
|
|
HRESULT OnGetWebViewContent(DWORD pv, SFVM_WEBVIEW_CONTENT_DATA* pData);
|
|
HRESULT OnGetWebViewTasks(DWORD pv, SFVM_WEBVIEW_TASKSECTION_DATA* pTasks);
|
|
public:
|
|
static HRESULT _CanShowHNW(IUnknown* pv, IShellItemArray *psiItemArray, BOOL fOkToBeSlow, UISTATE* puisState);
|
|
static HRESULT _CanViewComputersNearMe(IUnknown* pv, IShellItemArray *psiItemArray, BOOL fOkToBeSlow, UISTATE* puisState);
|
|
static HRESULT _CanSearchActiveDirectory(IUnknown* pv, IShellItemArray *psiItemArray, BOOL fOkToBeSlow, UISTATE* puisState);
|
|
|
|
static HRESULT _DoRunDll32(LPTSTR pszParameters); // helper to do a ShellExecute of RunDll32.
|
|
|
|
static HRESULT _OnViewNetConnections(IUnknown* pv, IShellItemArray *psiItemArray, IBindCtx *pbc);
|
|
static HRESULT _OnAddNetworkPlace(IUnknown* pv, IShellItemArray *psiItemArray, IBindCtx *pbc)
|
|
{ return _DoRunDll32(TEXT("netplwiz.dll,AddNetPlaceRunDll")); }
|
|
static HRESULT _OnHomeNetworkWizard(IUnknown* pv, IShellItemArray *psiItemArray, IBindCtx *pbc)
|
|
{ return _DoRunDll32(TEXT("hnetwiz.dll,HomeNetWizardRunDll")); }
|
|
static HRESULT _OnViewComputersNearMe(IUnknown* pv, IShellItemArray *psiItemArray, IBindCtx *pbc);
|
|
static HRESULT _OnSearchActiveDirectory(IUnknown* pv, IShellItemArray *psiItemArray, IBindCtx *pbc)
|
|
{ return _DoRunDll32(TEXT("dsquery.dll,OpenQueryWindow")); }
|
|
static HRESULT _HasPreviousVersions(IUnknown* pv, IShellItemArray *psiItemArray, BOOL fOkToBeSlow, UISTATE* puisState);
|
|
static HRESULT _OnPreviousVersions(IUnknown* pv, IShellItemArray *psiItemArray, IBindCtx *pbc);
|
|
};
|
|
|
|
#define NETFLDR_EVENTS \
|
|
SHCNE_RENAMEITEM | SHCNE_RENAMEFOLDER | \
|
|
SHCNE_CREATE | SHCNE_DELETE | SHCNE_UPDATEDIR | SHCNE_UPDATEITEM | \
|
|
SHCNE_MKDIR | SHCNE_RMDIR
|
|
|
|
CNetFolderViewCB::CNetFolderViewCB(CNetFolder *pFolder) :
|
|
CBaseShellFolderViewCB(pFolder->_pidl, NETFLDR_EVENTS), _pFolder(pFolder)
|
|
{
|
|
_pFolder->AddRef();
|
|
}
|
|
|
|
CNetFolderViewCB::~CNetFolderViewCB()
|
|
{
|
|
_pFolder->Release();
|
|
}
|
|
|
|
HRESULT CNetFolderViewCB::OnINVOKECOMMAND(DWORD pv, UINT wP)
|
|
{
|
|
return CNetwork_DFMCallBackBG(_pFolder, _hwndMain, NULL, DFM_INVOKECOMMAND, wP, 0);
|
|
}
|
|
|
|
HRESULT CNetFolderViewCB::OnGETHELPTEXT(DWORD pv, UINT wPl, UINT wPh, LPTSTR lP)
|
|
{
|
|
return CNetwork_DFMCallBackBG(_pFolder, _hwndMain, NULL, DFM_GETHELPTEXTW, MAKEWPARAM(wPl, wPh), (LPARAM)lP);
|
|
}
|
|
|
|
HRESULT CNetFolderViewCB::OnREFRESH(DWORD pv, BOOL fPreRefresh)
|
|
{
|
|
if (fPreRefresh)
|
|
{
|
|
RefreshNetCrawler();
|
|
}
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT CNetFolderViewCB::OnDELAYWINDOWCREATE(DWORD pv, HWND hwnd)
|
|
{
|
|
// only do delay window processing in the net root.
|
|
|
|
if (RESOURCEDISPLAYTYPE_GENERIC == _pFolder->_uDisplayType) // MyNetPlaces
|
|
{
|
|
RefreshNetCrawler();
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT CNetFolderViewCB::OnGETCOLSAVESTREAM(DWORD pv, WPARAM wP, IStream **pps)
|
|
{
|
|
LPCTSTR pszValName;
|
|
|
|
switch (_pFolder->_uDisplayType)
|
|
{
|
|
case RESOURCEDISPLAYTYPE_DOMAIN:
|
|
pszValName = TEXT("NetDomainColsX");
|
|
break;
|
|
|
|
case RESOURCEDISPLAYTYPE_SERVER:
|
|
pszValName = TEXT("NetServerColsX");
|
|
break;
|
|
|
|
default:
|
|
return E_FAIL;
|
|
}
|
|
|
|
*pps = OpenRegStream(HKEY_CURRENT_USER, REGSTR_PATH_EXPLORER, pszValName, (DWORD) wP);
|
|
return *pps ? S_OK : E_FAIL;
|
|
}
|
|
|
|
// HRESULT CNetFolderViewCB::OnGetZone(DWORD pv, DWORD * pdwZone);
|
|
|
|
HRESULT CNetFolderViewCB::OnEnumeratedItems(DWORD pv, UINT celt, LPCITEMIDLIST *rgpidl)
|
|
{
|
|
_cItems = celt;
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT CNetFolderViewCB::OnDefViewMode(DWORD pv, FOLDERVIEWMODE* pvm)
|
|
{
|
|
if (IsOS(OS_SERVERADMINUI))
|
|
*pvm = FVM_DETAILS; // Server Admin always gets DETAILS
|
|
else if (_cItems < DEFVIEW_FVM_MANY_CUTOFF)
|
|
*pvm = FVM_TILE;
|
|
else
|
|
*pvm = FVM_ICON; // used to pick icon only for My Net Places ((_pFolder->_uDisplayType == RESOURCEDISPLAYTYPE_GENERIC))
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT CNetFolderViewCB::OnGetDeferredViewSettings(DWORD pv, SFVM_DEFERRED_VIEW_SETTINGS* pSettings)
|
|
{
|
|
OnDefViewMode(pv, &pSettings->fvm);
|
|
|
|
// if this is the root folder then lets sort accordingly
|
|
if (_pFolder->_uDisplayType == RESOURCEDISPLAYTYPE_GENERIC)
|
|
{
|
|
pSettings->fGroupView = TRUE;
|
|
pSettings->uSortCol = ICOL_NETWORKLOCATION;
|
|
pSettings->iSortDirection = 1;
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT CNetFolderViewCB::OnGetZone(DWORD pv, DWORD * pdwZone)
|
|
{
|
|
if (pdwZone)
|
|
*pdwZone = URLZONE_INTRANET; // default is "Local Intranet"
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
HRESULT CNetFolderViewCB::OnGetWebViewLayout(DWORD pv, UINT uViewMode, SFVM_WEBVIEW_LAYOUT_DATA* pData)
|
|
{
|
|
ZeroMemory(pData, sizeof(*pData));
|
|
pData->dwLayout = SFVMWVL_NORMAL;
|
|
return S_OK;
|
|
}
|
|
|
|
// HNW is shown on X86 pro or personal workgroup only
|
|
HRESULT CNetFolderViewCB::_CanShowHNW(IUnknown* pv, IShellItemArray *psiItemArray, BOOL fOkToBeSlow, UISTATE* puisState)
|
|
{
|
|
#ifdef _WIN64
|
|
*puisState = UIS_DISABLED;
|
|
return S_OK;
|
|
#else
|
|
if (IsOS(OS_ANYSERVER))
|
|
*puisState = UIS_DISABLED; // Server-type OS
|
|
else
|
|
*puisState = !IsOS(OS_DOMAINMEMBER) ? UIS_ENABLED : UIS_DISABLED;
|
|
return S_OK;
|
|
#endif
|
|
}
|
|
|
|
HRESULT CNetFolderViewCB::_CanViewComputersNearMe(IUnknown* pv, IShellItemArray *psiItemArray, BOOL fOkToBeSlow, UISTATE* puisState)
|
|
{
|
|
if (!SHRestricted(REST_NOCOMPUTERSNEARME))
|
|
*puisState = !IsOS(OS_DOMAINMEMBER) ? UIS_ENABLED : UIS_DISABLED;
|
|
else
|
|
*puisState = UIS_DISABLED;
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT CNetFolderViewCB::_CanSearchActiveDirectory(IUnknown* pv, IShellItemArray *psiItemArray, BOOL fOkToBeSlow, UISTATE* puisState)
|
|
{
|
|
if (IsOS(OS_DOMAINMEMBER) && (GetEnvironmentVariable(TEXT("USERDNSDOMAIN"), NULL, 0) > 0))
|
|
*puisState = UIS_ENABLED;
|
|
else
|
|
*puisState = UIS_DISABLED;
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT CNetFolderViewCB::_OnViewNetConnections(IUnknown* pv, IShellItemArray *psiItemArray, IBindCtx *pbc)
|
|
{
|
|
LPITEMIDLIST pidl;
|
|
HRESULT hr = SHGetFolderLocation(NULL, CSIDL_CONNECTIONS, NULL, 0, &pidl);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = ((CNetFolderViewCB*)(void*)pv)->_BrowseObject(pidl);
|
|
ILFree(pidl);
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
HRESULT CNetFolderViewCB::_DoRunDll32(LPTSTR pszParameters)
|
|
{
|
|
SHELLEXECUTEINFO sei = {0};
|
|
sei.cbSize = sizeof(sei);
|
|
sei.lpFile = TEXT("rundll32.exe");
|
|
sei.lpParameters = pszParameters;
|
|
sei.nShow = SW_SHOWNORMAL;
|
|
|
|
return ShellExecuteEx(&sei) ? S_OK : E_FAIL;
|
|
}
|
|
|
|
HRESULT CNetFolderViewCB::_OnViewComputersNearMe(IUnknown* pv, IShellItemArray *psiItemArray, IBindCtx *pbc)
|
|
{
|
|
LPITEMIDLIST pidl;
|
|
HRESULT hr = SHGetFolderLocation(NULL, CSIDL_COMPUTERSNEARME, NULL, 0, &pidl);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = ((CNetFolderViewCB*)(void*)pv)->_BrowseObject(pidl);
|
|
ILFree(pidl);
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
HRESULT CNetFolderViewCB::_HasPreviousVersions(IUnknown* /*pv*/, IShellItemArray *psiItemArray, BOOL fOkToBeSlow, UISTATE* puisState)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
*puisState = UIS_HIDDEN;
|
|
|
|
if (NULL != psiItemArray)
|
|
{
|
|
#ifdef DEBUG
|
|
// Sanity check.
|
|
DWORD dwNumItems;
|
|
ASSERT(S_OK == psiItemArray->GetCount(&dwNumItems));
|
|
ASSERT(1 == dwNumItems);
|
|
#endif
|
|
BOOL bHavePV = FALSE;
|
|
|
|
// This returns E_PENDING if the answer is unknown
|
|
// and fOkToBeSlow is FALSE
|
|
hr = HavePreviousVersionsAt(psiItemArray, 0, fOkToBeSlow, &bHavePV);
|
|
if (S_OK == hr && bHavePV)
|
|
{
|
|
*puisState = UIS_ENABLED;
|
|
}
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
HRESULT CNetFolderViewCB::_OnPreviousVersions(IUnknown* pv, IShellItemArray *psiItemArray, IBindCtx *pbc)
|
|
{
|
|
ShowPreviousVersionsAt(psiItemArray, 0, ((CNetFolderViewCB*)(void*)pv)->_hwndMain);
|
|
return S_OK;
|
|
}
|
|
|
|
const WVTASKITEM c_MyNetPlacesTaskHeader = WVTI_HEADER(L"shell32.dll", IDS_HEADER_MYNETPLACES, IDS_HEADER_MYNETPLACES_TT);
|
|
const WVTASKITEM c_MyNetPlacesTaskList[] =
|
|
{
|
|
WVTI_ENTRY_ALL(CLSID_NULL, L"shell32.dll", IDS_TASK_ADDNETWORKPLACE, IDS_TASK_ADDNETWORKPLACE_TT, IDI_TASK_ADDNETWORKPLACE, NULL, CNetFolderViewCB::_OnAddNetworkPlace),
|
|
WVTI_ENTRY_ALL(CLSID_NULL, L"shell32.dll", IDS_TASK_VIEWNETCONNECTIONS, IDS_TASK_VIEWNETCONNECTIONS_TT, IDI_TASK_VIEWNETCONNECTIONS, NULL, CNetFolderViewCB::_OnViewNetConnections),
|
|
WVTI_ENTRY_ALL(CLSID_NULL, L"shell32.dll", IDS_TASK_HOMENETWORKWIZARD, IDS_TASK_HOMENETWORKWIZARD_TT, IDI_TASK_HOMENETWORKWIZARD, CNetFolderViewCB::_CanShowHNW, CNetFolderViewCB::_OnHomeNetworkWizard),
|
|
WVTI_ENTRY_ALL(CLSID_NULL, L"shell32.dll", IDS_TASK_COMPUTERSNEARME, IDS_TASK_COMPUTERSNEARME_TT, IDI_GROUP, CNetFolderViewCB::_CanViewComputersNearMe, CNetFolderViewCB::_OnViewComputersNearMe),
|
|
WVTI_ENTRY_ALL(CLSID_NULL, L"shell32.dll", IDS_TASK_SEARCHDS, IDS_TASK_SEARCHDS_TT, IDI_TASK_SEARCHDS, CNetFolderViewCB::_CanSearchActiveDirectory, CNetFolderViewCB::_OnSearchActiveDirectory),
|
|
WVTI_ENTRY_TITLE(UICID_PreviousVersions, L"shell32.dll", IDS_TASK_SHADOW, IDS_TASK_SHADOW, 0, IDS_TASK_SHADOW_TT, IDI_TASK_SHADOW, CNetFolderViewCB::_HasPreviousVersions, CNetFolderViewCB::_OnPreviousVersions),
|
|
};
|
|
|
|
BOOL CNetFolderViewCB::_EntireNetworkAvailable()
|
|
{
|
|
BOOL fRet = FALSE;
|
|
|
|
// Only enable if we're in a Domain
|
|
if (IsOS(OS_DOMAINMEMBER) && !SHRestricted(REST_NOENTIRENETWORK))
|
|
{
|
|
LPITEMIDLIST pidl;
|
|
if (SUCCEEDED(CNetFolder::_CreateEntireNetworkFullIDList(&pidl)))
|
|
{
|
|
// ... and we're not already in the "Entire Network" folder.
|
|
if (!ILIsEqual(_pidl, pidl))
|
|
{
|
|
fRet = TRUE;
|
|
}
|
|
ILFree(pidl);
|
|
}
|
|
}
|
|
|
|
return fRet;
|
|
}
|
|
|
|
HRESULT CNetFolderViewCB::OnGetWebViewContent(DWORD pv, SFVM_WEBVIEW_CONTENT_DATA* pData)
|
|
{
|
|
ZeroMemory(pData, sizeof(*pData));
|
|
|
|
Create_IUIElement(&c_MyNetPlacesTaskHeader, &(pData->pFolderTaskHeader));
|
|
|
|
LPCTSTR rgCsidls[] = { MAKEINTRESOURCE(CSIDL_DRIVES), MAKEINTRESOURCE(CSIDL_PERSONAL), MAKEINTRESOURCE(CSIDL_COMMON_DOCUMENTS), MAKEINTRESOURCE(CSIDL_PRINTERS) };
|
|
|
|
if (_EntireNetworkAvailable())
|
|
{
|
|
LPITEMIDLIST pidlEntireNetwork = NULL;
|
|
CNetFolder::_CreateEntireNetworkFullIDList(&pidlEntireNetwork);
|
|
CreateIEnumIDListOnCSIDLs2(_pidl, pidlEntireNetwork, rgCsidls, ARRAYSIZE(rgCsidls), &(pData->penumOtherPlaces));
|
|
ILFree(pidlEntireNetwork);
|
|
}
|
|
else
|
|
{
|
|
CreateIEnumIDListOnCSIDLs(_pidl, rgCsidls, ARRAYSIZE(rgCsidls), &(pData->penumOtherPlaces));
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT CNetFolderViewCB::OnGetWebViewTasks(DWORD pv, SFVM_WEBVIEW_TASKSECTION_DATA* pTasks)
|
|
{
|
|
ZeroMemory(pTasks, sizeof(*pTasks));
|
|
|
|
Create_IEnumUICommand((IUnknown*)(void*)this, c_MyNetPlacesTaskList, ARRAYSIZE(c_MyNetPlacesTaskList), &pTasks->penumFolderTasks);
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
STDMETHODIMP CNetFolderViewCB::RealMessage(UINT uMsg, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
switch (uMsg)
|
|
{
|
|
HANDLE_MSG(0, SFVM_INVOKECOMMAND, OnINVOKECOMMAND);
|
|
HANDLE_MSG(0, SFVM_GETHELPTEXT, OnGETHELPTEXT);
|
|
HANDLE_MSG(0, SFVM_DELAYWINDOWCREATE, OnDELAYWINDOWCREATE);
|
|
HANDLE_MSG(0, SFVM_GETCOLSAVESTREAM, OnGETCOLSAVESTREAM);
|
|
HANDLE_MSG(0, SFVM_GETZONE, OnGetZone);
|
|
HANDLE_MSG(0, SFVM_ENUMERATEDITEMS, OnEnumeratedItems);
|
|
HANDLE_MSG(0, SFVM_DEFVIEWMODE, OnDefViewMode);
|
|
HANDLE_MSG(0, SFVM_GETDEFERREDVIEWSETTINGS, OnGetDeferredViewSettings);
|
|
HANDLE_MSG(0, SFVM_REFRESH, OnREFRESH);
|
|
HANDLE_MSG(0, SFVM_GETWEBVIEWLAYOUT, OnGetWebViewLayout);
|
|
HANDLE_MSG(0, SFVM_GETWEBVIEWCONTENT, OnGetWebViewContent);
|
|
HANDLE_MSG(0, SFVM_GETWEBVIEWTASKS, OnGetWebViewTasks);
|
|
|
|
default:
|
|
return E_FAIL;
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
// Replace all the space characters in the provider name with '_'.
|
|
void ReplaceSpacesWithUnderscore(LPTSTR psz)
|
|
{
|
|
while (psz = StrChr(psz, TEXT(' ')))
|
|
{
|
|
*psz = TEXT('_');
|
|
psz++; // DBCS safe
|
|
}
|
|
}
|
|
|
|
// Define a collate order for the hood object types
|
|
#define _HOOD_COL_RON 0
|
|
#define _HOOD_COL_REMOTE 1
|
|
#define _HOOD_COL_FILE 2
|
|
#define _HOOD_COL_NET 3
|
|
|
|
const static ICONMAP c_aicmpNet[] = {
|
|
{ SHID_NET_NETWORK , II_NETWORK },
|
|
{ SHID_NET_DOMAIN , II_GROUP },
|
|
{ SHID_NET_SERVER , II_SERVER },
|
|
{ SHID_NET_SHARE , (UINT)EIRESID(IDI_SERVERSHARE) },
|
|
{ SHID_NET_DIRECTORY , II_FOLDER },
|
|
{ SHID_NET_PRINTER , II_PRINTER },
|
|
{ SHID_NET_RESTOFNET , II_WORLD },
|
|
{ SHID_NET_SHAREADMIN , II_DRIVEFIXED },
|
|
{ SHID_NET_TREE , II_TREE },
|
|
{ SHID_NET_NDSCONTAINER, (UINT)EIRESID(IDI_NDSCONTAINER) },
|
|
};
|
|
|
|
enum
|
|
{
|
|
NKID_PROVIDERTYPE = 0,
|
|
NKID_PROVIDER,
|
|
NKID_NETCLASS,
|
|
NKID_NETWORK,
|
|
NKID_DIRECTORY,
|
|
NKID_FOLDER
|
|
};
|
|
|
|
#define NKID_COUNT 6
|
|
|
|
|
|
// This is one-entry cache for remote junctions resolution
|
|
TCHAR g_szLastAttemptedJunctionName[MAX_PATH] = {0};
|
|
TCHAR g_szLastResolvedJunctionName[MAX_PATH] = {0};
|
|
|
|
REGITEMSINFO g_riiNetRoot =
|
|
{
|
|
REGSTR_PATH_EXPLORER TEXT("\\NetworkNeighborhood\\NameSpace"),
|
|
NULL,
|
|
TEXT(':'),
|
|
SHID_NET_REGITEM,
|
|
1,
|
|
SFGAO_CANLINK,
|
|
0,
|
|
NULL,
|
|
RIISA_ORIGINAL,
|
|
NULL,
|
|
0,
|
|
0,
|
|
};
|
|
|
|
CNetRootFolder* CNetRootFolder::_spThis = NULL;
|
|
|
|
HRESULT CNetFolder::_CreateInstance(LPCITEMIDLIST pidlAbs, LPCITEMIDLIST pidlTarget, UINT uDisplayType,
|
|
LPCIDNETRESOURCE pidnForProvider, LPCTSTR pszResName,
|
|
REFIID riid, void **ppv)
|
|
{
|
|
HRESULT hr = E_OUTOFMEMORY;
|
|
|
|
*ppv = NULL;
|
|
|
|
if (!ILIsEmpty(pidlAbs))
|
|
{
|
|
CNetFolder* pNetF = new CNetFolder(NULL);
|
|
if (NULL != pNetF)
|
|
{
|
|
pNetF->_uDisplayType = uDisplayType;
|
|
|
|
if (pidnForProvider)
|
|
{
|
|
//Make sure that the pidnProvider has provider information.
|
|
ASSERT(NET_FHasProvider(pidnForProvider))
|
|
|
|
//We are interested only in the provider informarion which is contained in the first entry.
|
|
//Its enough if we clone only the first item in the pidl.
|
|
pNetF->_pidnForProvider = (LPCIDNETRESOURCE)ILCloneFirst((LPCITEMIDLIST)pidnForProvider);
|
|
}
|
|
|
|
if (pszResName && *pszResName)
|
|
pNetF->_pszResName = StrDup(pszResName);
|
|
|
|
pNetF->_pidl = ILClone(pidlAbs);
|
|
pNetF->_pidlTarget = ILClone(pidlTarget);
|
|
|
|
if (pNetF->_pidl && (!pidlTarget || (pidlTarget && pNetF->_pidlTarget)))
|
|
{
|
|
if (uDisplayType == RESOURCEDISPLAYTYPE_SERVER)
|
|
{
|
|
// This is a remote computer. See if there are any remote
|
|
// computer registry items. If so, aggregate with the registry
|
|
// class.
|
|
|
|
REGITEMSINFO riiComputer =
|
|
{
|
|
REGSTR_PATH_EXPLORER TEXT("\\RemoteComputer\\NameSpace"),
|
|
NULL,
|
|
TEXT(':'),
|
|
SHID_NET_REMOTEREGITEM,
|
|
-1,
|
|
SFGAO_FOLDER | SFGAO_CANLINK,
|
|
0, // no required reg items
|
|
NULL,
|
|
RIISA_ORIGINAL,
|
|
pszResName,
|
|
0,
|
|
0,
|
|
};
|
|
|
|
CRegFolder_CreateInstance(&riiComputer,
|
|
(IUnknown*) (IShellFolder*) pNetF,
|
|
IID_PPV_ARG(IUnknown, &pNetF->_punkReg));
|
|
}
|
|
else if (uDisplayType == RESOURCEDISPLAYTYPE_ROOT)
|
|
{
|
|
//
|
|
// this is the entire net icon, so lets create an instance of the regitem folder
|
|
// so we can merge in the items from there.
|
|
//
|
|
|
|
REGITEMSINFO riiEntireNet =
|
|
{
|
|
REGSTR_PATH_EXPLORER TEXT("\\NetworkNeighborhood\\EntireNetwork\\NameSpace"),
|
|
NULL,
|
|
TEXT(':'),
|
|
SHID_NET_REGITEM,
|
|
-1,
|
|
SFGAO_CANLINK,
|
|
0, // no required reg items
|
|
NULL,
|
|
RIISA_ORIGINAL,
|
|
NULL,
|
|
0,
|
|
0,
|
|
};
|
|
|
|
CRegFolder_CreateInstance(&riiEntireNet,
|
|
(IUnknown*) (IShellFolder*) pNetF,
|
|
IID_PPV_ARG(IUnknown, &pNetF->_punkReg));
|
|
}
|
|
else
|
|
{
|
|
ASSERT(hr == E_OUTOFMEMORY);
|
|
}
|
|
hr = pNetF->QueryInterface(riid, ppv);
|
|
}
|
|
pNetF->Release();
|
|
}
|
|
else
|
|
{
|
|
ASSERT(hr == E_OUTOFMEMORY);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ASSERT(0);
|
|
hr = E_INVALIDARG;
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
HRESULT CNetwork_CreateInstance(IUnknown* punkOuter, REFIID riid, void **ppv)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
*ppv = NULL;
|
|
|
|
// Must enter critical section to avoid racing against v_HandleDelete
|
|
ENTERCRITICAL;
|
|
|
|
if (NULL != CNetRootFolder::_spThis)
|
|
{
|
|
hr = CNetRootFolder::_spThis->QueryInterface(riid, ppv);
|
|
}
|
|
else
|
|
{
|
|
CNetRootFolder* pNetRootF = new CNetRootFolder(punkOuter);
|
|
if (pNetRootF)
|
|
{
|
|
// Initialize it ourselves to ensure that the cached value
|
|
// is the correct one.
|
|
hr = pNetRootF->Initialize((LPCITEMIDLIST)&c_idlNet);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
pNetRootF->_uDisplayType = RESOURCEDISPLAYTYPE_GENERIC;
|
|
ASSERT(NULL == pNetRootF->_punkReg);
|
|
|
|
if (SHRestricted(REST_NOSETFOLDERS))
|
|
g_riiNetRoot.iReqItems = 0;
|
|
|
|
// create the regitems object, he has the NetRoot object as his outer guy.
|
|
|
|
hr = CRegFolder_CreateInstance(&g_riiNetRoot,
|
|
SAFECAST(pNetRootF, IShellFolder2*),
|
|
IID_PPV_ARG(IUnknown, &pNetRootF->_punkReg));
|
|
|
|
// NOTE: not using SHInterlockedCompareExchange() because we have the critsec
|
|
CNetRootFolder::_spThis = pNetRootF;
|
|
hr = pNetRootF->QueryInterface(riid, ppv);
|
|
}
|
|
|
|
// Release the self-reference, but keep the the _spThis pointer intact
|
|
// (it will be reset to NULL in the destructor)
|
|
pNetRootF->Release();
|
|
}
|
|
else
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
}
|
|
}
|
|
|
|
LEAVECRITICAL;
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
CNetFolder::CNetFolder(IUnknown* punkOuter) :
|
|
CAggregatedUnknown (punkOuter)
|
|
{
|
|
// Assert that we're still using a zero-init flag inside the new operator
|
|
ASSERT(NULL == _pidl);
|
|
ASSERT(NULL == _pidlTarget);
|
|
ASSERT(NULL == _pidnForProvider);
|
|
ASSERT(NULL == _pszResName);
|
|
ASSERT(0 == _uDisplayType);
|
|
ASSERT(NULL == _psfFiles);
|
|
ASSERT(NULL == _punkReg);
|
|
|
|
DllAddRef();
|
|
}
|
|
|
|
|
|
CNetFolder::~CNetFolder()
|
|
{
|
|
ILFree(_pidl);
|
|
ILFree(_pidlTarget);
|
|
ILFree((LPITEMIDLIST)_pidnForProvider);
|
|
|
|
if (NULL != _pszResName)
|
|
{
|
|
LocalFree(_pszResName);
|
|
}
|
|
|
|
if (_psfFiles)
|
|
{
|
|
_psfFiles->Release();
|
|
}
|
|
|
|
SHReleaseInnerInterface(SAFECAST(this, IShellFolder *), &_punkReg);
|
|
DllRelease();
|
|
}
|
|
|
|
CNetFolder *FolderToNetFolder(IUnknown *punk)
|
|
{
|
|
CNetFolder * pThis = NULL;
|
|
return punk && SUCCEEDED(punk->QueryInterface(CLSID_CNetFldr, (void **)&pThis)) ? pThis : NULL;
|
|
}
|
|
|
|
HRESULT CNetFolder::v_InternalQueryInterface(REFIID riid, void **ppv)
|
|
{
|
|
static const QITAB qit[] = {
|
|
QITABENT(CNetFolder, IShellFolder2), // IID_IShellFolder2
|
|
QITABENTMULTI(CNetFolder, IShellFolder, IShellFolder2), // IID_IShellFolder
|
|
QITABENT(CNetFolder, IPersistFolder3), // IID_IPersistFolder3
|
|
QITABENT(CNetFolder, IShellIconOverlay), // IID_IShellIconOverlay
|
|
QITABENTMULTI(CNetFolder, IPersistFolder2, IPersistFolder3), // IID_IPersistFolder2
|
|
QITABENTMULTI(CNetFolder, IPersistFolder, IPersistFolder3), // IID_IPersistFolder
|
|
QITABENTMULTI(CNetFolder, IPersist, IPersistFolder3), // IID_IPersist
|
|
QITABENTMULTI2(CNetFolder, IID_IPersistFreeThreadedObject, IPersist), // IID_IPersistFreeThreadedObject
|
|
{ 0 },
|
|
};
|
|
|
|
if (IsEqualIID(riid, CLSID_CNetFldr))
|
|
{
|
|
*ppv = this; // get class pointer (unrefed!)
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT hr;
|
|
if (_punkReg && RegGetsFirstShot(riid))
|
|
{
|
|
hr = _punkReg->QueryInterface(riid, ppv);
|
|
}
|
|
else
|
|
{
|
|
hr = QISearch(this, qit, riid, ppv);
|
|
if ((E_NOINTERFACE == hr) && _punkReg)
|
|
{
|
|
hr = _punkReg->QueryInterface(riid, ppv);
|
|
}
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
|
|
BOOL CNetRootFolder::v_HandleDelete(PLONG pcRef)
|
|
{
|
|
ASSERT(NULL != pcRef);
|
|
|
|
ENTERCRITICAL;
|
|
|
|
// Once inside the critical section things are slightly more stable.
|
|
// CNetwork_CreateInstance won't be able to rescue the cached reference
|
|
// (and bump the refcount from 0 to 1). And we don't have to worry
|
|
// about somebody Release()ing us down to zero a second time, since
|
|
// no new references can show up.
|
|
//
|
|
// HOWEVER! All those scary things could've happened WHILE WE WERE
|
|
// WAITING TO ENTER THE CRITICAL SECTION.
|
|
//
|
|
// While we were waiting, somebody could've called CNetwork_CreateInstance,
|
|
// which bumps the reference count back up. So don't destroy ourselves
|
|
// if our object got "rescued".
|
|
//
|
|
// What's more, while we were waiting, that somebody could've then
|
|
// Release()d us back down to zero, causing us to be called on that
|
|
// other thread, notice that the refcount is indeed zero, and destroy
|
|
// the object, all on that other thread. So if we are not the cached
|
|
// instance, then don't destroy ourselves since that other thread did
|
|
// it already.
|
|
//
|
|
// And even more, somebody might call CNetwork_CreateInstance again
|
|
// and create a brand new object, which might COINCIDENTALLY happen
|
|
// to have the same address as the old object we are trying to destroy
|
|
// here. But in that case, it's okay to destroy the new object because
|
|
// it is indeed the case that the object's reference count is zero and
|
|
// deserves to be destroyed.
|
|
|
|
if (this == _spThis && 0 == *pcRef)
|
|
{
|
|
*pcRef = 1000; // protect against cached pointers bumping us up then down
|
|
delete this;
|
|
}
|
|
LEAVECRITICAL;
|
|
// return TRUE to indicate that we've implemented this function
|
|
// (regardless of whether or not this object was actually deleted)
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
STDMETHODIMP CNetFolder::ParseDisplayName(HWND hwnd, LPBC pbc, WCHAR* pszName, ULONG* pchEaten,
|
|
LPITEMIDLIST* ppidl, DWORD* pdwAttributes)
|
|
{
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
|
|
// new for Win2K, this enables enuming the hidden admin shares
|
|
#ifndef RESOURCE_SHAREABLE
|
|
#define RESOURCE_SHAREABLE 0x00000006
|
|
#endif
|
|
|
|
//
|
|
// in:
|
|
// hwnd NULL indicates no UI.
|
|
// grfFlags IShellFolder::EnumObjects() SHCONTF_ flags
|
|
// pnr in/out params
|
|
//
|
|
//
|
|
DWORD CNetFolder::_OpenEnum(HWND hwnd, DWORD grfFlags, LPNETRESOURCE pnr, HANDLE *phEnum)
|
|
{
|
|
DWORD dwType = (grfFlags & SHCONTF_NETPRINTERSRCH) ? RESOURCETYPE_PRINT : RESOURCETYPE_ANY;
|
|
DWORD dwScope = pnr ? RESOURCE_GLOBALNET : RESOURCE_CONTEXT;
|
|
|
|
if ((_uDisplayType == RESOURCEDISPLAYTYPE_SERVER) &&
|
|
(grfFlags & SHCONTF_SHAREABLE))
|
|
{
|
|
dwScope = RESOURCE_SHAREABLE; // hidden admin shares for this server
|
|
}
|
|
|
|
DWORD err = WNetOpenEnum(dwScope, dwType, RESOURCEUSAGE_ALL, pnr, phEnum);
|
|
if ((err != WN_SUCCESS) && hwnd)
|
|
{
|
|
// If it failed because you are not authenticated yet,
|
|
// we need to let the user loggin to this network resource.
|
|
//
|
|
// REVIEW: Ask LenS to review this code.
|
|
if (err == WN_NOT_AUTHENTICATED ||
|
|
err == ERROR_LOGON_FAILURE ||
|
|
err == WN_BAD_PASSWORD ||
|
|
err == WN_ACCESS_DENIED)
|
|
{
|
|
// Retry with password dialog box.
|
|
err = WNetAddConnection3(hwnd, pnr, NULL, NULL, CONNECT_TEMPORARY | CONNECT_INTERACTIVE);
|
|
if (err == WN_SUCCESS)
|
|
{
|
|
err = WNetOpenEnum(dwScope, dwType, RESOURCEUSAGE_ALL, pnr, phEnum);
|
|
}
|
|
}
|
|
|
|
UINT idTemplate = pnr && pnr->lpRemoteName ? IDS_ENUMERR_NETTEMPLATE2 : IDS_ENUMERR_NETTEMPLATE1;
|
|
SHEnumErrorMessageBox(hwnd, idTemplate, err, pnr ? pnr->lpRemoteName : NULL, TRUE, MB_OK | MB_ICONHAND);
|
|
}
|
|
return err;
|
|
}
|
|
|
|
// find the share part of a UNC
|
|
// \\server\share
|
|
// return pointer to "share" or pointer to empty string if none
|
|
|
|
LPCTSTR PathFindShareName(LPCTSTR pszUNC)
|
|
{
|
|
LPCTSTR psz = SkipServerSlashes(pszUNC);
|
|
if (*psz)
|
|
{
|
|
psz = StrChr(psz + 1, TEXT('\\'));
|
|
if (psz)
|
|
psz++;
|
|
else
|
|
psz = TEXT("");
|
|
}
|
|
return psz;
|
|
}
|
|
|
|
// Flags for the dwRemote field
|
|
#define RMF_CONTEXT 0x00000001 // Entire network is being enumerated
|
|
#define RMF_SHOWREMOTE 0x00000002 // Return Remote Services for next enumeration
|
|
#define RMF_STOP_ENUM 0x00000004 // Stop enumeration
|
|
#define RMF_GETLINKENUM 0x00000008 // Hoodlinks enum needs to be fetched
|
|
#define RMF_SHOWLINKS 0x00000010 // Hoodlinks need to be shown
|
|
#define RMF_FAKENETROOT 0x00000020 // Don't enumerate the workgroup items
|
|
|
|
#define RMF_ENTIRENETSHOWN 0x40000000 // Entire network object shown
|
|
#define RMF_REMOTESHOWN 0x80000000 // Return Remote Services for next enumeration
|
|
|
|
|
|
class CNetFolderEnum : public CEnumIDListBase
|
|
{
|
|
public:
|
|
// IEnumIDList
|
|
STDMETHOD(Next)(ULONG celt, LPITEMIDLIST *rgelt, ULONG *pceltFetched);
|
|
|
|
private:
|
|
CNetFolderEnum(CNetFolder *pnf, DWORD grfFlags, DWORD dwRemote, HANDLE hEnum);
|
|
~CNetFolderEnum();
|
|
friend HRESULT Create_NetFolderEnum(CNetFolder* pnsf, DWORD grfFlags, DWORD dwRemote, HANDLE hEnum, IEnumIDList** ppenum);
|
|
|
|
CNetFolder *_pnsf; // CNetFolder object we're enumerating
|
|
HANDLE _hEnum;
|
|
DWORD _grfFlags;
|
|
LONG _cItems; // Count of items in buffer
|
|
LONG _iItem; // Current index of the item in the buffer
|
|
DWORD _dwRemote;
|
|
union {
|
|
NETRESOURCE _anr[0];
|
|
BYTE _szBuffer[8192];
|
|
};
|
|
IEnumIDList *_peunk; // used for enumerating file system items (links)
|
|
};
|
|
|
|
CNetFolderEnum::CNetFolderEnum(CNetFolder *pnsf, DWORD grfFlags, DWORD dwRemote, HANDLE hEnum) : CEnumIDListBase()
|
|
{
|
|
_pnsf = pnsf;
|
|
_pnsf->AddRef();
|
|
|
|
_grfFlags = grfFlags;
|
|
_dwRemote = dwRemote;
|
|
|
|
_hEnum = hEnum;
|
|
}
|
|
|
|
HRESULT Create_NetFolderEnum(CNetFolder* pnf, DWORD grfFlags, DWORD dwRemote, HANDLE hEnum, IEnumIDList** ppenum)
|
|
{
|
|
HRESULT hr;
|
|
CNetFolderEnum* p= new CNetFolderEnum(pnf, grfFlags, dwRemote, hEnum);
|
|
if (p)
|
|
{
|
|
hr = p->QueryInterface(IID_PPV_ARG(IEnumIDList, ppenum));
|
|
p->Release();
|
|
}
|
|
else
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
*ppenum = NULL;
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
CNetFolderEnum::~CNetFolderEnum()
|
|
{
|
|
_pnsf->Release(); // release the "this" ptr we have
|
|
|
|
if (_peunk)
|
|
_peunk->Release();
|
|
|
|
if (_hEnum)
|
|
WNetCloseEnum(_hEnum);
|
|
}
|
|
|
|
STDMETHODIMP CNetFolderEnum::Next(ULONG celt, LPITEMIDLIST *ppidl, ULONG *pceltFetched)
|
|
{
|
|
HRESULT hr;
|
|
|
|
*ppidl = NULL;
|
|
if (pceltFetched)
|
|
*pceltFetched = 0;
|
|
|
|
// Time to stop enumeration?
|
|
if (_dwRemote & RMF_STOP_ENUM)
|
|
return S_FALSE; // Yes
|
|
|
|
// should we try and get the links enumerator?
|
|
if (_dwRemote & RMF_GETLINKENUM)
|
|
{
|
|
IShellFolder2* psfNetHood;
|
|
if (SUCCEEDED(_pnsf->v_GetFileFolder(&psfNetHood)))
|
|
psfNetHood->EnumObjects(NULL, _grfFlags, &_peunk);
|
|
|
|
if (_peunk)
|
|
_dwRemote |= RMF_SHOWLINKS;
|
|
|
|
_dwRemote &= ~RMF_GETLINKENUM;
|
|
}
|
|
|
|
// should we be showing the links?
|
|
if (_dwRemote & RMF_SHOWLINKS)
|
|
{
|
|
if (_peunk)
|
|
{
|
|
ULONG celtFetched;
|
|
LPITEMIDLIST pidl;
|
|
|
|
hr = _peunk->Next(1, &pidl, &celtFetched);
|
|
if (hr == S_OK && celtFetched == 1)
|
|
{
|
|
*ppidl = pidl;
|
|
if (pceltFetched)
|
|
*pceltFetched = celtFetched;
|
|
return S_OK; // Added link
|
|
}
|
|
}
|
|
|
|
_dwRemote &= ~RMF_SHOWLINKS; // Done enumerating links
|
|
}
|
|
|
|
hr = S_OK;
|
|
|
|
// Do we add the remote folder?
|
|
// (Note: as a hack to ensure that the remote folder is added
|
|
// to the 'hood despite what MPR says, RMF_SHOWREMOTE can be
|
|
// set without RMF_CONTEXT set.)
|
|
if ((_dwRemote & RMF_SHOWREMOTE) && !(_dwRemote & RMF_REMOTESHOWN))
|
|
{
|
|
// Yes
|
|
// Only try to put the remote entry in once.
|
|
_dwRemote |= RMF_REMOTESHOWN;
|
|
|
|
// Is this not the Context container?
|
|
// (See note above as to why we are asking this question.)
|
|
if (!(_dwRemote & RMF_CONTEXT))
|
|
{
|
|
// Yes; stop after the next time
|
|
_dwRemote |= RMF_STOP_ENUM;
|
|
}
|
|
|
|
// We have fallen thru because the remote services is not
|
|
// installed.
|
|
|
|
// Is this not the Context container AND the remote folder
|
|
// is not installed?
|
|
if (!(_dwRemote & RMF_CONTEXT))
|
|
{
|
|
// Yes; nothing else to enumerate
|
|
return S_FALSE;
|
|
}
|
|
}
|
|
|
|
if (_dwRemote & RMF_FAKENETROOT)
|
|
{
|
|
if ((!(_dwRemote & RMF_ENTIRENETSHOWN)) &&
|
|
(S_FALSE != SHShouldShowWizards(_punkSite)))
|
|
{
|
|
_pnsf->_CreateEntireNetwork(ppidl); // fake entire net
|
|
_dwRemote |= RMF_ENTIRENETSHOWN;
|
|
}
|
|
else
|
|
{
|
|
return S_FALSE; // no more to enumerate
|
|
}
|
|
}
|
|
else
|
|
{
|
|
while (TRUE)
|
|
{
|
|
ULONG err = WN_SUCCESS;
|
|
LPNETRESOURCE pnr;
|
|
|
|
if (_iItem >= _cItems)
|
|
{
|
|
DWORD dwSize = sizeof(_szBuffer);
|
|
|
|
_cItems = -1; // its signed
|
|
_iItem = 0;
|
|
|
|
err = WNetEnumResource(_hEnum, (DWORD*)&_cItems, _szBuffer, &dwSize);
|
|
DebugMsg(DM_TRACE, TEXT("Net EnumCallback: err=%d Count=%d"), err, _cItems);
|
|
}
|
|
|
|
pnr = &_anr[_iItem++];
|
|
|
|
// Note: the <= below is correct as we already incremented the index...
|
|
if (err == WN_SUCCESS && (_iItem <= _cItems))
|
|
{
|
|
// decide if the thing is a folder or not
|
|
ULONG grfFlagsItem = ((pnr->dwUsage & RESOURCEUSAGE_CONTAINER) ||
|
|
(pnr->dwType == RESOURCETYPE_DISK) ||
|
|
(pnr->dwType == RESOURCETYPE_ANY)) ?
|
|
SHCONTF_FOLDERS : SHCONTF_NONFOLDERS;
|
|
|
|
// If this is the context enumeration, we want to insert the
|
|
// Remote Services after the first container.
|
|
//
|
|
// Remember that we need to return the Remote Services in the next iteration.
|
|
|
|
if ((pnr->dwUsage & RESOURCEUSAGE_CONTAINER) &&
|
|
(_dwRemote & RMF_CONTEXT))
|
|
{
|
|
_dwRemote |= RMF_SHOWREMOTE;
|
|
}
|
|
|
|
if ((_pnsf->_uDisplayType == RESOURCEDISPLAYTYPE_SERVER) &&
|
|
(_grfFlags & SHCONTF_SHAREABLE))
|
|
{
|
|
// filter out ADMIN$ and IPC$, based on str len
|
|
if (lstrlen(PathFindShareName(pnr->lpRemoteName)) > 2)
|
|
{
|
|
grfFlagsItem = 0;
|
|
}
|
|
}
|
|
|
|
// if this is a network object, work out if we should hide or note, so
|
|
// convert the provider to its type number and open the key under:
|
|
//
|
|
// HKEY_CLASSES_ROOT\Network\Type\<type string>
|
|
|
|
if ((pnr->dwDisplayType == RESOURCEDISPLAYTYPE_NETWORK) &&
|
|
!(_grfFlags & SHCONTF_INCLUDEHIDDEN))
|
|
{
|
|
DWORD dwType;
|
|
if (WNetGetProviderType(pnr->lpProvider, &dwType) == WN_SUCCESS)
|
|
{
|
|
TCHAR szRegValue[MAX_PATH];
|
|
wnsprintf(szRegValue, ARRAYSIZE(szRegValue), TEXT("Network\\Type\\%d"), HIWORD(dwType));
|
|
|
|
BOOL fHide = FALSE;
|
|
DWORD cb = sizeof(fHide);
|
|
if ((ERROR_SUCCESS == SHGetValue(HKEY_CLASSES_ROOT, szRegValue, TEXT("HideProvider"), NULL, &fHide, &cb)) && fHide)
|
|
{
|
|
grfFlagsItem = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Check if we found requested type of net resource.
|
|
if (_grfFlags & grfFlagsItem)
|
|
{
|
|
if (SUCCEEDED(_pnsf->_NetResToIDList(pnr, FALSE, TRUE, (_grfFlags & SHCONTF_NONFOLDERS), ppidl)))
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else if (err == WN_NO_MORE_ENTRIES)
|
|
{
|
|
hr = S_FALSE; // no more element
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
DebugMsg(DM_ERROR, TEXT("sh ER - WNetEnumResource failed (%lx)"), err);
|
|
hr = E_FAIL;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (pceltFetched)
|
|
*pceltFetched = (S_OK == hr) ? 1 : 0;
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
STDMETHODIMP CNetFolder::EnumObjects(HWND hwnd, DWORD grfFlags, IEnumIDList** ppenum)
|
|
{
|
|
NETRESOURCE nr = {0};
|
|
TCHAR szProvider[MAX_PATH];
|
|
|
|
nr.lpProvider = (LPTSTR) _GetProvider(NULL, NULL, szProvider, ARRAYSIZE(szProvider));
|
|
|
|
if (_uDisplayType != RESOURCEDISPLAYTYPE_ROOT &&
|
|
_uDisplayType != RESOURCEDISPLAYTYPE_NETWORK)
|
|
{
|
|
nr.lpRemoteName = _pszResName;
|
|
}
|
|
|
|
HRESULT hr;
|
|
HANDLE hEnum;
|
|
DWORD err = _OpenEnum(hwnd, grfFlags, &nr, &hEnum);
|
|
if (err == WN_SUCCESS)
|
|
{
|
|
hr = Create_NetFolderEnum(this, grfFlags, 0, hEnum, ppenum);
|
|
|
|
if (FAILED(hr))
|
|
{
|
|
WNetCloseEnum(hEnum);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
hr = HRESULT_FROM_WIN32(err);
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
LPCIDNETRESOURCE NET_IsValidID(LPCITEMIDLIST pidl)
|
|
{
|
|
if (pidl && !ILIsEmpty(pidl) && ((pidl->mkid.abID[0] & SHID_GROUPMASK) == SHID_NET))
|
|
return (LPCIDNETRESOURCE)pidl;
|
|
return NULL;
|
|
}
|
|
|
|
STDMETHODIMP CNetFolder::BindToObject(LPCITEMIDLIST pidl, LPBC pbc, REFIID riid, void **ppv)
|
|
{
|
|
HRESULT hr;
|
|
LPCIDNETRESOURCE pidn;
|
|
|
|
*ppv = NULL;
|
|
|
|
pidn = NET_IsValidID(pidl);
|
|
if (pidn)
|
|
{
|
|
IShellFolder *psfJunction;
|
|
LPITEMIDLIST pidlInit = NULL;
|
|
LPITEMIDLIST pidlTarget = NULL;
|
|
LPCITEMIDLIST pidlRight = _ILNext(pidl);
|
|
BOOL fRightIsEmpty = ILIsEmpty(pidlRight);
|
|
LPCIDNETRESOURCE pidnProvider = NET_FHasProvider(pidn) ? pidn :_pidnForProvider;
|
|
|
|
hr = S_OK;
|
|
|
|
// lets get the IDLISTs we are going to use to initialize the shell folder
|
|
// if we are doing a single level bind then then ILCombine otherwise
|
|
// be more careful.
|
|
|
|
pidlInit = ILCombineParentAndFirst(_pidl, pidl, pidlRight);
|
|
if (_pidlTarget)
|
|
pidlTarget = ILCombineParentAndFirst(_pidlTarget, pidl, pidlRight);
|
|
|
|
if (!pidlInit || (!pidlTarget && _pidlTarget))
|
|
hr = E_OUTOFMEMORY;
|
|
|
|
// now create the folder object we are using, and either return that
|
|
// object to the caller, or continue the binding down.
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = _CreateFolderForItem(pbc, pidlInit, pidlTarget, pidnProvider,
|
|
fRightIsEmpty ? riid : IID_IShellFolder,
|
|
fRightIsEmpty ? ppv : (void **)&psfJunction);
|
|
|
|
if (!fRightIsEmpty && SUCCEEDED(hr))
|
|
{
|
|
hr = psfJunction->BindToObject(pidlRight, pbc, riid, ppv);
|
|
psfJunction->Release();
|
|
}
|
|
}
|
|
|
|
ILFree(pidlInit);
|
|
ILFree(pidlTarget);
|
|
}
|
|
else
|
|
{
|
|
hr = E_INVALIDARG;
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
STDMETHODIMP CNetFolder::BindToStorage(LPCITEMIDLIST pidl, LPBC pbc, REFIID riid, void **ppv)
|
|
{
|
|
return BindToObject(pidl, pbc, riid, ppv);
|
|
}
|
|
|
|
STDMETHODIMP CNetFolder::CompareIDs(LPARAM iCol, LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2)
|
|
{
|
|
HRESULT hr = E_INVALIDARG;
|
|
LPCIDNETRESOURCE pidn1 = NET_IsValidID(pidl1);
|
|
LPCIDNETRESOURCE pidn2 = NET_IsValidID(pidl2);
|
|
|
|
if (pidn1 && pidn2)
|
|
{
|
|
TCHAR szBuff1[MAX_PATH], szBuff2[MAX_PATH];
|
|
|
|
switch (iCol & SHCIDS_COLUMNMASK)
|
|
{
|
|
case ICOL_COMMENT:
|
|
{
|
|
hr = ResultFromShort(StrCmpLogicalRestricted(NET_CopyComment(pidn1, szBuff1, ARRAYSIZE(szBuff1)),
|
|
NET_CopyComment(pidn2, szBuff2, ARRAYSIZE(szBuff2))));
|
|
|
|
if (hr != 0)
|
|
return hr;
|
|
|
|
// drop down into the name comparison
|
|
}
|
|
|
|
case ICOL_NAME:
|
|
{
|
|
// Compare by name. This is the one case where we need to handle
|
|
// simple ids in either place. We will try to resync the items
|
|
// if we find a case of this before do the compares.
|
|
// Check for relative IDs. In particular if one item is at
|
|
// a server and the other is at RestOfNet then try to resync
|
|
// the two
|
|
//
|
|
|
|
if (NET_IsFake(pidn1) || NET_IsFake(pidn2))
|
|
{
|
|
// if either pidn1 or pidn2 is fake then we assume they are identical,
|
|
// this allows us to compare a simple net ID to a real net ID. we
|
|
// assume that if this fails later then the world will be happy
|
|
|
|
hr = 0;
|
|
}
|
|
else
|
|
{
|
|
// otherwise lets look at the names and provider strings accordingly
|
|
|
|
NET_CopyResName(pidn1, szBuff1, ARRAYSIZE(szBuff1));
|
|
NET_CopyResName(pidn2, szBuff2, ARRAYSIZE(szBuff2));
|
|
hr = ResultFromShort(StrCmpLogicalRestricted(szBuff1, szBuff2));
|
|
|
|
// If they're still identical, compare provider names.
|
|
|
|
if ((hr == 0) && (iCol & SHCIDS_ALLFIELDS))
|
|
{
|
|
LPCTSTR pszProv1 = _GetProvider(pidn1, NULL, szBuff1, ARRAYSIZE(szBuff1));
|
|
LPCTSTR pszProv2 = _GetProvider(pidn2, NULL, szBuff2, ARRAYSIZE(szBuff2));
|
|
|
|
if (pszProv1 && pszProv2)
|
|
hr = ResultFromShort(lstrcmp(pszProv1, pszProv2));
|
|
else
|
|
{
|
|
if (pszProv1 || pszProv2)
|
|
hr = ResultFromShort(pszProv1 ? 1 : -1);
|
|
else
|
|
hr = ResultFromShort(0);
|
|
}
|
|
}
|
|
}
|
|
|
|
// If they identical, compare the rest of IDs.
|
|
|
|
if (hr == 0)
|
|
hr = ILCompareRelIDs((IShellFolder*)this, (LPCITEMIDLIST)pidn1, (LPCITEMIDLIST)pidn2, iCol);
|
|
}
|
|
}
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
STDMETHODIMP CNetFolder::CreateViewObject(HWND hwnd, REFIID riid, void **ppv)
|
|
{
|
|
HRESULT hr;
|
|
|
|
*ppv = NULL;
|
|
|
|
if (IsEqualIID(riid, IID_IShellView))
|
|
{
|
|
SFV_CREATE sSFV;
|
|
|
|
sSFV.cbSize = sizeof(sSFV);
|
|
sSFV.psvOuter = NULL;
|
|
sSFV.psfvcb = new CNetFolderViewCB(this); // failure is OK, we just get generic support
|
|
|
|
QueryInterface(IID_PPV_ARG(IShellFolder, &sSFV.pshf)); // in case we are agregated
|
|
|
|
hr = SHCreateShellFolderView(&sSFV, (IShellView**) ppv);
|
|
|
|
if (sSFV.pshf)
|
|
sSFV.pshf->Release();
|
|
|
|
if (sSFV.psfvcb)
|
|
sSFV.psfvcb->Release();
|
|
}
|
|
else if (IsEqualIID(riid, IID_IContextMenu))
|
|
{
|
|
IShellFolder* psfOuter;
|
|
hr = QueryInterface(IID_PPV_ARG(IShellFolder, &psfOuter));
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = CDefFolderMenu_Create(_pidl, hwnd, 0, NULL, psfOuter,
|
|
CNetwork_DFMCallBackBG, NULL, NULL, (IContextMenu**) ppv);
|
|
psfOuter->Release();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
hr = E_NOINTERFACE;
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
typedef HRESULT (CALLBACK *PFNGAOCALLBACK)(IShellFolder2 *psf, LPCITEMIDLIST pidl, ULONG* prgfInOut);
|
|
|
|
STDAPI GetAttributesCallback(IShellFolder2 *psf, UINT cidl, LPCITEMIDLIST* apidl, ULONG *prgfInOut, PFNGAOCALLBACK pfnGAOCallback)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
ULONG rgfOut = 0;
|
|
|
|
for (UINT i = 0; i < cidl; i++)
|
|
{
|
|
ULONG rgfT = *prgfInOut;
|
|
hr = pfnGAOCallback(psf, apidl[i], &rgfT);
|
|
if (FAILED(hr))
|
|
{
|
|
rgfOut = 0;
|
|
break;
|
|
}
|
|
rgfOut |= rgfT;
|
|
}
|
|
|
|
*prgfInOut &= rgfOut;
|
|
return hr;
|
|
}
|
|
|
|
STDMETHODIMP CNetFolder::GetAttributesOf(UINT cidl, LPCITEMIDLIST* apidl, ULONG* prgfInOut)
|
|
{
|
|
HRESULT hr;
|
|
if (IsSelf(cidl, apidl))
|
|
{
|
|
*prgfInOut &= (SFGAO_CANLINK | SFGAO_HASPROPSHEET | SFGAO_HASSUBFOLDER |
|
|
SFGAO_FOLDER | SFGAO_FILESYSANCESTOR);
|
|
hr = S_OK;
|
|
}
|
|
else
|
|
{
|
|
hr = GetAttributesCallback(SAFECAST(this, IShellFolder2*), cidl, apidl, prgfInOut, _AttributesCallback);
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
STDMETHODIMP CNetFolder::GetDisplayNameOf(LPCITEMIDLIST pidl, DWORD dwFlags, STRRET* pStrRet)
|
|
{
|
|
HRESULT hr;
|
|
LPCIDNETRESOURCE pidn = NET_IsValidID(pidl);
|
|
if (pidn)
|
|
{
|
|
TCHAR szPath[MAX_PATH];
|
|
LPCITEMIDLIST pidlNext = _ILNext(pidl);
|
|
|
|
if (dwFlags & SHGDN_FORPARSING)
|
|
{
|
|
if ((dwFlags & SHGDN_INFOLDER) ||
|
|
((dwFlags & SHGDN_FORADDRESSBAR) && (NET_GetDisplayType(pidn) == RESOURCEDISPLAYTYPE_ROOT))) // the non-infolder name for the root is not good for the address bar
|
|
{
|
|
NET_CopyResName(pidn, szPath, ARRAYSIZE(szPath));
|
|
if (ILIsEmpty(pidlNext))
|
|
{
|
|
// we just need the last part of the display name (IN FOLDER)
|
|
LPTSTR pszT = StrRChr(szPath, NULL, TEXT('\\'));
|
|
|
|
if (!pszT)
|
|
pszT = szPath;
|
|
else
|
|
pszT++; // move past '\'
|
|
hr = StringToStrRet(pszT, pStrRet);
|
|
}
|
|
else
|
|
{
|
|
hr = ILGetRelDisplayName((IShellFolder*) this, pStrRet, pidl, szPath, MAKEINTRESOURCE(IDS_DSPTEMPLATE_WITH_BACKSLASH), dwFlags);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
LPCITEMIDLIST pidlRight = _ILNext(pidl);
|
|
|
|
if (ILIsEmpty(pidlRight))
|
|
{
|
|
hr = _GetPathForItem(pidn, szPath, ARRAYSIZE(szPath));
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = StringToStrRet(szPath, pStrRet);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
IShellFolder *psfJunction;
|
|
//Get the pidn which has network provider information.
|
|
LPCIDNETRESOURCE pidnProvider = NET_FHasProvider(pidn) ? pidn :_pidnForProvider;
|
|
LPITEMIDLIST pidlInit, pidlTarget = NULL;
|
|
|
|
pidlInit = ILCombineParentAndFirst(_pidl, pidl, pidlRight);
|
|
if (_pidlTarget)
|
|
pidlTarget = ILCombineParentAndFirst(_pidlTarget, pidl, pidlRight);
|
|
|
|
if (!pidlInit || (_pidlTarget && !pidlTarget))
|
|
return E_OUTOFMEMORY;
|
|
|
|
hr = _CreateFolderForItem(NULL, pidlInit, pidlTarget, pidnProvider, IID_PPV_ARG(IShellFolder, &psfJunction));
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = psfJunction->GetDisplayNameOf(pidlRight, dwFlags, pStrRet);
|
|
psfJunction->Release();
|
|
}
|
|
|
|
ILFree(pidlInit);
|
|
ILFree(pidlTarget);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
hr = _GetFormatName(pidn, pStrRet);
|
|
if (SUCCEEDED(hr) && !(dwFlags & SHGDN_INFOLDER) && (NET_GetFlags(pidn) & SHID_JUNCTION))
|
|
{
|
|
TCHAR szServer[MAX_PATH];
|
|
hr = SHGetNameAndFlags(_pidlTarget ? _pidlTarget:_pidl, SHGDN_FORPARSING, szServer, ARRAYSIZE(szServer), NULL);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
TCHAR szDisplay[MAX_PATH];
|
|
hr = SHGetComputerDisplayName(szServer, 0x0, szDisplay, ARRAYSIZE(szDisplay));
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
StrRetFormat(pStrRet, pidl, MAKEINTRESOURCE(IDS_DSPTEMPLATE_WITH_ON), szDisplay);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
hr = E_INVALIDARG;
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
|
|
STDMETHODIMP CNetFolder::SetNameOf(HWND hwnd, LPCITEMIDLIST pidl, LPCOLESTR lpszName, DWORD dwRes, LPITEMIDLIST* ppidl)
|
|
{
|
|
if (ppidl)
|
|
{
|
|
*ppidl = NULL;
|
|
}
|
|
return E_NOTIMPL; // not supported
|
|
}
|
|
|
|
STDMETHODIMP CNetFolder::GetUIObjectOf(HWND hwnd, UINT cidl, LPCITEMIDLIST* apidl,
|
|
REFIID riid, UINT* prgfInOut, void **ppv)
|
|
{
|
|
HRESULT hr = E_INVALIDARG;
|
|
LPCIDNETRESOURCE pidn = cidl ? NET_IsValidID(apidl[0]) : NULL;
|
|
|
|
*ppv = NULL;
|
|
|
|
if ((IsEqualIID(riid, IID_IExtractIconA) || IsEqualIID(riid, IID_IExtractIconW)) && pidn)
|
|
{
|
|
UINT iIndex;
|
|
|
|
if (_IsPrintShare(pidn))
|
|
iIndex = (UINT)EIRESID(IDI_PRINTER_NET);
|
|
else if (NET_IsRemoteFld(pidn))
|
|
iIndex = II_RNA;
|
|
else
|
|
iIndex = SILGetIconIndex(apidl[0], c_aicmpNet, ARRAYSIZE(c_aicmpNet));
|
|
|
|
hr = SHCreateDefExtIcon(NULL, iIndex, iIndex, GIL_PERCLASS, II_FOLDER, riid, ppv);
|
|
}
|
|
else if (IsEqualIID(riid, IID_IContextMenu) && pidn)
|
|
{
|
|
HKEY ahkeys[NKID_COUNT];
|
|
|
|
hr = _OpenKeys(pidn, ahkeys);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
IShellFolder* psfOuter;
|
|
hr = QueryInterface(IID_PPV_ARG(IShellFolder, &psfOuter));
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = CDefFolderMenu_Create2(_pidl, hwnd, cidl, apidl,
|
|
psfOuter, _GetCallbackType(pidn),
|
|
ARRAYSIZE(ahkeys), ahkeys, (IContextMenu**) ppv);
|
|
psfOuter->Release();
|
|
}
|
|
|
|
SHRegCloseKeys(ahkeys, ARRAYSIZE(ahkeys));
|
|
}
|
|
}
|
|
else if (cidl && IsEqualIID(riid, IID_IDataObject))
|
|
{
|
|
// Point & Print printer installation assumes that the
|
|
// netresources from CNetData_GetData and the
|
|
// pidls from CIDLData_GetData are in the same order.
|
|
// Keep it this way.
|
|
|
|
CNetData *pnd = new CNetData(_pidl, cidl, apidl);
|
|
if (pnd)
|
|
{
|
|
hr = pnd->QueryInterface(riid, ppv);
|
|
pnd->Release();
|
|
}
|
|
else
|
|
hr = E_OUTOFMEMORY;
|
|
}
|
|
else if (pidn && IsEqualIID(riid, IID_IDropTarget))
|
|
{
|
|
// special support because this is an item (not a folder)
|
|
if (_IsPrintShare(pidn))
|
|
{
|
|
LPITEMIDLIST pidl;
|
|
hr = SHILCombine(_pidl, apidl[0], &pidl);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = CPrinterDropTarget_CreateInstance(hwnd, pidl, (IDropTarget**)ppv);
|
|
ILFree(pidl);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
IShellFolder *psf;
|
|
|
|
hr = BindToObject(apidl[0], NULL, IID_PPV_ARG(IShellFolder, &psf));
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = psf->CreateViewObject(hwnd, riid, ppv);
|
|
psf->Release();
|
|
}
|
|
}
|
|
}
|
|
else if (pidn && IsEqualIID(riid, IID_IQueryInfo))
|
|
{
|
|
if (NET_GetDisplayType(pidn) == RESOURCEDISPLAYTYPE_ROOT)
|
|
{
|
|
hr = CreateInfoTipFromText(MAKEINTRESOURCE(IDS_RESTOFNETTIP), riid, ppv);
|
|
}
|
|
else
|
|
{
|
|
// Someday maybe have infotips for other things too
|
|
}
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
STDMETHODIMP CNetFolder::GetDefaultSearchGUID(LPGUID pguid)
|
|
{
|
|
*pguid = SRCID_SFindComputer;
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
void WINAPI CNetFolder::_CopyEnumElement(void* pDest, const void* pSource, DWORD dwSize)
|
|
{
|
|
if (pDest && pSource)
|
|
memcpy(pDest, pSource, dwSize);
|
|
}
|
|
|
|
STDMETHODIMP CNetFolder::EnumSearches(IEnumExtraSearch** ppenum)
|
|
{
|
|
HRESULT hr = E_NOTIMPL;
|
|
|
|
*ppenum = NULL;
|
|
|
|
// if the restriction is set then this item should be enumerated from the registry
|
|
// so we fail, else enumerate it
|
|
// only enumerate if we actually have a network to search against
|
|
if (!SHRestricted(REST_HASFINDCOMPUTERS) &&
|
|
(GetSystemMetrics(SM_NETWORK) & RNC_NETWORKS))
|
|
{
|
|
EXTRASEARCH *pxs = (EXTRASEARCH *)LocalAlloc(LPTR, sizeof(EXTRASEARCH));
|
|
if (pxs)
|
|
{
|
|
pxs->guidSearch = SRCID_SFindComputer;
|
|
if (LoadStringW(g_hinst, IDS_FC_NAME, pxs->wszFriendlyName, ARRAYSIZE(pxs->wszFriendlyName)))
|
|
{
|
|
*ppenum = (IEnumExtraSearch*)CStandardEnum_CreateInstance(IID_IEnumExtraSearch, FALSE,
|
|
1, sizeof(EXTRASEARCH), pxs, _CopyEnumElement);
|
|
if (*ppenum == NULL)
|
|
{
|
|
LocalFree(pxs);
|
|
hr = E_OUTOFMEMORY;
|
|
}
|
|
else
|
|
{
|
|
hr = S_OK;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
}
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
STDMETHODIMP CNetFolder::GetDefaultColumn(DWORD dwRes, ULONG* pSort, ULONG* pDisplay)
|
|
{
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
HRESULT CNetFolder::_GetDefaultColumnState(UINT cColumns, UINT iColumn, DWORD* pdwState)
|
|
{
|
|
*pdwState = 0;
|
|
|
|
HRESULT hr = S_OK;
|
|
if (iColumn < cColumns)
|
|
{
|
|
*pdwState = s_net_cols[iColumn].csFlags;
|
|
if (iColumn >= 1)
|
|
{
|
|
*pdwState |= SHCOLSTATE_SLOW; // comment is slow for net root
|
|
}
|
|
}
|
|
else
|
|
{
|
|
hr = E_INVALIDARG;
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
STDMETHODIMP CNetFolder::GetDetailsEx(LPCITEMIDLIST pidl, const SHCOLUMNID* pscid, VARIANT* pv)
|
|
{
|
|
HRESULT hr = E_NOTIMPL;
|
|
LPCIDNETRESOURCE pidn = NET_IsValidID(pidl);
|
|
if (pidn)
|
|
{
|
|
if (IsEqualSCID(*pscid, SCID_NETRESOURCE))
|
|
{
|
|
// Office calls SHGetDataFromIDList() with a large buffer to hold all
|
|
// of the strings in the NETRESOURCE structure, so we need to make sure
|
|
// that our variant can hold enough data to pass back to it:
|
|
BYTE rgBuffer[sizeof(NETRESOURCEW) + (4 * MAX_PATH * sizeof(WCHAR))];
|
|
hr = _GetNetResource(pidn, (NETRESOURCEW*) rgBuffer, sizeof(rgBuffer));
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = InitVariantFromBuffer(pv, rgBuffer, sizeof(rgBuffer));
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
// Fixup pointers in structure to point within the variant
|
|
// instead of our stack variable (rgBuffer):
|
|
ASSERT(pv->vt == (VT_ARRAY | VT_UI1));
|
|
NETRESOURCEW* pnrw = (NETRESOURCEW*) pv->parray->pvData;
|
|
if (pnrw->lpLocalName)
|
|
{
|
|
pnrw->lpLocalName = (LPWSTR) ((BYTE*) pnrw +
|
|
((BYTE*) pnrw->lpLocalName - rgBuffer));
|
|
}
|
|
if (pnrw->lpRemoteName)
|
|
{
|
|
pnrw->lpRemoteName = (LPWSTR) ((BYTE*) pnrw +
|
|
((BYTE*) pnrw->lpRemoteName - rgBuffer));
|
|
}
|
|
if (pnrw->lpComment)
|
|
{
|
|
pnrw->lpComment = (LPWSTR) ((BYTE*) pnrw +
|
|
((BYTE*) pnrw->lpComment - rgBuffer));
|
|
}
|
|
if (pnrw->lpProvider)
|
|
{
|
|
pnrw->lpProvider = (LPWSTR) ((BYTE*) pnrw +
|
|
((BYTE*) pnrw->lpProvider - rgBuffer));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else if (IsEqualSCID(*pscid, SCID_DESCRIPTIONID))
|
|
{
|
|
SHDESCRIPTIONID did;
|
|
|
|
switch(SIL_GetType(pidl) & SHID_TYPEMASK)
|
|
{
|
|
case SHID_NET_DOMAIN:
|
|
did.dwDescriptionId = SHDID_NET_DOMAIN;
|
|
break;
|
|
|
|
case SHID_NET_SERVER:
|
|
did.dwDescriptionId = SHDID_NET_SERVER;
|
|
break;
|
|
|
|
case SHID_NET_SHARE:
|
|
did.dwDescriptionId = SHDID_NET_SHARE;
|
|
break;
|
|
|
|
case SHID_NET_RESTOFNET:
|
|
did.dwDescriptionId = SHDID_NET_RESTOFNET;
|
|
break;
|
|
|
|
default:
|
|
did.dwDescriptionId = SHDID_NET_OTHER;
|
|
break;
|
|
}
|
|
|
|
did.clsid = CLSID_NULL;
|
|
hr = InitVariantFromBuffer(pv, &did, sizeof(did));
|
|
}
|
|
else if (IsEqualSCID(*pscid, SCID_Comment))
|
|
{
|
|
TCHAR szTemp[MAX_PATH];
|
|
hr = InitVariantFromStr(pv, NET_CopyComment(pidn, szTemp, ARRAYSIZE(szTemp)));
|
|
}
|
|
else if (IsEqualSCID(*pscid, SCID_NAME))
|
|
{
|
|
TCHAR szTemp[MAX_PATH];
|
|
hr = InitVariantFromStr(pv, NET_CopyResName(pidn, szTemp, ARRAYSIZE(szTemp)));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
IShellFolder2* psfFiles;
|
|
hr = v_GetFileFolder(&psfFiles);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = psfFiles->GetDetailsEx(pidl, pscid, pv);
|
|
}
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
HRESULT CNetFolder::_GetDetailsOf(UINT cColumns, LPCITEMIDLIST pidl, UINT iColumn, SHELLDETAILS *pDetails)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
pDetails->str.uType = STRRET_CSTR;
|
|
pDetails->str.cStr[0] = 0;
|
|
|
|
if (NULL == pidl)
|
|
{
|
|
hr = GetDetailsOfInfo(s_net_cols, cColumns, iColumn, pDetails);
|
|
}
|
|
else
|
|
{
|
|
SHCOLUMNID scid;
|
|
hr = MapColumnToSCID(iColumn, &scid);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
VARIANT var;
|
|
hr = GetDetailsEx(pidl, &scid, &var);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
TCHAR szTemp[MAX_PATH];
|
|
hr = SHFormatForDisplay(scid.fmtid,
|
|
scid.pid,
|
|
(PROPVARIANT*)&var,
|
|
PUIFFDF_DEFAULT,
|
|
szTemp,
|
|
ARRAYSIZE(szTemp));
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = StringToStrRet(szTemp, &pDetails->str);
|
|
}
|
|
|
|
VariantClear(&var);
|
|
}
|
|
}
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
HRESULT CNetFolder::_MapColumnToSCID(UINT cColumns, UINT iColumn, SHCOLUMNID* pscid)
|
|
{
|
|
return MapColumnToSCIDImpl(s_net_cols, cColumns, iColumn, pscid);
|
|
}
|
|
|
|
|
|
// IPersist methods
|
|
|
|
STDMETHODIMP CNetFolder::GetClassID(CLSID* pCLSID)
|
|
{
|
|
switch (_uDisplayType)
|
|
{
|
|
case RESOURCEDISPLAYTYPE_ROOT:
|
|
*pCLSID = CLSID_NetworkRoot;
|
|
break;
|
|
|
|
case RESOURCEDISPLAYTYPE_SERVER:
|
|
*pCLSID = CLSID_NetworkServer;
|
|
break;
|
|
|
|
case RESOURCEDISPLAYTYPE_DOMAIN:
|
|
*pCLSID = CLSID_NetworkDomain;
|
|
break;
|
|
|
|
case RESOURCEDISPLAYTYPE_SHARE:
|
|
*pCLSID = CLSID_NetworkShare;
|
|
break;
|
|
|
|
default:
|
|
*pCLSID = CLSID_NULL;
|
|
break;
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
// IPersistFolder method
|
|
|
|
STDMETHODIMP CNetFolder::Initialize(LPCITEMIDLIST pidl)
|
|
{
|
|
ILFree(_pidl);
|
|
ILFree(_pidlTarget);
|
|
_pidl = _pidlTarget = NULL;
|
|
|
|
return SHILClone(pidl, &_pidl);
|
|
}
|
|
|
|
|
|
// IPersistFolder2 method
|
|
|
|
STDMETHODIMP CNetFolder::GetCurFolder(LPITEMIDLIST* ppidl)
|
|
{
|
|
return GetCurFolderImpl(_pidl, ppidl);
|
|
}
|
|
|
|
|
|
// IPersistFolder3 methods
|
|
|
|
STDMETHODIMP CNetFolder::InitializeEx(IBindCtx *pbc, LPCITEMIDLIST pidlRoot, const PERSIST_FOLDER_TARGET_INFO *pfti)
|
|
{
|
|
ILFree(_pidl);
|
|
ILFree(_pidlTarget);
|
|
_pidl = _pidlTarget = NULL;
|
|
|
|
HRESULT hr = SHILClone(pidlRoot, &_pidl);
|
|
if (SUCCEEDED(hr) && pfti && pfti->pidlTargetFolder)
|
|
{
|
|
hr = SHILClone(pfti->pidlTargetFolder, &_pidlTarget);
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
STDMETHODIMP CNetFolder::GetFolderTargetInfo(PERSIST_FOLDER_TARGET_INFO *pfti)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
ZeroMemory(pfti, sizeof(*pfti));
|
|
|
|
if (_pidlTarget)
|
|
hr = SHILClone(_pidlTarget, &pfti->pidlTargetFolder);
|
|
|
|
pfti->dwAttributes = FILE_ATTRIBUTE_DIRECTORY; // maybe add system?
|
|
pfti->csidl = -1;
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
// IShellIconOverlay
|
|
|
|
HRESULT CNetFolder::_GetIconOverlayInfo(LPCIDNETRESOURCE pidn, int *pIndex, DWORD dwFlags)
|
|
{
|
|
//
|
|
// For netshare objects we want to get the icon overlay.
|
|
// If the share is "pinned" to be available offline it will
|
|
// have the "Offline Files" overlay.
|
|
//
|
|
|
|
HRESULT hr = E_FAIL;
|
|
if (RESOURCEDISPLAYTYPE_SHARE == NET_GetDisplayType(pidn))
|
|
{
|
|
TCHAR szPath[MAX_PATH];
|
|
hr = _GetPathForItem(pidn, szPath, ARRAYSIZE(szPath));
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
IShellIconOverlayManager *psiom;
|
|
hr = GetIconOverlayManager(&psiom);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
WCHAR szPathW[MAX_PATH];
|
|
SHTCharToUnicode(szPath, szPathW, ARRAYSIZE(szPathW));
|
|
hr = psiom->GetFileOverlayInfo(szPathW, 0, pIndex, dwFlags);
|
|
psiom->Release();
|
|
}
|
|
}
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
|
|
STDMETHODIMP CNetFolder::GetOverlayIndex(LPCITEMIDLIST pidl, int *pIndex)
|
|
{
|
|
HRESULT hr = E_FAIL;
|
|
LPCIDNETRESOURCE pidn = NET_IsValidID(pidl);
|
|
if (NULL != pidn)
|
|
{
|
|
hr = _GetIconOverlayInfo(pidn, pIndex, SIOM_OVERLAYINDEX);
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
|
|
STDMETHODIMP CNetFolder::GetOverlayIconIndex(LPCITEMIDLIST pidl, int *pIndex)
|
|
{
|
|
HRESULT hr = E_FAIL;
|
|
LPCIDNETRESOURCE pidn = NET_IsValidID(pidl);
|
|
if (NULL != pidn)
|
|
{
|
|
hr = _GetIconOverlayInfo(pidn, pIndex, SIOM_ICONINDEX);
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// Helper function to allow external callers to query information from a
|
|
// network pidl...
|
|
//
|
|
// NOTE NOTE - This function returns a NETRESOURCE structure whose string
|
|
// pointers are not valid. On Win95 they were pointers back into the pidl's
|
|
// strings (even though the strings were copied into the supplied pv buffer.)
|
|
// Now we make the pointers really point into the buffer.
|
|
//
|
|
HRESULT CNetFolder::_GetNetResource(LPCIDNETRESOURCE pidn, NETRESOURCEW* pnr, int cb)
|
|
{
|
|
TCHAR szStrings[3][MAX_PATH];
|
|
LPWSTR psz, lpsz[3] = {NULL, NULL, NULL};
|
|
int i, cchT;
|
|
|
|
if (cb < sizeof(*pnr))
|
|
return DISP_E_BUFFERTOOSMALL;
|
|
|
|
ZeroMemory(pnr, cb);
|
|
|
|
NET_CopyResName(pidn, szStrings[0], ARRAYSIZE(szStrings[0]));
|
|
NET_CopyComment(pidn, szStrings[1], ARRAYSIZE(szStrings[1]));
|
|
_GetProvider(pidn, NULL, szStrings[2], ARRAYSIZE(szStrings[2]));
|
|
|
|
// Fill in some of the stuff first.
|
|
// pnr->dwScope = 0;
|
|
pnr->dwType = NET_GetType(pidn);
|
|
pnr->dwDisplayType = NET_GetDisplayType(pidn);
|
|
pnr->dwUsage = NET_GetUsage(pidn);
|
|
// pnr->lpLocalName = NULL;
|
|
|
|
// Now lets copy the strings into the buffer and make the pointers
|
|
// relative to the buffer...
|
|
psz = (LPWSTR)(pnr + 1);
|
|
cb -= sizeof(*pnr);
|
|
|
|
for (i = 0; i < ARRAYSIZE(szStrings); i++)
|
|
{
|
|
if (*szStrings[i])
|
|
{
|
|
cchT = (lstrlen(szStrings[i]) + 1) * sizeof(TCHAR);
|
|
if (cchT <= cb)
|
|
{
|
|
SHTCharToUnicode(szStrings[i], psz, cb/sizeof(TCHAR));
|
|
lpsz[i] = psz;
|
|
psz += cchT;
|
|
cb -= cchT * sizeof(TCHAR);
|
|
}
|
|
else
|
|
{
|
|
// A hint that the structure is ok, but the strings are missing
|
|
SetLastError(ERROR_INSUFFICIENT_BUFFER);
|
|
}
|
|
}
|
|
}
|
|
|
|
pnr->lpRemoteName = lpsz[0];
|
|
pnr->lpComment = lpsz[1];
|
|
pnr->lpProvider = lpsz[2];
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
//
|
|
// This function opens a reg. database key based on the "network provider".
|
|
//
|
|
// Returns: hkey
|
|
//
|
|
// The caller is responsibe to close the key by calling RegCloseKey().
|
|
//
|
|
HKEY CNetFolder::_OpenProviderKey(LPCIDNETRESOURCE pidn)
|
|
{
|
|
TCHAR szProvider[MAX_PATH];
|
|
if (_GetProvider(pidn, NULL, szProvider, ARRAYSIZE(szProvider)))
|
|
{
|
|
HKEY hkeyProgID = NULL;
|
|
ReplaceSpacesWithUnderscore(szProvider);
|
|
RegOpenKeyEx(HKEY_CLASSES_ROOT, szProvider, 0, KEY_READ, &hkeyProgID);
|
|
return hkeyProgID;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
//
|
|
// This function opens a reg. database key based on the network provider type.
|
|
// The type is a number that is not localized, as opposed to the provider name
|
|
// which may be localized.
|
|
//
|
|
// Arguments:
|
|
// pidlAbs -- Absolute IDList to a network resource object.
|
|
//
|
|
// Returns: hkey
|
|
//
|
|
// Notes:
|
|
// The caller is responsible to close the key by calling RegCloseKey().
|
|
//
|
|
|
|
HKEY CNetFolder::_OpenProviderTypeKey(LPCIDNETRESOURCE pidn)
|
|
{
|
|
HKEY hkeyProgID = NULL;
|
|
TCHAR szProvider[MAX_PATH];
|
|
|
|
if (_GetProvider(pidn, NULL, szProvider, ARRAYSIZE(szProvider)))
|
|
{
|
|
// Now that we've got the provider name, get the provider id.
|
|
DWORD dwType;
|
|
if (WNetGetProviderType(szProvider, &dwType) == WN_SUCCESS)
|
|
{
|
|
// convert nis.wNetType to a string, and then open the key
|
|
// HKEY_CLASSES_ROOT\Network\Type\<type string>
|
|
|
|
TCHAR szRegValue[MAX_PATH];
|
|
wnsprintf(szRegValue, ARRAYSIZE(szRegValue), TEXT("Network\\Type\\%d"), HIWORD(dwType));
|
|
RegOpenKeyEx(HKEY_CLASSES_ROOT, szRegValue, 0, KEY_READ, &hkeyProgID);
|
|
}
|
|
}
|
|
|
|
return hkeyProgID;
|
|
}
|
|
|
|
HRESULT CNetFolder::_OpenKeys(LPCIDNETRESOURCE pidn, HKEY ahkeys[NKID_COUNT])
|
|
{
|
|
// See if there is a key specific to the type of Network object...
|
|
COMPILETIME_ASSERT(6 == NKID_COUNT);
|
|
ahkeys[0] = ahkeys[1] = ahkeys[2] = ahkeys[3] = ahkeys[4] = ahkeys[5] = NULL;
|
|
ahkeys[NKID_PROVIDERTYPE] = _OpenProviderTypeKey(pidn);
|
|
ahkeys[NKID_PROVIDER] = _OpenProviderKey(pidn);
|
|
|
|
if (NET_GetDisplayType(pidn) == RESOURCEDISPLAYTYPE_SHARE)
|
|
RegOpenKeyEx(HKEY_CLASSES_ROOT, TEXT("NetShare"), 0, KEY_READ, &ahkeys[NKID_NETCLASS]);
|
|
else if (NET_GetDisplayType(pidn) == RESOURCEDISPLAYTYPE_SERVER)
|
|
RegOpenKeyEx(HKEY_CLASSES_ROOT, TEXT("NetServer"), 0, KEY_READ, &ahkeys[NKID_NETCLASS]);
|
|
|
|
RegOpenKeyEx(HKEY_CLASSES_ROOT, TEXT("Network"), 0, KEY_READ, &ahkeys[NKID_NETWORK]);
|
|
|
|
// make sure it is not a printer before adding "Folder" or "directory"
|
|
|
|
if (!_IsPrintShare(pidn))
|
|
{
|
|
// Shares should also support directory stuff...
|
|
if (NET_GetDisplayType(pidn) == RESOURCEDISPLAYTYPE_SHARE)
|
|
RegOpenKeyEx(HKEY_CLASSES_ROOT, TEXT("Directory"), 0, KEY_READ, &ahkeys[NKID_DIRECTORY]);
|
|
|
|
RegOpenKeyEx(HKEY_CLASSES_ROOT, c_szFolderClass, 0, KEY_READ, &ahkeys[NKID_FOLDER]);
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
#define WNFMT_PLATFORM WNFMT_ABBREVIATED | WNFMT_INENUM
|
|
|
|
//
|
|
// This function retrieves the formatted (display) name of the specified network object.
|
|
//
|
|
HRESULT CNetFolder::_GetFormatName(LPCIDNETRESOURCE pidn, STRRET* pStrRet)
|
|
{
|
|
HRESULT hr = E_FAIL;
|
|
TCHAR szName[MAX_PATH];
|
|
|
|
NET_CopyResName(pidn, szName, ARRAYSIZE(szName));
|
|
|
|
if (NET_GetDisplayType(pidn) == RESOURCEDISPLAYTYPE_SERVER)
|
|
{
|
|
TCHAR szMachineName[MAX_PATH];
|
|
TCHAR szComment[MAX_PATH];
|
|
|
|
NET_CopyResName(pidn, szMachineName, ARRAYSIZE(szMachineName));
|
|
NET_CopyComment(pidn, szComment, ARRAYSIZE(szComment));
|
|
|
|
hr = SHBuildDisplayMachineName(szMachineName, szComment, szName, ARRAYSIZE(szName));
|
|
}
|
|
|
|
if (FAILED(hr) &&
|
|
(NET_GetDisplayType(pidn) != RESOURCEDISPLAYTYPE_ROOT) &&
|
|
(NET_GetDisplayType(pidn) != RESOURCEDISPLAYTYPE_NETWORK))
|
|
{
|
|
TCHAR szDisplayName[MAX_PATH], szProvider[MAX_PATH];
|
|
DWORD dwSize = ARRAYSIZE(szDisplayName);
|
|
|
|
LPCTSTR pszProvider = _GetProvider(pidn, NULL, szProvider, ARRAYSIZE(szProvider));
|
|
if (pszProvider)
|
|
{
|
|
DWORD dwRes = WNetFormatNetworkName(pszProvider, szName, szDisplayName, &dwSize, WNFMT_PLATFORM, 8 + 1 + 3);
|
|
if (dwRes == WN_SUCCESS)
|
|
{
|
|
StrCpyN(szName, szDisplayName, ARRAYSIZE(szName));
|
|
}
|
|
}
|
|
}
|
|
|
|
return StringToStrRet(szName, pStrRet);
|
|
}
|
|
|
|
|
|
//
|
|
// resolve non-UNC share names (novell) to UNC style names
|
|
//
|
|
// returns:
|
|
// TRUE translated the name
|
|
// FALSE didn't translate (maybe error case)
|
|
//
|
|
// WARNING: If we use too much stack space then we will cause
|
|
// faults by over flowing the stack. Millennium #94818
|
|
BOOL CNetFolder::_GetPathForShare(LPCIDNETRESOURCE pidn, LPTSTR pszPath, int cchPath)
|
|
{
|
|
BOOL fRet = FALSE;
|
|
|
|
*pszPath = TEXT('\0');
|
|
|
|
LPTSTR pszAccessName = (LPTSTR)LocalAlloc(LPTR, sizeof(TCHAR) * MAX_PATH * 3); // *3 remote, provider and path
|
|
if (pszAccessName)
|
|
{
|
|
LPTSTR pszRemoteName = pszAccessName + MAX_PATH;
|
|
LPTSTR pszProviderName = pszRemoteName + MAX_PATH;
|
|
|
|
NET_CopyResName(pidn, pszRemoteName, MAX_PATH);
|
|
if (NULL != _pszResName)
|
|
{
|
|
//
|
|
// Combine the folder name with the share name
|
|
// to create a UNC path.
|
|
//
|
|
// Borrow the pszProviderName buffer for a bit.
|
|
//
|
|
PathCombine(pszProviderName, _pszResName, pszRemoteName);
|
|
|
|
//
|
|
// To be safe: UNC prefix implies that name is available using FS access
|
|
// Theoretically it also should be routed to MPR, but it is late to do this
|
|
//
|
|
if (PathIsUNC(pszProviderName))
|
|
{
|
|
StrCpyN(pszPath, pszProviderName, cchPath);
|
|
fRet = FALSE;
|
|
}
|
|
else
|
|
{
|
|
pszProviderName[0] = TEXT('\0');
|
|
}
|
|
}
|
|
|
|
if (!*pszPath)
|
|
{
|
|
// Check cache
|
|
ENTERCRITICAL;
|
|
if (lstrcmpi(g_szLastAttemptedJunctionName, pszRemoteName) == 0)
|
|
{
|
|
// cache hit
|
|
StrCpy(pszPath, g_szLastResolvedJunctionName);
|
|
fRet = TRUE;
|
|
}
|
|
LEAVECRITICAL;
|
|
}
|
|
|
|
if (!*pszPath)
|
|
{
|
|
NETRESOURCE nr = {0};
|
|
DWORD err, dwRedir, dwResult;
|
|
DWORD cchAccessName;
|
|
|
|
nr.lpRemoteName = pszRemoteName;
|
|
nr.lpProvider = (LPTSTR) _GetProvider(pidn, NULL, pszProviderName, MAX_PATH);
|
|
nr.dwType = NET_GetType(pidn);
|
|
nr.dwUsage = NET_GetUsage(pidn);
|
|
nr.dwDisplayType = NET_GetDisplayType(pidn);
|
|
|
|
dwRedir = CONNECT_TEMPORARY;
|
|
|
|
// Prepare access name buffer and net resource request buffer
|
|
//
|
|
cchAccessName = MAX_PATH;
|
|
pszAccessName[0] = 0;
|
|
|
|
err = WNetUseConnection(NULL, &nr, NULL, NULL, dwRedir, pszAccessName, &cchAccessName, &dwResult);
|
|
if ((WN_SUCCESS != err) || !pszAccessName[0])
|
|
{
|
|
// perf idea: might be good to cache the last failed junction bind
|
|
// and early out on the next attempt. One slight problem this
|
|
// might encounter: what if we cache a failure, the user changes
|
|
// state to fix the problem, but we hit our failure cache...
|
|
//
|
|
StrCpyN(pszPath, pszRemoteName, cchPath);
|
|
fRet = FALSE;
|
|
}
|
|
else
|
|
{
|
|
// Get the return name
|
|
StrCpyN(pszPath, pszAccessName, cchPath);
|
|
fRet = TRUE;
|
|
|
|
// Update success cache entry
|
|
ENTERCRITICAL;
|
|
|
|
StrCpyN(g_szLastAttemptedJunctionName, pszRemoteName, ARRAYSIZE(g_szLastAttemptedJunctionName));
|
|
StrCpyN(g_szLastResolvedJunctionName, pszAccessName, ARRAYSIZE(g_szLastResolvedJunctionName));
|
|
|
|
LEAVECRITICAL;
|
|
}
|
|
}
|
|
|
|
LocalFree(pszAccessName);
|
|
}
|
|
return fRet;
|
|
}
|
|
|
|
// in:
|
|
// pidn may be multi-level net resource pidl like
|
|
// [entire net] [provider] [server] [share] [... file sys]
|
|
// or [server] [share] [... file sys]
|
|
|
|
HRESULT CNetFolder::_GetPathForItem(LPCIDNETRESOURCE pidn, LPTSTR pszPath, int cchPath)
|
|
{
|
|
*pszPath = 0;
|
|
|
|
// loop down
|
|
for (; !ILIsEmpty((LPCITEMIDLIST)pidn) ; pidn = (LPCIDNETRESOURCE)_ILNext((LPCITEMIDLIST)pidn))
|
|
{
|
|
if (NET_GetFlags(pidn) & SHID_JUNCTION) // \\server\share or strike/sys
|
|
{
|
|
_GetPathForShare(pidn, pszPath, cchPath);
|
|
break; // below this we don't know about any of the PIDLs
|
|
}
|
|
else
|
|
{
|
|
// if this is entire network then return the canonical name for
|
|
// this object.
|
|
|
|
if (NET_GetDisplayType(pidn) == RESOURCEDISPLAYTYPE_ROOT)
|
|
StrCpyN(pszPath, TEXT("EntireNetwork"), cchPath);
|
|
else
|
|
NET_CopyResName(pidn, pszPath, cchPath);
|
|
}
|
|
}
|
|
return *pszPath ? S_OK : E_NOTIMPL;
|
|
}
|
|
|
|
|
|
// in:
|
|
// pidl
|
|
//
|
|
// takes the last items and create a folder for it, assuming the first section is the
|
|
// used to initialze. the riid and ppv are used to return an object.
|
|
//
|
|
|
|
HRESULT CNetFolder::_CreateFolderForItem(LPBC pbc, LPCITEMIDLIST pidl, LPCITEMIDLIST pidlTarget, LPCIDNETRESOURCE pidnForProvider, REFIID riid, void **ppv)
|
|
{
|
|
LPCITEMIDLIST pidlLast = ILFindLastID(pidl);
|
|
LPCIDNETRESOURCE pidn = NET_IsValidID(pidlLast);
|
|
|
|
if (!pidn)
|
|
return E_INVALIDARG;
|
|
|
|
HRESULT hr;
|
|
if (NET_IsRemoteFld(pidn))
|
|
{
|
|
// note: I think this is dead functionality. it was used in NT4 but we can't find
|
|
// the impl of this CLSID_Remote anymore...
|
|
IPersistFolder * ppf;
|
|
hr = SHCoCreateInstance(NULL, &CLSID_Remote, NULL, IID_PPV_ARG(IPersistFolder, &ppf));
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr= ppf->Initialize(pidl);
|
|
if (SUCCEEDED(hr))
|
|
hr = ppf->QueryInterface(riid, ppv);
|
|
ppf->Release();
|
|
}
|
|
}
|
|
else if (NET_GetFlags(pidn) & SHID_JUNCTION) // \\server\share or strike/sys
|
|
{
|
|
PERSIST_FOLDER_TARGET_INFO * ppfti = (PERSIST_FOLDER_TARGET_INFO *) LocalAlloc(LPTR, sizeof(PERSIST_FOLDER_TARGET_INFO));
|
|
if (ppfti)
|
|
{
|
|
ppfti->pidlTargetFolder = (LPITEMIDLIST)pidlTarget;
|
|
ppfti->csidl = -1;
|
|
ppfti->dwAttributes = FILE_ATTRIBUTE_DIRECTORY; // maybe add system?
|
|
|
|
_GetPathForItem(pidn, ppfti->szTargetParsingName, ARRAYSIZE(ppfti->szTargetParsingName));
|
|
|
|
hr = CFSFolder_CreateFolder(NULL, pbc, pidl, ppfti, riid, ppv);
|
|
LocalFree(ppfti);
|
|
}
|
|
else
|
|
hr = E_OUTOFMEMORY;
|
|
}
|
|
else
|
|
{
|
|
TCHAR szPath[MAX_PATH];
|
|
NET_CopyResName(pidn, szPath, ARRAYSIZE(szPath));
|
|
|
|
hr = _CreateInstance(pidl, pidlTarget, NET_GetDisplayType(pidn), pidnForProvider, szPath, riid, ppv);
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
|
|
// get the provider for an item or the folder itself. since some items don't have the
|
|
// provider stored we fall back to the folder to get the provider in that case
|
|
//
|
|
// in:
|
|
// pidn item to get provider for. if NULL get provider for the folder
|
|
// pbc IBindCtx to get provider for. if NULL get provider from pidn or folder.
|
|
//
|
|
// returns:
|
|
// NULL no provider in the item or the folder
|
|
// non NULL address of passed in buffer
|
|
|
|
LPCTSTR CNetFolder::_GetProvider(LPCIDNETRESOURCE pidn, IBindCtx *pbc, LPTSTR pszProvider, UINT cchProvider)
|
|
{
|
|
// attempt to get the provider from the property bag
|
|
IPropertyBag *ppb;
|
|
if (pbc && SUCCEEDED(pbc->GetObjectParam(STR_PARSE_NETFOLDER_INFO, (IUnknown**)&ppb)))
|
|
{
|
|
HRESULT hr = SHPropertyBag_ReadStr(ppb, STR_PARSE_NETFOLDER_PROVIDERNAME, pszProvider, cchProvider);
|
|
ppb->Release();
|
|
if (SUCCEEDED(hr) && *pszProvider)
|
|
{
|
|
return pszProvider;
|
|
}
|
|
}
|
|
|
|
// from the IDLIST
|
|
if (pidn && NET_CopyProviderName(pidn, pszProvider, cchProvider))
|
|
return pszProvider;
|
|
|
|
// from our state
|
|
if (_pidnForProvider)
|
|
{
|
|
NET_CopyProviderName(_pidnForProvider, pszProvider, cchProvider);
|
|
return pszProvider;
|
|
}
|
|
|
|
*pszProvider = 0;
|
|
return NULL;
|
|
}
|
|
|
|
|
|
const NETPROVIDERS c_rgProviderMap[] =
|
|
{
|
|
{ TEXT("Microsoft Network"), HIWORD(WNNC_NET_LANMAN) },
|
|
{ TEXT("NetWare"), HIWORD(WNNC_NET_NETWARE) }
|
|
};
|
|
|
|
|
|
// construct a net idlist either copying the existing data from a pidl or
|
|
// from a NETRESOURCE structure
|
|
|
|
HRESULT CNetFolder::_CreateNetIDList(LPIDNETRESOURCE pidnIn,
|
|
LPCTSTR pszName, LPCTSTR pszProvider, LPCTSTR pszComment,
|
|
LPITEMIDLIST *ppidl)
|
|
{
|
|
LPBYTE pb;
|
|
UINT cbmkid = sizeof(IDNETRESOURCE) - sizeof(CHAR);
|
|
UINT cchName, cchProvider, cchComment, cbProviderType = 0;
|
|
LPIDNETRESOURCE pidn;
|
|
WORD wNetType = 0;
|
|
BOOL fUnicode = FALSE;
|
|
UINT cchAnsiName, cchAnsiProvider, cchAnsiComment;
|
|
CHAR szAnsiName[MAX_PATH], szAnsiProvider[MAX_PATH], szAnsiComment[MAX_PATH];
|
|
|
|
ASSERT(ppidl != NULL);
|
|
*ppidl = NULL;
|
|
|
|
if (!pszName)
|
|
pszName = c_szNULL; // For now put in an empty string...
|
|
|
|
if (pszProvider)
|
|
cbProviderType += sizeof(WORD);
|
|
|
|
// Win9x shipped with one set of provider name which are
|
|
// different on NT. Therefore lets convert the NT one to
|
|
// something that Win9x can understand.
|
|
|
|
if (pszProvider)
|
|
{
|
|
cbProviderType = sizeof(WORD);
|
|
DWORD dwType, dwRes = WNetGetProviderType(pszProvider, &dwType);
|
|
if (dwRes == WN_SUCCESS)
|
|
{
|
|
wNetType = HIWORD(dwType);
|
|
for (int i = 0; i < ARRAYSIZE(c_rgProviderMap); i++)
|
|
{
|
|
if (c_rgProviderMap[i].wNetType == wNetType)
|
|
{
|
|
pszProvider = c_rgProviderMap[i].lpName;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// compute the string lengths ready to build an IDLIST
|
|
|
|
cchName = lstrlen(pszName)+1;
|
|
cchProvider = pszProvider ? lstrlen(pszProvider)+1 : 0;
|
|
cchComment = pszComment ? lstrlen(pszComment)+1 : 0;
|
|
|
|
cchAnsiName = 0;
|
|
cchAnsiProvider = 0;
|
|
cchAnsiComment = 0;
|
|
|
|
fUnicode = !DoesStringRoundTrip(pszName, szAnsiName, ARRAYSIZE(szAnsiProvider));
|
|
cchAnsiName = lstrlenA(szAnsiName)+1;
|
|
|
|
if (pszProvider)
|
|
{
|
|
fUnicode |= !DoesStringRoundTrip(pszProvider, szAnsiProvider, ARRAYSIZE(szAnsiProvider));
|
|
cchAnsiProvider = lstrlenA(szAnsiProvider)+1;
|
|
}
|
|
|
|
if (pszComment)
|
|
{
|
|
fUnicode |= !DoesStringRoundTrip(pszComment, szAnsiComment, ARRAYSIZE(szAnsiComment));
|
|
cchAnsiComment = lstrlenA(szAnsiComment)+1;
|
|
}
|
|
|
|
// allocate and fill the IDLIST header
|
|
|
|
cbmkid += cbProviderType+cchAnsiName + cchAnsiProvider + cchAnsiComment;
|
|
|
|
if (fUnicode)
|
|
cbmkid += (sizeof(WCHAR)*(cchName+cchProvider+cchComment));
|
|
|
|
pidn = (LPIDNETRESOURCE)_ILCreate(cbmkid + sizeof(USHORT));
|
|
if (!pidn)
|
|
return E_OUTOFMEMORY;
|
|
|
|
pidn->cb = (WORD)cbmkid;
|
|
pidn->bFlags = pidnIn->bFlags;
|
|
pidn->uType = pidnIn->uType;
|
|
pidn->uUsage = pidnIn->uUsage;
|
|
|
|
if (pszProvider)
|
|
pidn->uUsage |= NET_HASPROVIDER;
|
|
|
|
if (pszComment)
|
|
pidn->uUsage |= NET_HASCOMMENT;
|
|
|
|
pb = (LPBYTE) pidn->szNetResName;
|
|
|
|
//
|
|
// write the ANSI strings into the IDLIST
|
|
//
|
|
|
|
StrCpyA((PSTR)pb, szAnsiName); // buffer allocated above based on cch
|
|
pb += cchAnsiName;
|
|
|
|
if (pszProvider)
|
|
{
|
|
StrCpyA((PSTR) pb, szAnsiProvider); // buffer allocated above based on cch
|
|
pb += cchAnsiProvider;
|
|
}
|
|
|
|
if (pszComment)
|
|
{
|
|
StrCpyA((PSTR) pb, szAnsiComment); // buffer allocated above based on cch
|
|
pb += cchAnsiComment;
|
|
}
|
|
|
|
// if we are going to be UNICODE then lets write those strings also.
|
|
// Note that we must use unaligned string copies since the is no
|
|
// promse that the ANSI strings will have an even number of characters
|
|
// in them.
|
|
if (fUnicode)
|
|
{
|
|
pidn->uUsage |= NET_UNICODE;
|
|
|
|
ualstrcpyW((UNALIGNED WCHAR *)pb, pszName); // buffer allocated above based on cch
|
|
pb += cchName*sizeof(WCHAR);
|
|
|
|
if (pszProvider)
|
|
{
|
|
ualstrcpyW((UNALIGNED WCHAR *)pb, pszProvider); // buffer allocated above based on cch
|
|
pb += cchProvider*sizeof(WCHAR);
|
|
}
|
|
|
|
if (pszComment)
|
|
{
|
|
ualstrcpyW((UNALIGNED WCHAR *)pb, pszComment); // buffer allocated above based on cch
|
|
pb += cchComment*sizeof(WCHAR);
|
|
}
|
|
}
|
|
|
|
//
|
|
// and the trailing provider type
|
|
//
|
|
|
|
if (cbProviderType)
|
|
{
|
|
// Store the provider type
|
|
pb = (LPBYTE)pidn + pidn->cb - sizeof(WORD);
|
|
*((UNALIGNED WORD *)pb) = wNetType;
|
|
}
|
|
|
|
*ppidl = (LPITEMIDLIST)pidn;
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
// wrapper for converting a NETRESOURCE into an IDLIST via _CreateNetPidl
|
|
|
|
HRESULT CNetFolder::_NetResToIDList(NETRESOURCE *pnr,
|
|
BOOL fKeepNullRemoteName,
|
|
BOOL fKeepProviderName,
|
|
BOOL fKeepComment,
|
|
LPITEMIDLIST *ppidl)
|
|
{
|
|
NETRESOURCE nr = *pnr;
|
|
LPITEMIDLIST pidl;
|
|
LPTSTR pszName, pszProvider, pszComment;
|
|
IDNETRESOURCE idn;
|
|
LPTSTR psz;
|
|
|
|
if (ppidl)
|
|
*ppidl = NULL;
|
|
|
|
switch (pnr->dwDisplayType)
|
|
{
|
|
case RESOURCEDISPLAYTYPE_NETWORK:
|
|
pszName = pnr->lpProvider;
|
|
break;
|
|
|
|
case RESOURCEDISPLAYTYPE_ROOT:
|
|
pszName =pnr->lpComment;
|
|
break;
|
|
|
|
default:
|
|
{
|
|
pszName = pnr->lpRemoteName;
|
|
|
|
if (!fKeepNullRemoteName && (!pszName || !*pszName))
|
|
return E_FAIL;
|
|
|
|
if (pszName && *pszName)
|
|
{
|
|
psz = (LPTSTR)SkipServerSlashes(pnr->lpRemoteName);
|
|
if ( *psz )
|
|
PathMakePretty(psz);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
pszProvider = fKeepProviderName ? nr.lpProvider:NULL;
|
|
pszComment = fKeepComment ? nr.lpComment:NULL;
|
|
|
|
idn.bFlags = (BYTE)(SHID_NET | (pnr->dwDisplayType & 0x0f));
|
|
idn.uType = (BYTE)(pnr->dwType & 0x0f);
|
|
idn.uUsage = (BYTE)(pnr->dwUsage & 0x0f);
|
|
|
|
// Is the current resource a share of some kind and not a container
|
|
if ((pnr->dwDisplayType == RESOURCEDISPLAYTYPE_SHARE || pnr->dwDisplayType == RESOURCEDISPLAYTYPE_SHAREADMIN) &&
|
|
!(pnr->dwUsage & RESOURCEUSAGE_CONTAINER))
|
|
{
|
|
// If so, remember to delegate children of this folder to FSFolder
|
|
idn.bFlags |= (BYTE)SHID_JUNCTION; // \\server\share type thing
|
|
}
|
|
|
|
HRESULT hr = _CreateNetIDList(&idn, pszName, pszProvider, pszComment, &pidl);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
if (ppidl)
|
|
*ppidl = pidl;
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
HRESULT CNetFolder::_CreateEntireNetwork(LPITEMIDLIST *ppidl)
|
|
{
|
|
TCHAR szPath[MAX_PATH];
|
|
NETRESOURCE nr = {0};
|
|
|
|
// We need to add the Rest of network entry. This is psuedo
|
|
// bogus, as we should either always do it ourself or have
|
|
// MPR always do it, but here it goes...
|
|
LoadString(HINST_THISDLL, IDS_RESTOFNET, szPath, ARRAYSIZE(szPath));
|
|
nr.dwDisplayType = RESOURCEDISPLAYTYPE_ROOT;
|
|
nr.dwType = RESOURCETYPE_ANY;
|
|
nr.dwUsage = RESOURCEUSAGE_CONTAINER;
|
|
nr.lpComment = szPath;
|
|
|
|
return _NetResToIDList(&nr, FALSE, FALSE, FALSE, ppidl);
|
|
}
|
|
|
|
HRESULT CNetFolder::_CreateEntireNetworkFullIDList(LPITEMIDLIST *ppidl)
|
|
{
|
|
// CLSID_NetworkPlaces\EntireNetwork
|
|
return SHILCreateFromPath(TEXT("::{208D2C60-3AEA-1069-A2D7-08002B30309D}\\EntireNetwork"), ppidl, NULL);
|
|
}
|
|
|
|
//
|
|
// To be called back from within CDefFolderMenu
|
|
//
|
|
STDAPI CNetwork_DFMCallBackBG(IShellFolder *psf, HWND hwnd,
|
|
IDataObject *pdtobj, UINT uMsg,
|
|
WPARAM wParam, LPARAM lParam)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
CNetFolder *pThis = FolderToNetFolder(psf);
|
|
|
|
if (NULL == pThis)
|
|
return E_UNEXPECTED;
|
|
|
|
switch(uMsg)
|
|
{
|
|
case DFM_MERGECONTEXTMENU_BOTTOM:
|
|
if (!(wParam & (CMF_VERBSONLY | CMF_DVFILE)))
|
|
{
|
|
CDefFolderMenu_MergeMenu(HINST_THISDLL, POPUP_PROPERTIES_BG, 0, (LPQCMINFO)lParam);
|
|
}
|
|
break;
|
|
|
|
case DFM_GETHELPTEXT:
|
|
LoadStringA(HINST_THISDLL, LOWORD(wParam) + IDS_MH_FSIDM_FIRST, (LPSTR)lParam, HIWORD(wParam));
|
|
break;
|
|
|
|
case DFM_GETHELPTEXTW:
|
|
LoadStringW(HINST_THISDLL, LOWORD(wParam) + IDS_MH_FSIDM_FIRST, (LPWSTR)lParam, HIWORD(wParam));
|
|
break;
|
|
|
|
case DFM_INVOKECOMMAND:
|
|
switch (wParam)
|
|
{
|
|
case FSIDM_PROPERTIESBG:
|
|
hr = SHPropertiesForPidl(hwnd, pThis->_pidl, (LPCTSTR)lParam);
|
|
break;
|
|
|
|
default:
|
|
// This is one of view menu items, use the default code.
|
|
hr = S_FALSE;
|
|
break;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
hr = E_NOTIMPL;
|
|
break;
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
//
|
|
// To be called back from within CDefFolderMenu
|
|
//
|
|
STDAPI CNetFolder::DFMCallBack(IShellFolder* psf, HWND hwnd,
|
|
IDataObject* pdtobj, UINT uMsg,
|
|
WPARAM wParam, LPARAM lParam)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
switch(uMsg)
|
|
{
|
|
case DFM_MERGECONTEXTMENU:
|
|
if (pdtobj)
|
|
{
|
|
STGMEDIUM medium;
|
|
LPIDA pida;
|
|
LPQCMINFO pqcm = (LPQCMINFO)lParam;
|
|
UINT idCmdBase = pqcm->idCmdFirst; // must be called before merge
|
|
CDefFolderMenu_MergeMenu(HINST_THISDLL, POPUP_NETWORK_ITEM, 0, pqcm);
|
|
|
|
pida = DataObj_GetHIDA(pdtobj, &medium);
|
|
if (pida)
|
|
{
|
|
if (pida->cidl > 0)
|
|
{
|
|
LPIDNETRESOURCE pidn = (LPIDNETRESOURCE)IDA_GetIDListPtr(pida, 0);
|
|
|
|
// Only enable "connect" command if the first one is a share.
|
|
if (pidn)
|
|
{
|
|
ULONG rgf = 0;
|
|
if(NET_GetFlags(pidn) & SHID_JUNCTION &&
|
|
!SHRestricted(REST_NONETCONNECTDISCONNECT))
|
|
{
|
|
EnableMenuItem(pqcm->hmenu, idCmdBase + FSIDM_CONNECT,
|
|
MF_CHECKED | MF_BYCOMMAND);
|
|
}
|
|
}
|
|
}
|
|
HIDA_ReleaseStgMedium(pida, &medium);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case DFM_GETHELPTEXT:
|
|
LoadStringA(HINST_THISDLL, LOWORD(wParam) + IDS_MH_FSIDM_FIRST, (LPSTR)lParam, HIWORD(wParam));
|
|
break;
|
|
|
|
case DFM_GETHELPTEXTW:
|
|
LoadStringW(HINST_THISDLL, LOWORD(wParam) + IDS_MH_FSIDM_FIRST, (LPWSTR)lParam, HIWORD(wParam));
|
|
break;
|
|
|
|
case DFM_INVOKECOMMAND:
|
|
switch(wParam)
|
|
{
|
|
case DFM_CMD_PROPERTIES:
|
|
hr = SHLaunchPropSheet(_PropertiesThreadProc, pdtobj, (LPCTSTR)lParam, psf, NULL);
|
|
break;
|
|
|
|
case DFM_CMD_LINK:
|
|
{
|
|
hr = S_FALSE; // do the default shortcut stuff
|
|
CNetFolder *pThis = FolderToNetFolder(psf);
|
|
if (pThis)
|
|
{
|
|
// net hood special case. in this case we want to create the shortuct
|
|
// in the net hood, not offer to put this on the desktop
|
|
IShellFolder2* psfFiles;
|
|
if (SUCCEEDED(pThis->v_GetFileFolder(&psfFiles)))
|
|
{
|
|
CFSFolder_CreateLinks(hwnd, psfFiles, pdtobj, (LPCTSTR)lParam, CMIC_MASK_FLAG_NO_UI);
|
|
hr = S_OK; // we created the links
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
|
|
case FSIDM_CONNECT:
|
|
if (pdtobj)
|
|
{
|
|
STGMEDIUM medium;
|
|
LPIDA pida = DataObj_GetHIDA(pdtobj, &medium);
|
|
if (pida)
|
|
{
|
|
for (UINT i = 0; i < pida->cidl; i++)
|
|
{
|
|
LPIDNETRESOURCE pidn = (LPIDNETRESOURCE)IDA_GetIDListPtr(pida, i);
|
|
|
|
// Only execute "connect" on shares.
|
|
if (NET_GetFlags(pidn) & SHID_JUNCTION)
|
|
{
|
|
TCHAR szName[MAX_PATH];
|
|
LPTSTR pszName = NET_CopyResName(pidn, szName, ARRAYSIZE(szName));
|
|
DWORD err = SHStartNetConnectionDialog(hwnd, pszName, RESOURCETYPE_DISK);
|
|
DebugMsg(DM_TRACE, TEXT("CNet FSIDM_CONNECT (%s, %x)"), szName, err);
|
|
|
|
// events will get generated automatically
|
|
}
|
|
}
|
|
HIDA_ReleaseStgMedium(pida, &medium);
|
|
}
|
|
}
|
|
break;
|
|
|
|
default:
|
|
// This is one of view menu items, use the default code.
|
|
hr = S_FALSE;
|
|
break;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
hr = E_NOTIMPL;
|
|
break;
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
STDAPI CNetFolder::PrinterDFMCallBack(IShellFolder* psf, HWND hwnd,
|
|
IDataObject* pdtobj, UINT uMsg,
|
|
WPARAM wParam, LPARAM lParam)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
switch(uMsg)
|
|
{
|
|
case DFM_MERGECONTEXTMENU:
|
|
//
|
|
// Returning S_FALSE indicates no need to get verbs from
|
|
// extensions.
|
|
//
|
|
hr = S_FALSE;
|
|
break;
|
|
|
|
// if anyone hooks our context menu, we want to be on top (Open)
|
|
case DFM_MERGECONTEXTMENU_TOP:
|
|
if (pdtobj)
|
|
{
|
|
LPQCMINFO pqcm = (LPQCMINFO)lParam;
|
|
|
|
// insert verbs
|
|
CDefFolderMenu_MergeMenu(HINST_THISDLL, POPUP_NETWORK_PRINTER, 0, pqcm);
|
|
SetMenuDefaultItem(pqcm->hmenu, 0, MF_BYPOSITION);
|
|
}
|
|
break;
|
|
|
|
case DFM_GETHELPTEXT:
|
|
LoadStringA(HINST_THISDLL, LOWORD(wParam) + IDS_MH_FSIDM_FIRST, (LPSTR)lParam, HIWORD(wParam));
|
|
break;
|
|
|
|
case DFM_GETHELPTEXTW:
|
|
LoadStringW(HINST_THISDLL, LOWORD(wParam) + IDS_MH_FSIDM_FIRST, (LPWSTR)lParam, HIWORD(wParam));
|
|
break;
|
|
|
|
case DFM_INVOKECOMMAND:
|
|
switch (wParam)
|
|
{
|
|
case DFM_CMD_PROPERTIES:
|
|
hr = SHLaunchPropSheet(_PropertiesThreadProc, pdtobj, (LPCTSTR)lParam, psf, NULL);
|
|
break;
|
|
|
|
case DFM_CMD_LINK:
|
|
// do the default create shortcut crap
|
|
return S_FALSE;
|
|
|
|
case FSIDM_OPENPRN:
|
|
case FSIDM_NETPRN_INSTALL:
|
|
{
|
|
STGMEDIUM medium;
|
|
LPIDA pida = DataObj_GetHIDA(pdtobj, &medium);
|
|
if (pida)
|
|
{
|
|
UINT action;
|
|
|
|
// set up the operation we are going to perform
|
|
switch (wParam)
|
|
{
|
|
case FSIDM_OPENPRN:
|
|
action = PRINTACTION_OPENNETPRN;
|
|
break;
|
|
case FSIDM_NETPRN_INSTALL:
|
|
action = PRINTACTION_NETINSTALL;
|
|
break;
|
|
default: // FSIDM_CONNECT_PRN
|
|
action = (UINT)-1;
|
|
break;
|
|
}
|
|
|
|
for (UINT i = 0; i < pida->cidl; i++)
|
|
{
|
|
LPIDNETRESOURCE pidn = (LPIDNETRESOURCE)IDA_GetIDListPtr(pida, i);
|
|
|
|
// Only execute command for a net print share
|
|
if (_IsPrintShare(pidn))
|
|
{
|
|
TCHAR szName[MAX_PATH];
|
|
NET_CopyResName(pidn,szName,ARRAYSIZE(szName));
|
|
|
|
SHInvokePrinterCommand(hwnd, action, szName, NULL, FALSE);
|
|
}
|
|
} // for (i...
|
|
HIDA_ReleaseStgMedium(pida, &medium);
|
|
} // if (medium.hGlobal)
|
|
break;
|
|
} // case ID_NETWORK_PRINTER_INSTALL, FSIDM_CONNECT_PRN
|
|
|
|
default:
|
|
hr = E_FAIL;
|
|
break;
|
|
|
|
} // switch(wparam)
|
|
break;
|
|
|
|
default:
|
|
hr = E_NOTIMPL;
|
|
break;
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
|
|
//
|
|
// REVIEW: Almost identical code in fstreex.c
|
|
//
|
|
DWORD CALLBACK CNetFolder::_PropertiesThreadProc(void *pv)
|
|
{
|
|
PROPSTUFF* pps = (PROPSTUFF *)pv;
|
|
ULONG_PTR dwCookie = 0;
|
|
ActivateActCtx(NULL, &dwCookie);
|
|
CNetFolder *pThis = FolderToNetFolder(pps->psf);
|
|
if (pThis)
|
|
{
|
|
STGMEDIUM medium;
|
|
LPIDA pida = DataObj_GetHIDA(pps->pdtobj, &medium);
|
|
if (pida)
|
|
{
|
|
// Yes, do context menu.
|
|
HKEY ahkeys[NKID_COUNT];
|
|
LPCIDNETRESOURCE pnid = (LPCIDNETRESOURCE)IDA_GetIDListPtr(pida, 0);
|
|
if (pnid)
|
|
{
|
|
HRESULT hr = pThis->_OpenKeys(pnid, ahkeys);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
LPTSTR pszCaption = SHGetCaption(medium.hGlobal);
|
|
SHOpenPropSheet(pszCaption, ahkeys, ARRAYSIZE(ahkeys),
|
|
&CLSID_ShellNetDefExt,
|
|
pps->pdtobj, NULL, pps->pStartPage);
|
|
if (pszCaption)
|
|
SHFree(pszCaption);
|
|
|
|
SHRegCloseKeys(ahkeys, ARRAYSIZE(ahkeys));
|
|
}
|
|
}
|
|
|
|
HIDA_ReleaseStgMedium(pida, &medium);
|
|
}
|
|
}
|
|
return S_OK;
|
|
}
|
|
|
|
STDAPI CNetFolder::_AttributesCallback(IShellFolder2* psf, LPCITEMIDLIST pidl, ULONG* prgfInOut)
|
|
{
|
|
LPCIDNETRESOURCE pidn = (LPCIDNETRESOURCE)pidl;
|
|
ULONG rgfOut = SFGAO_CANLINK | SFGAO_HASPROPSHEET | SFGAO_HASSUBFOLDER |
|
|
SFGAO_FOLDER | SFGAO_FILESYSANCESTOR | SFGAO_STORAGEANCESTOR;
|
|
|
|
if (NET_GetFlags(pidn) & SHID_JUNCTION)
|
|
{
|
|
if ((NET_GetType(pidn) == RESOURCETYPE_DISK) ||
|
|
(NET_GetType(pidn) == RESOURCETYPE_ANY))
|
|
rgfOut |= SFGAO_FILESYSTEM | SFGAO_DROPTARGET | SFGAO_CANCOPY | SFGAO_CANMOVE | SFGAO_STORAGE;
|
|
else
|
|
rgfOut &= ~(SFGAO_FILESYSANCESTOR | SFGAO_STORAGEANCESTOR);
|
|
}
|
|
|
|
if (_IsPrintShare(pidn))
|
|
{
|
|
rgfOut |= SFGAO_DROPTARGET; // for drag and drop printing
|
|
rgfOut &= ~(SFGAO_FILESYSANCESTOR | SFGAO_STORAGEANCESTOR | SFGAO_FILESYSTEM | SFGAO_FOLDER | SFGAO_HASSUBFOLDER);
|
|
}
|
|
|
|
if (NET_IsRemoteFld(pidn))
|
|
{
|
|
rgfOut &= ~(SFGAO_FILESYSANCESTOR | SFGAO_STORAGEANCESTOR | SFGAO_FILESYSTEM);
|
|
}
|
|
|
|
*prgfInOut = rgfOut;
|
|
return S_OK;
|
|
|
|
}
|
|
|
|
// This is only used by the CNetRootFolder subclass, but because we can only QI for
|
|
// CLSID_NetFldr, and we can't access protected members of any CNetFolder instance
|
|
// from a member function of CNetRootFolder, we'll make it belong to CNetFolder
|
|
|
|
HRESULT CALLBACK CNetFolder::_AttributesCallbackRoot(IShellFolder2* psf, LPCITEMIDLIST pidl, ULONG* prgfInOut)
|
|
{
|
|
HRESULT hr;
|
|
CNetFolder* pNetF = FolderToNetFolder(psf);
|
|
if (pNetF)
|
|
{
|
|
if (NET_IsValidID(pidl))
|
|
{
|
|
hr = pNetF->CNetFolder::GetAttributesOf(1, &pidl, prgfInOut);
|
|
}
|
|
else
|
|
{
|
|
IShellFolder2* psfFiles;
|
|
hr = pNetF->v_GetFileFolder(&psfFiles);
|
|
if (SUCCEEDED(hr))
|
|
hr = psfFiles->GetAttributesOf(1, &pidl, prgfInOut);
|
|
}
|
|
}
|
|
else
|
|
hr = E_FAIL;
|
|
return hr;
|
|
}
|
|
|
|
// this is called by netfind.c
|
|
|
|
STDAPI CNetwork_EnumSearches(IShellFolder2* psf2, IEnumExtraSearch **ppenum)
|
|
{
|
|
*ppenum = NULL;
|
|
|
|
CNetFolder* pNetF = FolderToNetFolder(psf2);
|
|
return pNetF ? pNetF->EnumSearches(ppenum) : E_INVALIDARG;
|
|
}
|
|
|
|
|
|
// given the resulting ppidl and a pszRest continue to parse through and add in the remainder
|
|
// of the file system path.
|
|
|
|
HRESULT CNetFolder::_ParseRest(LPBC pbc, LPCWSTR pszRest, LPITEMIDLIST* ppidl, DWORD* pdwAttributes)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
// skip leading \ if there is one present
|
|
if (pszRest && pszRest[0] == L'\\')
|
|
pszRest++;
|
|
|
|
if (pszRest && pszRest[0])
|
|
{
|
|
// need to QI to get the agregated case
|
|
IShellFolder* psfBind;
|
|
hr = QueryInterface(IID_PPV_ARG(IShellFolder, &psfBind));
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
// pass down to pick off stuff below including regitems and file names
|
|
IShellFolder* psfSub;
|
|
hr = psfBind->BindToObject(*ppidl, NULL, IID_PPV_ARG(IShellFolder, &psfSub));
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
LPITEMIDLIST pidlSubDir;
|
|
hr = psfSub->ParseDisplayName(NULL, pbc, (LPWSTR)pszRest, NULL, &pidlSubDir, pdwAttributes);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = SHILAppend(pidlSubDir, ppidl);
|
|
}
|
|
psfSub->Release();
|
|
}
|
|
psfBind->Release();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (pdwAttributes)
|
|
{
|
|
LPCITEMIDLIST apidlLast[1] = { ILFindLastID(*ppidl) };
|
|
hr = GetAttributesOf(1, apidlLast, pdwAttributes);
|
|
}
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
// generate an IDLIST from the NETRESOURCESTRUCTURE we have by
|
|
// walking up its parents trying to determine where we
|
|
// are in the namespace
|
|
|
|
BOOL _GetParentResource(NETRESOURCE *pnr, DWORD *pdwbuf)
|
|
{
|
|
if ((pnr->dwDisplayType == RESOURCEDISPLAYTYPE_ROOT) ||
|
|
(WN_SUCCESS != WNetGetResourceParent(pnr, pnr, pdwbuf)))
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
HRESULT CNetFolder::_NetResToIDLists(NETRESOURCE *pnr, DWORD dwbuf, LPITEMIDLIST *ppidl)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
do
|
|
{
|
|
LPITEMIDLIST pidlT;
|
|
hr = _NetResToIDList(pnr, TRUE, TRUE, TRUE, &pidlT);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = SHILPrepend(pidlT, ppidl); // NOTE: SHILPrepend frees on failure
|
|
}
|
|
}
|
|
while (SUCCEEDED(hr) && _GetParentResource(pnr, &dwbuf));
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
// get the parsable network name from the object
|
|
|
|
LPTSTR CNetFolder::_GetNameForParsing(LPCWSTR pwszName, LPTSTR pszBuffer, INT cchBuffer, LPTSTR *ppszRegItem)
|
|
{
|
|
LPTSTR pszRegItem = NULL;
|
|
INT cSlashes = 0;
|
|
|
|
*ppszRegItem = NULL;
|
|
|
|
SHUnicodeToTChar(pwszName, pszBuffer, cchBuffer);
|
|
|
|
// remove the trailing \ if there is one, NTLanMan barfs if we pass a string containing it
|
|
|
|
INT cchPath = lstrlen(pszBuffer)-1;
|
|
if (cchPath > 2)
|
|
{
|
|
// We don't need to call CharPrev if cchPath <= 2.
|
|
// Calling CharPrev is expensive.
|
|
LPTSTR lpTmp = CharPrev(pszBuffer, pszBuffer + cchPath + 1);
|
|
if (*lpTmp == TEXT('\\'))
|
|
*lpTmp = TEXT('\0');
|
|
}
|
|
|
|
// lets walk the name, look for \:: squence to signify the start of a regitem name,
|
|
// and if the number of slashes is > 2 then we should bail
|
|
|
|
LPTSTR pszUNC = pszBuffer+2;
|
|
while (pszUNC && *pszUNC && (cSlashes < 2))
|
|
{
|
|
if ((pszUNC[0] == TEXT('\\')) &&
|
|
(pszUNC[1] == TEXT(':')) && (pszUNC[2] == TEXT(':')))
|
|
{
|
|
*ppszRegItem = pszUNC;
|
|
break;
|
|
}
|
|
|
|
pszUNC = StrChr(pszUNC+1, TEXT('\\'));
|
|
cSlashes++;
|
|
}
|
|
|
|
return pszUNC;
|
|
}
|
|
|
|
|
|
HRESULT CNetFolder::_ParseNetName(HWND hwnd, LPBC pbc,
|
|
LPCWSTR pwszName, ULONG* pchEaten,
|
|
LPITEMIDLIST *ppidl, DWORD *pdwAttrib)
|
|
{
|
|
HRESULT hr;
|
|
struct _NRTEMP
|
|
{
|
|
NETRESOURCE nr;
|
|
TCHAR szBuffer[1024];
|
|
} nrOut = { 0 };
|
|
TCHAR szPath[MAX_PATH];
|
|
DWORD dwres, dwbuf = sizeof(nrOut.szBuffer);
|
|
LPTSTR pszServerShare = NULL;
|
|
LPTSTR pszRestOfName = NULL;
|
|
LPTSTR pszFakeRestOfName = NULL;
|
|
LPTSTR pszRegItem = NULL;
|
|
|
|
// validate the name before we start cracking it...
|
|
|
|
pszFakeRestOfName = _GetNameForParsing(pwszName, szPath, ARRAYSIZE(szPath), &pszRegItem);
|
|
|
|
NETRESOURCE nr = { 0 };
|
|
nr.lpRemoteName = szPath;
|
|
nr.dwType = RESOURCETYPE_ANY;
|
|
|
|
TCHAR szProviderTemp[256];
|
|
nr.lpProvider = (LPTSTR)_GetProvider(NULL, pbc, szProviderTemp, ARRAYSIZE(szProviderTemp));
|
|
|
|
dwres = WNetGetResourceInformation(&nr, &nrOut.nr, &dwbuf, &pszRestOfName);
|
|
if (WN_SUCCESS != dwres)
|
|
{
|
|
TCHAR cT;
|
|
LPTSTR pszTemp;
|
|
|
|
// truncate the string at the \\server\share to try and parse the name,
|
|
// note at this point if MPR resolves the alias on a Novel server this could
|
|
// get very confusing (eg. \\strike\foo\bah may resolve to \\string\bla,
|
|
// yet our concept of what pszRestOfName will be wrong!
|
|
|
|
if (pszFakeRestOfName)
|
|
{
|
|
cT = *pszFakeRestOfName;
|
|
*pszFakeRestOfName = TEXT('\0');
|
|
}
|
|
|
|
dwres = WNetGetResourceInformation(&nr, &nrOut.nr, &dwbuf, &pszTemp);
|
|
if (dwres != WN_SUCCESS)
|
|
{
|
|
// we failed to get a net connection using the truncated string,
|
|
// so lets try and use a new connect (eg. prompt for creds)
|
|
|
|
// NOTE: shouldn't we only be doing this if its an access denied type error?
|
|
|
|
dwres = WNetUseConnection(hwnd, &nr, NULL, NULL, hwnd ? CONNECT_INTERACTIVE:0, NULL, NULL, NULL);
|
|
if (dwres == WN_SUCCESS)
|
|
{
|
|
dwres = WNetGetResourceInformation(&nr, &nrOut, &dwbuf, &pszTemp);
|
|
}
|
|
}
|
|
|
|
if (pszFakeRestOfName)
|
|
*pszFakeRestOfName = cT;
|
|
|
|
pszRestOfName = pszFakeRestOfName;
|
|
}
|
|
|
|
if (WN_SUCCESS == dwres)
|
|
{
|
|
WCHAR wszRestOfName[MAX_PATH] = { 0 };
|
|
|
|
if (pszRestOfName)
|
|
SHTCharToUnicode(pszRestOfName, wszRestOfName, ARRAYSIZE(wszRestOfName));
|
|
|
|
// assume we are truncating at the regitem and parsing through
|
|
|
|
if (pszRegItem)
|
|
pszRestOfName = pszRegItem;
|
|
|
|
// attempt to convert the NETRESOURCE to a string to IDLISTS by walking the
|
|
// parents, then add in Entire Network
|
|
|
|
hr = _NetResToIDLists(&nrOut.nr, dwbuf, ppidl);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
LPITEMIDLIST pidlT;
|
|
hr = _CreateEntireNetwork(&pidlT);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = SHILPrepend(pidlT, ppidl); // NOTE: SHILPrepend frees on failure
|
|
}
|
|
}
|
|
|
|
// if we have a local string then lets continue to parse it by binding to
|
|
// its parent folder, otherwise we just want to return the attributes
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
if (SUCCEEDED(DisplayNameOf(this, *ppidl, SHGDN_FORPARSING, szPath, ARRAYSIZE(szPath))))
|
|
{
|
|
NPTRegisterNameToPidlTranslation(szPath, *ppidl); // no _ILNext b/c this is relative to the Net Places folder
|
|
}
|
|
hr = _ParseRest(pbc, wszRestOfName, ppidl, pdwAttrib);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
hr = HRESULT_FROM_WIN32(dwres);
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
//
|
|
// simple name parsing for the network paths. this makes big assumptions about the
|
|
// \\server\share format we are given, and the type of IDLISTs to return.
|
|
//
|
|
|
|
HRESULT CNetFolder::_AddUnknownIDList(DWORD dwDisplayType, LPITEMIDLIST *ppidl)
|
|
{
|
|
NETRESOURCE nr = { 0 };
|
|
|
|
nr.dwScope = RESOURCE_GLOBALNET;
|
|
nr.dwDisplayType = dwDisplayType;
|
|
nr.dwUsage = RESOURCEUSAGE_CONTAINER;
|
|
nr.lpRemoteName = TEXT("\0"); // null name means fake item
|
|
|
|
LPITEMIDLIST pidlT;
|
|
HRESULT hr = _NetResToIDList(&nr, TRUE, FALSE, FALSE, &pidlT);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = SHILAppend(pidlT, ppidl);
|
|
if (FAILED(hr))
|
|
ILFree(pidlT);
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
HRESULT CNetFolder::_ParseSimple(LPBC pbc, LPWSTR pszName, LPITEMIDLIST* ppidl, DWORD* pdwAttributes)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
NETRESOURCE nr = {0};
|
|
LPWSTR pszSlash;
|
|
LPITEMIDLIST pidlT;
|
|
|
|
*ppidl = NULL;
|
|
|
|
// create the entire network IDLIST, provider and domain elements
|
|
|
|
hr = _CreateEntireNetwork(ppidl);
|
|
|
|
if (SUCCEEDED(hr))
|
|
hr = _AddUnknownIDList(RESOURCEDISPLAYTYPE_NETWORK, ppidl);
|
|
|
|
if (SUCCEEDED(hr))
|
|
hr = _AddUnknownIDList(RESOURCEDISPLAYTYPE_DOMAIN, ppidl);
|
|
|
|
// create the server IDLIST
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
pszSlash = StrChrW(pszName+2, L'\\');
|
|
|
|
if (pszSlash)
|
|
*pszSlash = L'\0';
|
|
|
|
nr.dwScope = RESOURCE_GLOBALNET;
|
|
nr.dwDisplayType = RESOURCEDISPLAYTYPE_SERVER;
|
|
nr.dwType = RESOURCETYPE_DISK;
|
|
nr.dwUsage = RESOURCEUSAGE_CONTAINER;
|
|
nr.lpRemoteName = pszName;
|
|
|
|
hr = _NetResToIDList(&nr, FALSE, FALSE, FALSE, &pidlT);
|
|
if (SUCCEEDED(hr))
|
|
hr = SHILAppend(pidlT, ppidl);
|
|
|
|
if (pszSlash)
|
|
*pszSlash = L'\\';
|
|
|
|
// if we have a trailing \ then lets add in the share part of the IDLIST
|
|
|
|
if (SUCCEEDED(hr) && pszSlash)
|
|
{
|
|
pszSlash = StrChrW(pszSlash+1, L'\\');
|
|
if (pszSlash)
|
|
*pszSlash = L'\0';
|
|
|
|
nr.dwDisplayType = RESOURCEDISPLAYTYPE_SHARE;
|
|
nr.dwUsage = RESOURCEUSAGE_CONNECTABLE;
|
|
nr.lpRemoteName = pszName;
|
|
|
|
hr = _NetResToIDList(&nr, FALSE, FALSE, FALSE, &pidlT);
|
|
if (SUCCEEDED(hr))
|
|
hr = SHILAppend(pidlT, ppidl);
|
|
|
|
if (pszSlash)
|
|
*pszSlash = L'\\';
|
|
}
|
|
}
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = _ParseRest(pbc, pszSlash, ppidl, pdwAttributes);
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
// try parsing out the EntireNet or localised version. if we find that object then try and
|
|
// parse through that to the regitems or other objects which live below. this inturn
|
|
// will cause an instance of CNetFolder to be created to generate the other parsing names.
|
|
//
|
|
// returns:
|
|
// S_FALSE - not rest of net, try something else
|
|
// S_OK - was rest of net, use this
|
|
// FAILED(hr) - error result, return
|
|
|
|
HRESULT CNetRootFolder::_TryParseEntireNet(HWND hwnd, LPBC pbc, WCHAR *pwszName, LPITEMIDLIST *ppidl, DWORD *pdwAttributes)
|
|
{
|
|
HRESULT hr = S_FALSE; // skip, not rest of net
|
|
|
|
*ppidl = NULL;
|
|
|
|
if (!PathIsUNCW(pwszName))
|
|
{
|
|
const WCHAR szEntireNetwork[] = L"EntireNetwork";
|
|
WCHAR szRestOfNet[128];
|
|
INT cchRestOfNet = LoadStringW(HINST_THISDLL, IDS_RESTOFNET, szRestOfNet, ARRAYSIZE(szRestOfNet));
|
|
|
|
BOOL fRestOfNet = !StrCmpNIW(szRestOfNet, pwszName, cchRestOfNet);
|
|
if (!fRestOfNet && !StrCmpNIW(szEntireNetwork, pwszName, ARRAYSIZE(szEntireNetwork)-1))
|
|
{
|
|
fRestOfNet = TRUE;
|
|
cchRestOfNet = ARRAYSIZE(szEntireNetwork)-1;
|
|
}
|
|
|
|
if (fRestOfNet)
|
|
{
|
|
hr = _CreateEntireNetwork(ppidl);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
if (pdwAttributes)
|
|
{
|
|
GetAttributesOf(1, (LPCITEMIDLIST *)ppidl, pdwAttributes);
|
|
}
|
|
hr = S_OK;
|
|
}
|
|
|
|
//
|
|
// if we find extra stuff after the name then lets bind and continue the parsing
|
|
// from there on. this is needed so the we can access regitems burried inside
|
|
// entire net.
|
|
//
|
|
// eg: EntireNetwork\\::{clsid}
|
|
//
|
|
|
|
if (SUCCEEDED(hr) &&
|
|
(pwszName[cchRestOfNet] == L'\\') && pwszName[cchRestOfNet+1])
|
|
{
|
|
IShellFolder *psfRestOfNet;
|
|
hr = BindToObject(*ppidl, NULL, IID_PPV_ARG(IShellFolder, &psfRestOfNet));
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
LPITEMIDLIST pidl;
|
|
hr = psfRestOfNet->ParseDisplayName(hwnd, pbc, pwszName+cchRestOfNet+1, NULL, &pidl, pdwAttributes);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = SHILAppend(pidl, ppidl);
|
|
}
|
|
psfRestOfNet->Release();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
// CNetRootFolder::ParseDisplayname
|
|
// - swtich based on the file system context to see if we need to do a simple parse or not,
|
|
// - check for "EntireNet" and delegate parsing as required.
|
|
|
|
STDMETHODIMP CNetRootFolder::ParseDisplayName(HWND hwnd, LPBC pbc, WCHAR* pszName, ULONG* pchEaten, LPITEMIDLIST* ppidl, DWORD* pdwAttributes)
|
|
{
|
|
if (!ppidl)
|
|
return E_INVALIDARG;
|
|
|
|
*ppidl = NULL;
|
|
|
|
if (!pszName)
|
|
return E_INVALIDARG;
|
|
|
|
HRESULT hr = _TryParseEntireNet(hwnd, pbc, pszName, ppidl, pdwAttributes);
|
|
if (hr == S_FALSE)
|
|
{
|
|
if (PathIsUNCW(pszName))
|
|
{
|
|
LPCITEMIDLIST pidlMapped;
|
|
LPTSTR pszRest = NPTMapNameToPidl(pszName, &pidlMapped);
|
|
if (pidlMapped)
|
|
{
|
|
hr = SHILClone(pidlMapped, ppidl);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = _ParseRest(pbc, pszRest, ppidl, pdwAttributes);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (S_OK == SHIsFileSysBindCtx(pbc, NULL))
|
|
{
|
|
hr = _ParseSimple(pbc, pszName, ppidl, pdwAttributes);
|
|
}
|
|
else
|
|
{
|
|
hr = _ParseNetName(hwnd, pbc, pszName, pchEaten, ppidl, pdwAttributes);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
hr = HRESULT_FROM_WIN32(ERROR_BAD_NET_NAME);
|
|
}
|
|
|
|
if ((HRESULT_FROM_WIN32(ERROR_BAD_NET_NAME) == hr))
|
|
{
|
|
IShellFolder2 *psfFiles;
|
|
if (SUCCEEDED(v_GetFileFolder(&psfFiles)))
|
|
{
|
|
hr = psfFiles->ParseDisplayName(hwnd, pbc, pszName, pchEaten, ppidl, pdwAttributes);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (FAILED(hr))
|
|
{
|
|
ILFree(*ppidl);
|
|
*ppidl = NULL;
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
|
|
STDMETHODIMP CNetRootFolder::EnumObjects(HWND hwnd, DWORD grfFlags, IEnumIDList** ppenum)
|
|
{
|
|
DWORD dwRemote = RMF_GETLINKENUM;
|
|
HANDLE hEnum = NULL;
|
|
|
|
// Do we enumerate the workgroup?
|
|
if (!SHRestricted(REST_ENUMWORKGROUP))
|
|
{
|
|
// Don't enumerate the workgroup, if the restriction says so
|
|
dwRemote |= RMF_FAKENETROOT;
|
|
|
|
// Check the WNet policy to see if we should be showing the
|
|
// entire net object. If not, mark it as shown so that the
|
|
// enumerator doesn't return it.
|
|
if (SHRestricted(REST_NOENTIRENETWORK))
|
|
dwRemote |= RMF_ENTIRENETSHOWN;
|
|
}
|
|
|
|
// if we are not faking the net root then lets call _OpenEnum, otherwise lets ignore
|
|
|
|
if (!(dwRemote & RMF_FAKENETROOT))
|
|
{
|
|
DWORD err = _OpenEnum(hwnd, grfFlags, NULL, &hEnum);
|
|
|
|
// Always add the remote folder to the 'hood
|
|
if (WN_SUCCESS != err)
|
|
{
|
|
// Yes; still show remote anyway (only)
|
|
dwRemote |= RMF_SHOWREMOTE;
|
|
}
|
|
else
|
|
{
|
|
// No; allow everything to be enumerated in the 'hood.
|
|
dwRemote |= RMF_CONTEXT;
|
|
}
|
|
}
|
|
|
|
HRESULT hr = Create_NetFolderEnum(this, grfFlags, dwRemote, hEnum, ppenum);
|
|
|
|
if (FAILED(hr) && hEnum)
|
|
{
|
|
WNetCloseEnum(hEnum);
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
STDMETHODIMP CNetRootFolder::BindToObject(LPCITEMIDLIST pidl, LPBC pbc, REFIID riid, void **ppv)
|
|
{
|
|
HRESULT hr;
|
|
if (NET_IsValidID(pidl))
|
|
hr = CNetFolder::BindToObject(pidl, pbc, riid, ppv);
|
|
else
|
|
{
|
|
IShellFolder2* psfFiles;
|
|
hr = v_GetFileFolder(&psfFiles);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = psfFiles->BindToObject(pidl, pbc, riid, ppv);
|
|
}
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
STDMETHODIMP CNetRootFolder::CompareIDs(LPARAM iCol, LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2)
|
|
{
|
|
HRESULT hr = E_INVALIDARG;
|
|
|
|
// First obtain the collate type of the pidls and their respective
|
|
// collate order.
|
|
|
|
LONG iColateType1 = _GetFilePIDLType(pidl1);
|
|
LONG iColateType2 = _GetFilePIDLType(pidl2);
|
|
|
|
if (iColateType1 == iColateType2)
|
|
{
|
|
// pidls are of same type.
|
|
if (iColateType1 == _HOOD_COL_FILE) // two file system pidls
|
|
{
|
|
IShellFolder2* psfFiles;
|
|
if (SUCCEEDED(v_GetFileFolder(&psfFiles)))
|
|
{
|
|
if (0 == (iCol & SHCIDS_COLUMNMASK))
|
|
{
|
|
// special case this for perf, this is the name compare
|
|
hr = psfFiles->CompareIDs(iCol, pidl1, pidl2);
|
|
}
|
|
else
|
|
{
|
|
SHCOLUMNID scid;
|
|
MapColumnToSCID((UINT)iCol & SHCIDS_COLUMNMASK, &scid);
|
|
int iRet = CompareBySCID(psfFiles, &scid, pidl1, pidl2);
|
|
hr = ResultFromShort(iRet);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// pidls same and are not of type file,
|
|
// so both must be a type understood
|
|
// by the CNetwork class - pass on to compare.
|
|
|
|
hr = CNetFolder::CompareIDs(iCol, pidl1, pidl2);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// ensure that entire network ends up at the head of the list
|
|
|
|
LPCIDNETRESOURCE pidn1 = NET_IsValidID(pidl1);
|
|
LPCIDNETRESOURCE pidn2 = NET_IsValidID(pidl2);
|
|
|
|
if ((pidn1 && (NET_GetDisplayType(pidn1) == RESOURCEDISPLAYTYPE_ROOT)) ||
|
|
(pidn2 && (NET_GetDisplayType(pidn2) == RESOURCEDISPLAYTYPE_ROOT)))
|
|
{
|
|
if (iColateType1 == _HOOD_COL_FILE)
|
|
return ResultFromShort(1);
|
|
else
|
|
return ResultFromShort(-1);
|
|
}
|
|
|
|
// pidls are not of same type, so have already been correctly
|
|
// collated (consequently, sorting is first by type and
|
|
// then by subfield).
|
|
|
|
hr = ResultFromShort(((iColateType2 - iColateType1) > 0) ? 1 : -1);
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
STDMETHODIMP CNetRootFolder::CreateViewObject(HWND hwnd, REFIID riid, void **ppv)
|
|
{
|
|
ASSERT(ILIsEqual(_pidl, (LPCITEMIDLIST)&c_idlNet));
|
|
|
|
if (IsEqualIID(riid, IID_IDropTarget))
|
|
{
|
|
return CNetRootDropTarget_CreateInstance(hwnd, _pidl, (IDropTarget**) ppv);
|
|
}
|
|
return CNetFolder::CreateViewObject(hwnd, riid, ppv);
|
|
}
|
|
|
|
STDMETHODIMP CNetRootFolder::GetAttributesOf(UINT cidl, LPCITEMIDLIST *apidl, ULONG* prgfInOut)
|
|
{
|
|
HRESULT hr;
|
|
|
|
if (IsSelf(cidl, apidl))
|
|
{
|
|
// The user can rename links in the hood.
|
|
hr = CNetFolder::GetAttributesOf(cidl, apidl, prgfInOut);
|
|
*prgfInOut |= SFGAO_CANRENAME;
|
|
}
|
|
else
|
|
{
|
|
hr = GetAttributesCallback(SAFECAST(this, IShellFolder2*), cidl, apidl, prgfInOut, _AttributesCallbackRoot);
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
STDMETHODIMP CNetRootFolder::GetDisplayNameOf(LPCITEMIDLIST pidl, DWORD dwFlags, STRRET* pStrRet)
|
|
{
|
|
HRESULT hr;
|
|
if (NET_IsValidID(pidl) || IsSelf(1, &pidl))
|
|
{
|
|
hr = CNetFolder::GetDisplayNameOf(pidl, dwFlags, pStrRet);
|
|
}
|
|
else
|
|
{
|
|
IShellFolder2* psfFiles;
|
|
hr = v_GetFileFolder(&psfFiles);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = psfFiles->GetDisplayNameOf(pidl, dwFlags, pStrRet);
|
|
}
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
STDMETHODIMP CNetRootFolder::SetNameOf(HWND hwnd, LPCITEMIDLIST pidl, LPCOLESTR lpszName,
|
|
DWORD dwRes, LPITEMIDLIST* ppidl)
|
|
{
|
|
HRESULT hr;
|
|
if (NET_IsValidID(pidl))
|
|
{
|
|
hr = CNetFolder::SetNameOf(hwnd, pidl, lpszName, dwRes, ppidl);
|
|
}
|
|
else
|
|
{
|
|
IShellFolder2* psfFiles;
|
|
hr = v_GetFileFolder(&psfFiles);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = psfFiles->SetNameOf(hwnd, pidl, lpszName, dwRes, ppidl);
|
|
}
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
STDMETHODIMP CNetRootFolder::GetUIObjectOf(HWND hwnd, UINT cidl, LPCITEMIDLIST* apidl,
|
|
REFIID riid, UINT* prgfInOut, void **ppv)
|
|
{
|
|
HRESULT hr = E_INVALIDARG;
|
|
LPCIDNETRESOURCE pidn = cidl ? NET_IsValidID(apidl[0]) : NULL;
|
|
BOOL fStriped = FALSE;
|
|
|
|
*ppv = NULL;
|
|
|
|
if (pidn)
|
|
{
|
|
fStriped = _MakeStripToLikeKinds(&cidl, &apidl, TRUE);
|
|
|
|
if (IsEqualIID(riid, IID_IContextMenu))
|
|
{
|
|
HKEY ahkeys[NKID_COUNT];
|
|
|
|
hr = _OpenKeys(pidn, ahkeys);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
IShellFolder* psfOuter;
|
|
hr = QueryInterface(IID_PPV_ARG(IShellFolder, &psfOuter));
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = CDefFolderMenu_Create2(_pidl, hwnd, cidl, apidl,
|
|
psfOuter, _GetCallbackType(pidn),
|
|
ARRAYSIZE(ahkeys), ahkeys, (IContextMenu**) ppv);
|
|
psfOuter->Release();
|
|
}
|
|
SHRegCloseKeys(ahkeys, ARRAYSIZE(ahkeys));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
hr = CNetFolder::GetUIObjectOf(hwnd, cidl, apidl, riid, prgfInOut, ppv);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
fStriped = _MakeStripToLikeKinds(&cidl, &apidl, FALSE);
|
|
|
|
IShellFolder2* psfFiles;
|
|
hr = v_GetFileFolder(&psfFiles);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = psfFiles->GetUIObjectOf(hwnd, cidl, apidl, riid, prgfInOut, ppv);
|
|
}
|
|
}
|
|
|
|
if (fStriped)
|
|
LocalFree((HLOCAL)apidl);
|
|
|
|
return hr;
|
|
}
|
|
|
|
STDMETHODIMP CNetRootFolder::GetClassID(CLSID* pCLSID)
|
|
{
|
|
*pCLSID = CLSID_NetworkPlaces;
|
|
return S_OK;
|
|
}
|
|
|
|
STDMETHODIMP CNetRootFolder::Initialize(LPCITEMIDLIST pidl)
|
|
{
|
|
ASSERT(ILIsEqual(pidl, (LPCITEMIDLIST)&c_idlNet));
|
|
ASSERT(AssertIsIDListInNameSpace(pidl, &CLSID_NetworkPlaces) && ILIsEmpty(_ILNext(pidl)));
|
|
// Only allow the Net root on the desktop
|
|
|
|
// Don't initialize more than once; we are a singleton object.
|
|
// This is theoretically redundant with the InterlockedCompareExchange
|
|
// below, but redundant reinitialization is by far the common case
|
|
// so we'll optimize it.
|
|
if (_pidl)
|
|
return S_OK;
|
|
|
|
LPITEMIDLIST pidlNew;
|
|
HRESULT hr = SHILClone(pidl, &pidlNew);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
if (SHInterlockedCompareExchange((void**)&_pidl, pidlNew, 0))
|
|
{
|
|
// Some other thread raced with us, throw away our copy
|
|
ILFree(pidlNew);
|
|
}
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
LONG CNetFolder::_GetFilePIDLType(LPCITEMIDLIST pidl)
|
|
{
|
|
if (NET_IsValidID(pidl))
|
|
{
|
|
if (NET_IsRemoteFld((LPIDNETRESOURCE)pidl))
|
|
{
|
|
return _HOOD_COL_REMOTE;
|
|
}
|
|
if (NET_GetDisplayType((LPIDNETRESOURCE)pidl) == RESOURCEDISPLAYTYPE_ROOT)
|
|
{
|
|
return _HOOD_COL_RON;
|
|
}
|
|
return _HOOD_COL_NET;
|
|
}
|
|
return _HOOD_COL_FILE;
|
|
}
|
|
|
|
|
|
BOOL CNetFolder::_MakeStripToLikeKinds(UINT *pcidl, LPCITEMIDLIST **papidl, BOOL fNetObjects)
|
|
{
|
|
BOOL bRet = FALSE;
|
|
LPITEMIDLIST *apidl = (LPITEMIDLIST*)*papidl;
|
|
int cidl = *pcidl;
|
|
|
|
for (int i = 0; i < cidl; i++)
|
|
{
|
|
if ((NET_IsValidID(apidl[i]) != NULL) != fNetObjects)
|
|
{
|
|
LPCITEMIDLIST *apidlHomo = (LPCITEMIDLIST *)LocalAlloc(LPTR, sizeof(*apidlHomo) * cidl);
|
|
if (!apidlHomo)
|
|
return FALSE;
|
|
|
|
int cpidlHomo = 0;
|
|
for (i = 0; i < cidl; i++)
|
|
{
|
|
if ((NET_IsValidID(apidl[i]) != NULL) == fNetObjects)
|
|
{
|
|
apidlHomo[cpidlHomo++] = apidl[i];
|
|
}
|
|
}
|
|
|
|
// Setup to use the stripped version of the pidl array...
|
|
*pcidl = cpidlHomo;
|
|
*papidl = apidlHomo;
|
|
bRet = TRUE;
|
|
}
|
|
}
|
|
return bRet;
|
|
}
|
|
|
|
HRESULT CNetRootFolder::v_GetFileFolder(IShellFolder2 **psf)
|
|
{
|
|
HRESULT hr = SHCacheTrackingFolder((LPCITEMIDLIST)&c_idlNet, CSIDL_NETHOOD, &_psfFiles);
|
|
*psf = _psfFiles;
|
|
return hr;
|
|
}
|
|
|
|
|
|
//
|
|
// pmedium and pformatetcIn == NULL if we are handling QueryGetData
|
|
//
|
|
HRESULT CNetData::GetHDrop(FORMATETC *pformatetcIn, STGMEDIUM *pmedium)
|
|
{
|
|
HRESULT hr = E_INVALIDARG; // assume error
|
|
STGMEDIUM medium;
|
|
LPIDA pida = DataObj_GetHIDA(this, &medium);
|
|
if (pida)
|
|
{
|
|
// Get the first one to see the type.
|
|
LPCIDNETRESOURCE pidn = (LPCIDNETRESOURCE)IDA_GetIDListPtr(pida, 0);
|
|
|
|
if (NULL == pidn)
|
|
hr = E_FAIL;
|
|
|
|
if (pidn && (NET_GetFlags(pidn) & SHID_JUNCTION) &&
|
|
(NET_GetType(pidn) == RESOURCETYPE_DISK))
|
|
{
|
|
// Get HDrop only if we are handling IDataObject::GetData (pmedium != NULL)
|
|
if (pmedium)
|
|
{
|
|
// We have non-null FORMATETC and STGMEDIUM - get the HDrop
|
|
hr = CFSIDLData::GetHDrop(pformatetcIn, pmedium);
|
|
}
|
|
else
|
|
{
|
|
hr = S_OK; // We were handling QueryGetData
|
|
}
|
|
}
|
|
HIDA_ReleaseStgMedium(pida, &medium);
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
LPTSTR NET_GetProviderFromRes(LPCIDNETRESOURCE pidn, LPTSTR pszBuff, UINT cchBuff);
|
|
|
|
// By the way...Win95 shipped with the below provider
|
|
// names. Since the name can be changed and be localized,
|
|
// we have to try and map these correctly for net pidl
|
|
// interop.
|
|
|
|
//
|
|
// get the network resource name from an item. this is not a file system path!
|
|
//
|
|
// example:
|
|
// server \\server or strike/sys
|
|
// share \\server\share or strike/sys
|
|
// printer \\server\printer
|
|
// provider "provider name"
|
|
// entire net "Entire Network"
|
|
//
|
|
// in:
|
|
// pidn the item
|
|
// cchBuff size of buffer in chars.
|
|
//
|
|
// out:
|
|
// pszBuff return buffer
|
|
//
|
|
// returns:
|
|
// address of the input buffer (pszBuff)
|
|
//
|
|
LPTSTR NET_CopyResName(LPCIDNETRESOURCE pidn, LPTSTR pszBuff, UINT cchBuff)
|
|
{
|
|
if (NET_IsUnicode(pidn))
|
|
{
|
|
LPBYTE pb = (LPBYTE)pidn->szNetResName;
|
|
pb += lstrlenA((LPSTR)pb) + 1; // Skip over ansi net name
|
|
if (NET_FHasProvider(pidn))
|
|
pb += lstrlenA((LPSTR)pb) + 1; // Skip over ansi provider
|
|
if (NET_FHasComment(pidn))
|
|
pb += lstrlenA((LPSTR)pb) + 1; // Skip over comment
|
|
ualstrcpyn(pszBuff, (LPNWSTR)pb, cchBuff);
|
|
}
|
|
else
|
|
{
|
|
SHAnsiToTChar(pidn->szNetResName, pszBuff, cchBuff);
|
|
}
|
|
return pszBuff;
|
|
}
|
|
|
|
//
|
|
// get the provider name from an item. some items do not have providers stored
|
|
// in them. for example the "*" indicates where the provider is stored in the
|
|
// two different forms of network pidls.
|
|
// [entire net] [provider *] [server] [share] [... file system]
|
|
// [server *] [share] [... file system]
|
|
// in:
|
|
// pidn item (single item PIDL) to try to get the provider name from
|
|
// cchBuff size in chars.
|
|
// out:
|
|
// pszBuff output
|
|
//
|
|
LPTSTR NET_CopyProviderName(LPCIDNETRESOURCE pidn, LPTSTR pszBuff, UINT cchBuff)
|
|
{
|
|
*pszBuff = 0;
|
|
|
|
if (!NET_FHasProvider(pidn))
|
|
return NULL;
|
|
|
|
// try the wNetType at the end of the pidl
|
|
|
|
const BYTE *pb = (LPBYTE)pidn + pidn->cb - sizeof(WORD);
|
|
DWORD dwNetType = *((UNALIGNED WORD *)pb) << 16;
|
|
|
|
if (dwNetType && (dwNetType <= WNNC_NET_LARGEST) &&
|
|
(WNetGetProviderName(dwNetType, pszBuff, (ULONG*)&cchBuff) == WN_SUCCESS))
|
|
{
|
|
return pszBuff;
|
|
}
|
|
|
|
// Try the old way...
|
|
|
|
pb = (LPBYTE)pidn->szNetResName + lstrlenA(pidn->szNetResName) + 1; // Skip over ansi net name
|
|
|
|
if (NET_IsUnicode(pidn))
|
|
{
|
|
pb += lstrlenA((LPSTR)pb) + 1; // Skip over ansi provider
|
|
if (NET_FHasComment(pidn))
|
|
pb += lstrlenA((LPSTR)pb) + 1; // Skip over comment
|
|
pb += (ualstrlen((LPNWSTR)pb) + 1) * sizeof(WCHAR); // skip over unicode net name
|
|
ualstrcpyn(pszBuff, (LPNWSTR)pb, cchBuff);
|
|
}
|
|
else
|
|
{
|
|
SHAnsiToTChar((LPSTR)pb, pszBuff, cchBuff);
|
|
}
|
|
|
|
// Map from Win95 net provider name if possible...
|
|
for (int i = 0; i < ARRAYSIZE(c_rgProviderMap); i++)
|
|
{
|
|
if (lstrcmp(pszBuff, c_rgProviderMap[i].lpName) == 0)
|
|
{
|
|
DWORD dwNetType = c_rgProviderMap[i].wNetType << 16;
|
|
if (dwNetType && (dwNetType <= WNNC_NET_LARGEST))
|
|
{
|
|
*pszBuff = 0;
|
|
WNetGetProviderName(dwNetType, pszBuff, (LPDWORD)&cchBuff);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
return pszBuff;
|
|
}
|
|
|
|
//
|
|
// get the comment if there is one from the net item
|
|
//
|
|
LPTSTR NET_CopyComment(LPCIDNETRESOURCE pidn, LPTSTR pszBuff, UINT cchBuff)
|
|
{
|
|
*pszBuff = 0;
|
|
|
|
LPCSTR pszT = pidn->szNetResName + lstrlenA(pidn->szNetResName) + 1;
|
|
if (NET_FHasComment(pidn))
|
|
{
|
|
if (NET_FHasProvider(pidn))
|
|
pszT += lstrlenA(pszT) + 1;
|
|
if (NET_IsUnicode(pidn))
|
|
{
|
|
pszT += lstrlenA(pszT) + 1; // Skip Ansi comment
|
|
|
|
LPNCWSTR pszW = (LPNCWSTR)pszT; // We're at the unicode portion of the pidl
|
|
pszW += ualstrlen(pszW) + 1; // Skip Unicode Name
|
|
if (NET_FHasProvider(pidn))
|
|
pszW += ualstrlen(pszW) + 1; // Skip Unicode Provider
|
|
ualstrcpyn(pszBuff, pszW, cchBuff);
|
|
}
|
|
else
|
|
{
|
|
SHAnsiToUnicode(pszT, pszBuff, cchBuff);
|
|
}
|
|
}
|
|
return pszBuff;
|
|
}
|
|
|
|
// pidlRemainder will be filled in (only in the TRUE return case) with a
|
|
// pointer to the part of the IDL (if any) past the remote regitem.
|
|
// This value may be used, for example, to differentiate between a remote
|
|
// printer folder and a printer under a remote printer folder
|
|
|
|
BOOL NET_IsRemoteRegItem(LPCITEMIDLIST pidl, REFCLSID rclsid, LPCITEMIDLIST* ppidlRemainder)
|
|
{
|
|
BOOL bRet = FALSE;
|
|
// in "My Network Places"
|
|
if (pidl && IsIDListInNameSpace(pidl, &CLSID_NetworkPlaces))
|
|
{
|
|
LPCITEMIDLIST pidlStart = pidl; // save this
|
|
|
|
// Now, search for a server item. HACKHACK: this assume everything from
|
|
// the NetHood to the server item is a shell pidl with a bFlags field!!
|
|
|
|
for (pidl = _ILNext(pidl); !ILIsEmpty(pidl); pidl = _ILNext(pidl))
|
|
{
|
|
if ((SIL_GetType(pidl) & SHID_TYPEMASK) == SHID_NET_SERVER)
|
|
{
|
|
LPITEMIDLIST pidlToTest;
|
|
|
|
// Found a server. Is the thing after it a remote registry item?
|
|
pidl = _ILNext(pidl);
|
|
|
|
*ppidlRemainder = _ILNext(pidl);
|
|
|
|
pidlToTest = ILCloneUpTo(pidlStart, *ppidlRemainder);
|
|
if (pidlToTest)
|
|
{
|
|
CLSID clsid;
|
|
bRet = SUCCEEDED(GetCLSIDFromIDList(pidlToTest, &clsid)) && IsEqualCLSID(rclsid, clsid);
|
|
ILFree(pidlToTest);
|
|
}
|
|
break; // done
|
|
}
|
|
}
|
|
}
|
|
return bRet;
|
|
}
|
|
|
|
|
|
|
|
|
|
//
|
|
// Get the provider name from an absolute IDLIST.
|
|
// Parameters:
|
|
// pidlAbs -- Specifies the Absolute IDList to the file system object
|
|
//
|
|
LPTSTR NET_GetProviderFromIDList(LPCITEMIDLIST pidlAbs, LPTSTR pszBuff, UINT cchBuff)
|
|
{
|
|
return NET_GetProviderFromRes((LPCIDNETRESOURCE)_ILNext(pidlAbs), pszBuff, cchBuff);
|
|
}
|
|
|
|
//
|
|
// Get the provider name from a relative IDLIST.
|
|
// in:
|
|
// pidn potentially multi level item to try to get the resource from
|
|
//
|
|
LPTSTR NET_GetProviderFromRes(LPCIDNETRESOURCE pidn, LPTSTR pszBuffer, UINT cchBuffer)
|
|
{
|
|
// If this guy is the REST of network item, we increment to the
|
|
// next IDLIST - If at root return NULL
|
|
if (pidn->cb == 0)
|
|
return NULL;
|
|
|
|
//
|
|
// If the IDLIST starts with a ROOT_REGITEM, then skip to the
|
|
// next item in the list...
|
|
if (pidn->bFlags == SHID_ROOT_REGITEM)
|
|
{
|
|
pidn = (LPIDNETRESOURCE)_ILNext((LPITEMIDLIST)pidn);
|
|
if (pidn->cb == 0)
|
|
return NULL;
|
|
}
|
|
|
|
// If the IDLIST includes Entire Network, the provider will be
|
|
// part of the next component.
|
|
if (NET_GetDisplayType(pidn) == RESOURCEDISPLAYTYPE_ROOT)
|
|
{
|
|
pidn = (LPIDNETRESOURCE)_ILNext((LPITEMIDLIST)pidn);
|
|
if (pidn->cb == 0)
|
|
return NULL;
|
|
}
|
|
|
|
// If the next component after the 'hood or Entire Network is
|
|
// a network object, its name is the provider name, else the
|
|
// provider name comes after the remote name.
|
|
if (NET_GetDisplayType(pidn) == RESOURCEDISPLAYTYPE_NETWORK)
|
|
{
|
|
// Simply return the name field back for the item.
|
|
return NET_CopyResName(pidn, pszBuffer, cchBuffer);
|
|
}
|
|
else
|
|
{
|
|
// Nope one of the items in the neighborhood view was selected
|
|
// The Provider name is stored after ther resource name
|
|
return NET_CopyProviderName(pidn, pszBuffer, cchBuffer);
|
|
}
|
|
}
|
|
|
|
#define PTROFFSET(pBase, p) ((int) ((LPBYTE)(p) - (LPBYTE)(pBase)))
|
|
|
|
|
|
//
|
|
// fill in pmedium with a NRESARRAY
|
|
//
|
|
// pmedium == NULL if we are handling QueryGetData
|
|
//
|
|
STDAPI CNetData_GetNetResource(IDataObject *pdtobj, STGMEDIUM *pmedium)
|
|
{
|
|
HRESULT hr = E_OUTOFMEMORY;
|
|
LPITEMIDLIST pidl;
|
|
STGMEDIUM medium;
|
|
LPIDA pida = DataObj_GetHIDA(pdtobj, &medium);
|
|
|
|
ASSERT(pida && pida->cidl);
|
|
|
|
// First, get the provider name from the first one (assuming they are common).
|
|
pidl = IDA_ILClone(pida, 0);
|
|
if (pidl)
|
|
{
|
|
TCHAR szProvider[MAX_PATH];
|
|
LPCTSTR pszProvider = NET_GetProviderFromIDList(pidl, szProvider, ARRAYSIZE(szProvider));
|
|
if (pmedium)
|
|
{
|
|
TCHAR szName[MAX_PATH];
|
|
UINT cbHeader = sizeof(NRESARRAY) + (sizeof(NETRESOURCE) * (pida->cidl - 1));
|
|
UINT cbRequired, iItem;
|
|
|
|
// Calculate required size
|
|
cbRequired = cbHeader;
|
|
if (pszProvider)
|
|
cbRequired += (lstrlen(pszProvider) + 1) * sizeof(TCHAR);
|
|
|
|
for (iItem = 0; iItem < pida->cidl; iItem++)
|
|
{
|
|
LPCIDNETRESOURCE pidn = (LPCIDNETRESOURCE)IDA_GetIDListPtr(pida, iItem);
|
|
NET_CopyResName(pidn, szName, ARRAYSIZE(szName));
|
|
cbRequired += (lstrlen(szName) + 1) * sizeof(TCHAR);
|
|
}
|
|
|
|
//
|
|
// Indicate that the caller should release hmem.
|
|
//
|
|
pmedium->pUnkForRelease = NULL;
|
|
pmedium->tymed = TYMED_HGLOBAL;
|
|
pmedium->hGlobal = GlobalAlloc(GPTR, cbRequired);
|
|
if (pmedium->hGlobal)
|
|
{
|
|
LPNRESARRAY panr = (LPNRESARRAY)pmedium->hGlobal;
|
|
LPTSTR pszT = (LPTSTR)((LPBYTE)panr + cbHeader);
|
|
LPTSTR pszEnd = (LPTSTR)((LPBYTE)panr + cbRequired);
|
|
UINT offProvider = 0;
|
|
|
|
panr->cItems = pida->cidl;
|
|
|
|
// Copy the provider name. This is not necessary,
|
|
// if we are dragging providers.
|
|
if (pszProvider)
|
|
{
|
|
StrCpy(pszT, pszProvider); // buffer compute above on cch
|
|
offProvider = PTROFFSET(panr, pszT);
|
|
pszT += lstrlen(pszT) + 1;
|
|
}
|
|
|
|
//
|
|
// For each item, fill each NETRESOURCE and append resource
|
|
// name at the end. Note that we should put offsets in
|
|
// lpProvider and lpRemoteName.
|
|
//
|
|
for (iItem = 0; iItem < pida->cidl; iItem++)
|
|
{
|
|
LPNETRESOURCE pnr = &panr->nr[iItem];
|
|
LPCIDNETRESOURCE pidn = (LPCIDNETRESOURCE)IDA_GetIDListPtr(pida, iItem);
|
|
|
|
ASSERT(pnr->dwScope == 0);
|
|
ASSERT(pnr->lpLocalName==NULL);
|
|
ASSERT(pnr->lpComment==NULL);
|
|
|
|
pnr->dwType = NET_GetType(pidn);
|
|
pnr->dwDisplayType = NET_GetDisplayType(pidn);
|
|
pnr->dwUsage = NET_GetUsage(pidn);
|
|
NET_CopyResName(pidn, pszT, (UINT)(pszEnd-pszT)); // buffer compute above on cch
|
|
|
|
if (pnr->dwDisplayType == RESOURCEDISPLAYTYPE_ROOT)
|
|
{
|
|
pnr->lpProvider = NULL;
|
|
pnr->lpRemoteName = NULL;
|
|
}
|
|
else if (pnr->dwDisplayType == RESOURCEDISPLAYTYPE_NETWORK)
|
|
{
|
|
*((UINT *) &pnr->lpProvider) = PTROFFSET(panr, pszT);
|
|
ASSERT(pnr->lpRemoteName == NULL);
|
|
}
|
|
else
|
|
{
|
|
*((UINT *) &pnr->lpProvider) = offProvider;
|
|
*((UINT *) &pnr->lpRemoteName) = PTROFFSET(panr, pszT);
|
|
}
|
|
pszT += lstrlen(pszT) + 1;
|
|
}
|
|
|
|
ASSERT(pszEnd == pszT);
|
|
hr = S_OK;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
hr = S_OK; // handing QueryGetData, yes, we have it
|
|
}
|
|
ILFree(pidl);
|
|
}
|
|
|
|
HIDA_ReleaseStgMedium(pida, &medium);
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
// fill in pmedium with an HGLOBAL version of a NRESARRAY
|
|
|
|
STDAPI CNetData_GetNetResourceForFS(IDataObject *pdtobj, STGMEDIUM *pmedium)
|
|
{
|
|
HRESULT hr = E_OUTOFMEMORY;
|
|
LPITEMIDLIST pidlAbs;
|
|
STGMEDIUM medium;
|
|
LPIDA pida = DataObj_GetHIDA(pdtobj, &medium);
|
|
|
|
ASSERT(pida && medium.hGlobal); // we created this...
|
|
|
|
//
|
|
// NOTES: Even though we may have multiple FS objects in this HIDA,
|
|
// we know that they share the root. Therefore, getting the pidl for
|
|
// the first item is always sufficient.
|
|
//
|
|
|
|
pidlAbs = IDA_ILClone(pida, 0);
|
|
if (pidlAbs)
|
|
{
|
|
LPITEMIDLIST pidl;
|
|
|
|
ASSERT(AssertIsIDListInNameSpace(pidlAbs, &CLSID_NetworkPlaces));
|
|
|
|
//
|
|
// Look for the JUNCTION point (starting from the second ID)
|
|
//
|
|
for (pidl = _ILNext(pidlAbs); !ILIsEmpty(pidl); pidl = _ILNext(pidl))
|
|
{
|
|
LPIDNETRESOURCE pidn = (LPIDNETRESOURCE)pidl;
|
|
if (NET_GetFlags(pidn) & SHID_JUNCTION)
|
|
{
|
|
//
|
|
// We found the JUNCTION point (which is s share).
|
|
// Return the HNRES to it.
|
|
//
|
|
TCHAR szProvider[MAX_PATH];
|
|
TCHAR szRemote[MAX_PATH];
|
|
UINT cbRequired;
|
|
LPCTSTR pszProvider = NET_GetProviderFromIDList(pidlAbs, szProvider, ARRAYSIZE(szProvider));
|
|
LPCTSTR pszRemoteName = NET_CopyResName(pidn, szRemote, ARRAYSIZE(szRemote));
|
|
UINT cbProvider = lstrlen(pszProvider) * sizeof(TCHAR) + sizeof(TCHAR);
|
|
|
|
//
|
|
// This should not be a provider node.
|
|
// This should not be the last ID in pidlAbs.
|
|
//
|
|
ASSERT(pszProvider != pszRemoteName);
|
|
ASSERT(!ILIsEmpty(_ILNext(pidl)));
|
|
|
|
cbRequired = sizeof(NRESARRAY) + cbProvider + lstrlen(pszRemoteName) * sizeof(TCHAR) + sizeof(TCHAR);
|
|
|
|
pmedium->pUnkForRelease = NULL;
|
|
pmedium->tymed = TYMED_HGLOBAL;
|
|
pmedium->hGlobal = GlobalAlloc(GPTR, cbRequired);
|
|
if (pmedium->hGlobal)
|
|
{
|
|
LPNRESARRAY panr = (LPNRESARRAY)pmedium->hGlobal;
|
|
LPNETRESOURCE pnr = &panr->nr[0];
|
|
LPTSTR pszT = (LPTSTR)(panr + 1);
|
|
|
|
ASSERT(pnr->dwScope == 0);
|
|
ASSERT(pnr->lpLocalName == NULL);
|
|
ASSERT(pnr->lpComment == NULL);
|
|
|
|
panr->cItems = 1;
|
|
|
|
pnr->dwType = NET_GetType(pidn);
|
|
pnr->dwDisplayType = NET_GetDisplayType(pidn);
|
|
pnr->dwUsage = NET_GetUsage(pidn);
|
|
|
|
*((UINT *) &pnr->lpProvider) = sizeof(NRESARRAY);
|
|
StrCpy(pszT, pszProvider); // buffer compute above on cch
|
|
ASSERT(PTROFFSET(panr, pszT) == sizeof(NRESARRAY));
|
|
pszT += cbProvider / sizeof(TCHAR);
|
|
|
|
*((UINT *) &pnr->lpRemoteName) = sizeof(NRESARRAY) + cbProvider;
|
|
ASSERT(PTROFFSET(panr, pszT) == (int)sizeof(NRESARRAY) + (int)cbProvider);
|
|
StrCpy(pszT, pszRemoteName); // buffer compute above on cch
|
|
|
|
ASSERT(((LPBYTE)panr) + cbRequired == (LPBYTE)pszT + (lstrlen(pszT) + 1) * sizeof(TCHAR));
|
|
hr = S_OK;
|
|
}
|
|
else
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
ASSERT(!ILIsEmpty(pidl)); // We should have found the junction point.
|
|
ILFree(pidlAbs);
|
|
}
|
|
HIDA_ReleaseStgMedium(pida, &medium);
|
|
return hr;
|
|
}
|
|
|
|
STDMETHODIMP CNetData::QueryGetData(FORMATETC *pformatetc)
|
|
{
|
|
if (pformatetc->tymed & TYMED_HGLOBAL)
|
|
{
|
|
if (pformatetc->cfFormat == g_cfNetResource)
|
|
return CNetData_GetNetResource(this, NULL);
|
|
|
|
if (pformatetc->cfFormat == CF_HDROP)
|
|
return GetHDrop(NULL, NULL);
|
|
}
|
|
|
|
return CFSIDLData::QueryGetData(pformatetc);
|
|
}
|
|
|
|
STDMETHODIMP CNetData::GetData(FORMATETC *pformatetc, STGMEDIUM *pmedium)
|
|
{
|
|
if (pformatetc->tymed & TYMED_HGLOBAL)
|
|
{
|
|
if (pformatetc->cfFormat == g_cfNetResource)
|
|
return CNetData_GetNetResource(this, pmedium);
|
|
|
|
if (pformatetc->cfFormat == CF_HDROP)
|
|
return GetHDrop(pformatetc, pmedium);
|
|
}
|
|
|
|
return CFSIDLData::GetData(pformatetc, pmedium);
|
|
}
|
|
|
|
BOOL GetPathFromDataObject(IDataObject *pdtobj, DWORD dwData, LPTSTR pszFileName)
|
|
{
|
|
BOOL bRet = FALSE;
|
|
BOOL fUnicode = FALSE;
|
|
HRESULT hr;
|
|
|
|
if (dwData & (DTID_FDESCW | DTID_FDESCA))
|
|
{
|
|
FORMATETC fmteW = {g_cfFileGroupDescriptorW, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
|
|
STGMEDIUM medium = {0};
|
|
|
|
hr = pdtobj->GetData(&fmteW, &medium);
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
fUnicode = TRUE;
|
|
}
|
|
else
|
|
{
|
|
FORMATETC fmteA = {g_cfFileGroupDescriptorA, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
|
|
hr = pdtobj->GetData(&fmteA, &medium);
|
|
}
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
if (fUnicode)
|
|
{
|
|
FILEGROUPDESCRIPTORW *pfgdW = (FILEGROUPDESCRIPTORW *)GlobalLock(medium.hGlobal);
|
|
if (pfgdW)
|
|
{
|
|
if (pfgdW->cItems == 1)
|
|
{
|
|
SHUnicodeToTChar(pfgdW->fgd[0].cFileName, pszFileName, MAX_PATH);
|
|
}
|
|
bRet = TRUE;
|
|
GlobalUnlock(medium.hGlobal);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
FILEGROUPDESCRIPTORA *pfgdA = (FILEGROUPDESCRIPTORA*)GlobalLock(medium.hGlobal);
|
|
if (pfgdA)
|
|
{
|
|
if (pfgdA->cItems == 1)
|
|
{
|
|
SHAnsiToTChar(pfgdA->fgd[0].cFileName, pszFileName, MAX_PATH);
|
|
}
|
|
bRet = TRUE;
|
|
GlobalUnlock(medium.hGlobal);
|
|
}
|
|
}
|
|
ReleaseStgMedium(&medium);
|
|
}
|
|
}
|
|
|
|
return bRet;
|
|
}
|
|
|
|
class CNetRootDropTarget : public CIDLDropTarget
|
|
{
|
|
friend HRESULT CNetRootDropTarget_CreateInstance(HWND hwnd, LPCITEMIDLIST pidl, IDropTarget **ppdropt);
|
|
public:
|
|
CNetRootDropTarget(HWND hwnd) : CIDLDropTarget(hwnd) { };
|
|
|
|
// IDropTarget methods overwirte
|
|
STDMETHODIMP DragEnter(IDataObject *pdtobj, DWORD grfKeyState, POINTL pt, DWORD *pdwEffect);
|
|
STDMETHODIMP Drop(IDataObject *pdtobj, DWORD grfKeyState, POINTL pt, DWORD *pdwEffect);
|
|
|
|
private:
|
|
~CNetRootDropTarget();
|
|
IDropTarget *_pdtgHood; // file system drop target
|
|
};
|
|
|
|
CNetRootDropTarget::~CNetRootDropTarget()
|
|
{
|
|
if (_pdtgHood)
|
|
_pdtgHood->Release();
|
|
}
|
|
|
|
STDMETHODIMP CNetRootDropTarget::DragEnter(IDataObject *pdtobj, DWORD grfKeyState, POINTL pt, DWORD *pdwEffect)
|
|
{
|
|
CIDLDropTarget::DragEnter(pdtobj, grfKeyState, pt, pdwEffect);
|
|
|
|
if ((m_dwData & (DTID_NETRES | DTID_HIDA)) == (DTID_NETRES | DTID_HIDA))
|
|
{
|
|
// NETRESOURCE (DTID_NETRES) allow link
|
|
*pdwEffect &= DROPEFFECT_LINK;
|
|
}
|
|
else if (((m_dwData & (DTID_FDESCW | DTID_CONTENTS)) == (DTID_FDESCW | DTID_CONTENTS)) ||
|
|
((m_dwData & (DTID_FDESCA | DTID_CONTENTS)) == (DTID_FDESCA | DTID_CONTENTS)) )
|
|
{
|
|
// dragging an URL from the web browser gives a FILECONTENTS version
|
|
// of a .URL file. accept that here for Internet Shortcut (.url)
|
|
TCHAR szFileName[MAX_PATH];
|
|
if (GetPathFromDataObject(pdtobj, m_dwData, szFileName) &&
|
|
(0 == lstrcmpi(PathFindExtension(szFileName), TEXT(".url"))))
|
|
{
|
|
*pdwEffect &= DROPEFFECT_LINK;
|
|
}
|
|
else
|
|
{
|
|
*pdwEffect = DROPEFFECT_NONE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
*pdwEffect = DROPEFFECT_NONE;
|
|
}
|
|
|
|
m_dwEffectLastReturned = *pdwEffect;
|
|
return S_OK;
|
|
}
|
|
|
|
STDMETHODIMP CNetRootDropTarget::Drop(IDataObject *pdtobj, DWORD grfKeyState, POINTL pt, DWORD *pdwEffect)
|
|
{
|
|
*pdwEffect &= DROPEFFECT_LINK;
|
|
|
|
HRESULT hr = CIDLDropTarget::DragDropMenu(DROPEFFECT_LINK, pdtobj,
|
|
pt, pdwEffect, NULL, NULL, POPUP_NONDEFAULTDD, grfKeyState);
|
|
if (*pdwEffect)
|
|
{
|
|
if (!_pdtgHood)
|
|
{
|
|
LPITEMIDLIST pidl = SHCloneSpecialIDList(NULL, CSIDL_NETHOOD, FALSE);
|
|
if (pidl)
|
|
{
|
|
IShellFolder *psf;
|
|
if (SUCCEEDED(SHBindToObject(NULL, IID_X_PPV_ARG(IShellFolder, pidl, &psf))))
|
|
{
|
|
psf->CreateViewObject(_GetWindow(), IID_PPV_ARG(IDropTarget, &_pdtgHood));
|
|
psf->Release();
|
|
}
|
|
ILFree(pidl);
|
|
}
|
|
}
|
|
|
|
if (_pdtgHood)
|
|
{
|
|
// force link through the dwEffect and keyboard
|
|
*pdwEffect &= DROPEFFECT_LINK;
|
|
grfKeyState = MK_LBUTTON | MK_CONTROL | MK_SHIFT | MK_FAKEDROP;
|
|
hr = SHSimulateDrop(_pdtgHood, pdtobj, grfKeyState, NULL, pdwEffect);
|
|
}
|
|
else
|
|
*pdwEffect = 0;
|
|
}
|
|
|
|
CIDLDropTarget::DragLeave();
|
|
return hr;
|
|
}
|
|
|
|
HRESULT CNetRootDropTarget_CreateInstance(HWND hwnd, LPCITEMIDLIST pidl, IDropTarget **ppdropt)
|
|
{
|
|
*ppdropt = NULL;
|
|
|
|
CNetRootDropTarget *pnrdt = new CNetRootDropTarget(hwnd);
|
|
if (!pnrdt)
|
|
return E_OUTOFMEMORY;
|
|
|
|
HRESULT hr = pnrdt->_Init(pidl);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = pnrdt->QueryInterface(IID_PPV_ARG(IDropTarget, ppdropt));
|
|
}
|
|
pnrdt->Release();
|
|
return hr;
|
|
}
|
|
|
|
// This part is psuedo bogus. Basically we have problems at times doing a
|
|
// translation from things like \\pyrex\user to the appropriate PIDL,
|
|
// especially if you want to avoid the overhead of hitting the network and
|
|
// also problems of knowing if the server is in the "HOOD"
|
|
//
|
|
// We must maintain the mapping table in UNICODE internally, because
|
|
// IShellFolder::ParseDisplayName uses UNICODE, and we don't want to have
|
|
// to deal with lstrlen(dbcs) != lstrlen(sbcs) problems.
|
|
//
|
|
|
|
typedef struct _NPT_ITEM
|
|
{
|
|
struct _NPT_ITEM *pnptNext; // Pointer to next item;
|
|
LPCITEMIDLIST pidl; // The pidl
|
|
USHORT cchName; // size of the name in characters.
|
|
WCHAR szName[1]; // The name to translate from
|
|
} NPT_ITEM;
|
|
|
|
// Each process will maintain their own list.
|
|
NPT_ITEM *g_pnptHead = NULL;
|
|
|
|
//
|
|
// Function to register translations from Path to IDList translations.
|
|
//
|
|
void NPTRegisterNameToPidlTranslation(LPCTSTR pszPath, LPCITEMIDLIST pidl)
|
|
{
|
|
NPT_ITEM *pnpt;
|
|
int cItemsRemoved = 0;
|
|
WCHAR szPath[MAX_PATH];
|
|
|
|
// We currently are only interested in UNC Roots
|
|
// If the table becomes large we can reduce this to only servers...
|
|
|
|
if (!PathIsUNC(pszPath))
|
|
return; // Not interested.
|
|
|
|
//
|
|
// If this item is not a root we need to count how many items to remove
|
|
//
|
|
SHTCharToUnicode(pszPath, szPath, ARRAYSIZE(szPath));
|
|
while (!PathIsUNCServerShare(szPath))
|
|
{
|
|
cItemsRemoved++;
|
|
if (!PathRemoveFileSpecW(szPath))
|
|
return; // Did not get back to a valid root
|
|
}
|
|
|
|
ENTERCRITICAL;
|
|
|
|
// We don't want to add duplicates
|
|
for (pnpt = g_pnptHead; pnpt != NULL ; pnpt = pnpt->pnptNext)
|
|
{
|
|
if (StrCmpIW(szPath, pnpt->szName) == 0)
|
|
break;
|
|
}
|
|
|
|
if (pnpt == NULL)
|
|
{
|
|
UINT cch = lstrlenW(szPath);
|
|
pnpt = (NPT_ITEM *)LocalAlloc(LPTR, sizeof(NPT_ITEM) + cch * sizeof(WCHAR));
|
|
if (pnpt)
|
|
{
|
|
pnpt->pidl = ILClone(pidl);
|
|
if (pnpt->pidl)
|
|
{
|
|
while (cItemsRemoved--)
|
|
{
|
|
ILRemoveLastID((LPITEMIDLIST)pnpt->pidl);
|
|
}
|
|
pnpt->pnptNext = g_pnptHead;
|
|
g_pnptHead = pnpt;
|
|
pnpt->cchName = (USHORT)cch;
|
|
StrCpyNW(pnpt->szName, szPath, cch + 1);
|
|
}
|
|
else
|
|
{
|
|
LocalFree((HLOCAL)pnpt);
|
|
}
|
|
}
|
|
}
|
|
LEAVECRITICAL;
|
|
}
|
|
|
|
// The main function to attemp to map a portion of the name into an idlist
|
|
// Right now limit it to UNC roots
|
|
//
|
|
LPWSTR NPTMapNameToPidl(LPCWSTR pszPath, LPCITEMIDLIST *ppidl)
|
|
{
|
|
NPT_ITEM *pnpt;
|
|
|
|
*ppidl = NULL;
|
|
|
|
ENTERCRITICAL;
|
|
|
|
// See if we can find the item in the list.
|
|
for (pnpt = g_pnptHead; pnpt != NULL ; pnpt = pnpt->pnptNext)
|
|
{
|
|
if (IntlStrEqNIW(pszPath, pnpt->szName, pnpt->cchName) &&
|
|
((pszPath[pnpt->cchName] == TEXT('\\')) || (pszPath[pnpt->cchName] == TEXT('\0'))))
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
LEAVECRITICAL;
|
|
|
|
// See if we found a match
|
|
if (pnpt == NULL)
|
|
return NULL;
|
|
|
|
// Found a match
|
|
*ppidl = pnpt->pidl;
|
|
return (LPWSTR)pszPath + pnpt->cchName; // points to slash
|
|
}
|