1375 lines
37 KiB
C++
1375 lines
37 KiB
C++
/////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Copyright (c) 1996-2000 Microsoft Corporation
|
|
//
|
|
// Module Name:
|
|
// ExtDll.cpp
|
|
//
|
|
// Abstract:
|
|
// Implementation of the extension DLL classes.
|
|
//
|
|
// Author:
|
|
// David Potter (davidp) May 31, 1996
|
|
//
|
|
// Revision History:
|
|
//
|
|
// Notes:
|
|
//
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
|
|
#include "stdafx.h"
|
|
#include <CluAdmEx.h>
|
|
#include "CluAdmID.h"
|
|
#include "ExtDll.h"
|
|
#include "CluAdmin.h"
|
|
#include "ExtMenu.h"
|
|
#include "TraceTag.h"
|
|
#include "ExcOper.h"
|
|
#include "ClusItem.h"
|
|
#include "BaseSht.h"
|
|
#include "BasePSht.h"
|
|
#include "BaseWiz.h"
|
|
|
|
#ifdef _DEBUG
|
|
#define new DEBUG_NEW
|
|
#undef THIS_FILE
|
|
static char THIS_FILE[] = __FILE__;
|
|
#endif
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// Global Variables
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
|
|
#ifdef _DEBUG
|
|
CTraceTag g_tagExtDll(_T("UI"), _T("EXTENSION DLL"), 0);
|
|
CTraceTag g_tagExtDllRef(_T("UI"), _T("EXTENSION DLL References"), 0);
|
|
#endif
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// CExtensions
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
|
|
IMPLEMENT_DYNAMIC(CExtensions, CObject);
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
//++
|
|
//
|
|
// CExtensions::CExtensions
|
|
//
|
|
// Routine Description:
|
|
// Default constructor.
|
|
//
|
|
// Arguments:
|
|
// None.
|
|
//
|
|
// Return Value:
|
|
// None.
|
|
//
|
|
//--
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
CExtensions::CExtensions(void)
|
|
{
|
|
m_pci = NULL;
|
|
m_hfont = NULL;
|
|
m_hicon = NULL;
|
|
|
|
m_pdoData = NULL;
|
|
m_plextdll = NULL;
|
|
m_psht = NULL;
|
|
m_pmenu = NULL;
|
|
m_plMenuItems = NULL;
|
|
|
|
m_nFirstCommandID = (ULONG) -1;
|
|
m_nNextCommandID = (ULONG) -1;
|
|
m_nFirstMenuID = (ULONG) -1;
|
|
m_nNextMenuID = (ULONG) -1;
|
|
|
|
} //*** CExtensions::CExtensions()
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
//++
|
|
//
|
|
// CExtensions::~CExtensions
|
|
//
|
|
// Routine Description:
|
|
// Destructor.
|
|
//
|
|
// Arguments:
|
|
// None.
|
|
//
|
|
// Return Value:
|
|
// None.
|
|
//
|
|
//--
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
CExtensions::~CExtensions(void)
|
|
{
|
|
UnloadExtensions();
|
|
|
|
} //*** CExtensions::~CExtensions()
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
//++
|
|
//
|
|
// CExtensions::Init
|
|
//
|
|
// Routine Description:
|
|
// Common initialize for all interfaces.
|
|
//
|
|
// Arguments:
|
|
// rlstrExtensions [IN] List of extension CLSID strings.
|
|
// pci [IN OUT] Cluster item to be administered.
|
|
// hfont [IN] Font for dialog text.
|
|
// hicon [IN] Icon for upper left corner.
|
|
//
|
|
// Return Value:
|
|
// None.
|
|
//
|
|
// Exceptions Thrown:
|
|
// Any exceptions thrown by new.
|
|
//
|
|
//--
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
void CExtensions::Init(
|
|
IN const CStringList & rlstrExtensions,
|
|
IN OUT CClusterItem * pci,
|
|
IN HFONT hfont,
|
|
IN HICON hicon
|
|
)
|
|
{
|
|
CWaitCursor wc;
|
|
|
|
ASSERT( rlstrExtensions.GetCount() > 0 );
|
|
|
|
UnloadExtensions();
|
|
|
|
// Save parameters.
|
|
m_plstrExtensions = &rlstrExtensions;
|
|
m_pci = pci;
|
|
m_hfont = hfont;
|
|
m_hicon = hicon;
|
|
|
|
// Allocate a new Data Object.
|
|
m_pdoData = new CComObject< CDataObject >;
|
|
if ( m_pdoData == NULL )
|
|
{
|
|
AfxThrowMemoryException();
|
|
} // if: error allocating memory
|
|
m_pdoData->AddRef();
|
|
|
|
// Construct the Data Object.
|
|
Pdo()->Init( pci, GetClusterAdminApp()->Lcid(), hfont, hicon );
|
|
|
|
// Allocate the extension list.
|
|
m_plextdll = new CExtDllList;
|
|
if ( m_plextdll == NULL )
|
|
{
|
|
AfxThrowMemoryException();
|
|
} // if: error allocating memory
|
|
ASSERT( Plextdll() != NULL );
|
|
|
|
// Loop through the extensions and load each one.
|
|
{
|
|
CComObject<CExtensionDll> * pextdll = NULL;
|
|
POSITION posName;
|
|
|
|
Trace( g_tagExtDll, _T("CExtensions::Init() - %d extensions"), rlstrExtensions.GetCount() );
|
|
posName = rlstrExtensions.GetHeadPosition();
|
|
while ( posName != NULL )
|
|
{
|
|
// Allocate an extension DLL object and add it to the list.
|
|
pextdll = new CComObject< CExtensionDll >;
|
|
if ( pextdll == NULL )
|
|
{
|
|
AfxThrowMemoryException();
|
|
} // if: error allocating memory
|
|
pextdll->AddRef();
|
|
Plextdll()->AddTail( pextdll );
|
|
try
|
|
{
|
|
pextdll->Init( rlstrExtensions.GetNext( posName ), this );
|
|
} // try
|
|
catch ( CException * pe )
|
|
{
|
|
POSITION pos;
|
|
|
|
pe->ReportError();
|
|
pe->Delete();
|
|
|
|
pos = Plextdll()->Find(pextdll);
|
|
ASSERT( pos != NULL );
|
|
Plextdll()->RemoveAt( pos );
|
|
delete pextdll;
|
|
} // catch: anything
|
|
} // while: more items in the list
|
|
} // Loop through the extensions and load each one
|
|
|
|
} //*** CExtensions::Init()
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
//++
|
|
//
|
|
// CExtensions::UnloadExtensions
|
|
//
|
|
// Routine Description:
|
|
// Unload the extension DLL.
|
|
//
|
|
// Arguments:
|
|
// None.
|
|
//
|
|
// Return Value:
|
|
// None.
|
|
//
|
|
//--
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
void CExtensions::UnloadExtensions(void)
|
|
{
|
|
// Delete all the extension DLL objects.
|
|
if (Plextdll() != NULL)
|
|
{
|
|
POSITION pos;
|
|
CComObject<CExtensionDll> * pextdll;
|
|
|
|
pos = Plextdll()->GetHeadPosition();
|
|
while (pos != NULL)
|
|
{
|
|
pextdll = Plextdll()->GetNext(pos);
|
|
pextdll->AddRef(); // See comment below.
|
|
pextdll->UnloadExtension();
|
|
if (pextdll->m_dwRef != 2)
|
|
{
|
|
Trace(g_tagError, _T("CExtensions::UnloadExtensions() - Extension DLL has ref count = %d"), pextdll->m_dwRef);
|
|
}
|
|
|
|
// We added a reference above. Combined with the reference that
|
|
// was added when the object was created, we typically will need
|
|
// to release two references. However, due to bogus code
|
|
// generated by earlier versions of the custom AppWizard where the
|
|
// extension was releasing the interface but not zeroing out its
|
|
// pointer in the error case, we may not need to release the
|
|
// second reference.
|
|
if (pextdll->Release() != 0)
|
|
{
|
|
pextdll->Release();
|
|
} // if: more references to release
|
|
} // while: more items in the list
|
|
delete m_plextdll;
|
|
m_plextdll = NULL;
|
|
} // if: there is a list of extensions
|
|
|
|
if (m_pdoData != NULL)
|
|
{
|
|
if (m_pdoData->m_dwRef != 1)
|
|
{
|
|
Trace(g_tagError, _T("CExtensions::UnloadExtensions() - Data Object has ref count = %d"), m_pdoData->m_dwRef);
|
|
}
|
|
m_pdoData->Release();
|
|
m_pdoData = NULL;
|
|
} // if: data object allocated
|
|
|
|
m_pci = NULL;
|
|
m_hfont = NULL;
|
|
m_hicon = NULL;
|
|
|
|
// Delete all menu items.
|
|
if (PlMenuItems() != NULL)
|
|
{
|
|
POSITION pos;
|
|
CExtMenuItem * pemi;
|
|
|
|
pos = PlMenuItems()->GetHeadPosition();
|
|
while (pos != NULL)
|
|
{
|
|
pemi = PlMenuItems()->GetNext(pos);
|
|
delete pemi;
|
|
} // while: more items in the list
|
|
delete m_plMenuItems;
|
|
m_plMenuItems = NULL;
|
|
} // if: there is a list of menu items
|
|
|
|
} //*** CExtensions::UnloadExtensions()
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
//++
|
|
//
|
|
// CExtensions::CreatePropertySheetPages
|
|
//
|
|
// Routine Description:
|
|
// Add pages to a property sheet.
|
|
//
|
|
// Arguments:
|
|
// psht [IN OUT] Property sheet to which pages are to be added.
|
|
// rlstrExtensions [IN] List of extension CLSID strings.
|
|
// pci [IN OUT] Cluster item to be administered.
|
|
// hfont [IN] Font for dialog text.
|
|
// hicon [IN] Icon for upper left corner.
|
|
//
|
|
// Return Value:
|
|
// None.
|
|
//
|
|
// Exceptions Thrown:
|
|
// Any exceptions thrown by CExtensionDll::AddPages().
|
|
//
|
|
//--
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
void CExtensions::CreatePropertySheetPages(
|
|
IN OUT CBasePropertySheet * psht,
|
|
IN const CStringList & rlstrExtensions,
|
|
IN OUT CClusterItem * pci,
|
|
IN HFONT hfont,
|
|
IN HICON hicon
|
|
)
|
|
{
|
|
POSITION pos;
|
|
CComObject<CExtensionDll> * pextdll;
|
|
|
|
ASSERT_VALID(psht);
|
|
|
|
m_psht = psht;
|
|
|
|
// Initialize for all extensions.
|
|
Init(rlstrExtensions, pci, hfont, hicon);
|
|
ASSERT(Plextdll() != NULL);
|
|
|
|
pos = Plextdll()->GetHeadPosition();
|
|
while (pos != NULL)
|
|
{
|
|
pextdll = Plextdll()->GetNext(pos);
|
|
ASSERT_VALID(pextdll);
|
|
try
|
|
{
|
|
pextdll->CreatePropertySheetPages();
|
|
} // try
|
|
catch (CException * pe)
|
|
{
|
|
pe->ReportError();
|
|
pe->Delete();
|
|
} // catch: CNTException
|
|
} // while: more items in the list
|
|
|
|
} //*** CExtensions::CreatePropertySheetPages()
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
//++
|
|
//
|
|
// CExtensions::CreateWizardPages
|
|
//
|
|
// Routine Description:
|
|
// Add pages to a wizard.
|
|
//
|
|
// Arguments:
|
|
// psht [IN OUT] Property sheet to which pages are to be added.
|
|
// rlstrExtensions [IN] List of extension CLSID strings.
|
|
// pci [IN OUT] Cluster item to be administered.
|
|
// hfont [IN] Font for dialog text.
|
|
// hicon [IN] Icon for upper left corner.
|
|
//
|
|
// Return Value:
|
|
// None.
|
|
//
|
|
// Exceptions Thrown:
|
|
// Any exceptions thrown by CExtensionDll::AddPages().
|
|
//
|
|
//--
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
void CExtensions::CreateWizardPages(
|
|
IN OUT CBaseWizard * psht,
|
|
IN const CStringList & rlstrExtensions,
|
|
IN OUT CClusterItem * pci,
|
|
IN HFONT hfont,
|
|
IN HICON hicon
|
|
)
|
|
{
|
|
POSITION pos;
|
|
CComObject<CExtensionDll> * pextdll;
|
|
|
|
ASSERT_VALID(psht);
|
|
|
|
m_psht = psht;
|
|
|
|
// Initialize for all extensions.
|
|
Init(rlstrExtensions, pci, hfont, hicon);
|
|
ASSERT(Plextdll() != NULL);
|
|
|
|
pos = Plextdll()->GetHeadPosition();
|
|
while (pos != NULL)
|
|
{
|
|
pextdll = Plextdll()->GetNext(pos);
|
|
ASSERT_VALID(pextdll);
|
|
try
|
|
{
|
|
pextdll->CreateWizardPages();
|
|
} // try
|
|
catch (CException * pe)
|
|
{
|
|
pe->ReportError();
|
|
pe->Delete();
|
|
} // catch: CNTException
|
|
} // while: more items in the list
|
|
|
|
} //*** CExtensions::CreateWizardPages()
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
//++
|
|
//
|
|
// CExtensions::AddContextMenuItems
|
|
//
|
|
// Routine Description:
|
|
// Query the extension DLL for new menu items to be added to the context
|
|
// menu.
|
|
//
|
|
// Arguments:
|
|
// pmenu [IN OUT] Menu to which items are to be added.
|
|
// rlstrExtensions [IN] List of extension CLSID strings.
|
|
// pci [IN OUT] Cluster item to be administered.
|
|
//
|
|
// Return Value:
|
|
// None.
|
|
//
|
|
// Exceptions Thrown:
|
|
// Any exceptions thrown by CExtensionDll::AddContextMenuItems() or
|
|
// CExtMenuItemList::new().
|
|
//
|
|
//--
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
void CExtensions::AddContextMenuItems(
|
|
IN OUT CMenu * pmenu,
|
|
IN const CStringList & rlstrExtensions,
|
|
IN OUT CClusterItem * pci
|
|
)
|
|
{
|
|
POSITION pos;
|
|
CComObject< CExtensionDll > * pextdll;
|
|
|
|
ASSERT(m_pmenu == NULL);
|
|
ASSERT_VALID(pmenu);
|
|
|
|
// Initialize for all extensions.
|
|
Init( rlstrExtensions, pci, NULL, NULL );
|
|
ASSERT( Plextdll() != NULL );
|
|
|
|
m_pmenu = pmenu;
|
|
m_nFirstCommandID = CAEXT_MENU_FIRST_ID;
|
|
m_nNextCommandID = m_nFirstCommandID;
|
|
m_nFirstMenuID = 0;
|
|
m_nNextMenuID = m_nFirstMenuID;
|
|
|
|
// Create the list of menu items.
|
|
ASSERT( m_plMenuItems == NULL );
|
|
m_plMenuItems = new CExtMenuItemList;
|
|
if ( m_plMenuItems == NULL )
|
|
{
|
|
AfxThrowMemoryException();
|
|
} // if: error allocating memory
|
|
|
|
pos = Plextdll()->GetHeadPosition();
|
|
while ( pos != NULL )
|
|
{
|
|
pextdll = Plextdll()->GetNext( pos );
|
|
ASSERT_VALID( pextdll );
|
|
try
|
|
{
|
|
pextdll->AddContextMenuItems();
|
|
} // try
|
|
catch ( CException * pe )
|
|
{
|
|
pe->ReportError();
|
|
pe->Delete();
|
|
} // catch: CNTException
|
|
} // while: more items in the list
|
|
|
|
} //*** CExtensions::AddContextMenuItems()
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
//++
|
|
//
|
|
// CExtensions::BExecuteContextMenuItem
|
|
//
|
|
// Routine Description:
|
|
// Execute a command associated with a menu item added to a context menu
|
|
// by the extension DLL.
|
|
//
|
|
// Arguments:
|
|
// nCommandID [IN] Command ID for the menu item chosen by the user.
|
|
//
|
|
// Return Value:
|
|
// TRUE Context menu item was executed.
|
|
// FALSE Context menu item was not executed.
|
|
//
|
|
// Exceptions Thrown:
|
|
// Any exceptions thrown by CExceptionDll::BExecuteContextMenuItem().
|
|
//
|
|
//--
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
BOOL CExtensions::BExecuteContextMenuItem(IN ULONG nCommandID)
|
|
{
|
|
BOOL bHandled = FALSE;
|
|
HRESULT hr;
|
|
CExtMenuItem * pemi;
|
|
|
|
// Find the item in our list.
|
|
pemi = PemiFromCommandID(nCommandID);
|
|
if (pemi != NULL)
|
|
{
|
|
Pdo()->AddRef();
|
|
hr = pemi->PiCommand()->InvokeCommand(pemi->NExtCommandID(), Pdo()->GetUnknown());
|
|
if (hr == NOERROR)
|
|
bHandled = TRUE;
|
|
} // if: found an item for the command ID
|
|
|
|
return bHandled;
|
|
|
|
} //*** CExtensions::BExecuteContextMenuItem()
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
//++
|
|
//
|
|
// CExtensions::BGetCommandString
|
|
//
|
|
// Routine Description:
|
|
// Get a command string from a menu ID.
|
|
//
|
|
// Arguments:
|
|
// nCommandID [IN] Command ID for the menu item.
|
|
// rstrMessage [OUT] String in which to return the message.
|
|
//
|
|
// Return Value:
|
|
// TRUE String is being returned.
|
|
// FALSE No string is being returned.
|
|
//
|
|
// Exceptions Thrown:
|
|
// Any exceptions thrown by CExtensionDll::BGetCommandString().
|
|
//
|
|
//--
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
BOOL CExtensions::BGetCommandString(
|
|
IN ULONG nCommandID,
|
|
OUT CString & rstrMessage
|
|
)
|
|
{
|
|
BOOL bHandled = FALSE;
|
|
CExtMenuItem * pemi;
|
|
|
|
// Find the item in our list.
|
|
pemi = PemiFromCommandID(nCommandID);
|
|
if (pemi != NULL)
|
|
{
|
|
rstrMessage = pemi->StrStatusBarText();
|
|
bHandled = TRUE;
|
|
} // if: found an item for the command ID
|
|
|
|
return bHandled;
|
|
|
|
} //*** CExtensions::BGetCommandString()
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
//++
|
|
//
|
|
// CExtensions::OnUpdateCommand
|
|
//
|
|
// Routine Description:
|
|
// Determines whether extension DLL menu items should be enabled or not.
|
|
//
|
|
// Arguments:
|
|
// pCmdUI [IN OUT] Command routing object.
|
|
//
|
|
// Return Value:
|
|
// None.
|
|
//
|
|
// Exceptions Thrown:
|
|
// Any exceptions thrown by CExtensionDll::BOnUpdateCommand().
|
|
//
|
|
//--
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
void CExtensions::OnUpdateCommand(CCmdUI * pCmdUI)
|
|
{
|
|
CExtMenuItem * pemi;
|
|
|
|
ASSERT(Plextdll() != NULL);
|
|
|
|
// Find the item in our list.
|
|
Trace(g_tagExtDll, _T("OnUpdateCommand() - ID = %d"), pCmdUI->m_nID);
|
|
pemi = PemiFromCommandID(pCmdUI->m_nID);
|
|
if (pemi != NULL)
|
|
{
|
|
Trace(g_tagExtDll, _T("OnUpdateCommand() - Found a match with '%s' ExtID = %d"), pemi->StrName(), pemi->NExtCommandID());
|
|
pCmdUI->Enable();
|
|
} // if: found an item for the command ID
|
|
|
|
} //*** CExtensions::OnUpdateCommand()
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
//++
|
|
//
|
|
// CExtensions::OnCmdMsg
|
|
//
|
|
// Routine Description:
|
|
// Processes command messages. Attempts to pass them on to a selected
|
|
// item first.
|
|
//
|
|
// Arguments:
|
|
// nID [IN] Command ID.
|
|
// nCode [IN] Notification code.
|
|
// pExtra [IN OUT] Used according to the value of nCode.
|
|
// pHandlerInfo [OUT] ???
|
|
//
|
|
// Return Value:
|
|
// TRUE Message has been handled.
|
|
// FALSE Message has NOT been handled.
|
|
//
|
|
//--
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
BOOL CExtensions::OnCmdMsg(
|
|
UINT nID,
|
|
int nCode,
|
|
void * pExtra,
|
|
AFX_CMDHANDLERINFO * pHandlerInfo
|
|
)
|
|
{
|
|
return BExecuteContextMenuItem(nID);
|
|
|
|
} //*** CExtensions::OnCmdMsg()
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
//++
|
|
//
|
|
// CExtensions::PemiFromCommandID
|
|
//
|
|
// Routine Description:
|
|
// Find the menu item for the specified command ID.
|
|
//
|
|
// Arguments:
|
|
// nCommandID [IN] Command ID for the menu item.
|
|
//
|
|
// Return Value:
|
|
// pemi Menu item or NULL if not found.
|
|
//
|
|
// Exceptions Thrown:
|
|
// None.
|
|
//
|
|
//--
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
CExtMenuItem * CExtensions::PemiFromCommandID(IN ULONG nCommandID) const
|
|
{
|
|
POSITION pos;
|
|
CExtMenuItem * pemi;
|
|
CExtMenuItem * pemiReturn = NULL;
|
|
|
|
if (PlMenuItems() != NULL)
|
|
{
|
|
pos = PlMenuItems()->GetHeadPosition();
|
|
while (pos != NULL)
|
|
{
|
|
pemi = PlMenuItems()->GetNext(pos);
|
|
ASSERT_VALID(pemi);
|
|
if (pemi->NCommandID() == nCommandID)
|
|
{
|
|
pemiReturn = pemi;
|
|
break;
|
|
} // if: match was found
|
|
} // while: more items in the list
|
|
} // if: item list exists
|
|
|
|
return pemiReturn;
|
|
|
|
} //*** CExtensions::PemiFromCommandID()
|
|
|
|
#ifdef _DEBUG
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
//++
|
|
//
|
|
// CExtensions::PemiFromExtCommandID
|
|
//
|
|
// Routine Description:
|
|
// Find the menu item for the specified extension command ID.
|
|
//
|
|
// Arguments:
|
|
// nExtCommandID [IN] Extension command ID for the menu item.
|
|
//
|
|
// Return Value:
|
|
// pemi Menu item or NULL if not found.
|
|
//
|
|
// Exceptions Thrown:
|
|
// None.
|
|
//
|
|
//--
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
CExtMenuItem * CExtensions::PemiFromExtCommandID(IN ULONG nExtCommandID) const
|
|
{
|
|
POSITION pos;
|
|
CExtMenuItem * pemi;
|
|
CExtMenuItem * pemiReturn = NULL;
|
|
|
|
if (PlMenuItems() != NULL)
|
|
{
|
|
pos = PlMenuItems()->GetHeadPosition();
|
|
while (pos != NULL)
|
|
{
|
|
pemi = PlMenuItems()->GetNext(pos);
|
|
ASSERT_VALID(pemi);
|
|
if (pemi->NExtCommandID() == nExtCommandID)
|
|
{
|
|
pemiReturn = pemi;
|
|
break;
|
|
} // if: match was found
|
|
} // while: more items in the list
|
|
} // if: item list exists
|
|
|
|
return pemiReturn;
|
|
|
|
} //*** CExtensions::PemiFromExtCommandID()
|
|
#endif
|
|
|
|
|
|
//*************************************************************************//
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// CComObject<CExtensionDll>
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
|
|
IMPLEMENT_DYNAMIC(CExtensionDll, CObject);
|
|
|
|
BEGIN_OBJECT_MAP(ObjectMap)
|
|
OBJECT_ENTRY(CLSID_CoCluAdmin, CExtensionDll)
|
|
END_OBJECT_MAP()
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
//++
|
|
//
|
|
// CExtensionDll::CExtensionDll
|
|
//
|
|
// Routine Description:
|
|
// Default constructor.
|
|
//
|
|
// Arguments:
|
|
// None.
|
|
//
|
|
// Return Value:
|
|
// None.
|
|
//
|
|
//--
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
CExtensionDll::CExtensionDll(void)
|
|
{
|
|
m_piExtendPropSheet = NULL;
|
|
m_piExtendWizard = NULL;
|
|
m_piExtendContextMenu = NULL;
|
|
m_piInvokeCommand = NULL;
|
|
|
|
m_pext = NULL;
|
|
|
|
m_pModuleState = AfxGetModuleState();
|
|
ASSERT(m_pModuleState != NULL);
|
|
|
|
} //*** CExtensionDll::CExtensionDll()
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
//++
|
|
//
|
|
// CExtensionDll::~CExtensionDll
|
|
//
|
|
// Routine Description:
|
|
// Destructor.
|
|
//
|
|
// Arguments:
|
|
// None.
|
|
//
|
|
// Return Value:
|
|
// None.
|
|
//
|
|
//--
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
CExtensionDll::~CExtensionDll(void)
|
|
{
|
|
UnloadExtension();
|
|
m_pModuleState = NULL;
|
|
|
|
} //*** CExtensionDll::~CExtensionDll()
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
//++
|
|
//
|
|
// CExtensionDll::Init
|
|
//
|
|
// Routine Description:
|
|
// Initialize this class in preparation for accessing the extension.
|
|
//
|
|
// Arguments:
|
|
// rstrCLSID [IN] CLSID of the extension in string form.
|
|
//
|
|
// Return Value:
|
|
// None.
|
|
//
|
|
// Exceptions Thrown:
|
|
// CNTException 0 (error converting CLSID from string)
|
|
// Any exceptions thrown by CString::operater=().
|
|
//
|
|
//--
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
void CExtensionDll::Init(
|
|
IN const CString & rstrCLSID,
|
|
IN OUT CExtensions * pext
|
|
)
|
|
{
|
|
HRESULT hr;
|
|
CWaitCursor wc;
|
|
|
|
ASSERT_VALID(pext);
|
|
|
|
Trace(g_tagExtDll, _T("Init() - CLSID = %s"), rstrCLSID);
|
|
|
|
// Save parameters.
|
|
ASSERT(StrCLSID().IsEmpty() || (StrCLSID() == rstrCLSID));
|
|
m_strCLSID = rstrCLSID;
|
|
m_pext = pext;
|
|
|
|
// Convert the CLSID string to a CLSID.
|
|
hr = ::CLSIDFromString((LPWSTR) (LPCTSTR) rstrCLSID, &m_clsid);
|
|
if (hr != S_OK)
|
|
ThrowStaticException(hr, IDS_CLSIDFROMSTRING_ERROR, rstrCLSID);
|
|
|
|
} //*** CExtensionDll::Init()
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
//++
|
|
//
|
|
// CExtensionDll::LoadInterface
|
|
//
|
|
// Routine Description:
|
|
// Load an extension DLL.
|
|
//
|
|
// Arguments:
|
|
// riid [IN] Interface ID.
|
|
//
|
|
// Return Value:
|
|
// piUnk IUnknown interface pointer for interface.
|
|
//
|
|
// Exceptions Thrown:
|
|
// CNTException IDS_EXT_CREATE_INSTANCE_ERROR
|
|
//
|
|
//--
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
IUnknown * CExtensionDll::LoadInterface(IN const REFIID riid)
|
|
{
|
|
HRESULT hr;
|
|
IUnknown * piUnk;
|
|
CWaitCursor wc;
|
|
|
|
// Load the inproc server and get the IShellExtInit interface pointer.
|
|
Trace(g_tagExtDllRef, _T("LoadInterface() - Getting interface pointer"));
|
|
hr = ::CoCreateInstance(
|
|
Rclsid(),
|
|
NULL,
|
|
CLSCTX_INPROC_SERVER,
|
|
riid,
|
|
(LPVOID *) &piUnk
|
|
);
|
|
if ((hr != S_OK)
|
|
&& (hr != REGDB_E_CLASSNOTREG)
|
|
&& (hr != E_NOINTERFACE)
|
|
)
|
|
ThrowStaticException(hr, IDS_EXT_CREATE_INSTANCE_ERROR, StrCLSID());
|
|
|
|
return piUnk;
|
|
|
|
} //*** CExtensionDll::LoadInterface()
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
//++
|
|
//
|
|
// CExtensionDll::UnloadExtension
|
|
//
|
|
// Routine Description:
|
|
// Unload the extension DLL.
|
|
//
|
|
// Arguments:
|
|
// None.
|
|
//
|
|
// Return Value:
|
|
// None.
|
|
//
|
|
//--
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
void CExtensionDll::UnloadExtension(void)
|
|
{
|
|
// Release the interface pointers in the opposite order in which they
|
|
// were obtained.
|
|
ReleaseInterface(&m_piExtendPropSheet);
|
|
ReleaseInterface(&m_piExtendWizard);
|
|
ReleaseInterface(&m_piExtendContextMenu);
|
|
ReleaseInterface(&m_piInvokeCommand);
|
|
|
|
m_strCLSID.Empty();
|
|
|
|
} //*** CExtensionDll::UnloadExtension()
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
//++
|
|
//
|
|
// CExtensionDll::CreatePropertySheetPages
|
|
//
|
|
// Routine Description:
|
|
// Add pages to a property sheet.
|
|
//
|
|
// Arguments:
|
|
// None.
|
|
//
|
|
// Return Value:
|
|
// None.
|
|
//
|
|
// Exceptions Thrown:
|
|
// CNTException IDS_EXT_ADD_PAGES_ERROR
|
|
//
|
|
//--
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
void CExtensionDll::CreatePropertySheetPages(void)
|
|
{
|
|
HRESULT hr;
|
|
|
|
ASSERT_VALID(Pext());
|
|
ASSERT(m_piExtendPropSheet == NULL);
|
|
|
|
// Load the interface.
|
|
m_piExtendPropSheet = (interface IWEExtendPropertySheet *) LoadInterface(IID_IWEExtendPropertySheet);
|
|
if (m_piExtendPropSheet == NULL)
|
|
return;
|
|
ASSERT(m_piExtendPropSheet != NULL);
|
|
|
|
// Add pages from the extension.
|
|
GetUnknown()->AddRef(); // Add a reference because extension is going to release.
|
|
Pdo()->AddRef();
|
|
try
|
|
{
|
|
hr = PiExtendPropSheet()->CreatePropertySheetPages(Pdo()->GetUnknown(), this);
|
|
} // try
|
|
catch ( ... )
|
|
{
|
|
hr = E_FAIL;
|
|
} // catch
|
|
|
|
if ((hr != NOERROR) && (hr != E_NOTIMPL))
|
|
ThrowStaticException(hr, IDS_EXT_ADD_PAGES_ERROR, StrCLSID());
|
|
|
|
} //*** CExtensionDll::CreatePropertySheetPages()
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
//++
|
|
//
|
|
// CExtensionDll::CreateWizardPages
|
|
//
|
|
// Routine Description:
|
|
// Add pages to a wizard.
|
|
//
|
|
// Arguments:
|
|
// None.
|
|
//
|
|
// Return Value:
|
|
// None.
|
|
//
|
|
// Exceptions Thrown:
|
|
// CNTException IDS_EXT_ADD_PAGES_ERROR
|
|
//
|
|
//--
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
void CExtensionDll::CreateWizardPages(void)
|
|
{
|
|
HRESULT hr;
|
|
|
|
ASSERT_VALID(Pext());
|
|
ASSERT(m_piExtendWizard == NULL);
|
|
ASSERT_VALID(Psht());
|
|
|
|
// Load the interface.
|
|
m_piExtendWizard = (interface IWEExtendWizard *) LoadInterface(IID_IWEExtendWizard);
|
|
if (m_piExtendWizard == NULL)
|
|
return;
|
|
ASSERT(m_piExtendWizard != NULL);
|
|
|
|
// Add pages from the extension.
|
|
GetUnknown()->AddRef(); // Add a reference because extension is going to release.
|
|
Pdo()->AddRef();
|
|
try
|
|
{
|
|
hr = PiExtendWizard()->CreateWizardPages(Pdo()->GetUnknown(), this);
|
|
} // try
|
|
catch ( ... )
|
|
{
|
|
hr = E_FAIL;
|
|
} // catch
|
|
|
|
if ((hr != NOERROR) && (hr != E_NOTIMPL))
|
|
ThrowStaticException(hr, IDS_EXT_ADD_PAGES_ERROR, StrCLSID());
|
|
|
|
} //*** CExtensionDll::CreateWizardPages()
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
//++
|
|
//
|
|
// CExtensionDll::AddContextMenuItems
|
|
//
|
|
// Routine Description:
|
|
// Ask the extension DLL to add items to the menu.
|
|
//
|
|
// Arguments:
|
|
// None.
|
|
//
|
|
// Return Value:
|
|
// None.
|
|
//
|
|
// Exceptions Thrown:
|
|
// CNTException IDS_EXT_QUERY_CONTEXT_MENU_ERROR
|
|
//
|
|
//--
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
void CExtensionDll::AddContextMenuItems(void)
|
|
{
|
|
HRESULT hr;
|
|
|
|
ASSERT_VALID(Pext());
|
|
ASSERT_VALID(Pmenu());
|
|
ASSERT(m_piExtendContextMenu == NULL);
|
|
|
|
// Load the interface.
|
|
m_piExtendContextMenu = (interface IWEExtendContextMenu *) LoadInterface(IID_IWEExtendContextMenu);
|
|
if (m_piExtendContextMenu == NULL)
|
|
return;
|
|
ASSERT(m_piExtendContextMenu != NULL);
|
|
|
|
hr = PiExtendContextMenu()->QueryInterface(IID_IWEInvokeCommand, (LPVOID *) &m_piInvokeCommand);
|
|
if (hr != NOERROR)
|
|
{
|
|
PiExtendContextMenu()->Release();
|
|
m_piExtendContextMenu = NULL;
|
|
ThrowStaticException(hr, IDS_EXT_QUERY_CONTEXT_MENU_ERROR, StrCLSID());
|
|
} // if: error getting the InvokeCommand interface
|
|
|
|
GetUnknown()->AddRef(); // Add a reference because extension is going to release.
|
|
Pdo()->AddRef();
|
|
Trace(g_tagExtDll, _T("CExtensionDll::AddContextMenuItem() - Adding context menu items from '%s'"), StrCLSID());
|
|
try
|
|
{
|
|
hr = PiExtendContextMenu()->AddContextMenuItems(Pdo()->GetUnknown(), this);
|
|
} // try
|
|
catch ( ... )
|
|
{
|
|
hr = E_FAIL;
|
|
} // catch
|
|
if (hr != NOERROR)
|
|
ThrowStaticException(hr, IDS_EXT_QUERY_CONTEXT_MENU_ERROR, StrCLSID());
|
|
|
|
// Add a separator after the extension's items.
|
|
Trace(g_tagExtDll, _T("CExtensionDll::AddContextMenuItem() - Adding separator"));
|
|
try
|
|
{
|
|
hr = AddExtensionMenuItem(NULL, NULL, (ULONG) -1, 0, MF_SEPARATOR);
|
|
} // try
|
|
catch ( ... )
|
|
{
|
|
hr = E_FAIL;
|
|
} // catch
|
|
if (hr != NOERROR)
|
|
ThrowStaticException(hr, IDS_EXT_QUERY_CONTEXT_MENU_ERROR, StrCLSID());
|
|
|
|
} //*** CExtensionDll::AddContextMenuItems()
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
//++
|
|
//
|
|
// CExtensionDll::InterfaceSupportsErrorInfo [ISupportsErrorInfo]
|
|
//
|
|
// Routine Description:
|
|
// Determines whether the interface supports error info (???).
|
|
//
|
|
// Arguments:
|
|
// riid [IN] Reference to the interface ID.
|
|
//
|
|
// Return Value:
|
|
// S_OK Interface supports error info.
|
|
// S_FALSE Interface does not support error info.
|
|
//
|
|
// Exceptions Thrown:
|
|
// None.
|
|
//
|
|
//--
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
STDMETHODIMP CExtensionDll::InterfaceSupportsErrorInfo(REFIID riid)
|
|
{
|
|
static const IID * rgiid[] =
|
|
{
|
|
&IID_IWCPropertySheetCallback,
|
|
&IID_IWCWizardCallback,
|
|
&IID_IWCContextMenuCallback,
|
|
};
|
|
int iiid;
|
|
|
|
for (iiid = 0 ; iiid < sizeof(rgiid) / sizeof(rgiid[0]) ; iiid++)
|
|
{
|
|
if (InlineIsEqualGUID(*rgiid[iiid], riid))
|
|
return S_OK;
|
|
}
|
|
return S_FALSE;
|
|
|
|
} //*** CExtensionDll::InterfaceSupportsErrorInfo()
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
//++
|
|
//
|
|
// CExtensionDll::AddPropertySheetPage [IWCPropertySheetCallback]
|
|
//
|
|
// Routine Description:
|
|
// Add a page to the property sheet.
|
|
//
|
|
// Arguments:
|
|
// hpage [IN] Page to add.
|
|
//
|
|
// Return Value:
|
|
// NOERROR Page added successfully.
|
|
// E_INVALIDARG NULL hpage.
|
|
// Any hresult returned from CBasePropertySheet::HrAddPage().
|
|
//
|
|
// Exceptions Thrown:
|
|
// None.
|
|
//
|
|
//--
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
STDMETHODIMP CExtensionDll::AddPropertySheetPage(
|
|
IN LONG * hpage
|
|
)
|
|
{
|
|
HRESULT hr = NOERROR;
|
|
|
|
AFX_MANAGE_STATE(m_pModuleState);
|
|
|
|
ASSERT(hpage != NULL);
|
|
ASSERT_VALID(Psht());
|
|
|
|
// Do this for the release build.
|
|
if ((hpage == NULL)
|
|
|| (Psht() == NULL))
|
|
hr = E_INVALIDARG;
|
|
else
|
|
hr = Psht()->HrAddPage((HPROPSHEETPAGE) hpage);
|
|
|
|
return hr;
|
|
|
|
} //*** CExtensionDll::AddPropertySheetPage()
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
//++
|
|
//
|
|
// CExtensionDll::AddWizardPage [IWCWizardCallback]
|
|
//
|
|
// Routine Description:
|
|
// Add a page to the wizard.
|
|
//
|
|
// Arguments:
|
|
// hpage [IN] Page to add.
|
|
//
|
|
// Return Value:
|
|
// NOERROR Page added successfully.
|
|
// E_INVALIDARG NULL hpage.
|
|
// Any hresult returned from CBaseSheet::HrAddPage().
|
|
//
|
|
// Exceptions Thrown:
|
|
// None.
|
|
//
|
|
//--
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
STDMETHODIMP CExtensionDll::AddWizardPage(
|
|
IN LONG * hpage
|
|
)
|
|
{
|
|
HRESULT hr = NOERROR;
|
|
|
|
AFX_MANAGE_STATE(m_pModuleState);
|
|
|
|
ASSERT(hpage != NULL);
|
|
ASSERT_VALID(Psht());
|
|
|
|
// Do this for the release build.
|
|
if ((hpage == NULL) || (Psht() == NULL))
|
|
hr = E_INVALIDARG;
|
|
else
|
|
hr = Psht()->HrAddPage((HPROPSHEETPAGE) hpage);
|
|
|
|
return hr;
|
|
|
|
} //*** CExtensionDll::AddWizardPage()
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
//++
|
|
//
|
|
// CExtensionDll::EnableNext [IWCWizardCallback]
|
|
//
|
|
// Routine Description:
|
|
// Enable or disable the NEXT button. If it is the last page, the
|
|
// FINISH button will be enabled or disabled.
|
|
//
|
|
// Arguments:
|
|
// hpage [IN] Page for which the button is being enabled or
|
|
// disabled.
|
|
// bEnable [IN] TRUE = Enable the button, FALSE = disable.
|
|
//
|
|
// Return Value:
|
|
// NOERROR Success.
|
|
// E_INVALIDARG Unknown hpage specified.
|
|
//
|
|
// Exceptions Thrown:
|
|
// None.
|
|
//
|
|
//--
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
STDMETHODIMP CExtensionDll::EnableNext(
|
|
IN LONG * hpage,
|
|
IN BOOL bEnable
|
|
)
|
|
{
|
|
HRESULT hr = NOERROR;
|
|
CBaseWizard * pwiz;
|
|
DWORD dwWizButtons;
|
|
|
|
AFX_MANAGE_STATE(m_pModuleState);
|
|
|
|
ASSERT(hpage != NULL);
|
|
ASSERT_VALID(Psht());
|
|
ASSERT_KINDOF(CBaseWizard, Psht());
|
|
|
|
pwiz = (CBaseWizard *) Psht();
|
|
|
|
// If this is the last extension page, enable/disable the FINISH button.
|
|
{
|
|
POSITION pos;
|
|
BOOL bMatch = FALSE;
|
|
|
|
pos = pwiz->Lhpage().GetHeadPosition();
|
|
while (pos != NULL)
|
|
{
|
|
if (pwiz->Lhpage().GetNext(pos) == hpage)
|
|
{
|
|
bMatch = TRUE;
|
|
break;
|
|
} // if: found a match
|
|
} // while: more items in the list
|
|
if (!bMatch)
|
|
return E_INVALIDARG;
|
|
if (pos == NULL)
|
|
dwWizButtons = PSWIZB_BACK | (bEnable ? PSWIZB_FINISH : PSWIZB_DISABLEDFINISH);
|
|
else
|
|
dwWizButtons = PSWIZB_BACK | (bEnable ? PSWIZB_NEXT : 0);
|
|
} // If this is the last extension page, set the FINISH button
|
|
|
|
// Set wizard buttons.
|
|
pwiz->SetWizardButtons(dwWizButtons);
|
|
|
|
return hr;
|
|
|
|
} //*** CExtensionDll::EnableNext()
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
//++
|
|
//
|
|
// CExtensionDll::AddExtensionMenuItem [IWCContextMenuCallback]
|
|
//
|
|
// Routine Description:
|
|
// Add a page to the wizard.
|
|
//
|
|
// Arguments:
|
|
// lpszName [IN] Name of item.
|
|
// lpszStatusBarText [IN] Text to appear on the status bar when the
|
|
// item is highlighted.
|
|
// nCommandID [IN] ID for the command when menu item is invoked.
|
|
// Must not be -1.
|
|
// nSubmenuCommandID [IN] ID for a submenu.
|
|
// uFlags [IN] Menu flags. The following are not supportd:
|
|
// MF_OWNERDRAW, MF_POPUP
|
|
//
|
|
// Return Value:
|
|
// NOERROR Item added successfully.
|
|
// E_INVALIDARG MF_OWNERDRAW or MF_POPUP were specified.
|
|
// E_OUTOFMEMORY Error allocating the item.
|
|
//
|
|
// Exceptions Thrown:
|
|
// None.
|
|
//
|
|
//--
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
STDMETHODIMP CExtensionDll::AddExtensionMenuItem(
|
|
IN BSTR lpszName,
|
|
IN BSTR lpszStatusBarText,
|
|
IN ULONG nCommandID,
|
|
IN ULONG nSubmenuCommandID,
|
|
IN ULONG uFlags
|
|
)
|
|
{
|
|
HRESULT hr = NOERROR;
|
|
CExtMenuItem * pemi = NULL;
|
|
|
|
AFX_MANAGE_STATE( m_pModuleState );
|
|
|
|
ASSERT_VALID( Pext() );
|
|
ASSERT( ! ( uFlags & (MF_OWNERDRAW | MF_POPUP) ) );
|
|
ASSERT_VALID( Pmenu() );
|
|
|
|
// Do this for the release build.
|
|
if ( ( uFlags & (MF_OWNERDRAW | MF_POPUP) ) != 0 )
|
|
{
|
|
hr = E_INVALIDARG;
|
|
} // if: trying to add invalid type of menu item
|
|
else
|
|
{
|
|
ASSERT( Pext()->PemiFromExtCommandID( nCommandID ) == NULL );
|
|
|
|
try
|
|
{
|
|
Trace( g_tagExtDll, _T("CExtensionDll::AddExtensionMenuItem() - Adding menu item '%s', ExtID = %d"), lpszName, nCommandID );
|
|
|
|
// Allocate a new item.
|
|
pemi = new CExtMenuItem(
|
|
OLE2CT( lpszName ),
|
|
OLE2CT( lpszStatusBarText ),
|
|
nCommandID,
|
|
NNextCommandID(),
|
|
NNextMenuID(),
|
|
uFlags,
|
|
FALSE, /*bMakeDefault*/
|
|
PiInvokeCommand()
|
|
);
|
|
if ( pemi == NULL )
|
|
{
|
|
AfxThrowMemoryException();
|
|
} // if: error allocating memory
|
|
|
|
// Insert the item in the menu.
|
|
if ( ! Pmenu()->InsertMenu( NNextMenuID(), MF_BYPOSITION | uFlags, NNextCommandID(), pemi->StrName() ) )
|
|
{
|
|
ThrowStaticException( ::GetLastError(), IDS_INSERT_MENU_ERROR, pemi->StrName() );
|
|
} // if: error inserting the menu item
|
|
|
|
// Add the item to the tail of the list.
|
|
Pext()->PlMenuItems()->AddTail( pemi );
|
|
pemi = NULL;
|
|
|
|
// Update the counters.
|
|
Pext()->m_nNextCommandID++;
|
|
Pext()->m_nNextMenuID++;
|
|
} // try
|
|
catch ( CNTException * pnte )
|
|
{
|
|
hr = pnte->Sc();
|
|
pnte->ReportError();
|
|
pnte->Delete();
|
|
} // catch: CException
|
|
catch ( CException * pe )
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
pe->ReportError();
|
|
pe->Delete();
|
|
} // catch: CException
|
|
} // else: we can add the item
|
|
|
|
delete pemi;
|
|
return hr;
|
|
|
|
} //*** CExtensionDll::AddExtensionMenuItem()
|