Source code of Windows XP (NT5)
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

//+---------------------------------------------------------------------------
//
// 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;
}