//+--------------------------------------------------------------------------- // // Microsoft Windows // Copyright (C) Microsoft Corporation, 1997. // // File: S H U T I L . C P P // // Contents: Various shell utilities to be used by the connections shell // // Notes: // // Author: jeffspr 21 Oct 1997 // //---------------------------------------------------------------------------- #include "pch.h" #pragma hdrstop #include #include #include #include #include #include "foldinc.h" // Standard shell\folder includes #include "ncnetcon.h" // FreeNetconProperties #include "smcent.h" // Statmon central #include "ctrayui.h" // For flushing tray messages extern HWND g_hwndTray; //+--------------------------------------------------------------------------- // // Function: HrDupeShellStringLength // // Purpose: Duplicate a string using SHAlloc, so we can return it to the // shell. This is required because the shell typically releases // the strings that we pass it (so we need to use their // allocator). // // Arguments: // pszInput [in] String to duplicate // cchInput [in] Count of characters to copy (not including null term) // ppszOutput [out] Return pointer for the newly allocated string. // // Returns: // // Author: jeffspr 21 Oct 1997 // // Notes: // HRESULT HrDupeShellStringLength( PCWSTR pszInput, ULONG cchInput, PWSTR * ppszOutput) { HRESULT hr = S_OK; Assert(pszInput); Assert(ppszOutput); ULONG cbString = (cchInput + 1) * sizeof(WCHAR); // Allocate a new POLESTR block, which the shell can then free. // PWSTR pszOutput = (PWSTR) SHAlloc(cbString); // If the alloc failed, return E_OUTOFMEMORY // if (NULL != pszOutput) { // Copy the memory into the alloc'd block // CopyMemory(pszOutput, pszInput, cbString); pszOutput[cchInput] = 0; *ppszOutput = pszOutput; } else { *ppszOutput = NULL; hr = E_OUTOFMEMORY; } TraceHr(ttidError, FAL, hr, FALSE, "HrDupeShellStringLength"); return hr; } //+--------------------------------------------------------------------------- // // Function: HrLoadPopupMenu // // Purpose: Load a popup menu as the first child of a loadable parent // menu // // Arguments: // hinst [in] Our instance handle // id [in] ID of the parent menu // phmenu [out] Return pointer for the popup menu // // Returns: // // Author: jeffspr 27 Oct 1997 // // Notes: // HRESULT HrLoadPopupMenu( HINSTANCE hinst, UINT id, HMENU * phmenu) { HRESULT hr = S_OK; HMENU hmParent = NULL; HMENU hmPopup = NULL; Assert(id); Assert(hinst); Assert(phmenu); // Load the parent menu // hmParent = LoadMenu(hinst, MAKEINTRESOURCE(id)); if (NULL == hmParent) { AssertSz(FALSE, "Can't load parent menu in HrLoadPopupMenu"); hr = HrFromLastWin32Error(); } else { // Load the popup from the parent (first submenu), then // remove the parent menu // hmPopup = GetSubMenu(hmParent, 0); RemoveMenu(hmParent, 0, MF_BYPOSITION); DestroyMenu(hmParent); } if (phmenu) { *phmenu = hmPopup; } TraceHr(ttidError, FAL, hr, FALSE, "HrLoadPopupMenu"); return hr; } HRESULT HrGetMenuFromID( HMENU hmenuMain, UINT uID, HMENU * phmenu) { HRESULT hr = S_OK; HMENU hmenuReturn = NULL; MENUITEMINFO mii; Assert(hmenuMain); Assert(uID); Assert(phmenu); ZeroMemory(&mii, sizeof(MENUITEMINFO)); mii.cbSize = sizeof(mii); mii.fMask = MIIM_SUBMENU; mii.cch = 0; // just in case if (!GetMenuItemInfo(hmenuMain, uID, FALSE, &mii)) { hr = E_FAIL; } else { hmenuReturn = mii.hSubMenu; } if (phmenu) { *phmenu = mii.hSubMenu; } TraceHr(ttidError, FAL, hr, FALSE, "HrGetMenuFromID"); return hr; } INT IMergePopupMenus( HMENU hmMain, HMENU hmMerge, int idCmdFirst, int idCmdLast) { HRESULT hr = S_OK; int iCount = 0; int idTemp = 0; int idMax = idCmdFirst; HMENU hmFromId = NULL; for (iCount = GetMenuItemCount(hmMerge) - 1; iCount >= 0; --iCount) { MENUITEMINFO mii; mii.cbSize = sizeof(mii); mii.fMask = MIIM_ID | MIIM_SUBMENU; mii.cch = 0; // just in case if (!GetMenuItemInfo(hmMerge, iCount, TRUE, &mii)) { TraceHr(ttidError, FAL, E_FAIL, FALSE, "GetMenuItemInfo failed in iMergePopupMenus"); continue; } hr = HrGetMenuFromID(hmMain, mii.wID, &hmFromId); if (SUCCEEDED(hr)) { idTemp = Shell_MergeMenus( hmFromId, mii.hSubMenu, 0, idCmdFirst, idCmdLast, MM_ADDSEPARATOR | MM_SUBMENUSHAVEIDS); if (idMax < idTemp) { idMax = idTemp; } } else { TraceHr(ttidError, FAL, E_FAIL, FALSE, "HrGetMenuFromId failed in iMergePopupMenus"); continue; } } return idMax; } VOID MergeMenu( HINSTANCE hinst, UINT idMainMerge, UINT idPopupMerge, LPQCMINFO pqcm) { HMENU hmMerge = NULL; UINT idMax = 0; UINT idTemp = 0; Assert(pqcm); Assert(idMainMerge); Assert(hinst); idMax = pqcm->idCmdFirst; if (idMainMerge && (SUCCEEDED(HrLoadPopupMenu(hinst, idMainMerge, &hmMerge)))) { Assert(hmMerge); if (hmMerge) { idMax = Shell_MergeMenus( pqcm->hmenu, hmMerge, pqcm->indexMenu, pqcm->idCmdFirst, pqcm->idCmdLast, MM_SUBMENUSHAVEIDS); DestroyMenu(hmMerge); } } if (idPopupMerge && (hmMerge = LoadMenu(hinst, MAKEINTRESOURCE(idPopupMerge))) != NULL) { idTemp = IMergePopupMenus( pqcm->hmenu, hmMerge, pqcm->idCmdFirst, pqcm->idCmdLast); if (idMax < idTemp) { idMax = idTemp; } DestroyMenu(hmMerge); } pqcm->idCmdFirst = idMax; } LPITEMIDLIST ILCombinePriv(LPCITEMIDLIST pidl1, LPCITEMIDLIST pidls); //+--------------------------------------------------------------------------- // // Function: GenerateEvent // // Purpose: Generate a Shell Notification event. // // Arguments: // lEventId [in] The event ID to post // pidlFolder [in] Folder pidl // pidlIn [in] First pidl that we reference // pidlNewIn [in] If needed, the second pidl. // // Returns: // // Author: jeffspr 16 Dec 1997 // // Notes: // VOID GenerateEvent( LONG lEventId, const PCONFOLDPIDLFOLDER& pidlFolder, const PCONFOLDPIDL& pidlIn, LPCITEMIDLIST pidlNewIn) { // Build an absolute pidl from the folder pidl + the object pidl // LPITEMIDLIST pidl = ILCombinePriv(pidlFolder.GetItemIdList(), pidlIn.GetItemIdList()); if (pidl) { // If we have two pidls, call the notify with both // if (pidlNewIn) { // Build the second absolute pidl // LPITEMIDLIST pidlNew = ILCombinePriv(pidlFolder.GetItemIdList(), pidlNewIn); if (pidlNew) { // Make the notification, and free the new pidl // SHChangeNotify(lEventId, SHCNF_IDLIST, pidl, pidlNew); FreeIDL(pidlNew); } } else { // Make the single-pidl notification // SHChangeNotify(lEventId, SHCNF_IDLIST, pidl, NULL); } // Always refresh, then free the newly allocated pidl // SHChangeNotifyHandleEvents(); FreeIDL(pidl); } } VOID ForceRefresh(HWND hwnd) { TraceFileFunc(ttidShellFolder); LPSHELLBROWSER psb = FileCabinet_GetIShellBrowser(hwnd); LPSHELLVIEW psv = NULL; // Did we get the shellview? #if 0 // We can't require this, since we may need to refresh without a folder // actually being open AssertSz(psb, "FileCabinet_GetIShellBrowser failed in ForceRefresh()"); #endif if (psb && SUCCEEDED(psb->QueryActiveShellView(&psv))) { // Flush our connection list, which will force us to re-enumerate // on refresh // g_ccl.FlushConnectionList(); Assert(psv); if (psv) { psv->Refresh(); psv->Release(); } } else { // In the case where we don't have a window to go off of, we'll just flush // and refresh the list. g_ccl.HrRefreshConManEntries(); } } //+--------------------------------------------------------------------------- // // Function: HrDeleteFromCclAndNotifyShell // // Purpose: Remove an object from the connection list and notify the // shell that it's going away. We call this when a user has // deleted a connection, and when a disconnect has caused a // connection to go away (as in an incoming connection) // // Arguments: // pidlFolder [in] Our folder pidl // pidlConnection [in] The pidl for this connection // ccfe [in] Our ConFoldEntry // // Returns: // // Author: jeffspr 22 Jul 1998 // // Notes: // HRESULT HrDeleteFromCclAndNotifyShell( const PCONFOLDPIDLFOLDER& pidlFolder, const PCONFOLDPIDL& pidlConnection, const CONFOLDENTRY& ccfe) { HRESULT hr = S_OK; BOOL fFlushPosts = FALSE; Assert(!pidlConnection.empty()); Assert(!ccfe.empty()); // Notify the shell that the object has gone away // if (!pidlFolder.empty()) { GenerateEvent(SHCNE_DELETE, pidlFolder, pidlConnection, NULL); } if (!ccfe.empty()) { // Remove this connection from the global list // hr = g_ccl.HrRemove(ccfe, &fFlushPosts); } // If we need to clear the tray PostMessages due to tray icon changes, // do so. // if (fFlushPosts && g_hwndTray) { FlushTrayPosts(g_hwndTray); } TraceHr(ttidShellFolder, FAL, hr, FALSE, "HrDeleteFromCclAndNotifyShell"); return hr; } //+--------------------------------------------------------------------------- // // Function: HrUpdateConnectionStatus // // Purpose: Update the connection status in the connection list, and perform the // appropriate tray actions to add/remove the item. Update the shell // // Arguments: // pcfp [in] pidl for this connection // ncs [in] new connection status // pidlFolder [in] our folder pidl // fUseCharacter [in] dwCharacter is valid // dwCharacter [in] if fUseCharacter was specified as TRUE, update the // characteristics using this value. // // Returns: // // Author: jeffspr 28 Aug 1998 // // Notes: // HRESULT HrUpdateConnectionStatus( const PCONFOLDPIDL& pcfp, NETCON_STATUS ncs, const PCONFOLDPIDLFOLDER& pidlFolder, BOOL fUseCharacter, DWORD dwCharacter) { HRESULT hr = S_OK; HRESULT hrFind = S_OK; ConnListEntry cle; // Raid #310390: If this is a RAS connection, we need to double check the status.. CONFOLDENTRY ccfeDup; hrFind = g_ccl.HrFindConnectionByGuid(&(pcfp->guidId), cle); if (S_OK == hrFind) { Assert(!cle.ccfe.empty()); if (!cle.ccfe.empty()) { if ((NCS_DISCONNECTED == ncs) && (cle.ccfe.GetCharacteristics() & NCCF_OUTGOING_ONLY)) { hr = ccfeDup.HrDupFolderEntry(cle.ccfe); if (FAILED(hr)) { ccfeDup.clear(); } } } } if (!ccfeDup.empty()) { // Raid #310390: If this is a Ras connection, then double check // the status HRESULT hrRas = S_OK; INetConnection * pNetCon = NULL; hrRas = ccfeDup.HrGetNetCon(IID_INetConnection, reinterpret_cast(&pNetCon)); if (SUCCEEDED(hrRas)) { NETCON_PROPERTIES * pProps; hrRas = pNetCon->GetProperties(&pProps); if (SUCCEEDED(hrRas)) { if (ncs != pProps->Status) { TraceTag(ttidShellFolder, "Resetting status, notified " "status: %d, actual status: %d", ncs, pProps->Status); ncs = pProps->Status; } FreeNetconProperties(pProps); } ReleaseObj(pNetCon); } } // MAKE SURE TO -- Release this lock in either find case below // g_ccl.AcquireWriteLock(); hrFind = g_ccl.HrFindConnectionByGuid(&(pcfp->guidId), cle); if (hrFind == S_OK) { Assert(!cle.ccfe.empty()); if (!cle.ccfe.empty()) { cle.ccfe.SetNetConStatus(ncs); if (fUseCharacter) { cle.ccfe.SetCharacteristics(dwCharacter); } const GUID ConnectionGuid = pcfp->guidId; // Fix IA64 alignment. g_ccl.HrUpdateConnectionByGuid(&ConnectionGuid, cle); } // Update the tray icon // GUID guidId; guidId = pcfp->guidId; hr = g_ccl.HrUpdateTrayIconByGuid(&guidId, TRUE); g_ccl.ReleaseWriteLock(); // Close statistics window if disconnecting if (NCS_DISCONNECTED == ncs || NCS_MEDIA_DISCONNECTED == ncs) { CNetStatisticsCentral * pnsc = NULL; HRESULT hrStatmon = S_OK; hrStatmon = CNetStatisticsCentral::HrGetNetStatisticsCentral(&pnsc, FALSE); if (S_OK == hrStatmon) { GUID guidId; guidId = pcfp->guidId; pnsc->CloseStatusMonitor(&guidId); ReleaseObj(pnsc); } } PCONFOLDPIDL pidlUpdated; cle.ccfe.ConvertToPidl(pidlUpdated); RefreshFolderItem(pidlFolder, pcfp, pidlUpdated); // Update the folder icon } else { g_ccl.ReleaseWriteLock(); LPCWSTR pszName = pcfp->PszGetNamePointer(); TraceTag(ttidShellFolder, "HrUpdateConnectionStatus: Connection not found " "in cache: %S. Connection deleted prior to notification?", pszName ? pszName : L""); } TraceHr(ttidShellFolder, FAL, hr, FALSE, "HrUpdateConnectionStatus"); return hr; } //+--------------------------------------------------------------------------- // // Function: HrOnNotifyUpdateStatus // // Purpose: Process Connection sink notifications -- Try to make good // decisions about where we can ignore notifications to prevent // unnecessary requeries or icon updates. // // Arguments: // pidlFolder [in] The pidl information for our folder. // pidlCached [in] Pidl generated from our connection cache // ncsNew [in] The new connection state // // Returns: // // Author: jeffspr 29 Apr 1999 // // Notes: // HRESULT HrOnNotifyUpdateStatus( const PCONFOLDPIDLFOLDER& pidlFolder, const PCONFOLDPIDL& pidlCached, NETCON_STATUS ncsNew) { HRESULT hr = S_OK; NETCFG_TRY PCONFOLDPIDL pcfp = pidlCached; Assert(!pidlCached.empty()); // Filter out the multilink resume case, meaning that we get a // connecting/connected notification on a link that's already active. // if ( (NCS_CONNECTED == pcfp->ncs) && ((ncsNew == NCS_CONNECTING) || NCS_CONNECTED == ncsNew)) { TraceTag(ttidShellFolder, "HrOnNotifyUpdateStatus: Multi-link resume " "('connecting' while 'connected')"); } else { // If we're in the process of dialing, and the user cancels the // dialer, we'll get "disconnecting" state. This is normally the // same as "connected" icon-wise, but if we've never gotten connected, // we don't want the icon to flash "connected" before going to the // disconnected state // if ((pcfp->ncs == NCS_CONNECTING) && (ncsNew == NCS_DISCONNECTING)) { TraceTag(ttidShellFolder, "HrOnNotifyUpdateStatus: Ignoring " "disconnecting notification during cancel of incomplete dial"); } else { // Ignore the update if the connection status hasn't really changed. // if (pcfp->ncs != ncsNew) { // This is a true state change that we want to show // hr = HrUpdateConnectionStatus(pcfp, ncsNew, pidlFolder, FALSE, 0); } } } NETCFG_CATCH(hr) return hr; } HRESULT HrOnNotifyUpdateConnection( const PCONFOLDPIDLFOLDER& pidlFolder, const GUID * pguid, NETCON_MEDIATYPE ncm, NETCON_SUBMEDIATYPE ncsm, NETCON_STATUS ncs, DWORD dwCharacteristics, PCWSTR pszwName, PCWSTR pszwDeviceName, PCWSTR pszwPhoneNumberOrHostAddress) { HRESULT hr = S_OK; HRESULT hrFind = S_FALSE; BOOL fIconChanged = FALSE; NETCFG_TRY ConnListEntry cle; PCONFOLDPIDL pidlFind; PCONFOLDPIDL pidlOld; PCONFOLDPIDLFOLDER pidlFolderAlloc; // If the folder pidl wasn't passed in, then we'll go through the hassle of // getting it ourselves. // if (pidlFolder.empty()) { hr = HrGetConnectionsFolderPidl(pidlFolderAlloc); if (FAILED(hr)) { return hr; } } else { pidlFolderAlloc = pidlFolder; } g_ccl.AcquireWriteLock(); // Find the connection using the GUID. Cast the const away from the GUID // hrFind = g_ccl.HrFindConnectionByGuid(pguid, cle); if (S_OK == hrFind) { TraceTag(ttidShellFolder, "Notify: Pre-Update %S, Ncm: %d, Ncs: %d, Char: 0x%08x", cle.ccfe.GetName(), cle.ccfe.GetNetConMediaType(), cle.ccfe.GetNetConStatus(), cle.ccfe.GetCharacteristics()); // Did the icon state change? // if ((cle.ccfe.GetCharacteristics() & NCCF_SHOW_ICON) != (dwCharacteristics & NCCF_SHOW_ICON)) { fIconChanged = TRUE; } // Is this a new "set default" command? If so we need to search for any other defaults and // unset them first if ( (dwCharacteristics & NCCF_DEFAULT) && !(cle.ccfe.GetCharacteristics() & NCCF_DEFAULT) ) { PCONFOLDPIDL pidlDefault; // Not the end of the world if this doesn't work, so use a HrT. HRESULT hrT = g_ccl.HrUnsetCurrentDefault(pidlDefault); if (S_OK == hrT) { // Let the shell know about the new state of affairs GenerateEvent(SHCNE_UPDATEITEM, pidlFolderAlloc, pidlDefault, NULL); } } // Save the old version of the pidl for the connection so we can use // it to determine which notifications we need. // CONFOLDENTRY &ccfe = cle.ccfe; // Very important to release the lock before doing any thing which // calls back into the shell. (e.g. GenerateEvent) // cle.ccfe.ConvertToPidl(pidlOld); // Save old status so we know whether or not to send the status change // notifications // ccfe.UpdateData(CCFE_CHANGE_MEDIATYPE | CCFE_CHANGE_STATUS | CCFE_CHANGE_CHARACTERISTICS | CCFE_CHANGE_NAME | CCFE_CHANGE_DEVICENAME | CCFE_CHANGE_PHONEORHOSTADDRESS, ncm, ncsm, ncs, dwCharacteristics, pszwName, pszwDeviceName, pszwPhoneNumberOrHostAddress); // NULL means go figure it out yourself... g_ccl.HrUpdateConnectionByGuid(pguid, cle); TraceTag(ttidShellFolder, "Notify: Post-Update %S, Ncm: %d, Ncs: %d, Char: 0x%08x, Icon change: %d", ccfe.GetName(), ccfe.GetNetConMediaType(), ccfe.GetNetConStatus(), ccfe.GetCharacteristics(), fIconChanged); // Get the pidl for the connection so we can use it to notify // the shell further below. // ccfe.ConvertToPidl(pidlFind); g_ccl.ReleaseWriteLock(); } else { // If the connection wasn't found in the cache, then it's likely that // the notification engine is giving us a notification for a connection // that hasn't yet been given to us. // g_ccl.ReleaseWriteLock(); if (S_FALSE == hrFind) { TraceTag(ttidShellFolder, "Notify: Modify notification received on a connection we don't know about"); } else { TraceTag(ttidShellFolder, "Notify: Modify: Error occurred during find of connection. hr = 0x%08x", hr); } } if (S_OK == hrFind) { if ( !(pidlFind.empty() || pidlOld.empty()) ) { // Don't do the update if the status isn't changing. // // Don't want to croak on a bad return code here. // (VOID) HrOnNotifyUpdateStatus(pidlFolderAlloc, pidlOld, ncs); if (fIconChanged) { hr = g_ccl.HrUpdateTrayIconByGuid(pguid, TRUE); TraceTag(ttidShellFolder, "Returned from HrUpdateTrayIconByGuid", hr); } GenerateEvent(SHCNE_UPDATEITEM, pidlFolderAlloc, pidlFind, NULL); } // Update status monitor title (LAN case) CNetStatisticsCentral * pnsc = NULL; HRESULT hrStatmon = S_OK; hrStatmon = CNetStatisticsCentral::HrGetNetStatisticsCentral(&pnsc, FALSE); if (S_OK == hrStatmon) { pnsc->UpdateTitle(pguid, pszwName); ReleaseObj(pnsc); } } NETCFG_CATCH(hr) TraceHr(ttidShellFolder, FAL, hr, FALSE, "HrOnNotifyUpdateConnection"); return hr; } BOOL g_fInRefreshAlready = FALSE; BOOL g_fRefreshAgain = FALSE; //+--------------------------------------------------------------------------- // // Function: HrForceRefreshNoFlush // // Purpose: Force a refresh, but without wiping out all existing data. // This lets us keep as much state as possible intact, while // also letting us remove old items and adding new items (doing // the correct things with tray icons and such along the way). // // Arguments: // pidlFolder [in] Our folder pidl // // Returns: // // Author: jeffspr 28 Aug 1999 // // Notes: // HRESULT HrForceRefreshNoFlush(const PCONFOLDPIDLFOLDER& pidlFolder) { HRESULT hr = S_OK; CConnectionList * pccl = NULL; NETCFG_TRY PCONFOLDPIDLFOLDER pidlFolderAlloc; TraceTag(ttidShellFolder, "HrForceRefreshNoFlush"); g_fRefreshAgain = TRUE; // If we're refreshing, then tell the thread already in here that it // should refresh again. // if (!g_fInRefreshAlready) { // This starts out being set, then we'll turn it off. If someone // turns it back on while we're in this long function, then we'll // do it again // while (g_fRefreshAgain) { g_fInRefreshAlready = TRUE; // We're now in refresh g_fRefreshAgain = FALSE; if (pidlFolder.empty()) { hr = HrGetConnectionsFolderPidl(pidlFolderAlloc); } else { pidlFolderAlloc = pidlFolder; } if (SUCCEEDED(hr)) { // First, create the secondary connection list in order to compare the known // state with the recently requested refresh state // pccl = new CConnectionList(); if (!pccl) { hr = E_OUTOFMEMORY; AssertSz(FALSE, "Couldn't create CConnectionList in HrForceRefreshNoFlush"); } else { PCONFOLDPIDLVEC apidl; PCONFOLDPIDLVEC apidlCached; PCONFOLDPIDLVEC::const_iterator iterLoop = 0; // Initialize the list. FALSE means we don't want to tie this // list to the tray // pccl->Initialize(FALSE, FALSE); // Retrieve the entries from the connection manager. // hr = pccl->HrRetrieveConManEntries(apidl); if (SUCCEEDED(hr)) { TraceTag(ttidShellFolder, "HrForceRefreshNoFlush -- %d entries retrieved", apidl.size); // Loop through the connections. If there are new connections // here that aren't in the cache, then add them and do the appropriate // icon updates // for (iterLoop = apidl.begin(); iterLoop != apidl.end(); iterLoop++) { PCONFOLDPIDL pcfp = *iterLoop; CONFOLDENTRY ccfe; // We don't need to update the wizard. // if (WIZARD_NOT_WIZARD == pcfp->wizWizard) { // Convert to the confoldentry // hr = iterLoop->ConvertToConFoldEntry(ccfe); if (SUCCEEDED(hr)) { // ConnListEntry cle; ConnListEntry cleDontCare; hr = g_ccl.HrFindConnectionByGuid(&(pcfp->guidId), cleDontCare); if (S_FALSE == hr) { if ((ccfe.GetCharacteristics() & NCCF_INCOMING_ONLY) && (ccfe.GetNetConStatus() == NCS_DISCONNECTED) && (ccfe.GetNetConMediaType() != NCM_NONE)) { TraceTag(ttidShellFolder, "Ignoring transient incoming connection (new, but status is disconnected)"); } else { TraceTag(ttidShellFolder, "HrForceRefreshNoFlush -- New connection: %S", ccfe.GetName()); // Insert the connection in the connection list // hr = g_ccl.HrInsert(ccfe); if (SUCCEEDED(hr)) { // the connection list has taken control of this structure // TraceTag(ttidShellFolder, "HrForceRefreshNoFlush -- successfully added connection to list. Notifying shell"); // don't delete ccfe on success. g_ccl owns it after an // insert. // GenerateEvent(SHCNE_CREATE, pidlFolderAlloc, *iterLoop, NULL); } else { TraceHr(ttidShellFolder, FAL, hr, FALSE, "HrForceRefreshNoFlush -- Failed to insert connection into shell"); } } } else { // Update the connection properties for this connection // hr = HrOnNotifyUpdateConnection( pidlFolderAlloc, &(ccfe.GetGuidID()), ccfe.GetNetConMediaType(), ccfe.GetNetConSubMediaType(), ccfe.GetNetConStatus(), ccfe.GetCharacteristics(), ccfe.GetName(), ccfe.GetDeviceName(), ccfe.GetPhoneOrHostAddress()); } } } } } else { TraceHr(ttidShellFolder, FAL, hr, FALSE, "HrForceRefreshNoFlush -- Failed to retrieve Conman entries"); } // Retrieve a pidl list from the connection cache. This should not force a // reload (which would defeat the purpose of the whole operation). // hr = g_ccl.HrRetrieveConManEntries(apidlCached); if (SUCCEEDED(hr)) { for (iterLoop = apidlCached.begin(); iterLoop != apidlCached.end(); iterLoop++) { CONFOLDENTRY ccfe; Assert(!iterLoop->empty()); if (!iterLoop->empty()) { const PCONFOLDPIDL& pcfp = *iterLoop; // If it's not a wizard pidl, then update the // icon data. // if (WIZARD_NOT_WIZARD == pcfp->wizWizard) { ConnListEntry cle; BOOL fDeadIncoming = FALSE; hr = pccl->HrFindConnectionByGuid(&(pcfp->guidId), cle); if (hr == S_OK) { DWORD dwChars = cle.ccfe.GetCharacteristics(); NETCON_STATUS ncs = cle.ccfe.GetNetConStatus(); NETCON_MEDIATYPE ncm = cle.ccfe.GetNetConMediaType(); // Figure out whether this is a transient incoming connection // (enumerated, but disconnected) // if ((ncs == NCS_DISCONNECTED) && (ncm != NCM_NONE) && (dwChars & NCCF_INCOMING_ONLY)) { fDeadIncoming = TRUE; } } // If it was either not found or is a dead incoming connection // (disconnected), blow it away from the list. // if ((S_FALSE == hr) || (fDeadIncoming)) { hr = iterLoop->ConvertToConFoldEntry(ccfe); if (SUCCEEDED(hr)) { hr = HrDeleteFromCclAndNotifyShell(pidlFolderAlloc, *iterLoop, ccfe); // Assuming that succeeded (or hr will be S_OK if // the HrGet... wasn't called) // if (SUCCEEDED(hr)) { GenerateEvent(SHCNE_DELETE, pidlFolderAlloc, *iterLoop, NULL); } } } } } } } pccl->Uninitialize(); delete pccl; pccl = NULL; } } if (g_fRefreshAgain) { TraceTag(ttidShellFolder, "Looping back for another refresh since g_fRefreshAgain got set"); } } // end while (g_fRefreshAgain) // Mark us as not being in the function // g_fInRefreshAlready = FALSE; } else { TraceTag(ttidShellFolder, "Marking for additional refresh and exiting"); } NETCFG_CATCH(hr) TraceHr(ttidShellFolder, FAL, hr, FALSE, "HrForceRefreshNoFlush"); return hr; } //+--------------------------------------------------------------------------- // // Function: HrShellView_GetSelectedObjects // // Purpose: Get the selected data objects. We only care about the first // one (we'll ignore the rest) // // Arguments: // hwnd [in] Our window handle // papidlSelection [out] Return array for selected pidls // lpcidl [out] Count of returned pidls // // Returns: S_OK if 1 or more items are selected. // S_FALSE if 0 items are selected // OLE HRESULT otherwise // // Author: jeffspr 13 Jan 1998 // // Notes: // HRESULT HrShellView_GetSelectedObjects( HWND hwnd, PCONFOLDPIDLVEC& apidlSelection) { HRESULT hr = S_OK; LPCITEMIDLIST * apidl = NULL; UINT cpidl = 0; // Get the selected object list from the shell // cpidl = ShellFolderView_GetSelectedObjects(hwnd, &apidl); // If the GetSelectedObjects failed, NULL out the return // params. // if (-1 == cpidl) { cpidl = 0; apidl = NULL; hr = E_OUTOFMEMORY; } else { // If no items were selected, return S_FALSE // if (0 == cpidl) { Assert(!apidl); hr = S_FALSE; } } // Fill in the out params // if (SUCCEEDED(hr)) { hr = PConfoldPidlVecFromItemIdListArray(apidl, cpidl, apidlSelection); SHFree(apidl); } TraceHr(ttidError, FAL, hr, (S_FALSE == hr), "HrShellView_GetSelectedObjects"); return hr; } //+--------------------------------------------------------------------------- // // Function: HrGetConnectionPidlWithRefresh // // Purpose: Utility function used by HrCreateDesktopIcon and HrLaunchConnection // // Arguments: // guidId: GUID of the connection // ppidlCon: PIDL of the connection, if found // // Returns: S_OK if succeeded // S_FALSE if the GUID does not match any existing connection // standard error code otherwise // // Author: tongl 19 Feb 1999 // // Notes: // HRESULT HrGetConnectionPidlWithRefresh(const GUID& guidId, PCONFOLDPIDL& ppidlCon) { HRESULT hr = S_OK; NETCFG_TRY PCONFOLDPIDL pidlCon; CConnectionFolderEnum * pCFEnum = NULL; DWORD dwFetched = 0; // refresh the folder before we enumerate PCONFOLDPIDLFOLDER pidlEmpty; hr = HrForceRefreshNoFlush(pidlEmpty); if (SUCCEEDED(hr)) { // Create the IEnumIDList object (CConnectionFolderEnum) // hr = CConnectionFolderEnum::CreateInstance ( IID_IEnumIDList, (VOID **)&pCFEnum); if (SUCCEEDED(hr)) { Assert(pCFEnum); // Call the PidlInitialize function to allow the enumeration // object to copy the list. // pCFEnum->PidlInitialize(TRUE, pidlEmpty, CFCOPT_ENUMALL); while (SUCCEEDED(hr) && (S_FALSE != hr)) { // Clear out the previous results, if any. // dwFetched = 0; // Get the next connection // LPITEMIDLIST pitemIdList; hr = pCFEnum->Next(1, &pitemIdList, &dwFetched); pidlCon.InitializeFromItemIDList(pitemIdList); if (S_OK == hr) { if (pidlCon->guidId == guidId) { hr = S_OK; ppidlCon = pidlCon; break; } } } } ReleaseObj(pCFEnum); } NETCFG_CATCH(hr) return hr; } //+--------------------------------------------------------------------------- // // Function: HrRenameConnectionInternal // // Purpose: The shared portion for renaming a connection through the // connections folder UI and the export function // // Arguments: // pszInput [in] String to duplicate // cchInput [in] Count of characters to copy (not including null term) // ppszOutput [out] Return pointer for the newly allocated string. // // Returns: // // Author: tongl 26 May 1999 // // Notes: // HRESULT HrRenameConnectionInternal( const PCONFOLDPIDL& pidlCon, const PCONFOLDPIDLFOLDER& pidlFolderRoot, LPCWSTR pszNewName, BOOL fRaiseError, HWND hwndOwner, PCONFOLDPIDL& ppidlOut) { HRESULT hr = S_OK; NETCFG_TRY INetConnection * pNetCon = NULL; PCONFOLDPIDL pidlNew; BOOL fRefresh = FALSE; BOOL fActivating = FALSE; CONFOLDENTRY ccfe; PCWSTR pszReservedName; if (fRaiseError) { Assert(hwndOwner); } Assert(FImplies(fRaiseError,IsWindow(hwndOwner))); Assert(pszNewName); if ( (pidlCon.empty()) || !pszNewName ) { hr = E_INVALIDARG; } else { hr = pidlCon.ConvertToConFoldEntry(ccfe); if (SUCCEEDED(hr)) { // Do a case sensitive compare to see if the new name is exactly // the same as the old one. If yes then, we ignore renaming. // if (lstrcmpW(ccfe.GetName(), pszNewName) == 0) { hr = S_FALSE; // Create a dupe pidl, if needed if (!ppidlOut.empty()) { pidlNew.ILClone(pidlCon); } } else { // New name is either completely different from the old one or // differs just in the case. // CONFOLDENTRY cfEmpty; hr = HrCheckForActivation(pidlCon, cfEmpty, &fActivating); if (S_OK == hr) { if (fActivating) { // You can't rename an activating connection // TraceTag(ttidShellFolder, "Can not rename an activating connection"); hr = E_FAIL; if (fRaiseError) { NcMsgBox(_Module.GetResourceInstance(), hwndOwner, IDS_CONFOLD_ERROR_RENAME_ACTIVATING_CAPTION, IDS_CONFOLD_ERROR_RENAME_ACTIVATING, MB_ICONEXCLAMATION); } } else { // If the old and new names differ in their case only then, we don't // need to check if the new name already exists. if (lstrcmpiW(ccfe.GetName(), pszNewName) == 0) { // New name differs from the old name in case only. hr = S_FALSE; } else { pszReservedName = SzLoadIds( IDS_CONFOLD_INCOMING_CONN ); if ( lstrcmpiW(pszNewName, pszReservedName) == 0 ) { hr = HRESULT_FROM_WIN32(ERROR_INVALID_NAME); } else { // New name is completely different, need to check if // we already have the new name in our list // ConnListEntry cleDontCare; hr = g_ccl.HrFindConnectionByName((PWSTR)pszNewName, cleDontCare); } } // if (SUCCEEDED(hr)) { // If there's no duplicate in the list, attempt to set the // new name in the connection // if (hr == S_FALSE) { // Convert our persist data back to a INetConnection pointer. // hr = HrNetConFromPidl(pidlCon, &pNetCon); if (SUCCEEDED(hr)) { Assert(pNetCon); // Call the connection's Rename with the new name // hr = pNetCon->Rename(pszNewName); if (SUCCEEDED(hr)) { GUID guidId; fRefresh = TRUE; // Update the name in the cache // guidId = pidlCon->guidId; // Note: There is a race condition with notify.cpp: // CConnectionNotifySink::ConnectionRenamed\HrUpdateNameByGuid can also update this hr = g_ccl.HrUpdateNameByGuid( &guidId, (PWSTR) pszNewName, pidlNew, TRUE); // Force the issue. it's an update, not a request GenerateEvent( SHCNE_RENAMEITEM, pidlFolderRoot, pidlCon, pidlNew.GetItemIdList()); } if (fRaiseError && FAILED(hr)) { // Leave hr at this value, as it will cause the UI to leave // the object in the "rename in progress" state, so the user // can change it again and hit enter. // if (HRESULT_FROM_WIN32(ERROR_DUP_NAME) == hr) { // Bring up the message box for the known DUPLICATE_NAME // error (void) NcMsgBox( _Module.GetResourceInstance(), hwndOwner, IDS_CONFOLD_RENAME_FAIL_CAPTION, IDS_CONFOLD_RENAME_DUPLICATE, MB_OK | MB_ICONEXCLAMATION); } else { // Bring up the generic failure error, with the win32 text // (void) NcMsgBoxWithWin32ErrorText( DwWin32ErrorFromHr (hr), _Module.GetResourceInstance(), hwndOwner, IDS_CONFOLD_RENAME_FAIL_CAPTION, IDS_TEXT_WITH_WIN32_ERROR, IDS_CONFOLD_RENAME_OTHER_FAIL, MB_OK | MB_ICONEXCLAMATION); } } ReleaseObj(pNetCon); // RAID 180252 } } else { if ( hr == HRESULT_FROM_WIN32(ERROR_INVALID_NAME) ) { if(fRaiseError) { // Bring up the invalid name message box. // error (void) NcMsgBox( _Module.GetResourceInstance(), hwndOwner, IDS_CONFOLD_RENAME_FAIL_CAPTION, IDS_CONFOLD_RENAME_INCOMING_CONN, MB_OK | MB_ICONEXCLAMATION); } } else { // A duplicate name was found. Return an error. // hr = HRESULT_FROM_WIN32(ERROR_FILE_EXISTS); if(fRaiseError) { // Bring up the message box for the known DUPLICATE_NAME // error (void) NcMsgBox( _Module.GetResourceInstance(), hwndOwner, IDS_CONFOLD_RENAME_FAIL_CAPTION, IDS_CONFOLD_RENAME_DUPLICATE, MB_OK | MB_ICONEXCLAMATION); } } } } } } } } } if (S_OK == hr) { Assert(!pidlNew.empty()); ppidlOut = pidlNew; } if (S_FALSE == hr) { hr = E_FAIL; } // Fill in the return parameter // NETCFG_CATCH(hr) TraceHr(ttidError, FAL, hr, FALSE, "HrRenameConnectionInternal"); return hr; }