text: //+-------------------------------------------------------------------------
  //
  //  Microsoft Windows
  //  Copyright (C) Microsoft Corporation, 1992 - 1999
  //
  //  File:      amc.cpp
  //
  //  Contents:  The one and only app
  //
  //  History:   01-Jan-96 TRomano    Created
  //             16-Jul-96 WayneSc    Add code to switch views
  //
  //--------------------------------------------------------------------------
  
  
  #include "stdafx.h"
  #include "AMC.h"
  
  
  #include "MainFrm.h"
  #include "ChildFrm.h"
  #include "AMCDoc.h"
  #include "AMCView.h"
  #include "amcdocmg.h"
  #include "sysmenu.h"
  #include <shlobj.h>
  #include "strings.h"
  #include "macros.h"
  #include "scripthost.h"
  #include "HtmlHelp.h"
  #include "scriptevents.h"
  #include "mmcutil.h"
  #include "guidhelp.h"       // for CLSID relational operators
  #include "archpicker.h"
  #include "classreg.h"
  
  #define DECLSPEC_UUID(x)    __declspec(uuid(x))
  #include "websnk.h"
  #include "websnk_i.c"
  
  // We aren't picking this up from winuser.h for some reason.
  #define ISOLATIONAWARE_NOSTATICIMPORT_MANIFEST_RESOURCE_ID MAKEINTRESOURCE(3)
  
  /*
   * define our own Win64 symbol to make it easy to include 64-bit only
   * code in the 32-bit build, so we can exercise some code on 32-bit Windows
   * where the debuggers are better
   */
  #ifdef _WIN64
  #define MMC_WIN64
  #endif
  
  #ifndef MMC_WIN64
  #include <wow64t.h>         // for Wow64DisableFilesystemRedirector
  #endif
  
  /*
   * multimon.h is included by stdafx.h, without defining COMPILE_MULTIMON_STUBS
   * first.  We need to include it again here after defining COMPILE_MULTIMON_STUBS
   * so we'll get the stub functions.
   */
  #if (_WIN32_WINNT < 0x0500)
  #define COMPILE_MULTIMON_STUBS
  #include <multimon.h>
  #endif
  
  #ifdef DBG
      CTraceTag  tagEnableScriptEngines(_T("MMCScriptEngines"), _T("Enable"));
      CTraceTag  tag32BitTransfer(_T("64/32-bit interop"), _T("64/32-bit interop"));
  #endif
  
  // Note: These strings do not need to be localizable.
  const TCHAR CAMCApp::m_szSettingsSection[]    = _T("Settings");
  const TCHAR CAMCApp::m_szUserDirectoryEntry[] = _T("Save Location");
  
  bool CanCloseDoc(void);
  SC ScExpandEnvironmentStrings (CString& str);
  
  //############################################################################
  //############################################################################
  //
  //  ATL Support
  //
  //############################################################################
  //############################################################################
  #include <atlimpl.cpp>
  #include <atlwin.cpp>
  
  // The one and only instance of CAtlGlobalModule
  CAtlGlobalModule _Module;
  
  //############################################################################
  //############################################################################
  //
  //  Trace Tags
  //
  //############################################################################
  //############################################################################
  #ifdef DBG
  // enable this tag if you suspect memory corruption
  // and you don't mind things slowing way down
  
  BEGIN_TRACETAG(CDebugCRTCheck)
      void OnEnable()
      {
          _CrtSetDbgFlag (_CrtSetDbgFlag(_CRTDBG_REPORT_FLAG)
                          | _CRTDBG_CHECK_ALWAYS_DF
                          | _CRTDBG_DELAY_FREE_MEM_DF);
      }
      void OnDisable()
      {
          _CrtSetDbgFlag (_CrtSetDbgFlag(_CRTDBG_REPORT_FLAG)
                          & ~(_CRTDBG_CHECK_ALWAYS_DF
                              | _CRTDBG_DELAY_FREE_MEM_DF) );
      }
  END_TRACETAG(CDebugCRTCheck, TEXT("Debug CRTs"), TEXT("Memory Check - SLOW!"))
  
  CTraceTag tagAMCAppInit(TEXT("CAMCView"), TEXT("InitInstance"));
  CTraceTag tagATLLock(TEXT("ATL"), TEXT("Lock/Unlock"));  // used by atlconui.h
  CTraceTag tagGDIBatching(TEXT("CAMCView"), TEXT("Disable Graphics/GDI Batching"));
  CTraceTag tagForceMirror(TEXT("Mirroring"), TEXT("Force MMC windows to be mirrored on non-mirrored systems"));
  #endif
  
  //############################################################################
  //############################################################################
  //
  //  Implementation of class CMMCApplication - the root level
  //  automation class
  //
  //############################################################################
  //############################################################################
  class CMMCApplication :
      public CMMCIDispatchImpl<_Application, &CLSID_Application>,
      public CComCoClass<CMMCApplication, &CLSID_Application>,
      // support for connection points (script events)
      public IConnectionPointContainerImpl<CMMCApplication>,
      public IConnectionPointImpl<CMMCApplication, &DIID_AppEvents, CComDynamicUnkArray>,
      public IProvideClassInfo2Impl<&CLSID_Application, &DIID_AppEvents, &LIBID_MMC20>
      {
  public:
      BEGIN_MMC_COM_MAP(CMMCApplication)
          COM_INTERFACE_ENTRY(IProvideClassInfo)
          COM_INTERFACE_ENTRY(IProvideClassInfo2)
          COM_INTERFACE_ENTRY(IConnectionPointContainer)
      END_MMC_COM_MAP()
  
      DECLARE_NOT_AGGREGATABLE(CMMCApplication)
  
  	static HRESULT WINAPI UpdateRegistry(BOOL bRegister)
  	{
  		CObjectRegParams op (
  			CLSID_Application,
  			_T("mmc.exe"),
  			_T("MMC Application Class"),
  			_T("MMC20.Application.1"),
  			_T("MMC20.Application"),
  			_T("LocalServer32") );
  
  		return (MMCUpdateRegistry (bRegister, &op, NULL));
  	}
  
      //hooks into ATL's construction
      HRESULT InternalFinalConstructRelease(); // not FinalConstruct() - this is to work around a bogus ATL assert.
  
      BEGIN_CONNECTION_POINT_MAP(CMMCApplication)
          CONNECTION_POINT_ENTRY(DIID_AppEvents)
      END_CONNECTION_POINT_MAP()
  
      // overriden to do more job than the base class does
      virtual ::SC ScOnDisconnectObjects();
  
  private:
  
      //IMMCApplication
  public:
      void  STDMETHODCALLTYPE  Help();
      void  STDMETHODCALLTYPE  Quit();
      STDMETHOD(get_Document)     (Document **ppDocument);
      STDMETHOD(Load)             (BSTR bstrFilename);
      STDMETHOD(get_Frame)        (Frame **ppFrame);
      STDMETHOD(get_Visible)      (BOOL *pVisible);
      STDMETHOD(Show)             ();
      STDMETHOD(Hide)             ();
      STDMETHOD(get_UserControl)  (PBOOL pUserControl);
      STDMETHOD(put_UserControl)  (BOOL  bUserControl);
      STDMETHOD(get_VersionMajor) (PLONG pVersionMajor);
      STDMETHOD(get_VersionMinor) (PLONG pVersionMinor);
  
  private:
      // Return the CAMCApp only if it is initialized. We do not want
      // object model methods to operate on app while initializing.
      CAMCApp *GetApp()
      {
          CAMCApp *pApp = AMCGetApp();
          if ( (! pApp) || (pApp->IsInitializing()) )
              return NULL;
  
          return pApp;
      }
  };
  
  //############################################################################
  //############################################################################
  //
  //  Event map for application events
  //
  //############################################################################
  //############################################################################
  
  DISPATCH_CALL_MAP_BEGIN(AppEvents)
  
      DISPATCH_CALL1( AppEvents, OnQuit,                   PAPPLICATION )
      DISPATCH_CALL2( AppEvents, OnDocumentOpen,           PDOCUMENT,        BOOL)
      DISPATCH_CALL1( AppEvents, OnDocumentClose,          PDOCUMENT )
      DISPATCH_CALL2( AppEvents, OnSnapInAdded,            PDOCUMENT,  PSNAPIN )
      DISPATCH_CALL2( AppEvents, OnSnapInRemoved,          PDOCUMENT,  PSNAPIN )
      DISPATCH_CALL1( AppEvents, OnNewView,                PVIEW )
      DISPATCH_CALL1( AppEvents, OnViewClose,              PVIEW )
      DISPATCH_CALL2( AppEvents, OnViewChange,             PVIEW,      PNODE );
      DISPATCH_CALL2( AppEvents, OnSelectionChange,        PVIEW,      PNODES )
      DISPATCH_CALL1( AppEvents, OnContextMenuExecuted,    PMENUITEM );
      DISPATCH_CALL0( AppEvents, OnToolbarButtonClicked )
      DISPATCH_CALL1( AppEvents, OnListUpdated,            PVIEW )
  
  DISPATCH_CALL_MAP_END()
  
  
  
  /*+-------------------------------------------------------------------------*
   *
   * CMMCApplication::InternalFinalConstructRelease
   *
   * PURPOSE: Hands the CAMCApp a pointer to the 'this' object.
   *
   *+-------------------------------------------------------------------------*/
  HRESULT
  CMMCApplication::InternalFinalConstructRelease()
  {
  	MMC_COM_MANAGE_STATE();
      DECLARE_SC(sc, TEXT("CMMCApplication::InternalFinalConstructRelease"));
  
      // Dont use GetApp, we need to get CAMCApp even if it is not fully initialized.
      CAMCApp *pApp = AMCGetApp();
      sc = ScCheckPointers(pApp);
      if(sc)
          return sc.ToHr(); // some wierd error.
  
      sc = pApp->ScRegister_Application(this);
  
      return sc.ToHr();
  }
  
  /*+-------------------------------------------------------------------------*
   *
   * CMMCApplication::GetFrame
   *
   * PURPOSE: A static function that hooks into the COM interface entry list
   *          and allows a tear-off object to be created that implements the
   *          Frame interface.
   *
   * PARAMETERS:
   *    void*   pv :   Defined by ATL to hold a pointer to the CMMCApplication object
   *                   because this is a static method.
   *    REFIID  riid :  As per QI
   *    LPVOID* ppv :   As per QI
   *    DWORD   dw :   ignored
   *
   * RETURNS:
   *    HRESULT WINAPI
   *
   *+-------------------------------------------------------------------------*/
  STDMETHODIMP
  CMMCApplication::get_Frame(Frame **ppFrame)
  {
  	MMC_COM_MANAGE_STATE();
      DECLARE_SC(sc, TEXT("CMMCApplication::get_Frame"));
  
      if(!ppFrame)
      {
          sc = E_POINTER;
          return sc.ToHr();
      }
  
      // get the app
      CAMCApp *pApp = GetApp();
      if(NULL == pApp)
      {
          sc = E_UNEXPECTED;
          return sc.ToHr();
      }
  
  
      CMainFrame *pMainFrame = pApp->GetMainFrame();
      if(!pMainFrame)
      {
          sc = E_UNEXPECTED;
          return sc.ToHr();
      }
  
      sc = pMainFrame->ScGetFrame(ppFrame);
  
      return sc.ToHr();
  }
  
  
  STDMETHODIMP
  CMMCApplication::get_Document(Document **ppDocument)
  {
  	MMC_COM_MANAGE_STATE();
      DECLARE_SC(sc, TEXT("CMMCApplication::get_Document"));
  
      CAMCDoc* const pDoc = CAMCDoc::GetDocument();
  
      ASSERT(ppDocument != NULL);
      if(ppDocument == NULL || (pDoc == NULL))
      {
          sc = E_POINTER;
          return sc.ToHr();
      }
  
      sc = pDoc->ScGetMMCDocument(ppDocument);
      if(sc)
          return sc.ToHr();
  
      return sc.ToHr();
  }
  
  /***************************************************************************\
   *
   * METHOD:  CMMCApplication::Load
   *
   * PURPOSE: implements Application.Load for object model
   *
   * PARAMETERS:
   *    BSTR bstrFilename - console file to load
   *
   * RETURNS:
   *    SC    - result code
   *
  \***************************************************************************/
  STDMETHODIMP
  CMMCApplication::Load(BSTR bstrFilename)
  {
  	MMC_COM_MANAGE_STATE();
      DECLARE_SC(sc, TEXT("CMMCApplication::Load"));
  
      CAMCApp *pApp = GetApp();
      sc = ScCheckPointers(pApp, E_UNEXPECTED);
      if (sc)
          return sc.ToHr();
  
      USES_CONVERSION;
      pApp->OpenDocumentFile(OLE2CT(bstrFilename));
      return sc.ToHr();
  }
  
  
  void
  STDMETHODCALLTYPE CMMCApplication::Help()
  {
  	MMC_COM_MANAGE_STATE();
      DECLARE_SC(sc, TEXT("CMMCApplication::Help"));
  
      CAMCApp *pApp = GetApp();
  
      if(NULL == pApp)
      {
          sc = E_UNEXPECTED;
          return;
      }
  
      sc = pApp->ScHelp();
      if(sc)
          return;
  
      return;
  }
  
  void
  STDMETHODCALLTYPE CMMCApplication::Quit()
  {
  	MMC_COM_MANAGE_STATE();
  
      SC sc;
      CAMCApp *pApp = GetApp();
  
      if(NULL == pApp)
          goto Error;
  
      // confiscate the control from user
      pApp->SetUnderUserControl(false);
  
      // get mainframe
      {
          CMainFrame * pMainFrame = pApp->GetMainFrame();
          if(NULL == pMainFrame)
              goto Error;
  
          // close it gracefully.
          pMainFrame->PostMessage(WM_CLOSE);
      }
  
  Cleanup:
      return;
  Error:
      sc = E_UNEXPECTED;
      TraceError(TEXT("CMMCApplication::Quit"), sc);
      goto Cleanup;
  }
  
  /*+-------------------------------------------------------------------------*
   *
   * CMMCApplication::get_VersionMajor
   *
   * PURPOSE: Returns the major version number for the installed version of MMC.
   *
   * PARAMETERS:
   *    PLONG  pVersionMajor :
   *
   * RETURNS:
   *    HRESULT
   *
   *+-------------------------------------------------------------------------*/
  HRESULT
  CMMCApplication::get_VersionMajor(PLONG pVersionMajor)
  {
  	MMC_COM_MANAGE_STATE();
      DECLARE_SC(sc, TEXT("CMMCApplication::get_VersionMajor"));
  
      sc = ScCheckPointers(pVersionMajor);
      if(sc)
          return sc.ToHr();
  
      *pVersionMajor = MMC_VERSION_MAJOR;
  
      return sc.ToHr();
  }
  
  /*+-------------------------------------------------------------------------*
   *
   * CMMCApplication::get_VersionMinor
   *
   * PURPOSE: Returns the minor version number for the installed version of MMC.
   *
   * PARAMETERS:
   *    PLONG  pVersionMinor :
   *
   * RETURNS:
   *    HRESULT
   *
   *+-------------------------------------------------------------------------*/
  HRESULT
  CMMCApplication::get_VersionMinor(PLONG pVersionMinor)
  {
  	MMC_COM_MANAGE_STATE();
      DECLARE_SC(sc, TEXT("CMMCApplication::get_VersionMinor"));
  
      sc = ScCheckPointers(pVersionMinor);
      if(sc)
          return sc.ToHr();
  
      *pVersionMinor = MMC_VERSION_MINOR;
  
      return sc.ToHr();
  }
  
  //+-------------------------------------------------------------------
  //
  //  Member:      CMMCApplication::get_Visible
  //
  //  Synopsis:    Returns the visible property
  //
  //  Arguments:   [PBOOL] - out bool
  //
  //  Returns:     HRESULT
  //
  //--------------------------------------------------------------------
  HRESULT CMMCApplication::get_Visible (PBOOL pbVisible)
  {
  	MMC_COM_MANAGE_STATE();
      DECLARE_SC(sc, _T("CMMCApplication::get_Visible"));
  
      sc = ScCheckPointers(pbVisible);
      if (sc)
          return sc.ToHr();
  
      // get the app
      CAMCApp *pApp = GetApp();
      sc = ScCheckPointers(pApp, E_UNEXPECTED);
      if (sc)
          return (sc.ToHr());
  
      CMainFrame *pMainFrame = pApp->GetMainFrame();
      sc = ScCheckPointers(pMainFrame, E_UNEXPECTED);
      if (sc)
          return (sc.ToHr());
  
      *pbVisible = pMainFrame->IsWindowVisible();
  
      return (sc.ToHr());
  }
  
  //+-------------------------------------------------------------------
  //
  //  Member:      CMMCApplication::Show
  //
  //  Synopsis:    Shows the application
  //
  //  Arguments:   None
  //
  //  Returns:     HRESULT
  //
  //--------------------------------------------------------------------
  HRESULT CMMCApplication::Show ()
  {
  	MMC_COM_MANAGE_STATE();
      DECLARE_SC(sc, _T("CMMCApplication::Show"));
  
      // get the app
      CAMCApp *pApp = GetApp();
      sc = ScCheckPointers(pApp, E_UNEXPECTED);
      if (sc)
          return (sc.ToHr());
  
      CMainFrame *pMainFrame = pApp->GetMainFrame();
      sc = ScCheckPointers(pMainFrame, E_UNEXPECTED);
      if (sc)
          return (sc.ToHr());
  
      sc = pMainFrame->ShowWindow(SW_SHOW);
  
      return (sc.ToHr());
  }
  
  //+-------------------------------------------------------------------
  //
  //  Member:      CMMCApplication::Hide
  //
  //  Synopsis:    Hides the application.
  //
  //  Arguments:   None
  //
  //  Returns:     HRESULT
  //
  //  Note:        If the user is under control (UserControl property is set)
  //               then Hide fails.
  //
  //--------------------------------------------------------------------
  HRESULT CMMCApplication::Hide ()
  {
  	MMC_COM_MANAGE_STATE();
      DECLARE_SC(sc, _T("CMMCApplication::Hide"));
  
      // get the app
      CAMCApp *pApp = GetApp();
      sc = ScCheckPointers(pApp, E_UNEXPECTED);
      if (sc)
          return (sc.ToHr());
  
      // Cant hide if app is under user control.
      if (pApp->IsUnderUserControl())
      {
          sc = E_FAIL;
          return sc.ToHr();
      }
  
      CMainFrame *pMainFrame = pApp->GetMainFrame();
      sc = ScCheckPointers(pMainFrame, E_UNEXPECTED);
      if (sc)
          return (sc.ToHr());
  
      sc = pMainFrame->ShowWindow(SW_HIDE);
  
      return (sc.ToHr());
  }
  
  
  //+-------------------------------------------------------------------
  //
  //  Member:      CMMCApplication::get_UserControl
  //
  //  Synopsis:    Returns the UserControl property
  //
  //  Arguments:   PBOOL - out param.
  //
  //  Returns:     HRESULT
  //
  //--------------------------------------------------------------------
  HRESULT CMMCApplication::get_UserControl (PBOOL pbUserControl)
  {
  	MMC_COM_MANAGE_STATE();
      DECLARE_SC(sc, _T("CMMCApplication::get_UserControl"));
  
      sc = ScCheckPointers(pbUserControl);
      if (sc)
          return (sc.ToHr());
  
      // get the app
      CAMCApp *pApp = GetApp();
      sc = ScCheckPointers(pApp, E_UNEXPECTED);
      if (sc)
          return (sc.ToHr());
  
      *pbUserControl = pApp->IsUnderUserControl();
  
      return (sc.ToHr());
  }
  
  //+-------------------------------------------------------------------
  //
  //  Member:      CMMCApplication::put_UserControl
  //
  //  Synopsis:    Sets the UserControl property
  //
  //  Arguments:   BOOL
  //
  //  Returns:     HRESULT
  //
  //--------------------------------------------------------------------
  HRESULT CMMCApplication::put_UserControl (BOOL bUserControl)
  {
  	MMC_COM_MANAGE_STATE();
      DECLARE_SC(sc, _T("CMMCApplication::put_UserControl"));
  
      // get the app
      CAMCApp *pApp = GetApp();
      sc = ScCheckPointers(pApp, E_UNEXPECTED);
      if (sc)
          return (sc.ToHr());
  
      pApp->SetUnderUserControl(bUserControl);
  
      return (sc.ToHr());
  }
  
  
  /***************************************************************************\
   *
   * METHOD:  CMMCApplication::ScOnDisconnectObjects
   *
   * PURPOSE: special disconnect implementation. For this object implementation
   *          provided by the base class is not enough, since connection point
   *          is an internal object which may also have strong references on it
   *
   * PARAMETERS:
   *
   * RETURNS:
   *    SC    - result code
   *
  \***************************************************************************/
  SC CMMCApplication::ScOnDisconnectObjects()
  {
  	MMC_COM_MANAGE_STATE();
      DECLARE_SC(sc, TEXT("CMMCApplication::ScOnDisconnectObjects"));
  
      // get the connection point container
      IConnectionPointContainerPtr spContainer(GetUnknown());
      sc = ScCheckPointers( spContainer, E_UNEXPECTED );
      if (sc)
          return sc;
  
      // get the connection point
      IConnectionPointPtr spConnectionPoint;
      sc = spContainer->FindConnectionPoint( DIID_AppEvents, &spConnectionPoint );
      if (sc)
          return sc;
  
      // cut connection point references
      sc = CoDisconnectObject( spConnectionPoint, 0/*dwReserved*/ );
      if (sc)
          return sc;
  
      // let the base class do the rest
      sc = CMMCIDispatchImplClass::ScOnDisconnectObjects();
      if (sc)
          return sc;
  
      return sc;
  }
  
  //############################################################################
  //############################################################################
  //
  // ATL GLobal Object Map
  //
  //############################################################################
  //############################################################################
  
  BEGIN_OBJECT_MAP(ObjectMap)
      OBJECT_ENTRY(CLSID_Application, CMMCApplication)
  END_OBJECT_MAP()
  
  
  /*+-------------------------------------------------------------------------*
   * CLockChildWindowUpdate
   *
   * Helper class whose constructor turns off redraw for all of the children
   * of the given window, and whose destructor turns redraw back on for all
   * of the windows for which it was turned off.
   *
   * This is used to prevent ugly transient drawing while opening console
   * files that take a long time to completely open (bug 150356).
   *--------------------------------------------------------------------------*/
  
  class CLockChildWindowUpdate
  {
  public:
      CLockChildWindowUpdate (CWnd* pwndLock) : m_pwndLock(pwndLock)
      {
          if (m_pwndLock != NULL)
          {
              CWnd* pwndChild;
  
              /*
               * turn off redraw for each child, saving the HWND for later
               * so we can turn it back on (we save the HWND instead of the
               * CWnd* because MFC might have returned a temporary object).
               */
              for (pwndChild  = m_pwndLock->GetWindow (GW_CHILD);
                   pwndChild != NULL;
                   pwndChild  = pwndChild->GetNextWindow())
              {
                  pwndChild->SetRedraw (false);
                  m_vChildren.push_back (pwndChild->GetSafeHwnd());
              }
          }
      }
  
      ~CLockChildWindowUpdate()
      {
          std::vector<HWND>::iterator it;
  
          /*
           * for every window for which we turned off redraw, turn it back on
           */
          for (it = m_vChildren.begin(); it != m_vChildren.end(); ++it)
          {
              HWND hWndChild = *it;
  
              if ( (hWndChild != NULL) && ::IsWindow(hWndChild) )
              {
                  CWnd *pwndChild = CWnd::FromHandle(hWndChild);
                  pwndChild->SetRedraw (true);
                  pwndChild->RedrawWindow (NULL, NULL,
                                           RDW_INVALIDATE | RDW_UPDATENOW |
                                           RDW_ERASE | RDW_FRAME | RDW_ALLCHILDREN);
              }
          }
      }
  
  private:
      CWnd* const         m_pwndLock;
      std::vector<HWND>   m_vChildren;
  };
  
  /*+-------------------------------------------------------------------------*
  * class CCausalityCounter
  * 
  *
  * PURPOSE: used to determine whether a function has resulted in a call back to itself on the same stack
  *
  * USAGE: Initialize with a variable that is set to zero.
  *
  * NOTE: Copied from MMCaxwin.cpp
  *+-------------------------------------------------------------------------*/
  class CCausalityCounter  
  {
      UINT & m_bCounter;
  
  public:
      CCausalityCounter(UINT &bCounter) : m_bCounter(bCounter){++m_bCounter;}
      ~CCausalityCounter() {--m_bCounter;}
  
      bool HasReentered() 
      {
          return (m_bCounter>1);
      }
  };
  
  //############################################################################
  //############################################################################
  //
  //  Implementation of class CAMCMultiDocTemplate
  //
  //############################################################################
  //############################################################################
  class CAMCMultiDocTemplate : public CMultiDocTemplate
  {
  public:
      CAMCMultiDocTemplate(UINT nIDResource, CRuntimeClass* pDocClass,
                           CRuntimeClass* pFrameClass, CRuntimeClass* pViewClass)
              : CMultiDocTemplate(nIDResource, pDocClass, pFrameClass, pViewClass)
          {
          }
  
      CDocument* OpenDocumentFile(LPCTSTR lpszPathName,
                                  BOOL bMakeVisible)
          {
              DECLARE_SC(sc, TEXT("CAMCMultiDocTemplate::OpenDocumentFile"));
  
              CAMCDoc* const pDoc = CAMCDoc::GetDocument();
              if (pDoc && (!pDoc->SaveModified() || !CanCloseDoc() ))
                  return NULL;        // leave the original one
  
              CLockChildWindowUpdate lock (AfxGetMainWnd());
              CAMCDoc* pDocument = (CAMCDoc*)CreateNewDocument();
  
              if (pDocument == NULL)
              {
                  TRACE0("CDocTemplate::CreateNewDocument returned NULL.\n");
                  AfxMessageBox(AFX_IDP_FAILED_TO_CREATE_DOC); // do not change to MMCMessageBox
                  return NULL;
              }
  
              HRESULT hr;
              if ((hr = pDocument->InitNodeManager()) != S_OK)
              {
                  TRACE1("CAMCDoc::InitNodeManager failed, 0x%08x\n", hr);
                  CAMCApp* pApp = AMCGetApp();
                  MMCErrorBox((pApp && pApp->IsWin9xPlatform())
                                      ? IDS_NODEMGR_FAILED_9x
                                      : IDS_NODEMGR_FAILED);
                  delete pDocument;       // explicit delete on error
                  return NULL;
              }
  
              ASSERT_VALID(pDocument);
  
              BOOL bAutoDelete = pDocument->m_bAutoDelete;
              pDocument->m_bAutoDelete = FALSE;   // don't destroy if something goes wrong
              CFrameWnd* pFrame = CreateNewFrame(pDocument, NULL);
              pDocument->m_bAutoDelete = bAutoDelete;
              if (pFrame == NULL)
              {
                  AfxMessageBox(AFX_IDP_FAILED_TO_CREATE_DOC);  // do not change to MMCMessageBox
                  delete pDocument;       // explicit delete on error
                  return NULL;
              }
              ASSERT_VALID(pFrame);
  
              if (lpszPathName == NULL)
              {
                  // create a new document - with default document name
                  SetDefaultTitle(pDocument);
  
                  // avoid creating temporary compound file when starting up invisible
                  if (!bMakeVisible)
                      pDocument->m_bEmbedded = TRUE;
  
                  if (!pDocument->OnNewDocument())
                  {
                      // user has be alerted to what failed in OnNewDocument
                      TRACE0("CDocument::OnNewDocument returned FALSE.\n");
                      AfxMessageBox (AFX_IDP_FAILED_TO_CREATE_DOC);  // do not change to MMCMessageBox
                      pFrame->DestroyWindow();
                      return NULL;
                  }
  
                  // it worked, now bump untitled count
                  m_nUntitledCount++;
  
                  InitialUpdateFrame(pFrame, pDocument, bMakeVisible);
              }
              else
              {
                  // open an existing document
                  CWaitCursor wait;
                  if (!pDocument->OnOpenDocument(lpszPathName))
                  {
                      // user has be alerted to what failed in OnOpenDocument
                      TRACE0("CDocument::OnOpenDocument returned FALSE.\n");
                      pFrame->DestroyWindow();
                      return NULL;
                  }
  #ifdef _MAC
                  // if the document is dirty, we must have opened a stationery pad
                  //  - don't change the pathname because we want to treat the document
                  //  as untitled
                  if (!pDocument->IsModified())
  #endif
                      pDocument->SetPathName(lpszPathName);
                  //REVIEW: dburg: InitialUpdateFrame(pFrame, pDocument, bMakeVisible);
                  pFrame->DestroyWindow();
                  pDocument->SetModifiedFlag      (false);
                  pDocument->SetFrameModifiedFlag (false);
              }
              // fire script event
              CAMCApp* pApp = AMCGetApp();
  
              sc = ScCheckPointers(pApp, E_UNEXPECTED);
              if (sc)
                  return pDocument;
  
              sc = pApp->ScOnNewDocument(pDocument, (lpszPathName != NULL));
              if (sc)
                  sc.TraceAndClear();
  
              return pDocument;
          }
          // this method is overrided to catch application quit event
          virtual void CloseAllDocuments( BOOL bEndSession )
          {
              DECLARE_SC(sc, TEXT("CAMCMultiDocTemplate::CloseAllDocuments"));
              
              /* Bug 620422: CloseAllDocuments can end up being called again 
               * before a previous invocation has returned, e.g., when the actions
               * of closing this application and of logging out overlap.
               * MFC's handlers for these events: CFrameWnd::OnClose and 
               * CFrameWnd::OnEndSession both call CloseAllDocuments. 
               *
               * We keep track of whether a call to CloseAllDocuments is in 
               * progress by a causality counter tied to the static variable 
               * cInvocations (initialized to 0). 
               * Following invocations, if any, simply return.
               */
  
              static UINT cInvocations = 0;
              CCausalityCounter counter(cInvocations); 
  
              if (counter.HasReentered())
                  return;
  
              // invoke base class to perform required tasks
              CMultiDocTemplate::CloseAllDocuments( bEndSession );
  
              // no other way we can get here but exit app
              // so that's a good time for script to know it
              CAMCApp* pApp = AMCGetApp();
              sc = ScCheckPointers(pApp, E_UNEXPECTED);
              if (sc)
                  return;
  
              // forward to application to emit the script event
              sc = pApp->ScOnQuitApp();
              if (sc)
                  sc.TraceAndClear();
  
              // cut off all strong references now.
              // Quit was executed - nothing else matters
              sc = GetComObjectEventSource().ScFireEvent( CComObjectObserver::ScOnDisconnectObjects );
              if (sc)
                  sc.TraceAndClear();
  
  	}
  };
  
  
  // Declare debug infolevel for this component
  DECLARE_INFOLEVEL(AMCConUI);
  
  //############################################################################
  //############################################################################
  //
  //  Implementation of class CAMCApp
  //
  //############################################################################
  //############################################################################
  IMPLEMENT_DYNAMIC(CAMCApp, CWinApp)
  
  BEGIN_MESSAGE_MAP(CAMCApp, CWinApp)
      //{{AFX_MSG_MAP(CAMCApp)
      ON_COMMAND(ID_APP_ABOUT, OnAppAbout)
      //}}AFX_MSG_MAP
  
      // Standard file based document commands
      ON_COMMAND(ID_FILE_NEW, CWinApp::OnFileNew)
      ON_COMMAND(ID_FILE_OPEN, CWinApp::OnFileOpen)
  
      // Standard print setup command
      ON_COMMAND(ID_FILE_PRINT_SETUP, CWinApp::OnFilePrintSetup)
  
      ON_COMMAND(ID_FILE_NEW_USER_MODE, OnFileNewInUserMode) // CTRL+N in user mode - do nothing
  
  END_MESSAGE_MAP()
  
  /////////////////////////////////////////////////////////////////////////////
  // CAMCApp construction
  
  CAMCApp::CAMCApp() :
      m_bOleInitialized(FALSE),
      m_bDefaultDirSet(FALSE),
      m_eMode(eMode_Error),
      m_fAuthorModeForced(false),
      m_fInitializing(true),
      m_fDelayCloseUntilIdle(false),
      m_fCloseCameFromMainPump(false),
      m_nMessagePumpNestingLevel(0),
      m_fIsWin9xPlatform(false),
      m_dwHelpCookie(0),
      m_bHelpInitialized(false),
      m_fUnderUserControl(true),
      m_fRunningAsOLEServer(false)
  {
  }
  
  
  /////////////////////////////////////////////////////////////////////////////
  // The one and only CAMCApp object
  
  CAMCApp theApp;
  const CRect g_rectEmpty (0, 0, 0, 0);
  
  void DeleteDDEKeys()
  {
      HKEY key;
  
      if (ERROR_SUCCESS == RegOpenKeyEx (HKEY_CLASSES_ROOT,
                                         _T("MSCFile\\shell\\open"),
                                         0, KEY_SET_VALUE, &key))
      {
          theApp.DelRegTree (key, _T("ddeexec"));
          RegCloseKey (key);
      }
  }
  
  /*+-------------------------------------------------------------------------*
   *
   * CAMCApp::GetMainFrame
   *
   * PURPOSE: Returns a pointer to the main frame.
   *
   * RETURNS:
   *    CMainFrame *
   *
   *+-------------------------------------------------------------------------*/
  CMainFrame *
  CAMCApp::GetMainFrame()
  {
      return dynamic_cast<CMainFrame *>(m_pMainWnd);
  }
  
  /*+-------------------------------------------------------------------------*
   *
   * CAMCApp::ScGet_Application
   *
   * PURPOSE: Returns a pointer to an _Application object.
   *
   * PARAMETERS:
   *    _Application ** pp_Application :
   *
   * RETURNS:
   *    SC
   *
   *+-------------------------------------------------------------------------*/
  SC
  CAMCApp::ScGet_Application(_Application **pp_Application)
  {
      DECLARE_SC(sc, TEXT("CAMCApp::ScGet_Application"));
  
      // parameter check
      sc = ScCheckPointers(pp_Application);
      if (sc)
          return sc;
  
      // init out param
      *pp_Application = NULL;
  
      // see if we have a chached one
      if (m_sp_Application != NULL)
      {
          *pp_Application = m_sp_Application;
          (*pp_Application)->AddRef(); // addref for the client.
  
          return sc;
      }
  
      // create an _Application object. This is needed if MMC was instantiated
      // by a user, not COM.
  
      sc = CMMCApplication::CreateInstance(pp_Application);
      if(sc)
          return sc;
  
      // The constructor of the CMMCApplication calls ScRegister_Application
      // which sets the m_sp_Application pointer. Do not set this pointer here.
  
      sc = ScCheckPointers(*pp_Application, E_UNEXPECTED);
      if (sc)
          return sc;
  
      // done
      return sc;
  }
  
  /*+-------------------------------------------------------------------------*
   *
   * CAMCApp::ScRegister_Application
   *
   * PURPOSE: called by a CMMCApplication object to enable the CAMCApp to store
   *          a pointer to it.
   *
   * PARAMETERS:
   *    _Application * p_Application :
   *
   * RETURNS:
   *    SC
   *
   *+-------------------------------------------------------------------------*/
  SC
  CAMCApp::ScRegister_Application(_Application *p_Application)
  {
      DECLARE_SC(sc, TEXT("CAMCApp::ScRegister_Application"));
  
      ASSERT(m_sp_Application == NULL); // only one _Application object should ever register.
  
      sc = ScCheckPointers(p_Application);
      if(sc)
          return sc;
  
      m_sp_Application = p_Application;
      return sc;
  }
  
  
  //+-------------------------------------------------------------------
  //
  //  Member:     RegisterShellFileTypes
  //
  //  Synopsis:   Register the file associations.
  //
  //  Note:       Also set all other relevant registry keys like
  //              Open, Author, RunAs. Eventhough the setup has
  //              done this it may have been deleted mistakenly.
  //
  //  History:
  //              [AnandhaG] - Added the registry repair.
  //  Returns:    None.
  //
  //--------------------------------------------------------------------
  void CAMCApp::RegisterShellFileTypes(BOOL bCompat)
  {
      CWinApp::RegisterShellFileTypes (bCompat);
      DECLARE_SC(sc, _T("CAMCApp::RegisterShellFileTypes"));
  
      do
      {
          // Create the top level MSCFile key.
          CRegKey regKey;
          LONG lRet = regKey.Create(HKEY_CLASSES_ROOT, _T("MSCFile"), REG_NONE,
                                    REG_OPTION_NON_VOLATILE, KEY_WRITE);
          if (ERROR_SUCCESS != lRet)
              break;
  
          /*
           * for platforms that support it (i.e. not Win9x), set the MUI-friendly
           * value for the MSCFile document type
           */
          if (!IsWin9xPlatform())
          {
              CString strMUIValue;
              strMUIValue.Format (_T("@%%SystemRoot%%\\system32\\mmcbase.dll,-%d"), IDR_MUIFRIENDLYNAME);
              lRet = RegSetValueEx (regKey, _T("FriendlyTypeName"), NULL, REG_EXPAND_SZ,
                                    (CONST BYTE *)(LPCTSTR) strMUIValue,
                                    sizeof(TCHAR) * (strMUIValue.GetLength()+1) );
              if (ERROR_SUCCESS != lRet)
                  break;
          }
  
          // Set the EditFlags value.
          lRet = regKey.SetValue(0x100000, _T("EditFlags"));
          if (ERROR_SUCCESS != lRet)
              break;
  
          // Create the Author verb.
          lRet = regKey.Create(HKEY_CLASSES_ROOT, _T("MSCFile\\shell\\Author"), REG_NONE,
                               REG_OPTION_NON_VOLATILE, KEY_WRITE);
          if (ERROR_SUCCESS != lRet)
              break;
  
          // And set default value for author (this reflects in shell menu).
          CString strRegVal;
          LoadString(strRegVal, IDS_MENUAUTHOR);
          lRet = RegSetValueEx ((HKEY)regKey, (LPCTSTR)NULL, NULL, REG_SZ,
                                (CONST BYTE *)(LPCTSTR)strRegVal, sizeof(TCHAR) * (strRegVal.GetLength()+1) );
          if (ERROR_SUCCESS != lRet)
              break;
  
          /*
           * for platforms that support it (i.e. not Win9x), set the MUI-friendly
           * value for the menu item
           */
          if (!IsWin9xPlatform())
          {
              CString strMUIValue;
              strMUIValue.Format (_T("@%%SystemRoot%%\\system32\\mmcbase.dll,-%d"), IDS_MENUAUTHOR);
              lRet = RegSetValueEx (regKey, _T("MUIVerb"), NULL, REG_EXPAND_SZ,
                                    (CONST BYTE *)(LPCTSTR) strMUIValue,
                                    sizeof(TCHAR) * (strMUIValue.GetLength()+1) );
              if (ERROR_SUCCESS != lRet)
                  break;
          }
  
          // Create the Author command.
          lRet = regKey.Create(HKEY_CLASSES_ROOT, _T("MSCFile\\shell\\Author\\command"), REG_NONE,
                               REG_OPTION_NON_VOLATILE, KEY_WRITE);
          if (ERROR_SUCCESS != lRet)
              break;
  
          //////////////////////////////////////////////////////////////
          // Win95 does not support REG_EXPAND_SZ for default values. //
          // So we set expand strings and set registry strings as     //
          // REG_SZ for Win9x.                                        //
          // The following declarations are for Win9x platform.       //
          //////////////////////////////////////////////////////////////
          TCHAR szRegValue[2 * MAX_PATH];
          int cchRegValue = 2 * MAX_PATH;
  
          TCHAR szWinDir[MAX_PATH];
          if (0 == ExpandEnvironmentStrings(_T("%WinDir%"), szWinDir, countof(szWinDir)) )
              break;
  
          DWORD dwCount = 0;
          LPTSTR lpszRegValue = NULL;
  
          // Set the default value for Author command.
          if (IsWin9xPlatform() == false)
          {
              lpszRegValue = _T("%SystemRoot%\\system32\\mmc.exe /a \"%1\" %*");
              dwCount = sizeof(TCHAR) * (1 + _tcslen(lpszRegValue));
              lRet = RegSetValueEx ((HKEY)regKey, (LPCTSTR)NULL, NULL, REG_EXPAND_SZ,
                                    (CONST BYTE *)lpszRegValue, dwCount);
          }
          else // Win9x platform
          {
              lpszRegValue = _T("\\mmc.exe /a \"%1\" %2 %3 %4 %5 %6 %7 %8 %9");
              sc = StringCchCopy(szRegValue, cchRegValue, szWinDir);
              if (sc)
                  return;
  
              sc = StringCchCat(szRegValue, cchRegValue, lpszRegValue);
              if (sc)
                  return;
  
              dwCount = sizeof(TCHAR) * (1 + _tcslen(szRegValue));
              lRet = RegSetValueEx ((HKEY)regKey, (LPCTSTR)NULL, NULL, REG_SZ,
                                    (CONST BYTE *)szRegValue, dwCount);
          }
  
          if (ERROR_SUCCESS != lRet)
              break;
  
          // Create the Open verb.
          lRet = regKey.Create(HKEY_CLASSES_ROOT, _T("MSCFile\\shell\\Open"),  REG_NONE,
                               REG_OPTION_NON_VOLATILE, KEY_WRITE);
          if (ERROR_SUCCESS != lRet)
              break;
  
          // Set default value for Open.
          LoadString(strRegVal, IDS_MENUOPEN);
          lRet = RegSetValueEx ((HKEY)regKey, (LPCTSTR)NULL, NULL, REG_SZ,
                                (CONST BYTE *)(LPCTSTR)strRegVal,sizeof(TCHAR) * (strRegVal.GetLength()+1) );
          if (ERROR_SUCCESS != lRet)
              break;
  
          /*
           * for platforms that support it (i.e. not Win9x), set the MUI-friendly
           * value for the menu item
           */
          if (!IsWin9xPlatform())
          {
              CString strMUIValue;
              strMUIValue.Format (_T("@%%SystemRoot%%\\system32\\mmcbase.dll,-%d"), IDS_MENUOPEN);
              lRet = RegSetValueEx (regKey, _T("MUIVerb"), NULL, REG_EXPAND_SZ,
                                    (CONST BYTE *)(LPCTSTR) strMUIValue,
                                    sizeof(TCHAR) * (strMUIValue.GetLength()+1) );
              if (ERROR_SUCCESS != lRet)
                  break;
          }
  
          // Create the Open command.
          lRet = regKey.Create(HKEY_CLASSES_ROOT, _T("MSCFile\\shell\\Open\\command"),  REG_NONE,
                               REG_OPTION_NON_VOLATILE, KEY_WRITE);
          if (ERROR_SUCCESS != lRet)
              break;
  
          // Set the default value for Open command.
          if (IsWin9xPlatform() == false)
          {
              lpszRegValue = _T("%SystemRoot%\\system32\\mmc.exe \"%1\" %*");
              dwCount = sizeof(TCHAR) * (1 + _tcslen(lpszRegValue));
              lRet = RegSetValueEx ((HKEY)regKey, (LPCTSTR)NULL, NULL, REG_EXPAND_SZ,
                                    (CONST BYTE *)lpszRegValue, dwCount);
          }
          else // Win9x platform
          {
              lpszRegValue = _T("\\mmc.exe \"%1\" %2 %3 %4 %5 %6 %7 %8 %9");
  
              sc = StringCchCopy(szRegValue, cchRegValue, szWinDir);
              if (sc)
                  return;
  
              sc = StringCchCat(szRegValue, cchRegValue, lpszRegValue);
              if (sc)
                  return;
  
              dwCount = sizeof(TCHAR) * (1 + _tcslen(szRegValue));
              lRet = RegSetValueEx ((HKEY)regKey, (LPCTSTR)NULL, NULL, REG_SZ,
                                    (CONST BYTE *)szRegValue, dwCount);
          }
  
          if (ERROR_SUCCESS != lRet)
              break;
  
          // Create the RunAs verb (only on NT).
          if (IsWin9xPlatform() == false)
          {
              lRet = regKey.Create(HKEY_CLASSES_ROOT, _T("MSCFile\\shell\\RunAs"),  REG_NONE,
                                   REG_OPTION_NON_VOLATILE, KEY_WRITE);
              if (ERROR_SUCCESS != lRet)
                  break;
  
              // Set default value for RunAs verb.
              LoadString(strRegVal, IDS_MENURUNAS);
              lRet = RegSetValueEx ((HKEY)regKey, (LPCTSTR)NULL, NULL, REG_SZ,
                                    (CONST BYTE *)(LPCTSTR)strRegVal,sizeof(TCHAR) * (strRegVal.GetLength()+1) );
              if (ERROR_SUCCESS != lRet)
                  break;
  
              /*
               * for platforms that support it (i.e. not Win9x), set the MUI-friendly
               * value for the menu item
               */
              if (!IsWin9xPlatform())
              {
                  CString strMUIValue;
                  strMUIValue.Format (_T("@%%SystemRoot%%\\system32\\mmcbase.dll,-%d"), IDS_MENURUNAS);
                  lRet = RegSetValueEx (regKey, _T("MUIVerb"), NULL, REG_EXPAND_SZ,
                                        (CONST BYTE *)(LPCTSTR) strMUIValue,
                                        sizeof(TCHAR) * (strMUIValue.GetLength()+1) );
                  if (ERROR_SUCCESS != lRet)
                      break;
              }
  
              // Create the RunAs command.
              lRet = regKey.Create(HKEY_CLASSES_ROOT, _T("MSCFile\\shell\\RunAs\\command"),  REG_NONE,
                                   REG_OPTION_NON_VOLATILE, KEY_WRITE);
              if (ERROR_SUCCESS != lRet)
                  break;
  
              // Set the default value for RunAs command. (Only on NT Unicode)
              lpszRegValue = _T("%SystemRoot%\\system32\\mmc.exe \"%1\" %*");
              dwCount = sizeof(TCHAR) * (1 + _tcslen(lpszRegValue));
              lRet = RegSetValueEx ((HKEY)regKey, (LPCTSTR)NULL, NULL, REG_EXPAND_SZ,
                                    (CONST BYTE *)lpszRegValue, dwCount);
          }
  
          if (ERROR_SUCCESS != lRet)
              break;
  
      } while ( FALSE );
  
      return;
  }
  
  
  /////////////////////////////////////////////////////////////////////////////
  // CAMCApp initialization
  
  #ifdef UNICODE
  SC ScLaunchMMC (eArchitecture eArch, int nCmdShow);
  #endif
  
  #ifdef MMC_WIN64
      class CMMCCommandLineInfo;
  
      SC ScDetermineArchitecture (const CMMCCommandLineInfo& rCmdInfo, eArchitecture& eArch);
  #else
      bool IsWin64();
  #endif  // MMC_WIN64
  
  
  class CMMCCommandLineInfo : public CCommandLineInfo
  {
  public:
  	eArchitecture	m_eArch;
      bool    		m_fForceAuthorMode;
      bool    		m_fRegisterServer;
      CString 		m_strDumpFilename;
  
  public:
      CMMCCommandLineInfo() :
  		m_eArch (eArch_Any),
  		m_fForceAuthorMode(false),
          m_fRegisterServer(false)
      {}
  
      virtual void ParseParam (LPCTSTR pszParam, BOOL bFlag, BOOL bLast)
      {
          DECLARE_SC(sc, _T("ParseParam"));
  
          bool fHandledHere = false;
  
          if (bFlag)
          {
              sc = ScCheckPointers(pszParam);
              if (sc)
                  return;
  
              /*
               * ignore the following parameters:
               * -dde (await DDE command), -s (splash screen, obsolete).
               */
              if ((lstrcmpi (pszParam, _T("s"))   == 0) ||
                  (lstrcmpi (pszParam, _T("dde")) == 0))
              {
                  fHandledHere = true;
              }
  
              // force author mode
              else if (lstrcmpi (pszParam, _T("a")) == 0)
              {
                  m_fForceAuthorMode = true;
                  fHandledHere = true;
              }
  
              // register the server only
              else if (lstrcmpi (pszParam, _T("RegServer")) == 0)
              {
                  m_fRegisterServer = true;
                  fHandledHere = true;
              }
  
              // force 64-bit MMC to run
              else if (lstrcmp (pszParam, _T("64")) == 0)
              {
                  m_eArch = eArch_64bit;
                  fHandledHere = true;
              }
  
              // force 32-bit MMC to run
              else if (lstrcmp (pszParam, _T("32")) == 0)
              {
                  m_eArch = eArch_32bit;
                  fHandledHere = true;
              }
  
              else
              {
                  static const TCHAR  szDumpParam[] = _T("dump:");
                  const int           cchDumpParam  = 1000;
                  TCHAR               szParam[cchDumpParam];
  
                  sc = StringCchCopy(szParam, cchDumpParam, pszParam);
                  if(sc)
                      sc.TraceAndClear();// suppress errors here. Truncation is OK. StringCchCopy will always add a NULL terminator
                  
  
                  // dump console file contents
                  if (lstrcmpi (szParam, szDumpParam) == 0)
                  {
                      m_strDumpFilename = pszParam + cchDumpParam - 1;
                      fHandledHere = true;
                  }
              }
          }
  
          // if not handled, pass it on to base class
          // if just handled last parameter, call base class ParseLast
          // so it can do the final processing
          if (!fHandledHere)
              CCommandLineInfo::ParseParam (pszParam, bFlag, bLast);
          else if (bLast)
              CCommandLineInfo::ParseLast(bLast);
  
      }
  
  }; // class CMMCCommandLineInfo
  
  
  
  /*+-------------------------------------------------------------------------*
   * CWow64FilesystemRedirectionDisabler
   *
   * Disables Wow64 file system redirection for the file represented in the
   * given CMMCCommandLineInfo.  We do this so MMC32 can open consoles in
   * %windir%\system32 without having the path redirected to %windir%\syswow64.
   *--------------------------------------------------------------------------*/
  
  class CWow64FilesystemRedirectionDisabler
  {
  public:
      CWow64FilesystemRedirectionDisabler (LPCTSTR pszFilename)
      {
  #ifndef MMC_WIN64
  		m_fDisabled = ((pszFilename != NULL) && IsWin64());
  
          if (m_fDisabled)
          {
              Trace (tag32BitTransfer, _T("Disabling Wow64 file system redirection for %s"), pszFilename);
              Wow64DisableFilesystemRedirector (pszFilename);
          }
  #endif  // !MMC_WIN64
      }
  
      ~CWow64FilesystemRedirectionDisabler ()
      {
  #ifndef MMC_WIN64
          if (m_fDisabled)
          {
              Trace (tag32BitTransfer, _T("Enabling Wow64 file system redirection"));
              Wow64EnableFilesystemRedirector();
          }
  #endif  // !MMC_WIN64
      }
  
  private:
  #ifndef MMC_WIN64
      bool    m_fDisabled;
  #endif  // !MMC_WIN64
  };
  
  
  
  /*+-------------------------------------------------------------------------*
   *
   * CAMCApp::ScProcessAuthorModeRestrictions
   *
   * PURPOSE: Determines whether author mode restrictions are being enforced
   *          by system policy, and if author mode is not allowed,
   *          displays an error box and exits.
   *
   * RETURNS:
   *    SC
   *
   *+-------------------------------------------------------------------------*/
  SC
  CAMCApp::ScProcessAuthorModeRestrictions()
  {
      DECLARE_SC(sc, TEXT("CAMCApp::ScProcessAuthorModeRestrictions"));
      CRegKey regKey;
  
      // The mode is initialized to "author", if it is not in
      // initialized state just return.
      if (eMode_Author != m_eMode)
          return sc;
  
      // The console file mode is already read.
      // Check if user policy permits author mode.
      long lResult = regKey.Open(HKEY_CURRENT_USER, POLICY_KEY, KEY_READ);
      if (lResult != ERROR_SUCCESS)
          return sc;
  
      // get the value of RestrictAuthorMode.
      DWORD dwRestrictAuthorMode = 0;
      lResult = regKey.QueryValue(dwRestrictAuthorMode, g_szRestrictAuthorMode);
      if (lResult != ERROR_SUCCESS)
          return sc;
  
      if (dwRestrictAuthorMode == 0)    // Author mode is not restricted so return.
          return sc;
  
      /*
       * If called from script (running as embedded server) see if policy
       * restricts scripts from entering into author mode.
       *
       * If restricted then script will fail, thus restricting rogue scripts.
       *
       * Even if not restricted here cannot add snapins that are restricted.
       */
      if (IsMMCRunningAsOLEServer())
      {
          DWORD dwRestrictScriptsFromEnteringAuthorMode = 0;
          lResult = regKey.QueryValue(dwRestrictScriptsFromEnteringAuthorMode, g_szRestrictScriptsFromEnteringAuthorMode);
          if (lResult != ERROR_SUCCESS)
              return sc;
  
          if (dwRestrictScriptsFromEnteringAuthorMode == 0)  // Scripts can enter author mode so return
              return sc;
  
          sc = ScFromMMC(IDS_AUTHORMODE_NOTALLOWED_FORSCRIPTS);
      }
      else
          // If author mode is not allowed and
          // the user tried to force author mode
          // then display error message and exit.
          sc = ScFromMMC(IDS_AUTHORMODE_NOTALLOWED);
  
      return sc;
  }
  
  
  /*+-------------------------------------------------------------------------*
   *
   * CAMCApp::ScCheckMMCPrerequisites
   *
   * PURPOSE: Checks all prerequisites. These are: (add to the list as appropriate)
   *          1) Internet Explorer 5.5 or greater must be installed
   *
   * RETURNS:
   *    SC
   *
   *+-------------------------------------------------------------------------*/
  SC
  CAMCApp::ScCheckMMCPrerequisites()
  {
      DECLARE_SC(sc, TEXT("CAMCApp::ScCheckMMCPrerequisites"));
  
      // 1. Determine the installed version of Internet Explorer.
      const int cchDATA = 100;
      TCHAR szVersion[cchDATA];
      BOOL bIE55Found    = false;
      HKEY hkey          = NULL;
      DWORD dwType       =0;
      DWORD dwMajor      =0;
      DWORD dwMinor      =0;
      DWORD dwRevision   =0;
      DWORD dwBuild      =0;
  
      if (ERROR_SUCCESS == RegOpenKeyEx(HKEY_LOCAL_MACHINE, TEXT("Software\\Microsoft\\Internet Explorer"), 0, KEY_READ, &hkey))
      {
          DWORD cbData = sizeof(TCHAR) * cchDATA;
  
          if (ERROR_SUCCESS == RegQueryValueEx(hkey, TEXT("Version"), 0, &dwType, (LPBYTE)szVersion, &cbData))
          {
              if (lstrlen(szVersion) > 0)
              {
                  if (_stscanf(szVersion, TEXT("%d.%d.%d.%d"), &dwMajor, &dwMinor, &dwRevision, &dwBuild) >= 2)
                  {
                      //Make sure IE 5.5 or greater is installed. To do this:
                      // 1) Check if the major version is >= 6. If so we're done.
                      // 2) If the major version is 5, the minor version should be >= 50
                      if (dwMajor >= 6)
                      {
                          bIE55Found = true;
                      }
                      if (dwMajor == 5)
                      {
                          if(dwMinor >= 50)
                              bIE55Found = true;
                      }
                  }
              }
          }
  
          RegCloseKey(hkey);
  
      }
      if (!bIE55Found)
      {
          sc = ScFromMMC(MMC_E_INCORRECT_IE_VERSION); // NOTE: update the string when the version requirement changes
          return sc;
      }
  
      return sc;
  }
  
  /*+-------------------------------------------------------------------------*
   *
   * CAMCApp::InitInstance
   *
   * PURPOSE: Initializes the document.
   *
   * NOTE: as an aside, if you need to break on, say,  the 269th allocation,
   *       add the following code:
   *
   *      #define ALLOCATION_NUM  269
   *      _CrtSetBreakAlloc(ALLOCATION_NUM);
   *      _crtBreakAlloc = ALLOCATION_NUM;
   *
   * RETURNS:
   *    BOOL
   *
   *+-------------------------------------------------------------------------*/
  BOOL CAMCApp::InitInstance()
  {
      DECLARE_SC(sc, TEXT("CAMCApp::InitInstance"));
  
  	/*
  	 * Initialize Fusion.
  	 */
      //RAID 656865: Prefix: return value from SHFusionInitializeFromModuleID ignored
  	if (!SHFusionInitializeFromModuleID (NULL, static_cast<int>(reinterpret_cast<ULONG_PTR>(SXS_MANIFEST_RESOURCE_ID))))
      {
          sc = E_UNEXPECTED;
          return FALSE;
      }
     
  #ifdef DBG
      if (tagForceMirror.FAny())
      {
          HINSTANCE hmodUser = GetModuleHandle (_T("user32.dll"));
  
          if (hmodUser != NULL)
          {
              BOOL (WINAPI* pfnSetProcessDefaultLayout)(DWORD);
              (FARPROC&)pfnSetProcessDefaultLayout = GetProcAddress (hmodUser, "SetProcessDefaultLayout");
  
              if (pfnSetProcessDefaultLayout != NULL)
                  (*pfnSetProcessDefaultLayout)(LAYOUT_RTL);
          }
      }
  #endif
  
      BOOL bRet = TRUE;
  
      // Initialize OLE libraries
      if (InitializeOLE() == FALSE)
          return FALSE;
  
  
      // Initialize the ATL Module
      _Module.Init(ObjectMap,m_hInstance);
  
  #ifdef DBG
      if(tagGDIBatching.FAny())
      {
          // disable GDI batching so we'll see drawing as it happens
          GdiSetBatchLimit (1);
      }
  #endif
  
      Unregister();
  
      Trace(tagAMCAppInit, TEXT("CAMCApp::InitInstance"));
  
      CMMCCommandLineInfo cmdInfo;
      ParseCommandLine(cmdInfo);
  
      /*
       * if we got a file on the command line, expand environment
       * variables in the filename so we can open files like
       * "%SystemRoot%\system32\compmgmt.msc"
       */
      if (!cmdInfo.m_strFileName.IsEmpty())
      {
          CWow64FilesystemRedirectionDisabler disabler (cmdInfo.m_strFileName);
  
          sc = ScExpandEnvironmentStrings (cmdInfo.m_strFileName);
          if (sc)
          {
              MMCErrorBox (sc);
              return (false);
          }
      }
  
      // Don't use an .ini file for the MRU or Settings
      // Note: This string does not need to be localizable.
      // HKEY_CURRENT_USER\\Software\\Microsoft\\Microsoft Management Console
      SetRegistryKey(_T("Microsoft"));
  
      // Find out OS version.
      OSVERSIONINFO versInfo;
      versInfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
      BOOL bStat = GetVersionEx(&versInfo);
      ASSERT(bStat);
      m_fIsWin9xPlatform = (versInfo.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS);
  
      // default to Author mode (loading a console may change this later)
      InitializeMode (eMode_Author);
  
      m_fAuthorModeForced = cmdInfo.m_fForceAuthorMode      ||
                            cmdInfo.m_strFileName.IsEmpty();
  
  
      /*
       * dump the snap-ins (and do nothing else) if we got "-dump:<filename>"
       */
      if (!cmdInfo.m_strDumpFilename.IsEmpty())
      {
          DumpConsoleFile (cmdInfo.m_strFileName, cmdInfo.m_strDumpFilename);
          return (false);
      }
  
  #ifdef MMC_WIN64
      /*
       * We're currently running the MMC64.  See if we need to defer to MMC32.
       * If we do, try to launch MMC32.  If we were able to launch MMC32
       * successfully, abort MMC64.
       */
      eArchitecture eArch = eArch_64bit;
      sc = ScDetermineArchitecture (cmdInfo, eArch);
      if (sc)
      {
          DisplayFileOpenError (sc, cmdInfo.m_strFileName);
          return (false);
      }
  
      switch (eArch)
      {
          /*
           * MMC64 is fine, do nothing
           */
          case eArch_64bit:
              break;
  
          /*
           * User cancelled action, abort
           */
          case eArch_None:
              return (false);
              break;
  
          /*
           * We need MMC32, so try to launch it.  If we were able to launch MMC32
           * successfully, abort MMC64; if not, continue running MMC64.
           */
          case eArch_32bit:
              if (!ScLaunchMMC(eArch_32bit, m_nCmdShow).IsError())
              {
                  Trace (tag32BitTransfer, _T("32-bit MMC launched successfully"));
                  return (false);
              }
  
              Trace (tag32BitTransfer, _T("32-bit MMC failed to launch"));
              MMCErrorBox (MMC_E_UnableToLaunchMMC32);
              break;
  
          default:
              ASSERT (false && "Unexpected architecture returned from ScDetermineArchitecture");
              break;
      }
  #elif defined(UNICODE)
      /*
       * We're currently running the MMC32.  If it's running on IA64 and 32-bit
       * wasn't specifically requested with a "-32" switch (this is what MMC64
       * will do when it defers to MMC32), defer to MMC64 so it can do snap-in
       * analysis and determine the appropriate "bitness" to run.
       */
      if ((cmdInfo.m_eArch != eArch_32bit) && IsWin64())
      {
          /*
           * We need MMC64, so try to launch it.  If we were able to launch MMC64
           * successfully, abort MMC32; if not, continue running MMC32.
           */
          if (!ScLaunchMMC(eArch_64bit, m_nCmdShow).IsError())
          {
              Trace (tag32BitTransfer, _T("64-bit MMC launched successfully"));
              return (false);
          }
  
          Trace (tag32BitTransfer, _T("64-bit MMC failed to launch"));
          MMCErrorBox (MMC_E_UnableToLaunchMMC64);
      }
  #endif // MMC_WIN64
  
      AfxEnableControlContainer();
  
      // Standard initialization
  
  #ifdef _AFXDLL
      Enable3dControls();         // Call this when using MFC in a shared DLL
  #else
      Enable3dControlsStatic();   // Call this when linking to MFC statically
  #endif
  
      LoadStdProfileSettings();  // Load standard INI file options (including MRU)
  
      // create our own CDocManager derivative before adding any templates
      // (CWinApp::~CWinApp will delete it)
      m_pDocManager = new CAMCDocManager;
  
      // Register document templates
      CMultiDocTemplate* pDocTemplate;
      pDocTemplate = new CAMCMultiDocTemplate(
          IDR_AMCTYPE,
          RUNTIME_CLASS(CAMCDoc),
          RUNTIME_CLASS(CChildFrame), // custom MDI child frame
          RUNTIME_CLASS(CAMCView));
      AddDocTemplate(pDocTemplate);
  
      // Note: MDI applications register all server objects without regard
      //  to the /Embedding or /Automation on the command line.
  
      if (cmdInfo.m_fRegisterServer)
      {
          sc = _Module.RegisterServer(TRUE);// ATL Classes
  
          if (sc == TYPE_E_REGISTRYACCESS)
              sc.TraceAndClear();
      }
  
      if (sc)
      {
          MMCErrorBox (sc);
          return (false);
      }
  
  
      // create main MDI Frame window
      CMainFrame *pMainFrame = new CMainFrame;
      if (!pMainFrame->LoadFrame(IDR_MAINFRAME))
          return FALSE;
      m_pMainWnd = pMainFrame;
  
      // set the HWND to use as the parent for modal error dialogs.
      SC::SetHWnd(pMainFrame->GetSafeHwnd());
  
      // save this main thread's ID to check if snapins call MMC
      // interfaces from main thread.
      SC::SetMainThreadID(::GetCurrentThreadId());
  
      m_fRunningAsOLEServer = false;
  
      // Check to see if launched as OLE server
      if (RunEmbedded() || RunAutomated())
      {
          m_fRunningAsOLEServer = true;
          // Application was run with /Embedding or /Automation.  Don't show the
          //  main window in this case.
          //return TRUE;
  
          // Also set that script is controlling the application not the user
          // The script can modify the UserControl property on the application.
          SetUnderUserControl(false);
  
          // When a server application is launched stand-alone, it is a good idea to register all objects
          // ATL ones specifically register with REGCLS_MULTIPLEUSE
          // we register class objects only when run as an OLE server. This way, cannot connect to
          // an existing instance of MMC.
          sc = _Module.RegisterClassObjects(CLSCTX_LOCAL_SERVER, REGCLS_SINGLEUSE);
          if(sc)
              goto Error;
      }
  
      if (cmdInfo.m_fRegisterServer)
      {
          CString strTypeLib;
          strTypeLib.Format(TEXT("\\%d"), IDR_WEBSINK_TYPELIB); // this should evaluate to something like "\\4"
  
          sc = _Module.RegisterTypeLib((LPCTSTR)strTypeLib);
  
          if (sc == TYPE_E_REGISTRYACCESS)
              sc.TraceAndClear();
  
          if(sc)
              goto Error;
      }
  
      // Don't Enable drag/drop open
      // m_pMainWnd->DragAcceptFiles();
  
      // Enable DDE Execute open
      if (cmdInfo.m_fRegisterServer)
          RegisterShellFileTypes(FALSE);
      EnableShellOpen();
      if (cmdInfo.m_fRegisterServer)
          DeleteDDEKeys();
  
      /*
       * At this point, all of our registration is complete.  If we were invoked
       * with -RegServer, we can bail now.
       */
      if (cmdInfo.m_fRegisterServer)
          return (false);
  
  
      {   // limit scope of disabler
          CWow64FilesystemRedirectionDisabler disabler (cmdInfo.m_strFileName);
  
          // Dispatch commands specified on the command line.
          // This loads a console file if necessary.
          if (!ProcessShellCommand(cmdInfo))
              return (false); // user is already informed about errors
      }
  
      // Now the console file is loaded. Check if Author mode
      // is permitted.
      sc = ScProcessAuthorModeRestrictions(); // check if there are any restrictions set by policy
      if(sc)
          goto Error;
  
      // if proccessing the command line put MMC into author mode,
      // it has to stick with it forever.
      // see bug 102465 openning an author mode console file and then
      //                a user mode console switched MMC into user mode
      if (eMode_Author == m_eMode)
          m_fAuthorModeForced = true;
  
      // create a document automatically only if we're not instantiated as an
      // OLE server.
      if(! IsMMCRunningAsOLEServer ())
      {
          // if we don't have a document now (maybe because
          // the Node Manager wasn't registered), punt
          CAMCDoc* pDoc = CAMCDoc::GetDocument ();
          if (pDoc == NULL)
              return (FALSE);
  
          pDoc->SetFrameModifiedFlag (false);
          pDoc->UpdateFrameCounts ();
  
          CMainFrame *pMainFrame = GetMainFrame();
          if (pMainFrame)
          {
              pMainFrame->ShowWindow(m_nCmdShow);
              pMainFrame->UpdateWindow();
          }
  
          // showing will set the frame and "Modified" - reset it
          pDoc->SetFrameModifiedFlag (false);
      }
  
      // register itself as dispatcher able to dispatch com events
      sc = CConsoleEventDispatcherProvider::ScSetConsoleEventDispatcher( this );
      if (sc)
          goto Error;
  
      m_fInitializing = false;
  
      // check for all MMC prerequisites
      sc = ScCheckMMCPrerequisites();
      if (sc)
          goto Error;
  
  
  // Comment out below line when script engines hosted in mmc are enabled.
  //    sc = ScRunTestScript();
  
  Cleanup:
      return bRet;
  
  Error:
      MMCErrorBox(sc);
      bRet = FALSE;
      goto Cleanup;
  }
  
  //+-------------------------------------------------------------------
  //
  //  Member:      ScRunTestScript
  //
  //  Synopsis:    Test program to run a script. Once script input mechanisms
  //               are defined this can be removed.
  //
  //  Arguments:   None
  //
  //  Returns:     SC
  //
  //--------------------------------------------------------------------
  SC CAMCApp::ScRunTestScript ()
  {
      DECLARE_SC(sc, _T("CAMCApp::ScRunTestScript"));
  
      // The Running of scripts is enabled only in debug mode.
      bool bEnableScriptEngines = false;
  
  #ifdef DBG
      if (tagEnableScriptEngines.FAny())
          bEnableScriptEngines = true;
  #endif
  
      if (!bEnableScriptEngines)
          return sc;
  
      // Get the IDispatch from MMC object, the script engine needs
      // the IUnknown to top level object & the ITypeInfo ptr.
      CComPtr<_Application> spApplication;
      sc = ScGet_Application(&spApplication);
      if (sc)
          return sc;
  
      IDispatchPtr spDispatch = NULL;
      sc = spApplication->QueryInterface(IID_IDispatch, (LPVOID*)&spDispatch);
      if (sc)
          return sc;
  
      // The CScriptHostMgr should be instead created on the stack (as we have only
      // one per app) and destroyed with app. This change can be made once we decide
      // how & when the script host will be used to execute the scripts.
      CScriptHostMgr* pMgr = new CScriptHostMgr(spDispatch);
      if (NULL == pMgr)
          return (sc = E_OUTOFMEMORY);
  
      LPOLESTR pszScript = L"set WShShell=CreateObject(\"WScript.Shell\")\n\
                              WshShell.Popup(\"Anand\")\n\
                              Select Case WshShell.Popup(\"Anand\",5,\"Ganesan\", vbyesnocancel)\n\
                              End Select";
  
      tstring strExtn = _T(".vbs");
      sc = pMgr->ScExecuteScript(pszScript, strExtn);
  
      tstring strFile = _T("E:\\newnt\\admin\\mmcdev\\test\\script\\MMCStartupScript.vbs");
  
      sc = pMgr->ScExecuteScript(strFile);
  
      delete pMgr;
  
      return (sc);
  }
  
  // App command to run the dialog
  void CAMCApp::OnAppAbout()
  {
      /*
       * load the title of the about dialog
       */
      CString strTitle (MAKEINTRESOURCE (IDS_APP_NAME));
  
      CString strVersion (MAKEINTRESOURCE (IDS_APP_VERSION));
      strTitle += _T(" ");
      strTitle += strVersion;
  
      ShellAbout(*AfxGetMainWnd(), strTitle, NULL, LoadIcon(IDR_MAINFRAME));
  }
  
  /*+-------------------------------------------------------------------------*
   *
   * CAMCApp::OnFileNewInUserMode
   *
   * PURPOSE: Do nothing in user mode when CTRL+N is pressed.
   *          This handler prevents the hotkey from going to any WebBrowser controls
   *
   * RETURNS:
   *    void
   *
   *+-------------------------------------------------------------------------*/
  void CAMCApp::OnFileNewInUserMode()
  {
      MessageBeep(-1);
  }
  
  
  //+-------------------------------------------------------------------
  //
  //  Member:     ScShowHtmlHelp
  //
  //  Synopsis:   Initialize and then call Help control to display help topic.
  //
  //  Arguments:  [pszFile]    - File to display.
  //              [dwData]     - Depends on uCommand for HH_DISPLAY_TOPIC it
  //                             is help topic string.
  //
  //  Note:       The command is always HH_DISPLAY_TOPIC. HWND is NULL so that
  //              Help can get behind MMC window.
  //              See ScUnintializeHelpControl's Note for more info.
  //
  //  Returns:     SC
  //
  //--------------------------------------------------------------------
  SC CAMCApp::ScShowHtmlHelp(LPCTSTR pszFile, DWORD_PTR dwData)
  {
      DECLARE_SC(sc, _T("CAMCApp::ScInitializeHelpControl"));
  
      /*
       * displaying HtmlHelp might take awhile, so show a wait cursor
       */
      CWaitCursor wait;
  
      if (! m_bHelpInitialized)
          HtmlHelp (NULL, NULL, HH_INITIALIZE, (DWORD_PTR)&m_dwHelpCookie);
  
      // No documented return value for HH_INITIALIZE so always assume
      // Initialize is successful.
      m_bHelpInitialized = true;
  
      HtmlHelp (NULL, pszFile, HH_DISPLAY_TOPIC, dwData);
  
      return sc;
  }
  
  //+-------------------------------------------------------------------
  //
  //  Member:     ScUninitializeHelpControl
  //
  //  Synopsis:   UnInitialize the help if it was initialized by MMC.
  //
  //  Note:       The help-control calls OleInitialize & OleUninitialize
  //              in its DllMain. If a snapin creates any free threaded object
  //              on main thread (STA), the OLE creates an MTA.
  //              The last OleUninitialize does OLEProcessUninitialize in which
  //              then OLE waits for the above MTA to cleanup and return.
  //              By the time help-control does last OleUninitialize in its
  //              DllMain the MTA is already terminated so OLE waits for this
  //              MTA to signal which it never would.
  //              We call HtmlHelp(.. HH_UNINITIALIZE..) to force help control
  //              to uninit so that MMC does last OleUninit.
  //              (This will not solve the problem of snapins calling help directly).
  //
  //  Arguments:
  //
  //  Returns:     SC, S_FALSE if already uninitialized else S_OK.
  //
  //--------------------------------------------------------------------
  SC CAMCApp::ScUninitializeHelpControl()
  {
      DECLARE_SC(sc, _T("CAMCApp::ScUninitializeHelpControl"));
  
      if (false == m_bHelpInitialized)
          return (sc = S_FALSE);
  
      HtmlHelp (NULL, NULL, HH_UNINITIALIZE, m_dwHelpCookie);
      m_bHelpInitialized = false;
      m_dwHelpCookie     = 0;
  
      return sc;
  }
  
  
  BOOL CAMCApp::InitializeOLE()
  {
      if (FAILED(::OleInitialize(NULL)))
          return FALSE;
  
      return (m_bOleInitialized = TRUE);
  }
  
  void CAMCApp::DeinitializeOLE()
  {
      // Uninit help, see ScUninitializeHelpControl note.
      SC sc = ScUninitializeHelpControl();
      if (sc)
      {
          TraceError(_T("Uninit Help control failed"), sc);
      }
  
      // Forced DllCanUnloadNow before mmc exits.
      ::CoFreeUnusedLibraries();
  
      if (m_bOleInitialized == TRUE)
      {
          ::OleUninitialize();
          m_bOleInitialized = FALSE;
      }
  }
  
  
  /////////////////////////////////////////////////////////////////////////////
  // CAMCApp diagnostics
  
  #ifdef _DEBUG
  void CAMCApp::AssertValid() const
  {
      CWinApp::AssertValid();
  }
  #endif //_DEBUG
  
  /////////////////////////////////////////////////////////////////////////////
  // CAMCApp commands
  
  int CAMCApp::ExitInstance()
  {
      DECLARE_SC(sc, TEXT("CAMCApp::ExitInstance"));
  
      // if the main window is not destroyed yet - do that now.
      // since we need to get rid of all the objects before denitializing OLE.
      // It is not requred in most cases, since usually quit starts from closing the mainframe,
      // but in cases like system shut down it will come here with a valid window
      // See WindowsBug(ntbug9) #178858
      if ( ::IsWindow( AfxGetMainWnd()->GetSafeHwnd() ) )
      {
          AfxGetMainWnd()->DestroyWindow();
      }
  
      // disconnect the pointers to event dispatcher
      sc = CConsoleEventDispatcherProvider::ScSetConsoleEventDispatcher( NULL );
      if (sc)
          sc.TraceAndClear();
  
      // release cached application object
      m_sp_Application = NULL;
  
      // MFC's class factories registration is automatically revoked by MFC itself
      if (RunEmbedded() || RunAutomated())
  	    _Module.RevokeClassObjects(); // Revoke class factories for ATL
  
      _Module.Term();               // clanup ATL GLobal Module
  
      // Ask node manager to cleanup what's got cached
      CComPtr<IComCacheCleanup> spComCacheCleanup;
      HRESULT hr = spComCacheCleanup.CoCreateInstance(CLSID_ComCacheCleanup, NULL, MMC_CLSCTX_INPROC);
      if (hr == S_OK)
      {
          spComCacheCleanup->ReleaseCachedOleObjects();
          spComCacheCleanup.Release();
      }
  
      // by now EVERY reference should be released
      ASSERT(_Module.GetLockCount() == 0 && "Outstanding references still exist on exit");
  
      DeinitializeOLE();
  
  	/*
  	 * uninitialize Fusion
  	 */
  	SHFusionUninitialize();
  
      int iRet = CWinApp::ExitInstance();
  
      DEBUG_VERIFY_INSTANCE_COUNT(CAMCTreeView);
      DEBUG_VERIFY_INSTANCE_COUNT(CAMCListView);
      DEBUG_VERIFY_INSTANCE_COUNT(CCCListViewCtrl);
  
      return iRet;
  }
  
  
  BOOL CAMCApp::PreTranslateMessage(MSG* pMsg)
  {
  	// Give HTML help a chance to crack the message. (Bug# 119355 & 206909).
  	if ( m_bHelpInitialized && HtmlHelp(NULL, NULL, HH_PRETRANSLATEMESSAGE, (DWORD_PTR)pMsg) )
  		return TRUE;
  
      // let all of the hook windows have a crack at this message
      WindowListIterator it = m_TranslateMessageHookWindows.begin();
  
      while (it != m_TranslateMessageHookWindows.end())
      {
          HWND  hwndHook = *it;
          CWnd* pwndHook = CWnd::FromHandlePermanent (hwndHook);
  
          // if this window is no longer valid, or it's not a permanent
          // window, remove it from the list
          if (!IsWindow (hwndHook) || (pwndHook == NULL))
              it = m_TranslateMessageHookWindows.erase (it);
  
          else
          {
              // otherwise if the hook window handled the message, bail
              if (pwndHook->PreTranslateMessage (pMsg))
                  return (TRUE);
  
              ++it;
          }
      }
  
      // give the MMC defined main window accelerators a crack at the message
      if (m_Accel.TranslateAccelerator (AfxGetMainWnd()->GetSafeHwnd(), pMsg))
          return TRUE;
  
      return CWinApp::PreTranslateMessage(pMsg);
  }
  
  
  /*+-------------------------------------------------------------------------*
   * CAMCApp::SaveUserDirectory
   *
   *
   *--------------------------------------------------------------------------*/
  
  void CAMCApp::SaveUserDirectory(LPCTSTR pszUserDir)
  {
      // if we got an empty string, change the pointer to NULL so
      // the entry will be removed from the registry
      if ((pszUserDir != NULL) && (lstrlen(pszUserDir) == 0))
          pszUserDir = NULL;
  
      WriteProfileString (m_szSettingsSection, m_szUserDirectoryEntry,
                          pszUserDir);
  }
  
  
  /*+-------------------------------------------------------------------------*
   * CAMCApp::GetUserDirectory
   *
   *
   *--------------------------------------------------------------------------*/
  
  CString CAMCApp::GetUserDirectory(void)
  {
      return (GetProfileString (m_szSettingsSection, m_szUserDirectoryEntry));
  }
  
  
  /*+-------------------------------------------------------------------------*
   * CAMCApp::GetDefaultDirectory
   *
   *
   *--------------------------------------------------------------------------*/
  
  CString CAMCApp::GetDefaultDirectory(void)
  {
      static CString strDefaultDir;
  
      if (strDefaultDir.IsEmpty())
      {
          LPITEMIDLIST pidl;
  
          if (SUCCEEDED(SHGetSpecialFolderLocation(
                                  AfxGetMainWnd()->GetSafeHwnd(),
                                  CSIDL_ADMINTOOLS | CSIDL_FLAG_CREATE, &pidl)))
          {
              // Convert to path name
              SHGetPathFromIDList (pidl, strDefaultDir.GetBuffer (MAX_PATH));
              strDefaultDir.ReleaseBuffer ();
  
              // Free IDList
              LPMALLOC pMalloc;
  
              if (SUCCEEDED(SHGetMalloc (&pMalloc)))
              {
                  pMalloc->Free(pidl);
                  pMalloc->Release();
              }
          }
      }
  
      return (strDefaultDir);
  }
  
  
  /*+-------------------------------------------------------------------------*
   * CAMCApp::SetDefaultDirectory
   *
   *
   *--------------------------------------------------------------------------*/
  
  void CAMCApp::SetDefaultDirectory(void)
  {
      // Only set default first time, so we don't override user selection
      if (m_bDefaultDirSet)
          return;
  
      // Set the current directory to the default directory
      CString strDirectory;
      BOOL    rc = FALSE;
  
      strDirectory = GetDefaultDirectory ();
  
      if (!strDirectory.IsEmpty())
          rc = SetCurrentDirectory (strDirectory);
  
      m_bDefaultDirSet = rc;
  }
  
  
  /*+-------------------------------------------------------------------------*
   * CAMCApp::PumpMessage
   *
   *
   *--------------------------------------------------------------------------*/
  
  BOOL CAMCApp::PumpMessage()
  {
      m_nMessagePumpNestingLevel++;
  
      MSG msg;
      ::PeekMessage(&msg, NULL, NULL, NULL, PM_NOREMOVE);
  
      if (msg.message == WM_CLOSE)
          m_fCloseCameFromMainPump = true;
  
      BOOL rc = CWinApp::PumpMessage();
  
      if (m_fDelayCloseUntilIdle && (m_nMessagePumpNestingLevel == 1))
      {
          m_fCloseCameFromMainPump = true;
          CMainFrame *pMainFrame = GetMainFrame();
          if (pMainFrame)
              pMainFrame->SendMessage (WM_CLOSE);
          m_fDelayCloseUntilIdle = false;
      }
  
      m_fCloseCameFromMainPump = false;
  
      m_nMessagePumpNestingLevel--;
      ASSERT (m_nMessagePumpNestingLevel >= 0);
      return (rc);
  }
  
  /*+-------------------------------------------------------------------------*
   *
   * CAMCApp::ScHelp
   *
   * PURPOSE: Displays help for the application.
   *
   * RETURNS:
   *    SC
   *
   *+-------------------------------------------------------------------------*/
  SC
  CAMCApp::ScHelp()
  {
      DECLARE_SC(sc, TEXT("CAMCApp::ScHelp"));
  
      CMainFrame * pMainFrame = GetMainFrame();
      if(!pMainFrame)
      {
          sc = E_UNEXPECTED;
          return sc;
      }
  
      pMainFrame->OnHelpTopics();
  
      return sc;
  }
  
  /*+-------------------------------------------------------------------------*
   * CAMCApp::OnIdle
   *
   * WM_IDLE handler for CAMCApp.
   *--------------------------------------------------------------------------*/
  
  BOOL CAMCApp::OnIdle(LONG lCount)
  {
      SC               sc;
      CIdleTaskQueue * pQueue = GetIdleTaskQueue();
      BOOL             fMoreIdleWork   = TRUE;
  
      if(NULL == pQueue)
      {
          ASSERT(0 && "Should not come here.");
          goto Error;
      }
  
      fMoreIdleWork = CWinApp::OnIdle(lCount);
  
      if (!fMoreIdleWork)
      {
          CMainFrame *pMainFrame = GetMainFrame();
          if (pMainFrame)
              pMainFrame->OnIdle ();
      }
  
      /*
       * if MFC doesn't have any more idle work to do,
       * check our idle task queue (if we have one)
       */
      if (!fMoreIdleWork && (pQueue != NULL))
      {
          LONG_PTR cIdleTasks;
          pQueue->ScGetTaskCount (&cIdleTasks);
          if(sc)
              goto Error;
  
          /*
           * do we have any idle tasks?
           */
          if (cIdleTasks > 0)
          {
              SC sc = pQueue->ScPerformNextTask();
              if(sc)
                  goto Error;
  
              /*
               * this idle task may have added others; refresh the count
               */
              sc = pQueue->ScGetTaskCount(&cIdleTasks);
              if(sc)
                  goto Error;
          }
  
          /*
           * do we have any more idle work to do?
           */
          fMoreIdleWork = (cIdleTasks > 0);
      }
  
      if (!fMoreIdleWork)
      {
          // this code is to trigger MMC exit sequence when it
          // is under the script control and the last reference is released.
          // (we do not use MFC [which would simply delete the mainframe] to do that)
          if ( !IsUnderUserControl() && CMMCStrongReferences::LastRefReleased() )
          {
              // we are in script control mode and all references are released
              // a good time to say goodbye
  
              CMainFrame *pMainFrame = GetMainFrame();
              sc = ScCheckPointers(pMainFrame, E_UNEXPECTED);
              if (sc)
                  goto Error;
  
              // disabled main window will probably mean we are under modal dialog
              // wait until it is dismissed ( and try again )
              if (pMainFrame->IsWindowEnabled())
              {
                  // here is the deal: if MMC is shown - we will initiate the exit sequence,
                  // but put into the user mode first, so if user chooses to cancel it - it will have
                  // the control over the application. He will also have to handle save request if
                  // something has changed in the console
                  if ( pMainFrame->IsWindowVisible() )
                  {
                      if ( !m_fUnderUserControl )
                          SetUnderUserControl();
  
                      pMainFrame->PostMessage(WM_CLOSE);
                  }
                  else
                  {
                      // if the application is hidden it should wait until user closes all open property sheets.
                      // since it will come back here, waiting means just doing nothing at this point.
                      if ( !FArePropertySheetsOpen(NULL, false /*bBringToFrontAndAskToClose*/ ) )
                      {
                          // if there are not sheets open - we must die silently
                          CAMCDoc* const pDoc = CAMCDoc::GetDocument();
                          if(pDoc == NULL)
                          {
                              sc = E_POINTER;
                              //fall thru; (need to close anyway)
                          }
                          else
                          {
                              // discard document without asking to save
                              pDoc->OnCloseDocument();
                          }
  
                          // say goodbye
                          pMainFrame->PostMessage(WM_CLOSE);
                      }
                  }
              }
          }
      }
  
  Cleanup:
      return (fMoreIdleWork);
  Error:
      TraceError(TEXT("CAMCApp::OnIdle"), sc);
      goto Cleanup;
  
  }
  
  //+-------------------------------------------------------------------
  //
  //  Member:     InitializeMode
  //
  //  Synopsis:   Set the mode and load the menus, accelerator tables.
  //
  //  Arguments:  [eMode] - New application mode.
  //
  //  Returns:    None.
  //
  //--------------------------------------------------------------------
  void CAMCApp::InitializeMode (ProgramMode eMode)
  {
      SetMode(eMode);
      UpdateFrameWindow(false);
  }
  
  //+-------------------------------------------------------------------
  //
  //  Member:     SetMode
  //
  //  Synopsis:   Set the mode.
  //
  //  Note:       Call UpdateFrameWindow some time soon to update
  //              menus/toolbars for this mode.
  //              Cannot do this in this method. This is called
  //              from CAMCDoc::LoadAppMode. The CAMCDoc::LoadFrame
  //              calls the UpdateFrameWindow.
  //
  //  Arguments:  [eMode] - New application mode.
  //
  //  Returns:    None.
  //
  //--------------------------------------------------------------------
  void CAMCApp::SetMode (ProgramMode eMode)
  {
      ASSERT (IsValidProgramMode (eMode));
  
      if (m_fAuthorModeForced)
      {
          ASSERT (m_eMode == eMode_Author);
          ASSERT (GetMainFrame()->IsMenuVisible ());
      }
      else
          m_eMode = eMode;
  }
  
  //+-------------------------------------------------------------------
  //
  //  Member:     UpdateFrameWindow
  //
  //  Synopsis:   Load the menu/accelerator tables and update
  //              them if loaded from console file.
  //
  //  Note:       Call UpdateFrameWindow some time soon after
  //              calling SetMode to update menus/toolbars for this mode.
  //              This is called from CAMCDoc::LoadFrame.
  //
  //  Arguments:  [bUpdate] - BOOL
  //                          We need to update the toolbar/menus only
  //                          if loaded from console file
  //
  //  Returns:    None.
  //
  //--------------------------------------------------------------------
  void CAMCApp::UpdateFrameWindow(bool bUpdate)
  {
      static const struct ModeDisplayParams
      {
          int     nResourceID;
          bool    fShowToolbar;
      } aDisplayParams[eMode_Count] =
      {
          {   IDR_AMCTYPE,            true    },      // eMode_Author
          {   IDR_AMCTYPE_USER,       false   },      // eMode_User
          {   IDR_AMCTYPE_MDI_USER,   false   },      // eMode_User_MDI
          {   IDR_AMCTYPE_SDI_USER,   false   },      // eMode_User_SDI
      };
  
      if (m_fAuthorModeForced)
      {
          ASSERT (m_eMode == eMode_Author);
          ASSERT (GetMainFrame()->IsMenuVisible ());
          return;
      }
  
      m_Menu.DestroyMenu ();
      m_Accel.DestroyAcceleratorTable ();
  
      VERIFY (m_Menu.LoadMenu          (aDisplayParams[m_eMode].nResourceID));
      m_Accel.LoadAccelerators (aDisplayParams[m_eMode].nResourceID);
  
      if (bUpdate)
      {
          CMainFrame *pMainFrame = GetMainFrame();
          ASSERT (pMainFrame != NULL);
          ASSERT_VALID (pMainFrame);
  
          CMDIChildWnd* pwndActive = pMainFrame ? pMainFrame->MDIGetActive () : NULL;
  
          // bypass CMainFrame::OnUpdateFrameMenu so CMainFrame::NotifyMenuChanged
          // doesn't get called twice and remove the new menu entirely
          if (pwndActive != NULL)
              pwndActive->OnUpdateFrameMenu (TRUE, pwndActive, m_Menu);
          else if (pMainFrame)
              pMainFrame->OnUpdateFrameMenu (m_Menu);
  
          if (m_eMode == eMode_User_SDI)
          {
              if (pwndActive != NULL)
                  pwndActive->MDIMaximize ();
  
              if (pMainFrame)
                  AppendToSystemMenu (pMainFrame, eMode_User_SDI);
          }
  
          if (pMainFrame)
              pMainFrame->ShowMenu    (true /*Always show menu*/);
      }
  }
  
  
  
  /*+-------------------------------------------------------------------------*
   * IsInContainer
   *
   *
   *--------------------------------------------------------------------------*/
  
  template<class InputIterator, class T>
  bool Find (InputIterator itFirst, InputIterator itLast, const T& t)
  {
      return (std::find (itFirst, itLast, t) != itLast);
  }
  
  
  /*+-------------------------------------------------------------------------*
   * CAMCApp::HookPreTranslateMessage
   *
   * Adds a window the the list of windows that get prioritized cracks at
   * PreTranslateMessage.  Hooks set later get priority over earlier hooks.
   *--------------------------------------------------------------------------*/
  
  void CAMCApp::HookPreTranslateMessage (CWnd* pwndHook)
  {
      HWND hwndHook = pwndHook->GetSafeHwnd();
      ASSERT (IsWindow (hwndHook));
  
      // this only makes sense for permanent windows
      ASSERT (CWnd::FromHandlePermanent(hwndHook) == pwndHook);
  
      /*
       * Put the hook window at the front of the hook list.  We're preserving
       * the HWND instead of the CWnd* so we don't have unnecessary list<>
       * code generated.  We already use a list<HWND> for m_DelayedUpdateWindows,
       * so using list<HWND> here doesn't cause any more code to be generated.
       */
      if (!Find (m_TranslateMessageHookWindows.begin(),
                 m_TranslateMessageHookWindows.end(),
                 hwndHook))
      {
          m_TranslateMessageHookWindows.push_front (hwndHook);
      }
  }
  
  
  /*+-------------------------------------------------------------------------*
   * CAMCApp::UnhookPreTranslateMessage
   *
   *
   *--------------------------------------------------------------------------*/
  
  void CAMCApp::UnhookPreTranslateMessage (CWnd* pwndUnhook)
  {
      HWND hwndUnhook = pwndUnhook->GetSafeHwnd();
      ASSERT (IsWindow (hwndUnhook));
  
      WindowListIterator itEnd   = m_TranslateMessageHookWindows.end();
      WindowListIterator itFound = std::find (m_TranslateMessageHookWindows.begin(),
                                              itEnd, hwndUnhook);
  
      if (itFound != itEnd)
          m_TranslateMessageHookWindows.erase (itFound);
  }
  
  
  /*+-------------------------------------------------------------------------*
   * CAMCApp::GetIdleTaskQueue
   *
   * Returns the IIdleTaskQueue interface for the application, creating it
   * if necessary.
   *--------------------------------------------------------------------------*/
  
  CIdleTaskQueue * CAMCApp::GetIdleTaskQueue ()
  {
      return &m_IdleTaskQueue;
  }
  
  
  
  /*+-------------------------------------------------------------------------*
   * ScExpandEnvironmentStrings
   *
   * Expands the any environment string (e.g. %SystemRoot%) in the input
   * string, in place.
   *--------------------------------------------------------------------------*/
  
  SC ScExpandEnvironmentStrings (CString& str)
  {
      DECLARE_SC (sc, _T("ScExpandEnvironmentStrings"));
  
      if (str.Find(_T('%')) != -1)
      {
          TCHAR szBuffer[MAX_PATH];
  
          if (!ExpandEnvironmentStrings (str, szBuffer, countof(szBuffer)))
              return (sc.FromLastError());
  
          str = szBuffer;
      }
  
      return (sc);
  }
  
  
  /*+-------------------------------------------------------------------------*
   * ScCreateDumpSnapins
   *
   * Creates a CLSID_MMCDocConfig object, opens it on the supplied filename,
   * and returns a pointer to the IDumpSnapins interface on the object.
   *--------------------------------------------------------------------------*/
  
  SC ScCreateDumpSnapins (
      CString&        strConsoleFile,     /* I/O:console file                 */
      IDumpSnapins**  ppDumpSnapins)      /* O:IDumpSnapins interface         */
  {
      DECLARE_SC (sc, _T("ScCreateDumpSnapins"));
  
      /*
       * validate input
       */
      sc = ScCheckPointers (ppDumpSnapins);
      if (sc)
          return (sc);
  
      *ppDumpSnapins = NULL;
  
      /*
       * create a doc config object
       */
      IDocConfigPtr spDocConfig;
      sc = spDocConfig.CreateInstance (CLSID_MMCDocConfig);
      if (sc)
          return (sc);
  
      /*
       * expand any embedded environment strings in the console filename
       */
      sc = ScExpandEnvironmentStrings (strConsoleFile);
      if (sc)
          return (sc);
  
      /*
       * open the console file
       */
      sc = spDocConfig->OpenFile (::ATL::CComBSTR (strConsoleFile));
      if (sc)
          return (sc);
  
      /*
       * get the IDumpSnapins interface
       */
      sc = spDocConfig.QueryInterface (IID_IDumpSnapins, *ppDumpSnapins);
      if (sc)
          return (sc);
  
      return (sc);
  }
  
  
  /*+-------------------------------------------------------------------------*
   * CAMCApp::DumpConsoleFile
   *
   *
   *--------------------------------------------------------------------------*/
  
  HRESULT CAMCApp::DumpConsoleFile (CString strConsoleFile, CString strDumpFile)
  {
      DECLARE_SC (sc, _T("CAMCApp::DumpConsoleFile"));
      const CString* pstrFileWithError = &strConsoleFile;
  
      /*
       * get an IDumpSnapins interface on this console file
       */
      IDumpSnapinsPtr spDumpSnapins;
      sc = ScCreateDumpSnapins (strConsoleFile, &spDumpSnapins);
      if (sc)
          goto Error;
  
      sc = ScCheckPointers (spDumpSnapins, E_UNEXPECTED);
      if (sc)
          goto Error;
  
      /*
       * expand the dump filename if necessary
       */
      sc = ScExpandEnvironmentStrings (strDumpFile);
      if (sc)
          goto Error;
  
      /*
       * If there's no directory specifier on the dump file, put a "current
       * directory" marker on it.  We do this to prevent WritePrivateProfile*
       * from putting the file in the Windows directory.
       */
      if (strDumpFile.FindOneOf(_T(":\\")) == -1)
          strDumpFile = _T(".\\") + strDumpFile;
  
      /*
       * future file-related errors will pertain to the dump file
       * (see Error handler)
       */
      pstrFileWithError = &strDumpFile;
  
      /*
       * wipe out the existing file, if any
       */
      if ((GetFileAttributes (strDumpFile) != 0xFFFFFFFF) && !DeleteFile (strDumpFile))
      {
          sc.FromLastError();
          goto Error;
      }
  
      /*
       * dump the contents of the console file
       */
      sc = spDumpSnapins->Dump (strDumpFile);
      if (sc)
          goto Error;
  
      return (sc.ToHr());
  
  Error:
      MMCErrorBox (*pstrFileWithError, sc);
      return (sc.ToHr());
  }
  
  /***************************************************************************\
   *
   * METHOD:  CAMCApp::ScOnNewDocument
   *
   * PURPOSE: Emits script event for application object
   *
   * PARAMETERS:
   *    CAMCDoc *pDocument      [in] document being created/opened
   *    BOOL bLoadedFromConsole [in] if document is loaded from file
   *
   * RETURNS:
   *    SC    - result code
   *
  \***************************************************************************/
  SC CAMCApp::ScOnNewDocument(CAMCDoc *pDocument, BOOL bLoadedFromConsole)
  {
      DECLARE_SC(sc, TEXT("CAMCApp::ScOnNewDocument"));
  
      // check if there are "listeners"
      sc = ScHasSinks(m_sp_Application, AppEvents);
      if (sc)
          return sc;
  
      if (sc == SC(S_FALSE)) // no sinks;
          return sc;
  
      // construct document com object
      DocumentPtr spComDoc;
      sc = pDocument->ScGetMMCDocument(&spComDoc);
      if (sc)
          return sc;
  
      // check pointer
      sc = ScCheckPointers(spComDoc, E_POINTER);
      if (sc)
          return sc;
  
      // fire the event
      sc = ScFireComEvent(m_sp_Application, AppEvents , OnDocumentOpen (spComDoc , bLoadedFromConsole == FALSE));
      if (sc)
          sc.TraceAndClear(); // failure to issue the com event is not critical to this action
  
      return sc;
  }
  
  /***************************************************************************\
   *
   * METHOD:  CAMCApp::ScOnQuitApp
   *
   * PURPOSE: Emits script event for application object
   *
   * PARAMETERS:
   *
   * RETURNS:
   *    SC    - result code
   *
  \***************************************************************************/
  SC CAMCApp::ScOnQuitApp()
  {
      DECLARE_SC(sc, TEXT("CAMCApp::ScOnQuitApp"));
  
      // fire the event
      sc = ScFireComEvent(m_sp_Application, AppEvents , OnQuit (m_sp_Application));
      if (sc)
          sc.TraceAndClear(); // failure to issue the com event is not critical to this action
  
      return sc;
  }
  
  /***************************************************************************\
   *
   * METHOD:  CAMCApp::ScOnCloseView
   *
   * PURPOSE: Script event firing helper. Invoked when the view is closed
   *
   * PARAMETERS:
   *    CAMCView *pView [in] - view being closed
   *
   * RETURNS:
   *    SC    - result code
   *
  \***************************************************************************/
  SC CAMCApp::ScOnCloseView( CAMCView *pView )
  {
      DECLARE_SC(sc, TEXT("CAMCApp::ScOnCloseView"));
  
      // parameter check
      sc = ScCheckPointers(pView);
      if (sc)
          return sc;
  
      // check if we have sinks connected
      sc = ScHasSinks(m_sp_Application, AppEvents);
      if (sc)
          return sc;
  
      if (sc == SC(S_FALSE)) // no sinks
          return sc;
  
      // construct view com object
      ViewPtr spView;
      sc = pView->ScGetMMCView(&spView);
      if (sc)
          return sc;
  
      // fire the event
      sc = ScFireComEvent(m_sp_Application, AppEvents , OnViewClose (spView));
      if (sc)
          sc.TraceAndClear(); // failure to issue the com event is not critical to this action
  
      return sc;
  }
  
  /***************************************************************************\
   *
   * METHOD:  CAMCApp::ScOnViewChange
   *
   * PURPOSE: Script event firing helper. Invoked when scope selection change
   *
   * PARAMETERS:
   *    CAMCView *pView [in] affected view
   *    HNODE hNode     [in] new selected scope node
   *
   * RETURNS:
   *    SC    - result code
   *
  \***************************************************************************/
  SC CAMCApp::ScOnViewChange( CAMCView *pView, HNODE hNode )
  {
      DECLARE_SC(sc, TEXT("CAMCApp::ScOnViewChange"));
  
      // parameter check
      sc = ScCheckPointers(pView);
      if (sc)
          return sc;
  
      // check if we have sinks connected
      sc = ScHasSinks(m_sp_Application, AppEvents);
      if (sc)
          return sc;
  
      if (sc == SC(S_FALSE)) // no sinks
          return sc;
  
      // construct view com object
      ViewPtr spView;
      sc = pView->ScGetMMCView(&spView);
      if (sc)
          return sc;
  
      // construct Node com object
      NodePtr spNode;
      sc = pView->ScGetScopeNode( hNode, &spNode );
      if (sc)
          return sc;
  
      // fire script event
      sc = ScFireComEvent(m_sp_Application, AppEvents , OnViewChange(spView, spNode));
      if (sc)
          sc.TraceAndClear(); // failure to issue the com event is not critical to this action
  
      return sc;
  }
  
  /***************************************************************************\
   *
   * METHOD:  CAMCApp::ScOnResultSelectionChange
   *
   * PURPOSE: Script event firing helper. Invoked when result selection change
   *
   * PARAMETERS:
   *    CAMCView *pView [in] - affected view
   *
   * RETURNS:
   *    SC    - result code
   *
  \***************************************************************************/
  SC CAMCApp::ScOnResultSelectionChange( CAMCView *pView )
  {
      DECLARE_SC(sc, TEXT("CAMCApp::ScOnResultSelectionChange"));
  
      // parameter check
      sc = ScCheckPointers(pView);
      if (sc)
          return sc;
  
      // check if we have sinks connected
      sc = ScHasSinks(m_sp_Application, AppEvents);
      if (sc)
          return sc;
  
      if (sc == SC(S_FALSE)) // no sinks
          return sc;
  
      // construct view com object
      ViewPtr spView;
      sc = pView->ScGetMMCView(&spView);
      if (sc)
          return sc;
  
      // construct Node com object
      NodesPtr spNodes;
      sc = pView->Scget_Selection( &spNodes );
      if (sc)
          return sc;
  
      // fire script event
      sc = ScFireComEvent(m_sp_Application, AppEvents , OnSelectionChange(spView, spNodes));
      if (sc)
          sc.TraceAndClear(); // failure to issue the com event is not critical to this action
  
      return sc;
  }
  
  
  /***************************************************************************\
   *
   * METHOD:  CMMCApplication::ScOnContextMenuExecuted
   *
   * PURPOSE: called when the context menu is executed to fire the event to script
   *
   * PARAMETERS:
   *    PMENUITEM pMenuItem - menu item (note: it may be NULL if menu item is gone)
   *
   * RETURNS:
   *    SC    - result code
   *
  \***************************************************************************/
  SC CAMCApp::ScOnContextMenuExecuted( PMENUITEM pMenuItem )
  {
  	MMC_COM_MANAGE_STATE();
      DECLARE_SC(sc, TEXT("CAMCApp::ScOnContextMenuExecuted"));
  
      // see if we have sinks connected
      sc = ScHasSinks(m_sp_Application, AppEvents);
      if (sc)
          return sc;
  
      if (sc == SC(S_FALSE)) // no sinks
          return sc;
  
      // fire the event
      sc = ScFireComEvent(m_sp_Application, AppEvents, OnContextMenuExecuted( pMenuItem ) );
      if (sc)
          sc.TraceAndClear(); // failure to issue the com event is not critical to this action
  
      return sc;
  }
  
  
  /*+-------------------------------------------------------------------------*
   *
   * CAMCApp::ScOnListViewItemUpdated
   *
   * PURPOSE:
   *
   * PARAMETERS:
   *    CAMCView * pView :
   *    int        nIndex :
   *
   * RETURNS:
   *    SC
   *
   *+-------------------------------------------------------------------------*/
  SC
  CAMCApp::ScOnListViewItemUpdated(CAMCView *pView , int nIndex)
  {
      DECLARE_SC(sc, TEXT("CAMCApp::ScOnListViewItemUpdated"));
  
      // parameter check
      sc = ScCheckPointers(pView);
      if (sc)
          return sc;
  
      // check if we have sinks connected
      sc = ScHasSinks(m_sp_Application, AppEvents);
      if (sc)
          return sc;
  
      if (sc == SC(S_FALSE)) // no sinks
          return sc;
  
      // construct view com object
      ViewPtr spView;
      sc = pView->ScGetMMCView(&spView);
      if (sc)
          return sc;
  
      // fire script event
      sc = ScFireComEvent(m_sp_Application, AppEvents , OnListUpdated(spView));
      if (sc)
          sc.TraceAndClear(); // failure to issue the com event is not critical to this action
  
      return sc;
  }
  
  
  /***************************************************************************\
   *
   * METHOD:  CAMCApp::ScOnSnapinAdded
   *
   * PURPOSE: Script event firing helper. Implements interface accessible from
   *          node manager
   *
   * PARAMETERS:
   *    PSNAPIN pSnapIn [in] - snapin added to the console
   *
   * RETURNS:
   *    SC    - result code
   *
  \***************************************************************************/
  SC CAMCApp::ScOnSnapinAdded(CAMCDoc *pAMCDoc, PSNAPIN pSnapIn)
  {
      DECLARE_SC(sc, TEXT("CAMCApp::ScOnSnapinAdded"));
  
      // parameter check
      sc = ScCheckPointers(pAMCDoc, pSnapIn);
      if (sc)
          return sc;
  
      // see if we have sinks connected
      sc = ScHasSinks(m_sp_Application, AppEvents);
      if (sc)
          return sc;
  
      if (sc == SC(S_FALSE)) // no sinks
          return sc;
  
      DocumentPtr spDocument;
      sc = pAMCDoc->ScGetMMCDocument(&spDocument);
      if (sc)
          return sc;
  
      // check
      sc = ScCheckPointers(spDocument, E_UNEXPECTED);
      if (sc)
          return sc;
  
      // fire the event
      sc = ScFireComEvent(m_sp_Application, AppEvents , OnSnapInAdded (spDocument, pSnapIn));
      if (sc)
          sc.TraceAndClear(); // failure to issue the com event is not critical to this action
  
      return sc;
  }
  
  /***************************************************************************\
   *
   * METHOD:  CAMCApp::ScOnSnapinRemoved
   *
   * PURPOSE: Script event firing helper. Implements interface accessible from
   *          node manager
   *
   * PARAMETERS:
   *    PSNAPIN pSnapIn [in] - snapin removed from console
   *
   * RETURNS:
   *    SC    - result code
   *
  \***************************************************************************/
  SC CAMCApp::ScOnSnapinRemoved(CAMCDoc *pAMCDoc, PSNAPIN pSnapIn)
  {
      DECLARE_SC(sc, TEXT("CAMCApp::ScOnSnapinRemoved"));
  
      // parameter check
      sc = ScCheckPointers(pAMCDoc, pSnapIn);
      if (sc)
          return sc;
  
      // see if we have sinks connected
      sc = ScHasSinks(m_sp_Application, AppEvents);
      if (sc)
          return sc;
  
      if (sc == SC(S_FALSE)) // no sinks
          return sc;
  
      DocumentPtr spDocument;
      sc = pAMCDoc->ScGetMMCDocument(&spDocument);
      if (sc)
          return sc;
  
      // check
      sc = ScCheckPointers(spDocument, E_UNEXPECTED);
      if (sc)
          return sc;
  
      // fire the event
      sc = ScFireComEvent(m_sp_Application, AppEvents , OnSnapInRemoved (spDocument, pSnapIn));
      if (sc)
          sc.TraceAndClear(); // failure to issue the com event is not critical to this action
  
      return sc;
  }
  
  /***************************************************************************\
   *
   * METHOD:  CAMCApp::ScOnNewView
   *
   * PURPOSE: Script event firing helper
   *
   * PARAMETERS:
   *    CAMCView *pView [in] - created view
   *
   * RETURNS:
   *    SC    - result code
   *
  \***************************************************************************/
  SC CAMCApp::ScOnNewView(CAMCView *pView)
  {
      DECLARE_SC(sc, TEXT("CAMCApp::ScOnNewView"));
  
      // parameter check
      sc = ScCheckPointers(pView);
      if (sc)
          return sc;
  
      // see if we have sinks connected
      sc = ScHasSinks(m_sp_Application, AppEvents);
      if (sc)
          return sc;
  
      if (sc == SC(S_FALSE)) // no sinks
          return sc;
  
      // construct View com object
      ViewPtr spView;
      sc = pView->ScGetMMCView(&spView);
      if (sc)
          return sc;
  
      // fire the event
      sc = ScFireComEvent(m_sp_Application, AppEvents , OnNewView(spView));
      if (sc)
          sc.TraceAndClear(); // failure to issue the com event is not critical to this action
  
      return sc;
  }
  
  /***************************************************************************\
   *
   * METHOD:  CAMCApp::OnCloseDocument
   *
   * PURPOSE: Helper for invoking com event
   *
   * PARAMETERS:
   *    CAMCDoc *pAMCDoc [in] - document being closed
   *
   * RETURNS:
   *    SC    - result code
   *
  \***************************************************************************/
  SC CAMCApp::ScOnCloseDocument(CAMCDoc *pAMCDoc)
  {
      DECLARE_SC(sc, TEXT("CAMCApp::OnCloseDocument"));
  
      // parameter check
      sc = ScCheckPointers(pAMCDoc);
      if (sc)
          return sc;
  
      // see if we have sinks connected
      sc = ScHasSinks(m_sp_Application, AppEvents);
      if (sc)
          return sc;
  
      if (sc == SC(S_FALSE)) // no sinks
          return sc;
  
      DocumentPtr spDocument;
      sc = pAMCDoc->ScGetMMCDocument(&spDocument);
      if (sc)
          return sc;
  
      // check
      sc = ScCheckPointers(spDocument, E_UNEXPECTED);
      if (sc)
          return sc;
  
      // fire the event
      sc = ScFireComEvent(m_sp_Application, AppEvents , OnDocumentClose (spDocument));
      if (sc)
          sc.TraceAndClear(); // failure to issue the com event is not critical to this action
  
      return sc;
  }
  
  /***************************************************************************\
   *
   * METHOD:  CAMCApp::ScOnToolbarButtonClicked
   *
   * PURPOSE: Observed toolbar event - used to fire com event
   *
   * PARAMETERS:
   *    CAMCView *pAMCView - [in] view which toobar is executed
   *
   * RETURNS:
   *    SC    - result code
   *
  \***************************************************************************/
  SC CAMCApp::ScOnToolbarButtonClicked( )
  {
      DECLARE_SC(sc, TEXT("CAMCApp::ScOnToolbarButtonClicked"));
  
      // see if we have sinks connected
      sc = ScHasSinks(m_sp_Application, AppEvents);
      if (sc)
          return sc;
  
      if (sc == SC(S_FALSE)) // no sinks
          return sc;
  
      // fire the event
      sc = ScFireComEvent(m_sp_Application, AppEvents , OnToolbarButtonClicked ( ));
      if (sc)
          sc.TraceAndClear(); // ignore the error - should not affect main behavior
  
      return sc;
  }
  
  /***************************************************************************\
   *
   * METHOD:  CAMCApp::SetUnderUserControl
   *
   * PURPOSE: puts application into user control/script control mode
   *          implements Application.UserControl property
   *
   * PARAMETERS:
   *    bool bUserControl [in] true == set user control
   *
   * RETURNS:
   *    void
   *
  \***************************************************************************/
  void CAMCApp::SetUnderUserControl(bool bUserControl /* = true */ )
  {
      m_fUnderUserControl = bUserControl;
  
      AfxOleSetUserCtrl(bUserControl); // allow MFC to know that
      if (bUserControl)
      {
          // make sure application is visible if it's under user control
  
          CMainFrame *pMainFrame = GetMainFrame();
          if(pMainFrame && !pMainFrame->IsWindowVisible())
          {
              pMainFrame->ShowWindow(SW_SHOW);
          }
      }
  }
  
  
  #ifdef MMC_WIN64
  
  /*+-------------------------------------------------------------------------*
   * CompareBasicSnapinInfo
   *
   * Implements a less-than comparison for CBasicSnapinInfo, based solely on
   * the CLSID.  Returns true if the CLSID for bsi1 is less than the CLSID
   * for bsi2, false otherwise.
   *--------------------------------------------------------------------------*/
  
  bool CompareBasicSnapinInfo (const CBasicSnapinInfo& bsi1, const CBasicSnapinInfo& bsi2)
  {
      return (bsi1.m_clsid < bsi2.m_clsid);
  }
  
  
  /*+-------------------------------------------------------------------------*
   * ScDetermineArchitecture
   *
   * Determines whether MMC64 (which is currently executing) should chain
   * to MMC32.  This will occur in one of three situations:
   *
   * 1.  The "-32" command line parameter was specified.
   *
   * 2.  A console file was specified on the command line, and it contains
   *     one or more snap-ins that were not registered in the 64-bit HKCR
   *     hive, but all snap-ins are registered in the 32-bit HKCR hive.
   *
   * 3.  A console file was specified on the command line, and it contained
   *     one or more snap-ins that were not registered in the 64-bit HKCR
   *     hive, and one or more snap-ins that are not registered in the 32-bit
   *     HKCR hive.  In this case we'll do one of three things:
   *
   *     a.   If the set of unavailable 64-bit snap-ins is a subset of the
   *          set of unavailable 32-bit snap-ins, the 64-bit console will be
   *          more functional than the 32-bit console, so we'll run MMC64.
   *
   *     b.   If the set of unavailable 32-bit snap-ins is a subset of the
   *          set of unavailable 64-bit snap-ins, the 32-bit console will be
   *          more functional than the 64-bit console, so we'll run MMC32.
   *
   *     c.   If neither a. or b. is true, we'll display UI asking which
   *          version of MMC to run.
   *
   * Returns:
   * eArch == eArch_64bit - 64-bit MMC is needed (or an error occurred)
   * eArch == eArch_32bit - 32-bit MMC is needed
   * eArch == eArch_None  - user cancelled the operation
   *--------------------------------------------------------------------------*/
  
  SC ScDetermineArchitecture (const CMMCCommandLineInfo& rCmdInfo, eArchitecture& eArch)
  {
      DECLARE_SC (sc, _T("ScDetermineArchitecture"));
  
      /*
       * default to 64-bit
       */
      eArch = eArch_64bit;
  
      /*
       * Case 0:  Was "-64" specified on the command line?  64-bit needed
       */
      if (rCmdInfo.m_eArch == eArch_64bit)
      {
          Trace (tag32BitTransfer, _T("\"-64\" parameter specified, 64-bit MMC needed"));
          return (sc);
      }
  
      /*
       * Case 1:  Was "-32" specified on the command line?  32-bit needed
       */
      if (rCmdInfo.m_eArch == eArch_32bit)
      {
          Trace (tag32BitTransfer, _T("\"-32\" parameter specified, 32-bit MMC needed"));
          eArch = eArch_32bit;
          return (sc);
      }
  
      /*
       * No file on the command line?  64-bit needed
       */
      if (rCmdInfo.m_nShellCommand != CCommandLineInfo::FileOpen)
      {
          Trace (tag32BitTransfer, _T("No console file specified, 64-bit MMC needed"));
          return (sc);
      }
  
      /*
       * Cases 2 and 3:  Analyze the specified console file
       */
      Trace (tag32BitTransfer, _T("Analyzing snap-ins in \"%s\""), (LPCTSTR) rCmdInfo.m_strFileName);
  
      /*
       * get an IDumpSnapins interface so we can analyze the console file
       */
      IDumpSnapinsPtr spDumpSnapins;
      CString strConsoleFile = rCmdInfo.m_strFileName;
      sc = ScCreateDumpSnapins (strConsoleFile, &spDumpSnapins);
      if (sc)
          return (sc);
  
      sc = ScCheckPointers (spDumpSnapins, E_UNEXPECTED);
      if (sc)
          return (sc);
  
      /*
       * analyze the 64-bit snap-ins in this console
       */
      CAvailableSnapinInfo asi64(false);
      sc = spDumpSnapins->CheckSnapinAvailability (asi64);
      if (sc)
          return (sc);
  
      /*
       * if no snap-ins are unavailable in 64-bit form, no need for MMC32
       */
      if (asi64.m_vAvailableSnapins.size() == asi64.m_cTotalSnapins)
      {
          Trace (tag32BitTransfer, _T("All snapins are available in 64-bit form, 64-bit MMC needed"));
          return (sc);
      }
  
      /*
       * analyze the 32-bit snap-ins in this console
       */
      CAvailableSnapinInfo asi32(true);
      sc = spDumpSnapins->CheckSnapinAvailability (asi32);
      if (sc)
          return (sc);
  
      /*
       * Case 2:  If no snap-ins are unavailable in 32-bit form, 32-bit needed
       */
      if (asi32.m_vAvailableSnapins.size() == asi32.m_cTotalSnapins)
      {
          Trace (tag32BitTransfer, _T("All snapins are available in 32-bit form, 32-bit MMC needed"));
          eArch = eArch_32bit;
          return (sc);
      }
  
      /*
       * std::includes depends on its ranges being sorted, so make sure
       * that's the case
       */
      std::sort (asi32.m_vAvailableSnapins.begin(), asi32.m_vAvailableSnapins.end(), CompareBasicSnapinInfo);
      std::sort (asi64.m_vAvailableSnapins.begin(), asi64.m_vAvailableSnapins.end(), CompareBasicSnapinInfo);
  
      /*
       * Case 3a:  If the set of available 64-bit snap-ins is a
       * superset of the set of available 32-bit snap-ins, run MMC64
       */
      if (std::includes (asi64.m_vAvailableSnapins.begin(), asi64.m_vAvailableSnapins.end(),
                         asi32.m_vAvailableSnapins.begin(), asi32.m_vAvailableSnapins.end(),
                         CompareBasicSnapinInfo))
      {
          Trace (tag32BitTransfer, _T("The set of available 64-bit snapins is a superset of..."));
          Trace (tag32BitTransfer, _T("...the set of available 32-bit snapins, 64-bit MMC needed"));
          return (sc);
      }
  
      /*
       * Case 3b:  If the set of available 32-bit snap-ins is a
       * superset of the set of available 64-bit snap-ins, run MMC32
       */
      if (std::includes (asi32.m_vAvailableSnapins.begin(), asi32.m_vAvailableSnapins.end(),
                         asi64.m_vAvailableSnapins.begin(), asi64.m_vAvailableSnapins.end(),
                         CompareBasicSnapinInfo))
      {
          Trace (tag32BitTransfer, _T("The set of available 32-bit snapins is a superset of..."));
          Trace (tag32BitTransfer, _T("...the set of available 64-bit snapins, 32-bit MMC needed"));
          eArch = eArch_32bit;
          return (sc);
      }
  
      /*
       * Case 3c:  Ask the user which to run
       */
      CArchitecturePicker dlg (rCmdInfo.m_strFileName, asi64, asi32);
  
      if (dlg.DoModal() == IDOK)
      {
          eArch = dlg.GetArchitecture();
          Trace (tag32BitTransfer, _T("User chose %d-bit, %d-bit MMC needed"), (eArch == eArch_32bit) ? 32 : 64, (eArch == eArch_32bit) ? 32 : 64);
      }
      else
      {
          Trace (tag32BitTransfer, _T("User chose to exit, terminating"));
          eArch = eArch_None;
      }
  
      return (sc);
  }
  
  #endif // MMC_WIN64
  
  
  #ifdef UNICODE
  
  /*+-------------------------------------------------------------------------*
   * ScLaunchMMC
   *
   * Launches a specific architecture of MMC (i.e. MMC32 from MMC64 or vice
   * versa) with the same command line used to launch this process.
   *
   * Returns S_OK if the given architecture of MMC was launched successfully,
   * or an error code if an error occurred.
   *--------------------------------------------------------------------------*/
  
  SC ScLaunchMMC (
  	eArchitecture	eArch,				/* I:desired architecture           */
  	int				nCmdShow)			/* I:show state                     */
  {
      DECLARE_SC (sc, _T("ScLaunchMMC"));
  
  	CString strArgs;
  	int nFolder;
  
  	switch (eArch)
  	{
  		case eArch_64bit:
  			nFolder = CSIDL_SYSTEM;
  			break;
  
  		case eArch_32bit:
  			/*
  			 * make sure we give MMC32 a "-32" argument so it won't defer
  			 * to MMC64 again (see CAMCApp::InitInstance)
  			 */
  			strArgs = _T("-32 ");
  			nFolder = CSIDL_SYSTEMX86;
              break;
  
  		default:
  			return (sc = E_INVALIDARG);
  			break;
  	}
  
      /*
       * Get the directory where MMC32 lives (%SystemRoot%\syswow64) and
       * append the executable name
       */
      CString strProgram, strPath;
      sc = SHGetFolderPath (NULL, nFolder, NULL, 0, strProgram.GetBuffer(MAX_PATH));
      if (sc)
          return (sc);
  
      strProgram.ReleaseBuffer();
      strPath = strProgram;
      strProgram += _T("\\mmc.exe");
  
  	/*
  	 * disable file system redirection so MMC32 will be able to launch MMC64
  	 */
  	CWow64FilesystemRedirectionDisabler disabler (strProgram);
  
      /*
       * get the arguments for the original invocation of MMC, skipping
       * argv[0] (the executable name) and any "-32" or "-64" parameters
       */
      int argc;
      CAutoGlobalPtr<LPWSTR> argv (CommandLineToArgvW (GetCommandLine(), &argc));
      if (argv == NULL)
          return (sc.FromLastError());
  
      for (int i = 1; i < argc; i++)
      {
          CString strArg = argv[i];
  
          if ((strArg != _T("-32")) && (strArg != _T("/32")) &&
              (strArg != _T("-64")) && (strArg != _T("/64")))
          {
              strArgs += _T("\"") + strArg + _T("\" ");
          }
      }
  
  	/*
  	 * start the requested architecture of MMC
  	 */
  	Trace (tag32BitTransfer, _T("Attempting to run: %s %s"), (LPCTSTR) strProgram, (LPCTSTR) strArgs);
  
      SHELLEXECUTEINFO sei = {0};
      sei.cbSize       = sizeof (sei);
      sei.fMask        = SEE_MASK_FLAG_NO_UI;
      sei.lpFile       = strProgram;
      sei.lpDirectory  = strPath;
      sei.lpParameters = strArgs;
      sei.nShow        = nCmdShow;
  
      if (!ShellExecuteEx (&sei))
          return (sc.FromLastError());
  
      return (sc);
  }
  
  #endif  // UNICODE
  
  
  #ifndef MMC_WIN64
  
  /*+-------------------------------------------------------------------------*
   * IsWin64
   *
   * Returns true if we're running on Win64, false otherwise.
   *--------------------------------------------------------------------------*/
  
  bool IsWin64()
  {
  #ifdef UNICODE
      /*
       * get a pointer to kernel32!GetSystemWow64Directory
       */
      HMODULE hmod = GetModuleHandle (_T("kernel32.dll"));
      if (hmod == NULL)
          return (false);
  
      UINT (WINAPI* pfnGetSystemWow64Directory)(LPTSTR, UINT);
      (FARPROC&)pfnGetSystemWow64Directory = GetProcAddress (hmod, "GetSystemWow64DirectoryW");
  
      if (pfnGetSystemWow64Directory == NULL)
          return (false);
  
      /*
       * if GetSystemWow64Directory fails and sets the last error to
       * ERROR_CALL_NOT_IMPLEMENTED, we're on a 32-bit OS
       */
      TCHAR szWow64Dir[MAX_PATH];
      if (((pfnGetSystemWow64Directory)(szWow64Dir, countof(szWow64Dir)) == 0) &&
          (GetLastError() == ERROR_CALL_NOT_IMPLEMENTED))
      {
          return (false);
      }
  
      /*
       * if we get here, we're on Win64
       */
      return (true);
  #else
      /*
       * non-Unicode platforms cannot be Win64
       */
      return (false);
  #endif  // UNICODE
  }
  
  #endif // !MMC_WIN64
  