mirror of https://github.com/tongzx/nt5src
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1496 lines
51 KiB
1496 lines
51 KiB
//+---------------------------------------------------------------------------
|
|
//
|
|
// 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 <wtypes.h>
|
|
#include <ntddndis.h>
|
|
#include <ndisprv.h>
|
|
#include <devioctl.h>
|
|
#include <ndispnp.h>
|
|
#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<VOID**>(&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"<name missing>");
|
|
}
|
|
|
|
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;
|
|
}
|