//+--------------------------------------------------------------------------- // // Microsoft Windows // Copyright (C) Microsoft Corporation, 1997. // // File: I S H E L L F . C P P // // Contents: IShellFolder implementation for CUPnPDeviceFolder // Implemention of CUPnPDeviceFoldPidl // // Notes: The IShellFolder interface is used to manage folders within // the namespace. Objects that support IShellFolder are // usually created by other shell folder objects, with the root // object (the Desktop shell folder) being returned from the // SHGetDesktopFolder function. // // Author: jeffspr 22 Sep 1997 // //---------------------------------------------------------------------------- #include "pch.h" #pragma hdrstop #include #include "tfind.h" #include "clist.h" #include "clistndn.h" #include "tconst.h" CUPnPDeviceFoldPidl::CUPnPDeviceFoldPidl() { // if these change, the UPNPUI_PIDL_HEADER structure // above needs to change to be the right size // Why we used 4 different types, I don't know... Assert(2 == sizeof(WORD)); Assert(2 == sizeof(USHORT)); Assert(4 == sizeof(DWORD)); Assert(4 == sizeof(ULONG)); m_pszName = NULL; m_pszUrl = NULL; m_pszUdn = NULL; m_pszType = NULL; m_pszDesc = NULL; } CUPnPDeviceFoldPidl::~CUPnPDeviceFoldPidl() { if (m_pszName) { delete [] m_pszName; } if (m_pszUrl) { delete [] m_pszUrl; } if (m_pszUdn) { delete [] m_pszUdn; } if (m_pszType) { delete [] m_pszType; } if (m_pszDesc) { delete [] m_pszDesc; } } HRESULT CUPnPDeviceFoldPidl::HrInit(FolderDeviceNode * pDeviceNode) { Assert(!m_pszName); Assert(!m_pszUrl); Assert(!m_pszUdn); Assert(!m_pszType); Assert(!m_pszDesc); Assert(pDeviceNode); Assert(pDeviceNode->pszDisplayName); Assert(pDeviceNode->pszPresentationURL); Assert(pDeviceNode->pszUDN); Assert(pDeviceNode->pszType); HRESULT hr; LPWSTR pszName; LPWSTR pszUrl; LPWSTR pszUdn; LPWSTR pszType; LPWSTR pszDesc; ULONG cchName; ULONG cchUrl; ULONG cchUdn; ULONG cchType; ULONG cchDesc; hr = E_OUTOFMEMORY; pszName = NULL; pszUrl = NULL; pszUdn = NULL; pszType = NULL; pszDesc = NULL; // Get the size of the name, and tack on a trailing NULL (since we now // have something else in the buffer behind it. // cchName = lstrlenW(pDeviceNode->pszDisplayName); pszName = new WCHAR [cchName + 1]; if (!pszName) { goto Cleanup; } cchUrl = lstrlenW(pDeviceNode->pszPresentationURL); pszUrl = new WCHAR [cchUrl + 1]; if (!pszUrl) { goto Cleanup; } cchUdn = lstrlenW(pDeviceNode->pszUDN); pszUdn = new WCHAR [cchUdn + 1]; if (!pszUdn) { goto Cleanup; } cchType = lstrlenW(pDeviceNode->pszType); pszType = new WCHAR [cchType + 1]; if (!pszType) { goto Cleanup; } cchDesc = lstrlenW(pDeviceNode->pszDescription); pszDesc = new WCHAR [cchDesc + 1]; if (!pszDesc) { goto Cleanup; } // everything that can fail has succeeded. hr = S_OK; // We don't need to check these since we know there's // enough room. wcscpy(pszName, pDeviceNode->pszDisplayName); wcscpy(pszUrl, pDeviceNode->pszPresentationURL); wcscpy(pszUdn, pDeviceNode->pszUDN); wcscpy(pszType, pDeviceNode->pszType); wcscpy(pszDesc, pDeviceNode->pszDescription); m_pszName = pszName; m_pszUrl = pszUrl; m_pszUdn = pszUdn; m_pszType = pszType; m_pszDesc = pszDesc; Cleanup: if (FAILED(hr)) { if (pszName) { delete [] pszName; } if (pszUrl) { delete [] pszUrl; } if (pszUdn) { delete [] pszUdn; } if (pszType) { delete [] pszType; } if (pszDesc) { delete [] pszDesc; } } Assert(FImplies(SUCCEEDED(hr), pszName)); Assert(FImplies(SUCCEEDED(hr), pszUrl)); Assert(FImplies(SUCCEEDED(hr), pszUdn)); Assert(FImplies(SUCCEEDED(hr), pszType)); Assert(FImplies(SUCCEEDED(hr), pszDesc)); return hr; } HRESULT HrCopyUnalignedBytesToNewString(BYTE * pByteData, ULONG cbData, LPWSTR * ppszResult) { Assert(ppszResult); HRESULT hr; LPWSTR pszResult; ULONG cchMax; hr = S_OK; pszResult = NULL; { BOOL fInvalid; fInvalid = IsBadReadPtr(pByteData, cbData); if (fInvalid) { hr = E_POINTER; goto Cleanup; } } if (!cbData || cbData % sizeof(WCHAR)) { hr = E_INVALIDARG; goto Cleanup; } cchMax = (cbData / sizeof(WCHAR)) - 1; pszResult = new WCHAR [ cchMax + 1 ]; if (!pszResult) { hr = E_OUTOFMEMORY; goto Cleanup; } ::CopyMemory(pszResult, pByteData, cbData); // make sure that the data is null-terminated. pszResult[cchMax] = UNICODE_NULL; Cleanup: if (FAILED(hr)) { if (pszResult) { delete [] pszResult; pszResult = NULL; } } *ppszResult = pszResult; TraceError("HrCopyUnalignedBytesToNewString", hr); return hr; } HRESULT CUPnPDeviceFoldPidl::HrInit(PUPNPDEVICEFOLDPIDL pidl) { Assert(!m_pszName); Assert(!m_pszUrl); Assert(!m_pszUdn); Assert(!m_pszType); Assert(!m_pszDesc); HRESULT hr; UNALIGNED UPNPUI_PIDL_HEADER * puph; LPWSTR pszName; LPWSTR pszUrl; LPWSTR pszUdn; LPWSTR pszType; LPWSTR pszDesc; hr = S_OK; puph = (UPNPUI_PIDL_HEADER *) pidl; pszName = NULL; pszUrl = NULL; pszUdn = NULL; pszType = NULL; pszDesc = NULL; { BOOL fInvalid; fInvalid = IsBadReadPtr(pidl, sizeof(UPNPUI_PIDL_HEADER)); if (fInvalid) { hr = E_POINTER; goto Cleanup; } } // minimal version checking should have happened already, // so we just assert that everything is ok here // Assert(UPNPDEVICEFOLDPIDL_LEADID == puph->uLeadId); Assert(UPNPDEVICEFOLDPIDL_TRAILID == puph->uTrailId); { BYTE * pbString; ULONG ulOffset; ULONG cb; pbString = (BYTE *)pidl; ulOffset = puph->ulNameOffset; ulOffset += sizeof(UPNPUI_PIDL_HEADER); cb = puph->cbName; pbString += ulOffset; hr = HrCopyUnalignedBytesToNewString(pbString, cb, &pszName); if (FAILED(hr)) { goto Cleanup; } } { BYTE * pbString; ULONG ulOffset; ULONG cb; pbString = (BYTE *)pidl; ulOffset = puph->ulUrlOffset; ulOffset += sizeof(UPNPUI_PIDL_HEADER); cb = puph->cbUrl; pbString += ulOffset; hr = HrCopyUnalignedBytesToNewString(pbString, cb, &pszUrl); if (FAILED(hr)) { goto Cleanup; } } { BYTE * pbString; ULONG ulOffset; ULONG cb; pbString = (BYTE *)pidl; ulOffset = puph->ulUdnOffset; ulOffset += sizeof(UPNPUI_PIDL_HEADER); cb = puph->cbUdn; pbString += ulOffset; hr = HrCopyUnalignedBytesToNewString(pbString, cb, &pszUdn); if (FAILED(hr)) { goto Cleanup; } } { BYTE * pbString; ULONG ulOffset; ULONG cb; pbString = (BYTE *)pidl; ulOffset = puph->ulTypeOffset; ulOffset += sizeof(UPNPUI_PIDL_HEADER); cb = puph->cbType; pbString += ulOffset; hr = HrCopyUnalignedBytesToNewString(pbString, cb, &pszType); if (FAILED(hr)) { goto Cleanup; } } { BYTE * pbString; ULONG ulOffset; ULONG cb; pbString = (BYTE *)pidl; ulOffset = puph->ulDescOffset; ulOffset += sizeof(UPNPUI_PIDL_HEADER); cb = puph->cbDesc; pbString += ulOffset; hr = HrCopyUnalignedBytesToNewString(pbString, cb, &pszDesc); if (FAILED(hr)) { goto Cleanup; } } m_pszName = pszName; m_pszUrl = pszUrl; m_pszUdn = pszUdn; m_pszType = pszType; m_pszDesc = pszDesc; Cleanup: if (FAILED(hr)) { if (pszName) { delete [] pszName; } if (pszUrl) { delete [] pszUrl; } if (pszUdn) { delete [] pszUdn; } if (pszType) { delete [] pszType; } if (pszDesc) { delete [] pszDesc; } } Assert(FImplies(SUCCEEDED(hr), pszName)); Assert(FImplies(SUCCEEDED(hr), pszUrl)); Assert(FImplies(SUCCEEDED(hr), pszUdn)); Assert(FImplies(SUCCEEDED(hr), pszType)); Assert(FImplies(SUCCEEDED(hr), pszDesc)); TraceError("CUPnPDeviceFoldPidl::HrInit", hr); return hr; } HRESULT CUPnPDeviceFoldPidl::HrPersist(IMalloc * pMalloc, LPITEMIDLIST * ppidl) { Assert(m_pszName); Assert(m_pszUrl); Assert(m_pszUdn); Assert(m_pszType); Assert(m_pszDesc); Assert(pMalloc); Assert(ppidl); HRESULT hr; ULONG cbTotalPidlSize; ULONG cbName; ULONG cbUrl; ULONG cbUdn; ULONG cbType; ULONG cbDesc; ULONG ulNameOffset; ULONG ulUrlOffset; ULONG ulUdnOffset; ULONG ulTypeOffset; ULONG ulDescOffset; UNALIGNED UPNPUI_PIDL_HEADER * puph; LPBYTE pbData; hr = S_OK; pbData = NULL; cbName = wcslen(m_pszName); cbName = (cbName + 1) * sizeof(WCHAR); cbUrl = wcslen(m_pszUrl); cbUrl = (cbUrl + 1) * sizeof(WCHAR); cbUdn = wcslen(m_pszUdn); cbUdn = (cbUdn + 1) * sizeof(WCHAR); cbType = wcslen(m_pszType); cbType = (cbType + 1) * sizeof(WCHAR); cbDesc = wcslen(m_pszDesc); cbDesc = (cbDesc + 1) * sizeof(WCHAR); ulNameOffset = 0; ulUrlOffset = ulNameOffset + cbName; ulUdnOffset = ulUrlOffset + cbUrl; ulTypeOffset = ulUdnOffset + cbUdn; ulDescOffset = ulTypeOffset + cbType; cbTotalPidlSize = sizeof(UPNPUI_PIDL_HEADER); cbTotalPidlSize += cbName; cbTotalPidlSize += cbUrl; cbTotalPidlSize += cbUdn; cbTotalPidlSize += cbType; cbTotalPidlSize += cbDesc; // don't count the PIDL-terminating bytes in the size // pbData = (BYTE *) pMalloc->Alloc(cbTotalPidlSize + FIELD_OFFSET(ITEMIDLIST, mkid.cb) + sizeof(USHORT)); if (!pbData) { hr = E_OUTOFMEMORY; TraceError("CUPnPDeviceFoldPidl::HrPersist: Alloc()", hr); goto Cleanup; } // delegate folder alert: since we're a delete folder, the Alloc() above // doesn't just allocate the bytes we asked for, but rather a bunch for our // delegate folder prefix, then the bytes we asked for. We need to skip // the prefix bytes and just write to our own. puph = (UPNPUI_PIDL_HEADER *)ConvertToUPnPDevicePIDL((ITEMIDLIST*)pbData); puph->iCB = cbTotalPidlSize; puph->uLeadId = UPNPDEVICEFOLDPIDL_LEADID; puph->dwVersion = UP_DEVICE_FOLDER_IDL_VERSION; puph->uTrailId = UPNPDEVICEFOLDPIDL_TRAILID; puph->uVOID = 0; puph->dwCharacteristics = 0; puph->ulNameOffset = ulNameOffset; puph->cbName = cbName; puph->ulUrlOffset = ulUrlOffset; puph->cbUrl = cbUrl; puph->ulUdnOffset = ulUdnOffset; puph->cbUdn = cbUdn; puph->ulTypeOffset = ulTypeOffset; puph->cbType = cbType; puph->ulDescOffset = ulDescOffset; puph->cbDesc = cbDesc; { LPBYTE pbDynamicField; LPBYTE pbName; LPBYTE pbUrl; LPBYTE pbUdn; LPBYTE pbType; LPBYTE pbDesc; // note: this has to be puph (not pbData) because we still // have to skip the "delegate folder prefix" junk. pbDynamicField = ((BYTE *)puph) + sizeof(UPNPUI_PIDL_HEADER); pbName = pbDynamicField + ulNameOffset; pbUrl = pbDynamicField + ulUrlOffset; pbUdn = pbDynamicField + ulUdnOffset; pbType = pbDynamicField + ulTypeOffset; pbDesc = pbDynamicField + ulDescOffset; ::CopyMemory(pbName, m_pszName, cbName); ::CopyMemory(pbUrl, m_pszUrl, cbUrl); ::CopyMemory(pbUdn, m_pszUdn, cbUdn); ::CopyMemory(pbType, m_pszType, cbType); ::CopyMemory(pbDesc, m_pszDesc, cbDesc); } { // terminate the PIDL LPITEMIDLIST pidlNext; pidlNext = ILNext((LPITEMIDLIST)puph); Assert((FIELD_OFFSET(ITEMIDLIST, mkid.cb) + sizeof(pidlNext->mkid.cb)) == sizeof(USHORT)); pidlNext->mkid.cb = 0; } Cleanup: Assert(FImplies(FAILED(hr), !pbData)); Assert(FImplies(SUCCEEDED(hr), pbData)); *ppidl = (LPITEMIDLIST)pbData; TraceError("CUPnPDeviceFoldPidl::HrPersist", hr); return hr; } PCWSTR CUPnPDeviceFoldPidl::PszGetNamePointer() const { return m_pszName; } PCWSTR CUPnPDeviceFoldPidl::PszGetURLPointer() const { return m_pszUrl; } PCWSTR CUPnPDeviceFoldPidl::PszGetUDNPointer() const { return m_pszUdn; } PCWSTR CUPnPDeviceFoldPidl::PszGetTypePointer() const { return m_pszType; } PCWSTR CUPnPDeviceFoldPidl::PszGetDescriptionPointer() const { return m_pszDesc; } HRESULT CUPnPDeviceFoldPidl::HrSetName(PCWSTR szName) { HRESULT hr = S_OK; if (szName) { // Free old name delete [] m_pszName; // Copy in new name m_pszName = WszDupWsz(szName); if (!m_pszName) { hr = E_OUTOFMEMORY; } } TraceError("CUPnPDeviceFoldPidl::HrSetName", hr); return hr; } //+--------------------------------------------------------------------------- // // Member: CUPnPDeviceFolder::HrMakeUPnPDevicePidl // // Purpose: Private function of the folder object that constructs the // UPNP device pidl using the delegated allocator // // Arguments: // FolderDeviceNode [in] Structure that contains all the // strings we need for the pidl // ppidl [out] The result pidl // // Returns: Returns NOERROR if successful or an OLE-defined error // value otherwise // // Author: tongl 15 Feb 2000 // // Notes: // HRESULT CUPnPDeviceFolder::HrMakeUPnPDevicePidl(FolderDeviceNode * pDeviceNode, LPITEMIDLIST * ppidl) { HRESULT hr; CUPnPDeviceFoldPidl udfp; hr = udfp.HrInit(pDeviceNode); if (SUCCEEDED(hr)) { hr = udfp.HrPersist(m_pDelegateMalloc, ppidl); } return hr; } HRESULT CUPnPDeviceFolder::HrMakeUPnPDevicePidl(IUPnPDevice * pDevice, LPITEMIDLIST * ppidl) { HRESULT hr = S_OK; Assert(pDevice); BSTR bstrUDN = NULL; BSTR bstrDisplayName = NULL; BSTR bstrType = NULL; BSTR bstrPresentationURL = NULL; BSTR bstrDescription = NULL; Assert(pDevice); pDevice->AddRef(); hr = pDevice->get_UniqueDeviceName(&bstrUDN); if (SUCCEEDED(hr)) { hr = pDevice->get_FriendlyName(&bstrDisplayName); if (SUCCEEDED(hr)) { hr = pDevice->get_Type(&bstrType); if (SUCCEEDED(hr)) { hr = pDevice->get_PresentationURL(&bstrPresentationURL); if (SUCCEEDED(hr)) { hr = pDevice->get_Description(&bstrDescription); if (SUCCEEDED(hr)) { FolderDeviceNode * pDevNode = new FolderDeviceNode; if (pDevNode) { // the buffers in FolderDeviceNode are MAX_PATH // wide, so we can only copy MAX_PATH - 1 chars // and still have room for the terminating null // CONST SIZE_T cchMax = MAX_PATH - 1; Assert(bstrUDN); wcscpy(pDevNode->pszUDN, L""); wcsncat(pDevNode->pszUDN,(PWSTR)bstrUDN,cchMax); Assert(bstrDisplayName); wcscpy(pDevNode->pszDisplayName, L""); wcsncat(pDevNode->pszDisplayName,(PWSTR)bstrDisplayName,cchMax); Assert(bstrType); wcscpy(pDevNode->pszType, L""); wcsncat(pDevNode->pszType,(PWSTR)bstrType,cchMax); wcscpy(pDevNode->pszPresentationURL, L""); if (bstrPresentationURL) { wcsncat(pDevNode->pszPresentationURL, (PWSTR)bstrPresentationURL, cchMax); } wcscpy(pDevNode->pszDescription, L""); if (bstrDescription) { wcsncat(pDevNode->pszDescription, (PWSTR)bstrDescription, cchMax); } hr = HrMakeUPnPDevicePidl(pDevNode, ppidl); } else { hr = E_OUTOFMEMORY; } } else { TraceTag(ttidShellFolder, "Failed in pDevice->get_Description from HrMakeUPnPDevicePidl"); } } else { TraceTag(ttidShellFolder, "Failed in pDevice->get_PresentationURL from HrMakeUPnPDevicePidl"); } } else { TraceTag(ttidShellFolder, "Failed in pDevice->get_Type from HrMakeUPnPDevicePidl"); } } else { TraceTag(ttidShellFolder, "Failed in pDevice->get_FriendlyName from HrMakeUPnPDevicePidl"); } } else { TraceTag(ttidShellFolder, "Failed in pDevice->get_UniqueDeviceName from HrMakeUPnPDevicePidl"); } SysFreeString(bstrUDN); SysFreeString(bstrDisplayName); SysFreeString(bstrPresentationURL); SysFreeString(bstrType); SysFreeString(bstrDescription); ReleaseObj(pDevice); TraceError("CUPnPDeviceFolder::HrMakeUPnPDevicePidl", hr); return hr; } //+--------------------------------------------------------------------------- // // Member: CUPnPDeviceFolder::ParseDisplayName // // Purpose: Translates a file object or folder's display name into an // item identifier. // // Arguments: // hwndOwner [in] Handle of owner window // pbcReserved [in] Reserved // lpszDisplayName [in] Pointer to diplay name // pchEaten [out] Pointer to value for parsed characters // ppidl [out] Pointer to new item identifier list // pdwAttributes [out] Address receiving attributes of file object // // Returns: Returns NOERROR if successful or an OLE-defined error // value otherwise // // Author: tongl 16 Feb 2000 // // Notes: // STDMETHODIMP CUPnPDeviceFolder::ParseDisplayName( HWND hwndOwner, LPBC pbcReserved, LPOLESTR lpszDisplayName, ULONG * pchEaten, LPITEMIDLIST * ppidl, ULONG * pdwAttributes) { TraceTag(ttidShellFolderIface, "CUPnPDeviceFolder::ParseDisplayName"); HRESULT hr = S_OK; // note: this is bogus, but we're doing this all over... Assert(lpszDisplayName); Assert(ppidl); *ppidl = NULL; // first, make sure that this is one of our display names: // it must start with c_szDelegateFolderPrefix // int result; result = wcsncmp(lpszDisplayName, c_szDelegateFolderPrefix, c_cchDelegateFolderPrefix); if (0 == result) { LPCWSTR pszUdn = NULL; LPITEMIDLIST pidlDevice = NULL; FolderDeviceNode * pDeviceNode = NULL; // this is OK since lpszDisplayName is an LPOLESTR pszUdn = lpszDisplayName + c_cchDelegateFolderPrefix; // search our list of devices and try to find a matching UDN if (g_CListFolderDeviceNode.FFind(pszUdn, &pDeviceNode)) { Assert(pDeviceNode); // yes, this is one of our devices, construct the pidl using // the allocator we are given hr = HrMakeUPnPDevicePidl(pDeviceNode, &pidlDevice); if (SUCCEEDED(hr)) { Assert(pidlDevice); *ppidl = pidlDevice; if (pdwAttributes) { *pdwAttributes = 0; } } } else { // no, we don't have such a device in the list // (tongl): try to do a SearchByUDN before we fail the call, // as this may be a device discovered by the search from the // tray icon, and we are asked for the pidl to create a shortcut. BSTR bstrUdn = NULL; IUPnPDeviceFinder * pdf = NULL; bstrUdn = ::SysAllocString(pszUdn); if (bstrUdn) { hr = CoCreateInstance(CLSID_UPnPDeviceFinder, NULL, CLSCTX_INPROC_SERVER, IID_IUPnPDeviceFinder, (LPVOID *)&pdf); if (SUCCEEDED(hr)) { IUPnPDevice * pdev = NULL; hr = pdf->FindByUDN(bstrUdn, &pdev); if (S_OK == hr) { hr = HrMakeUPnPDevicePidl(pdev, &pidlDevice); ReleaseObj(pdev); } ReleaseObj(pdf); } ::SysFreeString(bstrUdn); } else { hr = E_OUTOFMEMORY; TraceError("CUPnPDeviceFolder::ParseDisplayName: " "SysAllocString", hr); } } } else { TraceTag(ttidShellFolderIface, "CUPnPDeviceFolder::ParseDisplayName: " "passed non-upnp display name"); hr = E_FAIL; } TraceHr(ttidShellFolder, FAL, hr, FALSE, "CUPnPDeviceFolder::ParseDisplayName"); return hr; } //+--------------------------------------------------------------------------- // // Member: CUPnPDeviceFolder::EnumObjects // // Purpose: Determines the contents of a folder by creating an item // enumeration object (a set of item identifiers) that can be // retrieved using the IEnumIDList interface. // // Arguments: // hwndOwner [in] Handle of owner window // grfFlags [in] Items to include in enumeration // ppenumIDList [out] Pointer to IEnumIDList // // Returns: Returns NOERROR if successful or an OLE-defined error // value otherwise // // Author: jeffspr 18 Oct 1997 // // Notes: // STDMETHODIMP CUPnPDeviceFolder::EnumObjects( HWND hwndOwner, DWORD grfFlags, LPENUMIDLIST * ppenumIDList) { HRESULT hr = NOERROR; Assert(ppenumIDList); *ppenumIDList = NULL; if ((grfFlags & SHCONTF_FOLDERS) && !(grfFlags & SHCONTF_NONFOLDERS)) { // if shell wants to enumerate only folders, we don't return anything hr = E_NOTIMPL; } else { // Create the IEnumIDList object (CUPnPDeviceFolderEnum) // hr = CUPnPDeviceFolderEnum::CreateInstance ( IID_IEnumIDList, reinterpret_cast(ppenumIDList)); if (SUCCEEDED(hr)) { Assert(*ppenumIDList); // Call the PidlInitialize function to allow the enumeration // object to copy the list. // reinterpret_cast(*ppenumIDList)->Initialize( m_pidlFolderRoot, this); } else { // On all failures, this should be NULL. if (*ppenumIDList) { ReleaseObj(*ppenumIDList); } *ppenumIDList = NULL; } } TraceHr(ttidError, FAL, hr, FALSE, "CUPnPDeviceFolder::EnumObjects"); return hr; } //+--------------------------------------------------------------------------- // // Member: CUPnPDeviceFolder::BindToObject // // Purpose: Creates an IShellFolder object for a subfolder. // // Arguments: // pidl [in] Pointer to an ITEMIDLIST // pbcReserved [in] Reserved - specify NULL // riid [in] Interface to return // ppvOut [out] Address that receives interface pointer; // // Returns: Returns NOERROR if successful or an OLE-defined error // value otherwise // // Author: jeffspr 18 Oct 1997 // // Notes: We don't need this function, since we don't have subfolders. // STDMETHODIMP CUPnPDeviceFolder::BindToObject( LPCITEMIDLIST pidl, LPBC pbcReserved, REFIID riid, LPVOID * ppvOut) { HRESULT hr = E_NOTIMPL; // Note - If we add code here, then we ought to param check pidl // Assert(pidl); *ppvOut = NULL; TraceHr(ttidShellFolder, FAL, hr, FALSE, "CUPnPDeviceFolder::BindToObject"); return hr; } //+--------------------------------------------------------------------------- // // Member: CUPnPDeviceFolder::BindToStorage // // Purpose: Reserved for a future use. This method should // return E_NOTIMPL. // // Arguments: // pidl [] Pointer to an ITEMIDLIST // pbcReserved [] Reserved¾specify NULL // riid [] Interface to return // ppvObj [] Address that receives interface pointer); // // Returns: E_NOTIMPL always // // Author: jeffspr 18 Oct 1997 // // Notes: // STDMETHODIMP CUPnPDeviceFolder::BindToStorage( LPCITEMIDLIST pidl, LPBC pbcReserved, REFIID riid, LPVOID * ppvObj) { HRESULT hr = E_NOTIMPL; // Note - If we add code here, then we ought to param check pidl // Assert(pidl); *ppvObj = NULL; TraceHr(ttidShellFolder, FAL, hr, FALSE, "CUPnPDeviceFolder::BindToStorage"); return hr; } //+--------------------------------------------------------------------------- // // Member: CUPnPDeviceFolder::CompareIDs // // Purpose: Determines the relative ordering of two file objects or // folders, given their item identifier lists. // // Arguments: // lParam [in] Type of comparison to perform // pidl1 [in] Address of ITEMIDLIST structure // pidl2 [in] Address of ITEMIDLIST structure // // Returns: Returns a handle to a result code. If this method is // successful, the CODE field of the status code (SCODE) has // the following meaning: // // CODE field Meaning // ---------- ------- // Less than zero The first item should precede the second // (pidl1 < pidl2). // Greater than zero The first item should follow the second // (pidl1 > pidl2) // Zero The two items are the same (pidl1 = pidl2) // // Author: jeffspr 18 Oct 1997 // // Notes: Passing 0 as the lParam indicates sort by name. // 0x00000001-0x7fffffff are for folder specific sorting rules. // 0x80000000-0xfffffff are used the system. // STDMETHODIMP CUPnPDeviceFolder::CompareIDs( LPARAM lParam, LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2) { HRESULT hr = S_OK; PUPNPDEVICEFOLDPIDL pupdfp1 = NULL; PUPNPDEVICEFOLDPIDL pupdfp2 = NULL; CUPnPDeviceFoldPidl udfp1; CUPnPDeviceFoldPidl udfp2; LPCWSTR psz1; LPCWSTR psz2; CONST INT ciTieBreaker = -1; int iCompare = 0; int result; TraceTag(ttidShellFolderIface, "OBJ: CUPnPDeviceFolder::CompareIDs"); // Make sure that the pidls passed in are our pidls. // if (!FIsUPnPDeviceFoldPidl(pidl1) || !FIsUPnPDeviceFoldPidl(pidl2)) { hr = E_INVALIDARG; goto Exit; } pupdfp1 = ConvertToUPnPDevicePIDL(pidl1); pupdfp2 = ConvertToUPnPDevicePIDL(pidl2); hr = udfp1.HrInit(pupdfp1); if (FAILED(hr)) { goto Exit; } hr = udfp2.HrInit(pupdfp2); if (FAILED(hr)) { goto Exit; } // We use the following procedure to compare PIDLs: // 1. If two UDNs are the same, the PIDLs are the same // 2. Otherwise, the PIDLs _must_ be different, and // A. will be sorted based on the desired column // B. If the column text matches, we'll return // ciTieBreaker (-1), so that the PIDLs are // distinguished. // psz1 = udfp1.PszGetUDNPointer(); psz2 = udfp2.PszGetUDNPointer(); Assert(psz1 && psz2); result = wcscmp(psz1, psz2); if (0 == result) { // The UDNs match, these are effectively the same PIDL TraceTag(ttidShellFolder, "CUPnPDeviceFolder::CompareIDs: " "UDN equal, automatically returning equality"); iCompare = 0; } else { // Sort based on the desired column switch(lParam & SHCIDS_COLUMNMASK) { case ICOL_NAME: psz1 = udfp1.PszGetNamePointer(); psz2 = udfp2.PszGetNamePointer(); break; case ICOL_URL: psz1 = udfp1.PszGetURLPointer(); psz2 = udfp2.PszGetURLPointer(); break; case ICOL_UDN: psz1 = udfp1.PszGetUDNPointer(); psz2 = udfp2.PszGetUDNPointer(); break; case ICOL_TYPE: psz1 = udfp1.PszGetTypePointer(); psz2 = udfp2.PszGetTypePointer(); break; default: AssertSz(FALSE, "Sorting on unknown category"); break; } Assert(psz1 && psz2); iCompare = _wcsicmp(psz1, psz2); // Ensure that we don't return equality if (0 == iCompare) { TraceTag(ttidShellFolder, "CUPnPDeviceFolder::CompareIDs: " "UDNs unequal but column-text equal, breaking tie"); iCompare = ciTieBreaker; } } if (SUCCEEDED(hr)) { hr = ResultFromShort(iCompare); } TraceTag(ttidShellFolder, "CUPnPDeviceFolder::CompareIDs: returning %d", iCompare); Exit: TraceHr(ttidError, FAL, hr, SUCCEEDED(hr), "CUPnPDeviceFolder::CompareIDs"); return hr; } //+--------------------------------------------------------------------------- // // Member: CUPnPDeviceFolder::CreateViewObject // // Purpose: Creates a view object of a folder. // // Arguments: // hwndOwner [in] Handle of owner window // riid [in] Interface identifier // ppvOut [none] Reserved // // Returns: Returns NOERROR if successful or an OLE defined error // value otherwise. // // Author: jeffspr 18 Oct 1997 // // Notes: // STDMETHODIMP CUPnPDeviceFolder::CreateViewObject( HWND hwndOwner, REFIID riid, LPVOID * ppvOut) { HRESULT hr = E_NOINTERFACE; TraceTag(ttidShellFolderIface, "OBJ: CUPnPDeviceFolder::CreateViewObject"); Assert(ppvOut); // Pre-initialize the out param, per OLE guidelines // *ppvOut = NULL; // (tongl) We are a delegate folder now, CreateViewObject is never called. return E_NOTIMPL; } //+--------------------------------------------------------------------------- // // Member: CUPnPDeviceFolder::GetAttributesOf // // Purpose: Retrieves the attributes that all passed-in objects (file // objects or subfolders) have in common. // // Arguments: // cidl [in] Number of file objects // apidl [in] Pointer to array of pointers to ITEMIDLIST structures // rgfInOut [out] Address of value containing attributes of the // file objects // // Returns: Returns NOERROR if successful or an OLE-defined error // value otherwise. // // Author: jeffspr 18 Oct 1997 // // Notes: // STDMETHODIMP CUPnPDeviceFolder::GetAttributesOf( UINT cidl, LPCITEMIDLIST * apidl, ULONG * rgfInOut) { HRESULT hr = S_OK; ULONG rgfMask = 0; PUPNPDEVICEFOLDPIDL pupdfp = NULL; TraceTag(ttidShellFolderIface, "OBJ: CUPnPDeviceFolder::GetAttributesOf"); if (cidl > 0) { // Prepopulate with all values (removed CANCOPY and CANMOVE) // rgfMask = /* SFGAO_CANDELETE | */ // Don't support delete SFGAO_CANRENAME | SFGAO_CANLINK | SFGAO_HASPROPSHEET; // Disable propsheets for > 1 object // if (cidl > 1) { rgfMask &= ~SFGAO_HASPROPSHEET; } } else { // Apparently, we're called with 0 objects to indicate that we're // supposed to return flags for the folder itself, not an individual // object. Weird. rgfMask = SFGAO_CANCOPY | SFGAO_CANDELETE | SFGAO_CANMOVE | SFGAO_CANRENAME; } if (SUCCEEDED(hr)) { *rgfInOut &= rgfMask; } return hr; } //+--------------------------------------------------------------------------- // // Member: CUPnPDeviceFolder::GetUIObjectOf // // Purpose: Creates a COM object that can be used to carry out actions // on the specified file objects or folders, typically, to // create context menus or carry out drag-and-drop operations. // // Arguments: // hwndOwner [in] Handle to owner window // cidl [in] Number of objects specified in apidl // apidl [in] Pointer to an array of pointers to an ITEMIDLIST // riid [in] Interface to return // prgfInOut [none] Reserved // ppvOut [out] Address to receive interface pointer // // Returns: Returns NOERROR if successful or an OLE-defined error // value otherwise // // Author: jeffspr 18 Oct 1997 // // Notes: // STDMETHODIMP CUPnPDeviceFolder::GetUIObjectOf( HWND hwndOwner, UINT cidl, LPCITEMIDLIST * apidl, REFIID riid, UINT * prgfInOut, LPVOID * ppvOut) { HRESULT hr = E_NOINTERFACE; TraceTag(ttidShellFolderIface, "OBJ: CUPnPDeviceFolder::GetUIObjectOf"); if (cidl >= 1) { Assert(apidl); Assert(apidl[0]); Assert(ppvOut); if (riid == IID_IDataObject) { // Need to initialize so the SUCCEEED check below doesn't fail. // hr = S_OK; Assert(m_pidlFolderRoot); if (SUCCEEDED(hr)) { Assert(m_pidlFolderRoot); // Internal IDataObject impl removed. Replaced with common // shell code. // hr = CIDLData_CreateFromIDArray(m_pidlFolderRoot, cidl, apidl, (IDataObject **) ppvOut); } } else if (riid == IID_IContextMenu) { // Create our context menu object if only one device is selected // hr = CUPnPDeviceFolderContextMenu::CreateInstance ( IID_IContextMenu, reinterpret_cast(ppvOut), CMT_OBJECT, hwndOwner, cidl, apidl, this); if (SUCCEEDED(hr)) { Assert(*ppvOut); } else { hr = E_NOINTERFACE; } } else if (riid == IID_IExtractIconA || riid == IID_IExtractIconW) { if (cidl == 1) { hr = CUPnPDeviceFolderExtractIcon::CreateInstance ( apidl[0], riid, reinterpret_cast(ppvOut)); if(SUCCEEDED(hr)) { hr = reinterpret_cast (*ppvOut)->Initialize((LPITEMIDLIST)apidl[0]); } if (SUCCEEDED(hr)) { Assert(*ppvOut); } } else { hr = E_NOINTERFACE; } } else if (riid == IID_IDropTarget) { // We don't support drag/drop // hr = E_NOINTERFACE; } else if (riid == IID_IQueryInfo) { if (cidl == 1) { // Create the IQueryInfo interface hr = CUPnPDeviceFolderQueryInfo::CreateInstance ( IID_IQueryInfo, reinterpret_cast(ppvOut)); if (SUCCEEDED(hr)) { Assert(*ppvOut); reinterpret_cast (*ppvOut)->PidlInitialize((LPITEMIDLIST)apidl[0]); // Normalize return code // hr = NOERROR; } } else { AssertSz(FALSE, "GetUIObjectOf asked for query info for more than one item!"); hr = E_NOINTERFACE; } } else { TraceTag(ttidShellFolder, "CUPnPDeviceFolder::GetUIObjectOf asked for object " "that it didn't know how to create. 0x%08x", riid.Data1); hr = E_NOINTERFACE; } } if (FAILED(hr)) { *ppvOut = NULL; } TraceHr(ttidError, FAL, hr, (hr == E_NOINTERFACE), "CUPnPDeviceFolder::GetUIObjectOf"); return hr; } //+--------------------------------------------------------------------------- // // Function: HrTszToStrRet // // Purpose: Convert a TCHAR string to a STRRET, depending on the platform // // Arguments: // pszName [in] input string // pStrRet [in/out] output STRRET // // Returns: // // Author: jeffspr 25 Jan 2000 // // Notes: // HRESULT HrTszToStrRet(LPCTSTR pszName, STRRET *pStrRet) { HRESULT hr = S_OK; #ifdef UNICODE pStrRet->uType = STRRET_WSTR; // Allocate a new POLESTR block, which the shell can then free, // and copy the displayable portion to it. // hr = HrDupeShellString( pszName, &pStrRet->pOleStr); #else pStrRet->uType = STRRET_CSTR; lstrcpyn(pStrRet->cStr, pszName, celems(pStrRet->cStr)); hr = S_OK; #endif return hr; } //+--------------------------------------------------------------------------- // // Member: CUPnPDeviceFolder::GetDisplayNameOf // // Purpose: Retrieves the display name for the specified file object or // subfolder, returning it in a STRRET structure. // // Arguments: // pidl [in] Pointer to an ITEMIDLIST // uFlags [in] Type of display to return // lpName [out] Pointer to a STRRET structure // // Returns: Returns NOERROR if successful or an OLE-defined error // value otherwise. // // Author: jeffspr 18 Oct 1997 // // Notes: // STDMETHODIMP CUPnPDeviceFolder::GetDisplayNameOf( LPCITEMIDLIST pidl, DWORD uFlags, LPSTRRET lpName) { HRESULT hr = S_OK; PCWSTR pwszStrToCopy = NULL; PTSTR pszTemp = NULL; PUPNPDEVICEFOLDPIDL pupdfp = NULL; TraceTag(ttidShellFolderIface, "OBJ: CUPnPDeviceFolder::GetDisplayNameOf"); Assert(pidl); Assert(lpName); if (!pidl || !lpName) { hr = E_INVALIDARG; } else if (FIsUPnPDeviceFoldPidl(pidl)) { CUPnPDeviceFoldPidl udfp; pupdfp = ConvertToUPnPDevicePIDL(pidl); #ifdef DBG // Throw these in here just so I can quickly peek at the values // set while I'm dorking around in the debugger. // DWORD dwInFolder = (uFlags & SHGDN_INFOLDER); DWORD dwForAddressBar = (uFlags & SHGDN_FORADDRESSBAR); DWORD dwForParsing = (uFlags & SHGDN_FORPARSING); #endif if (uFlags & SHGDN_FORPARSING) { #if 0 AssertSz(FALSE, "SHGDN_FORPARSING support NYI in GetDisplayNameOf"); #endif } hr = udfp.HrInit(pupdfp); if (SUCCEEDED(hr)) { pwszStrToCopy = udfp.PszGetNamePointer(); Assert(pwszStrToCopy); pszTemp = TszFromWsz(pwszStrToCopy); if (!pszTemp) { hr = E_OUTOFMEMORY; } else { hr = HrTszToStrRet(pszTemp, lpName); free((PVOID) pszTemp); } } } else { // not a valid connections pidl (neither Win2K nor Win98). // hr = E_INVALIDARG; } TraceHr(ttidError, FAL, hr, FALSE, "CUPnPDeviceFolder::GetDisplayNameOf"); return hr; } //+--------------------------------------------------------------------------- // // Member: CUPnPDeviceFolder::SetNameOf // // Purpose: Changes the name of a file object or subfolder, changing its // item identifier in the process. // // Arguments: // hwndOwner [in] Handle of owner window // pidl [in] Pointer to an ITEMIDLIST structure // lpszName [in] Pointer to string specifying new display name // uFlags [in] Type of name specified in lpszName // ppidlOut [out] Pointer to new ITEMIDLIST // // Returns: Returns NOERROR if successful or an OLE-defined error // value otherwise. // // Author: jeffspr 18 Oct 1997 // // Notes: // STDMETHODIMP CUPnPDeviceFolder::SetNameOf( HWND hwndOwner, LPCITEMIDLIST pidl, LPCOLESTR lpszName, DWORD uFlags, LPITEMIDLIST * ppidlOut) { HRESULT hr = S_OK; PUPNPDEVICEFOLDPIDL pupdfp = NULL; TraceTag(ttidShellFolderIface, "OBJ: CUPnPDeviceFolder::SetNameOf"); Assert(hwndOwner); Assert(pidl); Assert(lpszName); if (!pidl && !lpszName) { hr = E_INVALIDARG; } else if (!*lpszName) { hr = S_OK; } else if (FIsUPnPDeviceFoldPidl(pidl)) { CUPnPDeviceFoldPidl udfp; pupdfp = ConvertToUPnPDevicePIDL(pidl); hr = udfp.HrInit(pupdfp); if (SUCCEEDED(hr)) { // Change the name of the item in the PIDL hr = udfp.HrSetName(lpszName); } if (SUCCEEDED(hr)) { LPITEMIDLIST pidlOut; // Persist the PIDL data to a new PIDL so we can generate an event // for it hr = udfp.HrPersist(m_pDelegateMalloc, &pidlOut); if (SUCCEEDED(hr)) { NAME_MAP * pnm; pnm = new NAME_MAP; if (pnm) { // Copy the name and UDN to a struct to keep in a list // of mapped UDNs to friendly names. // pnm->szName = TszDupTsz(udfp.PszGetNamePointer()); if (!pnm->szName) { hr = E_OUTOFMEMORY; } else { pnm->szUdn = TszDupTsz(udfp.PszGetUDNPointer()); if (!pnm->szUdn) { hr = E_OUTOFMEMORY; } else { // Delete the item and re-add it again g_CListNameMap.FDelete(udfp.PszGetUDNPointer()); g_CListNameMap.FAdd(pnm); // Notify the shell of the rename GenerateEvent(SHCNE_RENAMEITEM, m_pidlFolderRoot, pidl, pidlOut); FolderDeviceNode * pDeviceNode; if (g_CListFolderDeviceNode.FFind(pnm->szUdn, &pDeviceNode)) { // Delete the node's old name and give it the new // one // wcscpy(pDeviceNode->pszDisplayName, pnm->szName); } } } } else { hr = E_OUTOFMEMORY; } if (ppidlOut) { *ppidlOut = pidlOut; } else { FreeIDL(pidlOut); } } } } else { // not a valid UPnP pidl // hr = E_INVALIDARG; } TraceHr(ttidError, FAL, hr, FALSE, "CUPnPDeviceFolder::SetNameOf"); return hr; }