// cmponent.cpp : Implementation of CFileMgmtComponent

#include "stdafx.h"
#include "cookie.h"
#include "safetemp.h"

#include "macros.h"
USE_HANDLE_MACROS("FILEMGMT(cmponent.cpp)")

#include "ShrProp.h"    // Share Properties Pages

#include "FileSvc.h" // FileServiceProvider
#include "smb.h"
#include "fpnw.h"
#include "sfm.h"

#include "dataobj.h"
#include "cmponent.h" // CFileMgmtComponent
#include "compdata.h" // CFileMgmtComponentData
#include "stdutils.h" // SynchronousCreateProcess

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif


#include "stdcmpnt.cpp" // CComponent

UINT g_aColumns0[2] =
  {IDS_ROOT_NAME, 0};
UINT g_aColumns1[6] =
  {IDS_SHARES_SHARED_FOLDER, IDS_SHARES_SHARED_PATH, IDS_SHARES_TRANSPORT,
    IDS_SHARES_NUM_SESSIONS, IDS_SHARES_COMMENT, 0};
UINT g_aColumns2[8] =
  {IDS_CONN_USERNAME, IDS_CONN_COMPUTERNAME, IDS_CONN_TRANSPORT, IDS_CONN_NUM_FILES,
    IDS_CONN_CONNECTED_TIME, IDS_CONN_IDLE_TIME, IDS_CONN_IS_GUEST, 0};
UINT g_aColumns3[6] =
  {IDS_FILE_FILENAME, IDS_FILE_USERNAME, IDS_FILE_TRANSPORT, IDS_FILE_NUM_LOCKS,
    IDS_FILE_OPEN_MODE, 0};
UINT g_aColumns4[6] =
  { IDS_SERVICE_SERVICENAME, IDS_SERVICE_DESCRIPTION, IDS_SERVICE_STATUS,
    IDS_SERVICE_STARTUPTYPE, IDS_SERVICE_SECURITYCONTEXT, 0};

UINT* g_Columns[FILEMGMT_NUMTYPES] =
  {  g_aColumns0, // FILEMGMT_ROOT
    g_aColumns1, // FILEMGMT_SHARES
    g_aColumns2, // FILEMGMT_SESSIONS
    g_aColumns3, // FILEMGMT_RESOURCES
    g_aColumns4, // FILEMGMT_SERVICES
    NULL,        // FILEMGMT_SHARE
    NULL,        // FILEMGMT_SESSION
    NULL,        // FILEMGMT_RESOURCE
    NULL         // FILEMGMT_SERVICE
  };

UINT** g_aColumns = g_Columns;
/*
const UINT aColumns[STD_NODETYPE_NUMTYPES][STD_MAX_COLUMNS] =
  {  {IDS_ROOT_NAME, 0,0,0,0,0,0},
    {IDS_SHARES_SHARED_FOLDER, IDS_SHARES_SHARED_PATH, IDS_SHARES_TRANSPORT,
      IDS_SHARES_NUM_SESSIONS, IDS_SHARES_COMMENT, 0,0},
    {IDS_CONN_USERNAME, IDS_CONN_COMPUTERNAME, IDS_CONN_TRANSPORT, IDS_CONN_NUM_FILES,
      IDS_CONN_CONNECTED_TIME, IDS_CONN_IDLE_TIME, IDS_CONN_IS_GUEST},
    {IDS_FILE_FILENAME, IDS_FILE_USERNAME, IDS_FILE_TRANSPORT, IDS_FILE_NUM_LOCKS,
      IDS_FILE_OPEN_MODE, 0,0},
    { IDS_SERVICE_SERVICENAME, IDS_SERVICE_DESCRIPTION, IDS_SERVICE_STATUS,
      IDS_SERVICE_STARTUPTYPE, IDS_SERVICE_SECURITYCONTEXT, 0,0 },
    {0,0,0,0,0,0},
    {0,0,0,0,0,0},
    {0,0,0,0,0,0},
    {0,0,0,0,0,0}
};
*/

//
// CODEWORK this should be in a resource, for example code on loading data resources see
//   D:\nt\private\net\ui\common\src\applib\applib\lbcolw.cxx ReloadColumnWidths()
//   JonN 10/11/96
//

int g_aColumnWidths0[1] = {150};
int g_aColumnWidths1[5] = {AUTO_WIDTH,120       ,90        ,AUTO_WIDTH,150};
int g_aColumnWidths2[7] = {100       ,AUTO_WIDTH,90        ,AUTO_WIDTH,AUTO_WIDTH,AUTO_WIDTH,AUTO_WIDTH};
int g_aColumnWidths3[5] = {120       ,AUTO_WIDTH,90        ,AUTO_WIDTH,AUTO_WIDTH};
int g_aColumnWidths4[5] = {130       ,AUTO_WIDTH,AUTO_WIDTH,AUTO_WIDTH,AUTO_WIDTH};
int* g_ColumnWidths[FILEMGMT_NUMTYPES] =
  {  g_aColumnWidths0, // FILEMGMT_ROOT
    g_aColumnWidths1, // FILEMGMT_SHARES
    g_aColumnWidths2, // FILEMGMT_SESSIONS
    g_aColumnWidths3, // FILEMGMT_RESOURCES
    g_aColumnWidths4, // FILEMGMT_SERVICES
    NULL,             // FILEMGMT_SHARE
    NULL,             // FILEMGMT_SESSION
    NULL,             // FILEMGMT_RESOURCE
    NULL              // FILEMGMT_SERVICE
  };
int** g_aColumnWidths = g_ColumnWidths;
/*
const int aColumnWidths[STD_NODETYPE_NUMTYPES][STD_MAX_COLUMNS] =
  {  {AUTO_WIDTH,AUTO_WIDTH,AUTO_WIDTH,AUTO_WIDTH,AUTO_WIDTH,AUTO_WIDTH,AUTO_WIDTH}, // FILEMGMT_ROOT
    {AUTO_WIDTH,120       ,90        ,AUTO_WIDTH,AUTO_WIDTH,AUTO_WIDTH,AUTO_WIDTH}, // FILEMGMT_SHARES
    {100       ,AUTO_WIDTH,90        ,AUTO_WIDTH,AUTO_WIDTH,AUTO_WIDTH,AUTO_WIDTH}, // FILEMGMT_SESSIONS
    {120       ,AUTO_WIDTH,90        ,AUTO_WIDTH,AUTO_WIDTH,AUTO_WIDTH,AUTO_WIDTH}, // FILEMGMT_RESOURCES
    {130       ,AUTO_WIDTH,AUTO_WIDTH,AUTO_WIDTH,AUTO_WIDTH,AUTO_WIDTH,AUTO_WIDTH}, // FILEMGMT_SERVICES
    {AUTO_WIDTH,AUTO_WIDTH,AUTO_WIDTH,AUTO_WIDTH,AUTO_WIDTH,AUTO_WIDTH,AUTO_WIDTH}, // FILEMGMT_SHARE
    {AUTO_WIDTH,AUTO_WIDTH,AUTO_WIDTH,AUTO_WIDTH,AUTO_WIDTH,AUTO_WIDTH,AUTO_WIDTH}, // FILEMGMT_SESSION
    {AUTO_WIDTH,AUTO_WIDTH,AUTO_WIDTH,AUTO_WIDTH,AUTO_WIDTH,AUTO_WIDTH,AUTO_WIDTH}, // FILEMGMT_RESOURCE
    {AUTO_WIDTH,AUTO_WIDTH,AUTO_WIDTH,AUTO_WIDTH,AUTO_WIDTH,AUTO_WIDTH,AUTO_WIDTH}  // FILEMGMT_SERVICE
};
*/

CString g_cstrClientName;
CString g_cstrGuest;
CString g_cstrYes;
CString g_cstrNo;

// Note that m_pFileMgmtData is still NULL during construction
CFileMgmtComponent::CFileMgmtComponent()
:  m_pControlbar( NULL )
,  m_pSvcMgmtToolbar( NULL )
,  m_pFileMgmtToolbar( NULL )
,  m_pViewedCookie( NULL )
,  m_pSelectedCookie( NULL )
,  m_iSortColumn(0)
,  m_dwSortFlags(0)
{
    AFX_MANAGE_STATE(AfxGetStaticModuleState( ));
}

CFileMgmtComponent::~CFileMgmtComponent()
{
  TRACE_METHOD(CFileMgmtComponent,ReleaseAll);
/* now in CFileMgmtComponentData
  if (m_hScManager != NULL)
    {
      AFX_MANAGE_STATE(AfxGetStaticModuleState( )); // required for CWaitCursor
    CWaitCursor wait;
    // Close the service control manager
    (void)::CloseServiceHandle(m_hScManager);
    } // if
*/
  VERIFY( SUCCEEDED(ReleaseAll()) );
}

HRESULT CFileMgmtComponent::ReleaseAll()
{
  MFC_TRY;

  TRACE_METHOD(CFileMgmtComponent,ReleaseAll);

  if ( NULL != m_pViewedCookie )
  {
    // We did not get an equal number of MMCN_SHOW(1) and
    // MMCN_SHOW(0) notifications
    // CODEWORK should assert here but MMC is currently broken
    // ASSERT(FALSE);
    m_pViewedCookie->ReleaseResultChildren();
    m_pViewedCookie = NULL;
  }

  // We should get an equal number of MMCN_SELECT(1) and MMCN_SELECT(0) notifications
  // CODEWORK should assert this but MMC is broken ASSERT( NULL == m_pSelectedCookie ); 

  SAFE_RELEASE(m_pSvcMgmtToolbar);
  SAFE_RELEASE(m_pFileMgmtToolbar);
  SAFE_RELEASE(m_pControlbar);

  return CComponent::ReleaseAll();

  MFC_CATCH;
}

FileServiceProvider* CFileMgmtComponent::GetFileServiceProvider(
  FILEMGMT_TRANSPORT transport )
{
  return QueryComponentDataRef().GetFileServiceProvider(transport);
}

BOOL CFileMgmtComponent::IsServiceSnapin()
{
  return QueryComponentDataRef().IsServiceSnapin();
}


/////////////////////////////////////////////////////////////////////////////
// IComponent Implementation


HRESULT CFileMgmtComponent::LoadStrings()
{
  Service_LoadResourceStrings();
  return S_OK;
}

HRESULT CFileMgmtComponent::LoadColumns( CFileMgmtCookie* pcookie )
{
  TEST_NONNULL_PTR_PARAM(pcookie);

  ASSERT(m_pHeader != NULL);

  #ifdef SNAPIN_PROTOTYPER
  (void)Prototyper_FInsertColumns(pcookie);
  return S_OK;
  #endif

  if (g_cstrGuest.IsEmpty())
    VERIFY(g_cstrGuest.LoadString(IDS_GUEST));
  if (g_cstrYes.IsEmpty())
    VERIFY(g_cstrYes.LoadString(IDS_YES));
  if (g_cstrNo.IsEmpty())
    VERIFY(g_cstrNo.LoadString(IDS_NO));

  return LoadColumnsFromArrays( pcookie->QueryObjectType() );
}

// OnPropertyChange() is generated by MMCPropertyChangeNotify( param )
HRESULT CFileMgmtComponent::OnPropertyChange( LPARAM param )
{
  LPDATAOBJECT pdataobject = reinterpret_cast<LPDATAOBJECT> (param);
  VERIFY( SUCCEEDED( RefreshAllViews(pdataobject) ) );

  // The recipient of this notification is required to release the data object
  (void) pdataobject->Release();

  return S_OK;
} // CFileMgmtComponent::OnPropertyChange()

//
// In case of multiselect, piDataObject may point to a composite data object (MMC_MS_DO).
// RefreshAllViewsOnSelectedObject will crack down MMC_MS_DO to retrieve SI_MS_DO, then call
// RefreshAllViews on one of the selected objects in the internal list.
//
HRESULT CFileMgmtComponent::RefreshAllViewsOnSelectedObject(LPDATAOBJECT piDataObject)
{
    BOOL bMultiSelectObject = IsMultiSelectObject(piDataObject);
    if (!bMultiSelectObject)
        return RefreshAllViews(piDataObject);

    //
    // piDataObject is the composite data object (MMC_MS_DO) created by MMC.
    // We need to crack it to retrieve the multiselect data object (SI_MS_DO)
    // we provided to MMC in QueryDataObject().
    //
    IDataObject *piSIMSDO = NULL;
    HRESULT hr = GetSnapinMultiSelectDataObject(piDataObject, &piSIMSDO);
    if (SUCCEEDED(hr))
    {
        CFileMgmtDataObject *pDataObj = NULL;
        hr = ExtractData(piSIMSDO, CFileMgmtDataObject::m_CFInternal, &pDataObj, sizeof(pDataObj));
        if (SUCCEEDED(hr))
        {
            //
            // get the internal list of data objects of selected items, operate on one of them.
            //
            CDataObjectList* pMultiSelectObjList = pDataObj->GetMultiSelectObjList();
            ASSERT(!pMultiSelectObjList->empty());
            hr = RefreshAllViews(*(pMultiSelectObjList->begin()));
        }
    }

    return hr;
}

// Forces all views of the specified data object to refresh
HRESULT CFileMgmtComponent::RefreshAllViews( LPDATAOBJECT pDataObject )
{
  if ( NULL == pDataObject || NULL == m_pConsole )
  {
    ASSERT(FALSE);
    return ERROR_INVALID_PARAMETER;
  }

// This is new code for updating the Service list using a mark-and-sweep algorithm.
// Eventually this should be applied to all result cookies.
  CCookie* pbasecookie = NULL;
  HRESULT hr = ExtractData(
    pDataObject,
    CDataObject::m_CFRawCookie,
    &pbasecookie,
    sizeof(pbasecookie) );
  RETURN_HR_IF_FAIL; // MMC shouldn't have given me someone else's cookie
  pbasecookie = QueryBaseComponentDataRef().ActiveBaseCookie( pbasecookie );
  CFileMgmtCookie* pUpdatedCookie = dynamic_cast<CFileMgmtCookie*>(pbasecookie);
  RETURN_E_FAIL_IF_NULL(pUpdatedCookie);
  FileMgmtObjectType objTypeForUpdatedCookie = pUpdatedCookie->QueryObjectType();
  if ( FILEMGMT_SERVICE == objTypeForUpdatedCookie )
  {
    if (   NULL == m_pViewedCookie
        || FILEMGMT_SERVICES != m_pViewedCookie->QueryObjectType()
       )
    {
      return S_OK; // not a service cookie update
    }
    pUpdatedCookie = dynamic_cast<CFileMgmtCookie*>(m_pViewedCookie);
    RETURN_E_FAIL_IF_NULL(pUpdatedCookie);
    objTypeForUpdatedCookie = FILEMGMT_SERVICES;
  }
  if ( FILEMGMT_SERVICES == objTypeForUpdatedCookie )
  {
    CFileMgmtScopeCookie* pScopeCookie = dynamic_cast<CFileMgmtScopeCookie*>(pUpdatedCookie);
    RETURN_E_FAIL_IF_NULL(pScopeCookie);

    // "Mark" -- Mark all existing list elements as "delete"
    pScopeCookie->MarkResultChildren( NEWRESULTCOOKIE_DELETE );

    // "Sweep" -- Read the new list.  When a new element is the same object
    // as an existing element not yet seen, mark the old element as "old"
    // and update its fields. Otherwise, add it as a "new" element.
    hr = QueryComponentDataRef().Service_PopulateServices(m_pResultData, pScopeCookie);
    RETURN_HR_IF_FAIL;

    // Refresh all views to conform with the new list.
    hr = m_pConsole->UpdateAllViews( pDataObject, 2L, 0L );
    RETURN_HR_IF_FAIL;

    // UpdateToolbar if selected
    hr = m_pConsole->UpdateAllViews( pDataObject, 3L, 0L );
    RETURN_HR_IF_FAIL;

    // Remove items which are still marked "delete".
    pScopeCookie->RemoveMarkedChildren();

    pScopeCookie->MarkResultChildren( NEWRESULTCOOKIE_OLD );

    return S_OK;
  }

  //
  // JonN 1/27/00: WinSE 5875: The refresh action is liable to delete pDataObject
  // unless we keep an extra refcount.  In practice, this only appears to happen
  // when we delete a share in taskpad view.
  //
  CComPtr<IDataObject> spDataObject = pDataObject;

  // clear all views of this data
  hr = m_pConsole->UpdateAllViews( pDataObject, 0L, 0L );
  RETURN_HR_IF_FAIL;

  // reread all views of this data
  hr = m_pConsole->UpdateAllViews( pDataObject, 1L, 0L );
  RETURN_HR_IF_FAIL;

  // UpdateToolbar if selected
  hr = m_pConsole->UpdateAllViews( pDataObject, 3L, 0L );
  
  return hr;
} // CFileMgmtComponent::RefreshAllViews()

// OnViewChange is generated by UpdateAllViews( lpDataObject, data, hint )
HRESULT CFileMgmtComponent::OnViewChange( LPDATAOBJECT lpDataObject, LPARAM data, LPARAM /*hint*/ )
{
  AFX_MANAGE_STATE(AfxGetStaticModuleState( ));
  CWaitCursor wait;

  ASSERT( NULL != lpDataObject );

  if (NULL == m_pViewedCookie) // skip this component if not being viewed
    return S_OK;

  CCookie* pbasecookie = NULL;
  HRESULT hr = ExtractData( lpDataObject,
    CDataObject::m_CFRawCookie,
    &pbasecookie,
    sizeof(pbasecookie) );

  RETURN_HR_IF_FAIL; // MMC shouldn't have given me someone else's cookie
  pbasecookie = QueryBaseComponentDataRef().ActiveBaseCookie( pbasecookie );
  CFileMgmtCookie* pUpdatedCookie = dynamic_cast<CFileMgmtCookie*>(pbasecookie);
  RETURN_E_FAIL_IF_NULL(pUpdatedCookie);
  FileMgmtObjectType objTypeForUpdatedCookie = pUpdatedCookie->QueryObjectType();

  switch (m_pViewedCookie->QueryObjectType())
  {
  case FILEMGMT_ROOT:
    return S_OK; // there is never any need to refresh this
  case FILEMGMT_RESOURCES:
    if ( FILEMGMT_RESOURCE == objTypeForUpdatedCookie ||
         FILEMGMT_RESOURCES == objTypeForUpdatedCookie)
      break;
    // fall through
  case FILEMGMT_SESSIONS:
    if ( FILEMGMT_SESSION == objTypeForUpdatedCookie ||
         FILEMGMT_SESSIONS == objTypeForUpdatedCookie)
      break;
    // fall through
  case FILEMGMT_SHARES:
    if ( FILEMGMT_SHARE == objTypeForUpdatedCookie ||
         FILEMGMT_SHARES == objTypeForUpdatedCookie)
      break;
    return S_OK;
  case FILEMGMT_SERVICES:
    if ( FILEMGMT_SERVICE == objTypeForUpdatedCookie ||
         FILEMGMT_SERVICES == objTypeForUpdatedCookie)
      break;
    return S_OK;
  case FILEMGMT_SHARE:
  case FILEMGMT_SESSION:
  case FILEMGMT_RESOURCE:
  case FILEMGMT_SERVICE:
  default:
    ASSERT(FALSE); // this shouldn't be possible
    return S_OK;
  }

  // There should be no need to compare machine name, since these are both from the
  // same instance.

  if ( 0L == data )
  {
    ASSERT( NULL != m_pResultData );
    VERIFY( SUCCEEDED(m_pResultData->DeleteAllRsltItems()) );
    m_pViewedCookie->ReleaseResultChildren();
    //
    // At this point, m_pViewedCookie is still the viewed cookie for this IComponent
    // but (once this has happened to all of the views) its list of result children
    // is empty and its m_nResultCookiesRefcount is zero.  This must be followed
    // promptly with PopulateListbox calls for these views since this is not a good
    // state for the cookie.
    //
  }
  else if ( 1L == data )
  {
    VERIFY( SUCCEEDED(PopulateListbox( m_pViewedCookie )) );
  }
  else if ( 2L == data )
  {
    VERIFY( SUCCEEDED(RefreshNewResultCookies( *m_pViewedCookie )) );
  }
  else if ( 3L == data )
  {
    if (m_pSelectedCookie == pbasecookie)
      UpdateToolbar(lpDataObject, TRUE);
  }
  else
  {
    ASSERT(FALSE);
  }

  return S_OK;
} // CFileMgmtComponent::OnViewChange()


/////////////////////////////////////////////////////////////////////
// CFileMgmtComponent::CComponent::OnNotifyRefresh()
// 
// Virtual function called by CComponent::IComponent::Notify(MMCN_REFRESH)
// OnNotifyRefresh is generated by enabling the verb MMC_VERB_REFRESH.
HRESULT CFileMgmtComponent::OnNotifyRefresh( LPDATAOBJECT lpDataObject )
{
  TRACE0("CFileMgmtComponent::OnNotifyRefresh()\n");
  ASSERT(m_pResultData != NULL);
  if ( !m_pResultData )
    return E_POINTER;
  if ( !m_pViewedCookie )
  {
    ASSERT(FALSE);
    return S_OK;
  }

  // We used to use the cookie here from lpDataObject.  However, if one node
  // is selected and the user right clicks on a different one we an
  // lpDataObject for a node that is not enumerated in the result pane.
  // It results in bizarre behavior.  So use the m_pViewedCookie, instead.
  HRESULT    hr = S_OK;
  switch (m_pViewedCookie->QueryObjectType())
  {
  case FILEMGMT_SHARES:
  case FILEMGMT_SESSIONS:
  case FILEMGMT_RESOURCES:
  case FILEMGMT_SERVICES:
    (void) RefreshAllViews( lpDataObject );
    break;

  case FILEMGMT_ROOT:
  case FILEMGMT_SERVICE:   // Service was selected
  case FILEMGMT_SHARE:     // Share was selected
  case FILEMGMT_SESSION:   // Session was selected
  case FILEMGMT_RESOURCE:  // Open file was selected
  default:
    // This can happen if you select Shares, then select Shared Folders,
    // then right-click Shares and choose Refresh.  JonN 12/7/98
    break; // no need to refresh
  }

  return S_OK;
}

/////////////////////////////////////////////////////////////////////
// CFileMgmtComponent::RefreshNewResultCookies()
// 12/03/98 JonN     Created
// In the mark-and-sweep refresh algorithm, we have already marked all cookies
// as "old", "new" or "delete".  The view must now be made to conform with the list.
HRESULT CFileMgmtComponent::RefreshNewResultCookies( CCookie& refparentcookie )
{
  ASSERT( NULL != m_pResultData );

  RESULTDATAITEM tRDItem;
  ::ZeroMemory( &tRDItem, sizeof(tRDItem) );
  tRDItem.nCol = 0;
  tRDItem.mask = RDI_STR | RDI_IMAGE | RDI_PARAM;
  tRDItem.str = MMC_CALLBACK;
  // CODEWORK should use MMC_ICON_CALLBACK here

  HRESULT hr = S_OK;
  POSITION pos = refparentcookie.m_listResultCookieBlocks.GetHeadPosition();
  while (NULL != pos)
  {
    CBaseCookieBlock* pblock = refparentcookie.m_listResultCookieBlocks.GetNext( pos );
    ASSERT( NULL != pblock && 1 == pblock->QueryNumCookies() );
    CCookie* pbasecookie = pblock->QueryBaseCookie(0);
    CNewResultCookie* pcookie = dynamic_cast<CNewResultCookie*>(pbasecookie);
    RETURN_E_FAIL_IF_NULL(pcookie);
    if ( pcookie->IsMarkedOld() )
    {
      continue; // Leave this one alone
    }
    else if ( pcookie->IsMarkedNew() )
    { // This one was just added to the list, add it to the view
      tRDItem.nImage = QueryBaseComponentDataRef().QueryImage( *pbasecookie, FALSE );
      // WARNING cookie cast
      tRDItem.lParam = reinterpret_cast<LPARAM>(pbasecookie);
      hr = m_pResultData->InsertItem(&tRDItem);
      if ( FAILED(hr) )
      {
        ASSERT(FALSE);
        break;
      }
    }
    else if ( pcookie->IsMarkedChanged() )
    { // This one was already in the list but its fields were altered, update
      HRESULTITEM hItem = 0;
      hr = m_pResultData->FindItemByLParam( reinterpret_cast<LPARAM>(pbasecookie), &hItem );
      if ( FAILED(hr) || 0 == hItem )
      {
        ASSERT(FALSE);
        continue;
      }
      VERIFY( SUCCEEDED(m_pResultData->UpdateItem( hItem )) );
    }
    else
    { // This one was just marked for deletion, remove it from the view
      // CODEWORK This may be a performance problem when the list is long
      // CODEWORK BryanWal doesn't trust FindItemByLParam!  Test carefully!
      ASSERT( pcookie->IsMarkedForDeletion() );
      HRESULTITEM hItem = 0;
      hr = m_pResultData->FindItemByLParam( reinterpret_cast<LPARAM>(pbasecookie), &hItem );
      if ( FAILED(hr) || 0 == hItem )
      {
        ASSERT(FALSE);
        continue;
      }
      VERIFY( SUCCEEDED(m_pResultData->DeleteItem( hItem, 0 )) );
    }
  }
  VERIFY( SUCCEEDED(m_pResultData->Sort( m_iSortColumn , m_dwSortFlags, 0 )) );
  return hr;
}

HRESULT CFileMgmtComponent::OnNotifySelect( LPDATAOBJECT lpDataObject, BOOL fSelected )
{
    HRESULT hr = S_OK;
    BOOL    bMultiSelectObject = FALSE;

    //
    // MMC passes in SI_MS_DO in MMCN_SELECT in case of multiselection.
    //
    CFileMgmtDataObject *pDataObj = NULL;
    hr = ExtractData(lpDataObject, CFileMgmtDataObject::m_CFInternal, &pDataObj, sizeof(pDataObj));
    if (SUCCEEDED(hr))
    {
        CDataObjectList* pMultiSelectObjList = pDataObj->GetMultiSelectObjList();
        bMultiSelectObject = !(pMultiSelectObjList->empty());
    }

    //
    // no verbs to add for multi-selected SharedFolders items
    //
    if (!bMultiSelectObject)
    {
        CCookie* pbasecookie = NULL;
        hr = ExtractData( lpDataObject,
                        CDataObject::m_CFRawCookie,
                        &pbasecookie,
                        sizeof(pbasecookie) );
        RETURN_HR_IF_FAIL; // MMC shouldn't have given me someone else's cookie
        pbasecookie = QueryBaseComponentDataRef().ActiveBaseCookie( pbasecookie );
        CFileMgmtCookie* pUpdatedCookie = dynamic_cast<CFileMgmtCookie*>(pbasecookie);
        RETURN_E_FAIL_IF_NULL(pUpdatedCookie);

        m_pSelectedCookie = (fSelected) ? pUpdatedCookie : NULL;

        UpdateDefaultVerbs();
    }

    return S_OK;
}

void CFileMgmtComponent::UpdateDefaultVerbs()
{
  if (NULL == m_pSelectedCookie)
    return;

  FileMgmtObjectType objtypeSelected = m_pSelectedCookie->QueryObjectType();

  if (NULL != m_pViewedCookie)
  {
    BOOL fEnableRefresh = FALSE;
    FileMgmtObjectType objtypeViewed = m_pViewedCookie->QueryObjectType();

    switch (objtypeViewed)
    {
    case FILEMGMT_SHARES:
      if (FILEMGMT_SHARES == objtypeSelected || FILEMGMT_SHARE == objtypeSelected)
        fEnableRefresh = TRUE;
      break;
    case FILEMGMT_SESSIONS:
      if (FILEMGMT_SESSIONS == objtypeSelected || FILEMGMT_SESSION == objtypeSelected)
        fEnableRefresh = TRUE;
      break;
    case FILEMGMT_RESOURCES:
      if (FILEMGMT_RESOURCES == objtypeSelected || FILEMGMT_RESOURCE == objtypeSelected)
        fEnableRefresh = TRUE;
      break;
    case FILEMGMT_SERVICES:
      if (FILEMGMT_SERVICES == objtypeSelected || FILEMGMT_SERVICE == objtypeSelected)
        fEnableRefresh = TRUE;
      break;
    }
    if (fEnableRefresh)
    {
      // Enable the refresh menuitem
      VERIFY( SUCCEEDED(m_pConsoleVerb->SetVerbState(MMC_VERB_REFRESH, ENABLED, TRUE)) );
    }
  }

  switch (objtypeSelected)
  {
  case FILEMGMT_SHARE:  // Share was selected
    //
    // don't enable Properties on the menu whenever SimpleSharingUI appears in NT Explorer
    //
    if (QueryComponentDataRef().GetIsSimpleUI())
    {
        VERIFY( SUCCEEDED(m_pConsoleVerb->SetDefaultVerb(MMC_VERB_NONE)) );
        break;
    }
    // fall through
  case FILEMGMT_SERVICE:  // Service was selected
    // Set the default verb to display the properties of the selected object
    VERIFY( SUCCEEDED(m_pConsoleVerb->SetVerbState(MMC_VERB_PROPERTIES, ENABLED, TRUE)) );
    VERIFY( SUCCEEDED(m_pConsoleVerb->SetDefaultVerb(MMC_VERB_PROPERTIES)) );
    break;

  case FILEMGMT_SESSION:  // Session was selected
  case FILEMGMT_RESOURCE:  // Open file was selected
    VERIFY( SUCCEEDED(m_pConsoleVerb->SetDefaultVerb(MMC_VERB_NONE)) );
    break;

  case FILEMGMT_SHARES:
  case FILEMGMT_SESSIONS:
  case FILEMGMT_RESOURCES:
  case FILEMGMT_SERVICES:
  case FILEMGMT_ROOT:  // Root node was selected
    // set the default verb to open/expand the folder
    VERIFY( SUCCEEDED(m_pConsoleVerb->SetDefaultVerb(MMC_VERB_OPEN)) );
    break;

  default: // shouldn't happen
    ASSERT(FALSE);
    break;

  } // switch

} // CFileMgmtComponent::OnNotifySelect()

STDMETHODIMP CFileMgmtComponent::QueryDataObject(MMC_COOKIE cookie, DATA_OBJECT_TYPES type, LPDATAOBJECT* ppDataObject)
{
    HRESULT hr = S_OK;

    MFC_TRY;

    //
    // MMC queries us for a multiselect data object (SI_MS_DO) by 
    // passing the special cookie in this QueryDataObject call.
    //
    if (IS_SPECIAL_COOKIE(cookie) && MMC_MULTI_SELECT_COOKIE == cookie) 
    {
        CComObject<CFileMgmtDataObject>* pDataObject = NULL;
        hr = CComObject<CFileMgmtDataObject>::CreateInstance(&pDataObject);

        if (SUCCEEDED(hr))
            hr = pDataObject->InitMultiSelectDataObjects(QueryComponentDataRef());

        if (SUCCEEDED(hr))
        {
            //
            // We create a multiselect data object (SI_MS_DO), which contains
            // an internal list of data objects of selected items.
            //
            RESULTDATAITEM rdi = {0};
            int nIndex = -1;
            do
            {
                ZeroMemory(&rdi, sizeof(RESULTDATAITEM));
                rdi.mask    = RDI_STATE;
                rdi.nCol    = 0;
                rdi.nIndex  = nIndex;            // nIndex == -1 to start at first item
                rdi.nState  = LVIS_SELECTED;    // only interested in selected items

                hr = m_pResultData->GetNextItem(&rdi);
                if (FAILED(hr))
                    break;

                if (rdi.nIndex != -1)
                {
                    //
                    // rdi is the RESULTDATAITEM of a selected item. its lParam contains the cookie.
                    // Add it to the internal data object list.
                    //
                    CCookie* pbasecookie = reinterpret_cast<CCookie*>(rdi.lParam);
                    CFileMgmtCookie* pUseThisCookie = QueryComponentDataRef().ActiveCookie((CFileMgmtCookie*)pbasecookie);
                    pDataObject->AddMultiSelectDataObjects(pUseThisCookie, type);
                }

                nIndex = rdi.nIndex;

            } while (-1 != nIndex);
        }

        //
        // return this SI_MS_DO to MMC
        //
        if (SUCCEEDED(hr)) 
            hr = pDataObject->QueryInterface(IID_IDataObject, (void **)ppDataObject);
        
        if (FAILED(hr))
            delete pDataObject;
    }
    else
    {
        // Delegate it to the IComponentData
        hr = QueryBaseComponentDataRef().QueryDataObject(cookie, type, ppDataObject);
    }

    MFC_CATCH;

    return hr;
}

STDMETHODIMP CFileMgmtComponent::GetResultViewType(MMC_COOKIE cookie,
                                           BSTR* ppViewType,
                                           long* pViewOptions)
{
    *ppViewType = NULL;

    //
    // we support multiselection in SharedFolders snapin
    //
    CCookie* pbasecookie = reinterpret_cast<CCookie*>(cookie);
    CFileMgmtCookie* pUseThisCookie = QueryComponentDataRef().ActiveCookie((CFileMgmtCookie*)pbasecookie);
    FileMgmtObjectType objecttype = pUseThisCookie->QueryObjectType();
    if ( FILEMGMT_SHARES == objecttype ||
        FILEMGMT_SESSIONS == objecttype ||
        FILEMGMT_RESOURCES == objecttype )
    {
        *pViewOptions = MMC_VIEW_OPTIONS_MULTISELECT;
    } else
    {
        *pViewOptions = MMC_VIEW_OPTIONS_NONE;
    }

    return S_FALSE;
}

HRESULT CFileMgmtComponent::Show( CCookie* pcookie, LPARAM arg, HSCOPEITEM /*hScopeItem*/ )
{
  TEST_NONNULL_PTR_PARAM(pcookie);

  #ifndef SNAPIN_PROTOTYPER
  if ( 0 == arg )
  {
    //
    // This is a Hide notification
    //
    if ( NULL == m_pResultData )
    {
      ASSERT( FALSE );
      return E_UNEXPECTED;
    }

    // We should not get a Hide notification if we are not currently showing
    // CODEWORK see 287399: MMC: two MMCN_SHOW(0) notifications
    // ASSERT( (CFileMgmtCookie*)pcookie == m_pViewedCookie );
    if ( (CFileMgmtScopeCookie*)pcookie == m_pViewedCookie )
    {
      //
      // Only delete the cookies if no other views are using them
      //
      pcookie->ReleaseResultChildren();

      m_pViewedCookie = NULL;

      UpdateDefaultVerbs();
    }
    
    return S_OK;
  } // if
  #else
    CPrototyperScopeCookie* pScopeCookie = (CPrototyperScopeCookie*) pcookie;
    if (pScopeCookie->m_ScopeType == HTML)
        return S_OK;
  #endif // SNAPIN_PROTOTYPER

  // We should not get a Show notification if we are already showing
  if ( NULL != m_pViewedCookie )
  {
    ASSERT(FALSE);
    return S_OK;
  }

  //
  // This is a Show notification
  // Build new cookies and insert them into the cookie and the view
  //

  ASSERT( IsAutonomousObjectType( ((CFileMgmtCookie*)pcookie)->QueryObjectType() ) );

  m_pViewedCookie = (CFileMgmtScopeCookie*)pcookie;

  LoadColumns( m_pViewedCookie );

  UpdateDefaultVerbs();

  return PopulateListbox( m_pViewedCookie );
} // CFileMgmtComponent::Show()


HRESULT CFileMgmtComponent::OnNotifyAddImages( LPDATAOBJECT /*lpDataObject*/,
                                               LPIMAGELIST lpImageList,
                                               HSCOPEITEM /*hSelectedItem*/ )
{
  return QueryComponentDataRef().LoadIcons(lpImageList,TRUE);
}


/////////////////////////////////////////////////////////////////////
// CODEWORK: Rather than repeating the FPNW calls every time, whether
// or not the FPNW server is running or even installed, I should
// remember whether the server is installed.  Andy Herron has
// suggested an API which can determine whether FPNW is installed,
// but it has two drawbacks:
// -- It can only determine whether FPNW is installed anywhere on the
//    domain, not on an individual server; and
// -- The API will fail if called by a non-administrator.
//
// JonN  11/1/96
//
HRESULT CFileMgmtComponent::PopulateListbox(CFileMgmtScopeCookie* pcookie)
{
  TEST_NONNULL_PTR_PARAM(pcookie);

  CWaitCursor cwait;

  HRESULT hr = S_OK;
  //
  // If this is the second view on the same data, just insert the same cookies
  // which are in the other views
  //
  if ( 1 < pcookie->AddRefResultChildren() )
  {
    hr = InsertResultCookies( *pcookie );
    if ( SUCCEEDED(hr) )
      hr = m_pResultData->Sort( m_iSortColumn , m_dwSortFlags, 0 );
    return hr;
  }

  INT iTransport;
  switch ( pcookie->QueryObjectType() )
  {
  case FILEMGMT_SHARES:
    for (iTransport = FILEMGMT_FIRST_TRANSPORT;
         iTransport < FILEMGMT_NUM_TRANSPORTS;
       iTransport++)
    {
      hr = GetFileServiceProvider(iTransport)->PopulateShares(m_pResultData,pcookie);
      if( FAILED(hr) )
        return hr;
    }
    break;

  case FILEMGMT_SESSIONS:
    for (iTransport = FILEMGMT_FIRST_TRANSPORT;
         iTransport < FILEMGMT_NUM_TRANSPORTS;
       iTransport++)
    {
      ASSERT( NULL != m_pResultData ); // otherwise we close all sessions
      hr = GetFileServiceProvider(iTransport)->EnumerateSessions (
          m_pResultData, pcookie, true);
      if( FAILED(hr) )
        return hr;
    }
    break;
    
  case FILEMGMT_RESOURCES:
    for (iTransport = FILEMGMT_FIRST_TRANSPORT;
         iTransport < FILEMGMT_NUM_TRANSPORTS;
       iTransport++)
    {
      ASSERT( NULL != m_pResultData ); // otherwise we close all sessions
      hr = GetFileServiceProvider(iTransport)->EnumerateResources(m_pResultData,pcookie);
      if( FAILED(hr) )
        return hr;
    }
    break;

  case FILEMGMT_SERVICES:
    //
    // JonN 12/03/98 Service_PopulateServices no longer inserts items into the list
    //
    hr = QueryComponentDataRef().Service_PopulateServices(m_pResultData, pcookie);
    if ( SUCCEEDED(hr) )
      hr = InsertResultCookies( *pcookie );
    if( FAILED(hr) )
      return hr;

  #ifdef SNAPIN_PROTOTYPER
  case FILEMGMT_PROTOTYPER:
    return Prototyper_HrPopulateResultPane(pcookie);
  #endif

  case FILEMGMT_ROOT:
    // We no longer need to explicitly insert these
    break;

  default:
    ASSERT( FALSE );
    // fall through
  }

  return m_pResultData->Sort( m_iSortColumn , m_dwSortFlags, 0 );
} // CFileMgmtComponent::PopulateListbox()

HRESULT CFileMgmtComponent::GetSnapinMultiSelectDataObject(
    LPDATAOBJECT i_pMMCMultiSelectDataObject,
    LPDATAOBJECT *o_ppSnapinMultiSelectDataObject
    )
{
    if (!i_pMMCMultiSelectDataObject || !o_ppSnapinMultiSelectDataObject)
        return E_INVALIDARG;

    *o_ppSnapinMultiSelectDataObject = NULL;

    //
    // i_pMMCMultiSelectDataObject is the composite data object (MMC_MS_DO) created by MMC.
    // We need to crack it to retrieve the multiselect data object (SI_MS_DO)
    // we provided to MMC in QueryDataObject().
    //
    STGMEDIUM   stgmedium = {TYMED_HGLOBAL, NULL, NULL};
    FORMATETC   formatetc = {CFileMgmtDataObject::m_CFMultiSelectSnapins, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
    HRESULT     hr = i_pMMCMultiSelectDataObject->GetData(&formatetc, &stgmedium);

    if (SUCCEEDED(hr))
    {
        if (!stgmedium.hGlobal)
            return E_FAIL;

        //
        // Locate the SI_MS_DO we have provided to MMC in QueryDataObject().
        //
        SMMCDataObjects *pMMCDO = (SMMCDataObjects*)::GlobalLock(stgmedium.hGlobal);

        GUID guidSnapin = GUID_NULL;
        VERIFY( SUCCEEDED(QueryComponentDataRef().GetClassID(&guidSnapin)) );

        for (int i = 0; i < pMMCDO->count; i++)
        {
            GUID guid = GUID_NULL;
            hr = ExtractData(pMMCDO->lpDataObject[i], CFileMgmtDataObject::m_CFSnapInCLSID, &guid, sizeof(GUID));
            if (SUCCEEDED(hr) && guid == guidSnapin)
            {
                //
                // pMMCDO->lpDataObject[i] is the SI_MS_DO we have provided to MMC in QueryDataObject().
                //
                *o_ppSnapinMultiSelectDataObject = pMMCDO->lpDataObject[i];
                (*o_ppSnapinMultiSelectDataObject)->AddRef();

                break;
            }
        }

        ::GlobalUnlock(stgmedium.hGlobal);
        ::GlobalFree(stgmedium.hGlobal);
    }

    return hr;
}

/////////////////////////////////////////////////////////////////////////////
// IExtendContextMenu Implementation

STDMETHODIMP CFileMgmtComponent::AddMenuItems(
                    IDataObject*          piDataObject,
                    IContextMenuCallback* piCallback,
                    long*                 pInsertionAllowed)
{
  MFC_TRY;

  TRACE_METHOD(CFileMgmtComponent,AddMenuItems);
  TEST_NONNULL_PTR_PARAM(piDataObject);
  TEST_NONNULL_PTR_PARAM(piCallback);
  TEST_NONNULL_PTR_PARAM(pInsertionAllowed);
  TRACE( "FileMgmt snapin: extending menu\n" );

    HRESULT hr = S_OK;
    FileMgmtObjectType objecttype = FILEMGMT_NUMTYPES;

    //
    // need to find out the object type in case of multiselection
    //
    BOOL bMultiSelectObject = IsMultiSelectObject(piDataObject);
    if (!bMultiSelectObject)
    {
        objecttype = FileMgmtObjectTypeFromIDataObject(piDataObject);
    } else
    {
        //
        // piDataObject is the composite data object (MMC_MS_DO) created by MMC.
        // We need to crack it to retrieve the multiselect data object (SI_MS_DO)
        // we provided to MMC in QueryDataObject().
        //
        IDataObject *piSIMSDO = NULL;
        hr = GetSnapinMultiSelectDataObject(piDataObject, &piSIMSDO);
        if (SUCCEEDED(hr))
        {
            //
            // Note: we assume all multiselected items are of the same type.
            // 
            // Now retrieve data type of the currently selected items
            //
            STGMEDIUM stgmedium = {TYMED_HGLOBAL, NULL, NULL};
            FORMATETC formatetc = {CFileMgmtDataObject::m_CFObjectTypesInMultiSelect,
                                    NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
            hr = piSIMSDO->GetData(&formatetc, &stgmedium);
            if (SUCCEEDED(hr) && stgmedium.hGlobal)
            {
                BYTE* pb = (BYTE*)::GlobalLock(stgmedium.hGlobal);

                GUID* pguid = (GUID*)(pb + sizeof(DWORD)); // skip the 1st DWORD - count
                objecttype = (FileMgmtObjectType)CheckObjectTypeGUID(pguid);

                ::GlobalUnlock(stgmedium.hGlobal);
                ::GlobalFree(stgmedium.hGlobal);
            }

            piSIMSDO->Release();
        }
    }

  switch (objecttype)
  {
  case FILEMGMT_SHARE:
    //
    // don't add acl-related menu items whenever SimpleSharingUI appears in NT Explorer
    //
    if (QueryComponentDataRef().GetIsSimpleUI())
        break;

    if ( CCM_INSERTIONALLOWED_TOP & (*pInsertionAllowed) )
    {
      hr = LoadAndAddMenuItem( piCallback, IDS_DELETE_SHARE_TOP, IDS_DELETE_SHARE_TOP,
        CCM_INSERTIONPOINTID_PRIMARY_TOP, 0, AfxGetInstanceHandle() );
      ASSERT( SUCCEEDED(hr) );
    }
    if ( CCM_INSERTIONALLOWED_TASK & (*pInsertionAllowed) )
    {
      hr = LoadAndAddMenuItem( piCallback, IDS_DELETE_SHARE_TASK, IDS_DELETE_SHARE_TASK,
        CCM_INSERTIONPOINTID_PRIMARY_TASK, 0, AfxGetInstanceHandle() );
      ASSERT( SUCCEEDED(hr) );
    }
    if ( CCM_INSERTIONALLOWED_NEW & (*pInsertionAllowed) )
    {
      hr = LoadAndAddMenuItem( piCallback, IDS_NEW_SHARE_NEW, IDS_NEW_SHARE_NEW,
        CCM_INSERTIONPOINTID_PRIMARY_NEW, 0, AfxGetInstanceHandle() );
      ASSERT( SUCCEEDED(hr) );
    }
    break;

  case FILEMGMT_SESSION:
    if ( CCM_INSERTIONALLOWED_TOP & (*pInsertionAllowed) )
    {
      hr = LoadAndAddMenuItem( piCallback, IDS_CLOSE_SESSION_TOP, IDS_CLOSE_SESSION_TOP,
        CCM_INSERTIONPOINTID_PRIMARY_TOP, 0, AfxGetInstanceHandle() );
      ASSERT( SUCCEEDED(hr) );
    }
    if ( CCM_INSERTIONALLOWED_TASK & (*pInsertionAllowed) )
    {
      hr = LoadAndAddMenuItem( piCallback, IDS_CLOSE_SESSION_TASK, IDS_CLOSE_SESSION_TASK,
        CCM_INSERTIONPOINTID_PRIMARY_TASK, 0, AfxGetInstanceHandle() );
      ASSERT( SUCCEEDED(hr) );
    }
    break;

  case FILEMGMT_RESOURCE:
    if ( CCM_INSERTIONALLOWED_TOP & (*pInsertionAllowed) )
    {
      hr = LoadAndAddMenuItem( piCallback, IDS_CLOSE_RESOURCE_TOP, IDS_CLOSE_RESOURCE_TOP,
        CCM_INSERTIONPOINTID_PRIMARY_TOP, 0, AfxGetInstanceHandle() );
      ASSERT( SUCCEEDED(hr) );
    }
    if ( CCM_INSERTIONALLOWED_TASK & (*pInsertionAllowed) )
    {
      hr = LoadAndAddMenuItem( piCallback, IDS_CLOSE_RESOURCE_TASK, IDS_CLOSE_RESOURCE_TASK,
        CCM_INSERTIONPOINTID_PRIMARY_TASK, 0, AfxGetInstanceHandle() );
      ASSERT( SUCCEEDED(hr) );
    }
    break;

  case FILEMGMT_SERVICE:
    QueryComponentDataRef().Service_FAddMenuItems(piCallback, piDataObject);
    break;

  #ifdef SNAPIN_PROTOTYPER
    case FILEMGMT_PROTOTYPER_LEAF:
        Prototyper_AddMenuItems(piCallback, piDataObject);
        break;
    #endif // SNAPIN_PROTOTYPER

  default:
      {
        DATA_OBJECT_TYPES dataobjecttype = CCT_SCOPE;
        hr = ExtractData( piDataObject,
                            CFileMgmtDataObject::m_CFDataObjectType,
                            &dataobjecttype,
                            sizeof(dataobjecttype) );
        ASSERT( SUCCEEDED(hr) );

        // perhaps this is a scope node in the result pane
        hr = QueryComponentDataRef().DoAddMenuItems( piCallback,
                                                    objecttype,
                                                    dataobjecttype,
                                                    pInsertionAllowed,
                                                    piDataObject );
      }
    break;
  } // switch

    return hr;

    MFC_CATCH;

} // CFileMgmtComponent::AddMenuItems()

STDMETHODIMP CFileMgmtComponent::Command(
                    LONG            lCommandID,
                    IDataObject*    piDataObject )
{
  MFC_TRY;

  TRACE_METHOD(CFileMgmtComponent,Command);
  TEST_NONNULL_PTR_PARAM(piDataObject);
  TRACE( "CFileMgmtComponent::Command: command %ld selected\n", lCommandID );

  #ifdef SNAPIN_PROTOTYPER
  Prototyper_ContextMenuCommand(lCommandID, piDataObject);
  return S_OK;
  #endif

  BOOL fFSMRefresh = FALSE;
  BOOL fSVCRefresh = FALSE;
  switch (lCommandID)
  {
  case IDS_DELETE_SHARE_TASK:
  case IDS_DELETE_SHARE_TOP:
    fFSMRefresh = DeleteShare( piDataObject );
    break;
  case IDS_CLOSE_SESSION_TASK:
  case IDS_CLOSE_SESSION_TOP:
    fFSMRefresh = CloseSession( piDataObject );
    break;
  case IDS_CLOSE_RESOURCE_TASK:
  case IDS_CLOSE_RESOURCE_TOP:
    fFSMRefresh = CloseResource( piDataObject );
    break;

  case cmServiceStart:
  case cmServiceStop:
  case cmServicePause:
  case cmServiceResume:
  case cmServiceRestart:
  case cmServiceStartTask:
  case cmServiceStopTask:
  case cmServicePauseTask:
  case cmServiceResumeTask:
  case cmServiceRestartTask:
    fSVCRefresh = QueryComponentDataRef().Service_FDispatchMenuCommand(lCommandID, piDataObject);
    break;

  default:
    return QueryComponentDataRef().Command(lCommandID, piDataObject);
  } // switch

  if (fFSMRefresh)
  {
    //
    // In case of multiselect, piDataObject may point to a composite data object (MMC_MS_DO).
    // RefreshAllViewsOnSelectedObject will crack down MMC_MS_DO to retrieve SI_MS_DO, then call
    // RefreshAllViews on one of the selected objects in the internal list.
    //
    (void) RefreshAllViewsOnSelectedObject(piDataObject);
  }

  if (fSVCRefresh)
  {
    (void) RefreshAllViews( piDataObject );
  }

    return S_OK;
    MFC_CATCH;
} // CFileMgmtComponent::Command()

BOOL CFileMgmtComponent::DeleteShare(LPDATAOBJECT piDataObject)
{
    ASSERT( piDataObject != NULL );

    BOOL bMultiSelectObject = IsMultiSelectObject(piDataObject);
    if (!bMultiSelectObject)
        return DeleteThisOneShare(piDataObject, FALSE);

    BOOL bRefresh = FALSE;
    if (IDYES == DoErrMsgBox(GetActiveWindow(), MB_YESNO, 0, IDS_s_CONFIRM_DELETEMULTISHARES))
    {
        AFX_MANAGE_STATE(AfxGetStaticModuleState( ));
        CWaitCursor wait;

        //
        // piDataObject is the composite data object (MMC_MS_DO) created by MMC.
        // We need to crack it to retrieve the multiselect data object (SI_MS_DO)
        // we provided to MMC in QueryDataObject().
        //
        IDataObject *piSIMSDO = NULL;
        HRESULT hr = GetSnapinMultiSelectDataObject(piDataObject, &piSIMSDO);
        if (SUCCEEDED(hr))
        {
            CFileMgmtDataObject *pDataObj = NULL;
            hr = ExtractData(piSIMSDO, CFileMgmtDataObject::m_CFInternal, &pDataObj, sizeof(pDataObj));
            if (SUCCEEDED(hr))
            {
                //
                // get the internal list of data objects of selected items, operate on each one of them.
                //
                CDataObjectList* pMultiSelectObjList = pDataObj->GetMultiSelectObjList();
                for (CDataObjectList::iterator i = pMultiSelectObjList->begin(); i != pMultiSelectObjList->end(); i++)
                {
                     BOOL bDeleted = DeleteThisOneShare(*i, TRUE);
                     if (bDeleted)
                         bRefresh = TRUE;
                }
            }

            piSIMSDO->Release();
        }
    }

    return bRefresh;
}

BOOL CFileMgmtComponent::DeleteThisOneShare(LPDATAOBJECT piDataObject, BOOL bQuietMode)
{
  AFX_MANAGE_STATE(AfxGetStaticModuleState( ));
  ASSERT( piDataObject != NULL );
#ifdef DEBUG
  {
    FileMgmtObjectType objecttype = FileMgmtObjectTypeFromIDataObject(piDataObject);
    ASSERT(FILEMGMT_SHARE == objecttype);
  }
#endif

  CString strServerName;
  HRESULT hr = ExtractString( piDataObject, CFileMgmtDataObject::m_CFMachineName, &strServerName, MAX_PATH );
  RETURN_FALSE_IF_FAIL;

  CString strShareName;
  hr = ExtractString( piDataObject, CFileMgmtDataObject::m_CFShareName, &strShareName, MAX_PATH );
  RETURN_FALSE_IF_FAIL;

  FILEMGMT_TRANSPORT transport;
  hr = ExtractData( piDataObject,
                  CFileMgmtDataObject::m_CFTransport,
            &transport,
            sizeof(DWORD) );
  RETURN_FALSE_IF_FAIL;

  BOOL fNetLogonShare = (!lstrcmpi(strShareName, _T("SYSVOL")) || !lstrcmpi(strShareName, _T("NETLOGON")));
  BOOL fIPC = FALSE;
  BOOL fAdminShare = FALSE;

  if (!fNetLogonShare && transport == FILEMGMT_SMB)
  {
    DWORD dwShareType = 0;
    GetFileServiceProvider(transport)->ReadShareType(strServerName, strShareName, &dwShareType);
    fAdminShare = (dwShareType & STYPE_SPECIAL);
    fIPC = (STYPE_IPC == (dwShareType & STYPE_IPC));
  }

  if (fIPC)
  {
      DoErrMsgBox(
                  GetActiveWindow(),
                  MB_OK | MB_ICONEXCLAMATION,
                  0,
                  IDS_s_DELETE_IPCSHARE
                  );
      return FALSE;
  }

  if ((fNetLogonShare || fAdminShare) &&
      IDYES != DoErrMsgBox(
                  GetActiveWindow(),
                  MB_YESNO | MB_ICONEXCLAMATION | MB_DEFBUTTON2,
                  0,
                  (fAdminShare ? IDS_s_CONFIRM_DELETE_ADMINSHARE : IDS_s_CONFIRM_DELETE_NETLOGONSHARE),
                  strShareName) )
  {
      return FALSE;
  }

  if (!bQuietMode && 
      IDYES != DoErrMsgBox(
                  GetActiveWindow(),
                  MB_YESNO,
                  0,
                  IDS_s_CONFIRM_DELETESHARE,
                  strShareName) )
  {
    return FALSE;
  }

  DWORD retval = 0L;
  switch (transport)
  {
  case FILEMGMT_SMB:
  case FILEMGMT_FPNW:
  case FILEMGMT_SFM:
    if (bQuietMode)
    {
        retval = GetFileServiceProvider(transport)->DeleteShare(strServerName, strShareName);
    } else
    {
        CWaitCursor wait;
        retval = GetFileServiceProvider(transport)->DeleteShare(strServerName, strShareName);
    }
    break;
  default:
    ASSERT(FALSE);
    break;
  }
  if (0L != retval)
  {
    (void) DoErrMsgBox(GetActiveWindow(), MB_OK | MB_ICONSTOP, retval, IDS_POPUP_DELETE_SHARE, strShareName);
    return FALSE;
  }
  return TRUE;
}

BOOL CFileMgmtComponent::CloseSession(LPDATAOBJECT piDataObject)
{
    ASSERT( piDataObject != NULL );

    BOOL bMultiSelectObject = IsMultiSelectObject(piDataObject);
    if (!bMultiSelectObject)
        return CloseThisOneSession(piDataObject, FALSE);

    BOOL bRefresh = FALSE;
    if (IDYES == DoErrMsgBox(GetActiveWindow(), MB_YESNO, 0, IDS_CONFIRM_CLOSEMULTISESSIONS))
    {
        AFX_MANAGE_STATE(AfxGetStaticModuleState( ));
        CWaitCursor wait;

        //
        // piDataObject is the composite data object (MMC_MS_DO) created by MMC.
        // We need to crack it to retrieve the multiselect data object (SI_MS_DO)
        // we provided to MMC in QueryDataObject().
        //
        IDataObject *piSIMSDO = NULL;
        HRESULT hr = GetSnapinMultiSelectDataObject(piDataObject, &piSIMSDO);
        if (SUCCEEDED(hr))
        {
            CFileMgmtDataObject *pDataObj = NULL;
            hr = ExtractData(piSIMSDO, CFileMgmtDataObject::m_CFInternal, &pDataObj, sizeof(pDataObj));
            if (SUCCEEDED(hr))
            {
                //
                // get the internal list of data objects of selected items, operate on each one of them.
                //
                CDataObjectList* pMultiSelectObjList = pDataObj->GetMultiSelectObjList();
                for (CDataObjectList::iterator i = pMultiSelectObjList->begin(); i != pMultiSelectObjList->end(); i++)
                {
                     BOOL bDeleted = CloseThisOneSession(*i, TRUE);
                     if (bDeleted)
                         bRefresh = TRUE;
                }
            }

            piSIMSDO->Release();
        }
    }

    return bRefresh;
}

BOOL CFileMgmtComponent::CloseThisOneSession(LPDATAOBJECT piDataObject, BOOL bQuietMode)
{
  AFX_MANAGE_STATE(AfxGetStaticModuleState( ));

  ASSERT( piDataObject != NULL );

  CCookie* pbasecookie = NULL;
  FileMgmtObjectType objecttype = FILEMGMT_ROOT;
  HRESULT hr = ExtractBaseCookie( piDataObject, &pbasecookie, &objecttype );
  ASSERT( SUCCEEDED(hr) && NULL != pbasecookie && FILEMGMT_SESSION == objecttype );
  CFileMgmtResultCookie* pcookie = (CFileMgmtResultCookie*)pbasecookie;

  if ( !bQuietMode && IDYES != DoErrMsgBox(GetActiveWindow(), MB_YESNO, 0, IDS_CONFIRM_CLOSESESSION) )
  {
    return FALSE;
  }

  FILEMGMT_TRANSPORT transport = FILEMGMT_SMB;
  VERIFY( SUCCEEDED( pcookie->GetTransport( &transport ) ) );
  DWORD retval = 0;

  if (bQuietMode)
  {
    retval = GetFileServiceProvider(transport)->CloseSession( pcookie );
  } else
  {
    CWaitCursor wait;
    retval = GetFileServiceProvider(transport)->CloseSession( pcookie );
  }

  if (0L != retval)
  {
    (void) DoErrMsgBox(GetActiveWindow(), MB_OK | MB_ICONSTOP, retval, IDS_POPUP_CLOSE_SESSION);
    return FALSE;
  }
  return TRUE;
}

BOOL CFileMgmtComponent::CloseResource(LPDATAOBJECT piDataObject)
{
    ASSERT( piDataObject != NULL );

    BOOL bMultiSelectObject = IsMultiSelectObject(piDataObject);
    if (!bMultiSelectObject)
        return CloseThisOneResource(piDataObject, FALSE);

    BOOL bRefresh = FALSE;
    if (IDYES == DoErrMsgBox(GetActiveWindow(), MB_YESNO, 0, IDS_CONFIRM_CLOSEMULTIRESOURCES))
    {
        AFX_MANAGE_STATE(AfxGetStaticModuleState( ));
        CWaitCursor wait;

        //
        // piDataObject is the composite data object (MMC_MS_DO) created by MMC.
        // We need to crack it to retrieve the multiselect data object (SI_MS_DO)
        // we provided to MMC in QueryDataObject().
        //
        IDataObject *piSIMSDO = NULL;
        HRESULT hr = GetSnapinMultiSelectDataObject(piDataObject, &piSIMSDO);
        if (SUCCEEDED(hr))
        {
            CFileMgmtDataObject *pDataObj = NULL;
            hr = ExtractData(piSIMSDO, CFileMgmtDataObject::m_CFInternal, &pDataObj, sizeof(pDataObj));
            if (SUCCEEDED(hr))
            {
                //
                // get the internal list of data objects of selected items, operate on each one of them.
                //
                CDataObjectList* pMultiSelectObjList = pDataObj->GetMultiSelectObjList();
                for (CDataObjectList::iterator i = pMultiSelectObjList->begin(); i != pMultiSelectObjList->end(); i++)
                {
                     BOOL bDeleted = CloseThisOneResource(*i, TRUE);
                     if (bDeleted)
                         bRefresh = TRUE;
                }
            }

            piSIMSDO->Release();
        }
    }

    return bRefresh;
}

BOOL CFileMgmtComponent::CloseThisOneResource(LPDATAOBJECT piDataObject, BOOL bQuietMode)
{
  AFX_MANAGE_STATE(AfxGetStaticModuleState( ));

  ASSERT( piDataObject != NULL );

  CCookie* pbasecookie = NULL;
  FileMgmtObjectType objecttype;
  HRESULT hr = ExtractBaseCookie( piDataObject, &pbasecookie, &objecttype );
  ASSERT( SUCCEEDED(hr) && NULL != pbasecookie && FILEMGMT_RESOURCE == objecttype );
  CFileMgmtResultCookie* pcookie = (CFileMgmtResultCookie*)pbasecookie;

  if ( !bQuietMode && IDYES != DoErrMsgBox(GetActiveWindow(), MB_YESNO, 0, IDS_CONFIRM_CLOSERESOURCE) )
  {
    return FALSE;
  }

  FILEMGMT_TRANSPORT transport = FILEMGMT_SMB;
  VERIFY( SUCCEEDED( pcookie->GetTransport( &transport ) ) );
  DWORD retval = 0;

  if (bQuietMode)
  {
    retval = GetFileServiceProvider(transport)->CloseResource( pcookie );
  } else
  {
    CWaitCursor wait;
    retval = GetFileServiceProvider(transport)->CloseResource( pcookie );
  }

  if (0L != retval)
  {
    (void) DoErrMsgBox(GetActiveWindow(), MB_OK | MB_ICONSTOP, retval, IDS_POPUP_CLOSE_RESOURCE);
    return FALSE;
  }
  return TRUE;

}

///////////////////////////////////////////////////////////////////////////////
/// IExtendPropertySheet

STDMETHODIMP CFileMgmtComponent::QueryPagesFor(LPDATAOBJECT pDataObject)
{
  MFC_TRY;

  if (NULL == pDataObject)
  {
    ASSERT(FALSE);
    return E_POINTER;
  }

  HRESULT hr = S_OK;
  DATA_OBJECT_TYPES dataobjecttype = CCT_SCOPE;
  // extract data from data object
  FileMgmtObjectType objecttype = FileMgmtObjectTypeFromIDataObject(pDataObject);
  hr = ExtractData( pDataObject, CFileMgmtDataObject::m_CFDataObjectType, &dataobjecttype, sizeof(dataobjecttype) );
  ASSERT( SUCCEEDED(hr) );
  ASSERT( CCT_SCOPE == dataobjecttype ||
        CCT_RESULT == dataobjecttype ||
        CCT_SNAPIN_MANAGER == dataobjecttype );

    // determine if it needs property pages
  switch (objecttype)
  {
  case FILEMGMT_SESSION:
  case FILEMGMT_RESOURCE:
    ASSERT(CCT_SNAPIN_MANAGER != dataobjecttype);
    return S_FALSE;
  case FILEMGMT_SHARE: // now has a property page
      {
          CString strServerName;
          CString strShareName;
          FILEMGMT_TRANSPORT transport;
          hr = ExtractString(pDataObject, CFileMgmtDataObject::m_CFMachineName, &strServerName, MAX_PATH);

          if (SUCCEEDED(hr))
              hr = ExtractString(pDataObject, CFileMgmtDataObject::m_CFShareName, &strShareName, MAX_PATH);

          if (SUCCEEDED(hr))
              hr = ExtractData(pDataObject, CFileMgmtDataObject::m_CFTransport, &transport, sizeof(DWORD));

          if (SUCCEEDED(hr))
          {
              CString strDescription;
              CString strPath;
              DWORD dwRet = GetFileServiceProvider(transport)->ReadShareProperties(
                                                                      strServerName,
                                                                      strShareName,
                                                                      NULL, // ppvPropertyBlock
                                                                      strDescription,
                                                                      strPath,
                                                                      NULL, // pfEditDescription
                                                                      NULL, // pfEditPath
                                                                      NULL // pdwShareType
                                                                      );
              if (NERR_Success == dwRet)
              {
                  return S_OK; // yes, we have a property page to display
              } else
              {
                  DoErrMsgBox(GetActiveWindow(), MB_OK | MB_ICONSTOP, dwRet, IDS_POPUP_QUERY_SHARE, strShareName);
                  if ((FILEMGMT_SMB == transport || FILEMGMT_FPNW == transport) && (NERR_NetNameNotFound == dwRet) ||
                      (FILEMGMT_SFM == transport) && (AFPERR_VolumeNonExist == dwRet))
                  {
                      RefreshAllViews(pDataObject);
                  }
              }
          }

          return S_FALSE;
      }
  case FILEMGMT_SERVICE:
    ASSERT(CCT_SNAPIN_MANAGER != dataobjecttype);
    return S_OK;
    #ifdef SNAPIN_PROTOTYPER
    case FILEMGMT_PROTOTYPER_LEAF:
        return S_OK;
    #endif
  default:
    break;
  }
  ASSERT(FALSE);
  return S_FALSE;

  MFC_CATCH;
}

STDMETHODIMP CFileMgmtComponent::CreatePropertyPages(
  LPPROPERTYSHEETCALLBACK pCallBack,
  LONG_PTR handle,    // This handle must be saved in the property page object to notify the parent when modified
  LPDATAOBJECT pDataObject)
{
  MFC_TRY;
  if (NULL == pCallBack || NULL == pDataObject)
  {
    ASSERT(FALSE);
    return E_POINTER;
  }
  HRESULT hr;
  // extract data from data object
  FileMgmtObjectType objecttype = FileMgmtObjectTypeFromIDataObject(pDataObject);
  DATA_OBJECT_TYPES dataobjecttype = CCT_SCOPE;
  hr = ExtractData( pDataObject, CFileMgmtDataObject::m_CFDataObjectType, &dataobjecttype, sizeof(dataobjecttype) );
  ASSERT( SUCCEEDED(hr) );
  ASSERT( CCT_SCOPE == dataobjecttype ||
          CCT_RESULT == dataobjecttype ||
          CCT_SNAPIN_MANAGER == dataobjecttype );

    // determine if it needs property pages
  switch (objecttype)
  {
  case FILEMGMT_SHARE:
  {
    CWaitCursor cwait;

    if (CCT_SNAPIN_MANAGER == dataobjecttype)
    {
      ASSERT(FALSE);
      return E_UNEXPECTED;
    }

    FILEMGMT_TRANSPORT transport;
    hr = ExtractData( pDataObject,
                    CFileMgmtDataObject::m_CFTransport,
                    &transport,
                    sizeof(DWORD) );
    if ( FAILED(hr) )
    {
      ASSERT( FALSE );
      return E_UNEXPECTED;
    }

    // CODEWORK probably not necessary to split off transport at this point
    GetFileServiceProvider(transport)->DisplayShareProperties(pCallBack, pDataObject, handle);
    return S_OK;
  }
  case FILEMGMT_SESSION:
  case FILEMGMT_RESOURCE:
    ASSERT(FALSE);
    return E_UNEXPECTED;
  case FILEMGMT_SERVICE:
    if (CCT_RESULT != dataobjecttype)
    {
      ASSERT(FALSE);
      return E_UNEXPECTED;
    }

    if (!QueryComponentDataRef().Service_FInsertPropertyPages(OUT pCallBack, IN pDataObject, handle))
    {
      // Unable to open the service and query service info
      return S_FALSE;
    }
    return S_OK;
  default:
    break;
  }
  ASSERT(FALSE);
  return S_FALSE;

  MFC_CATCH;
}

STDMETHODIMP CFileMgmtComponent::Compare(
  LPARAM /*lUserParam*/, MMC_COOKIE cookieA, MMC_COOKIE cookieB, int* pnResult)
{
  ASSERT(NULL != pnResult);
  // WARNING cookie cast
  CCookie* pBaseCookieA = reinterpret_cast<CCookie*>(cookieA);
  CCookie* pBaseCookieB = reinterpret_cast<CCookie*>(cookieB);
  ASSERT( NULL != pBaseCookieA && NULL != pBaseCookieB );
  CFileMgmtCookie* pCookieA = QueryComponentDataRef().ActiveCookie(
    (CFileMgmtCookie*)pBaseCookieA);
  CFileMgmtCookie* pCookieB = QueryComponentDataRef().ActiveCookie(
    (CFileMgmtCookie*)pBaseCookieB);
  ASSERT( NULL != pCookieA && NULL != pCookieB );
  FileMgmtObjectType objecttypeA = pCookieA->QueryObjectType();
  FileMgmtObjectType objecttypeB = pCookieB->QueryObjectType();
  ASSERT( IsValidObjectType(objecttypeA) && IsValidObjectType(objecttypeB) );
  if (objecttypeA != objecttypeB)
  {
    // assign an arbitrary ordering to cookies with different nodetypes
    *pnResult = ((int)objecttypeA) - ((int)objecttypeB);
    return S_OK;
  }

  return pCookieA->CompareSimilarCookies( pBaseCookieB, pnResult);
}


STDMETHODIMP CFileMgmtComponent::GetProperty( 
            /* [in] */ LPDATAOBJECT pDataObject,
            /* [in] */ BSTR szPropertyName,
            /* [out] */ BSTR* pbstrProperty)
{
  if (   IsBadReadPtr(pDataObject,sizeof(*pDataObject))
      || IsBadStringPtr(szPropertyName,0x7FFFFFFF)
      || IsBadWritePtr(pbstrProperty,sizeof(*pbstrProperty))
     )
  {
      ASSERT(FALSE);
      return E_POINTER;
  }

  CCookie* pbasecookie = NULL;
  HRESULT hr = ExtractBaseCookie( pDataObject, &pbasecookie );
  RETURN_HR_IF_FAIL;
  ASSERT(NULL != pbasecookie);
  CFileMgmtCookie* pcookie = (CFileMgmtCookie*)pbasecookie;

  CString strProperty;
  if (!_wcsicmp(L"CCF_HTML_DETAILS",szPropertyName))
  {
    if (FILEMGMT_SERVICE != pcookie->QueryObjectType())
      return S_FALSE;

    if (NULL == QueryComponentDataRef().m_hScManager)
    {
      ASSERT(FALSE);
      return S_FALSE;
    }

    CString strServiceName;
    if (!QueryComponentDataRef().Service_FGetServiceInfoFromIDataObject(
      pDataObject,
      NULL,
      OUT &strServiceName,
      NULL))
    {
      ASSERT(FALSE);
      return S_FALSE;
    }

    BOOL rgfMenuFlags[iServiceActionMax];
    ::ZeroMemory(rgfMenuFlags,sizeof(rgfMenuFlags));

    AFX_MANAGE_STATE(AfxGetStaticModuleState()); // required for CWaitCursor
    CWaitCursor wait;
    if (!Service_FGetServiceButtonStatus( // this will report errors itself
          QueryComponentDataRef().m_hScManager,
          strServiceName,
          OUT rgfMenuFlags,
          NULL,  // pdwCurrentState
          TRUE)) // fSilentError
    {
      return S_FALSE;
    }

    for (INT i = 0; i < iServiceActionMax; i++)
    {
      if (rgfMenuFlags[i])
      {
        CString strTemp;
        VERIFY(strTemp.LoadString(IDS_HTML_DETAILS_START+i));
        strProperty += strTemp;
      }
    }
  }
  else if (!_wcsicmp(L"CCF_DESCRIPTION",szPropertyName))
  {
    hr = pcookie->GetExplorerViewDescription( strProperty );
  }
  else
  {
    return S_FALSE; // unknown strPropertyName
  }

  *pbstrProperty = ::SysAllocString(strProperty);

  return S_OK;
}

/////////////////////////////////////////////////////////////////////
// Virtual function called by CComponent::IComponent::Notify(MMCN_COLUMN_CLICK)
HRESULT CFileMgmtComponent::OnNotifyColumnClick( LPDATAOBJECT /*lpDataObject*/, LPARAM iColumn, LPARAM uFlags )
{
  m_iSortColumn = (int)iColumn;
  m_dwSortFlags = (DWORD) uFlags;
  return m_pResultData->Sort ((int)iColumn, (DWORD)uFlags, 0);
}

HRESULT CFileMgmtComponent::OnNotifySnapinHelp (LPDATAOBJECT /*pDataObject*/)
{
  return ShowHelpTopic( IsServiceSnapin()
                          ? L"sys_srv_overview.htm"
                          : L"file_srv_overview.htm" );
}