//+-------------------------------------------------------------------------- // // Microsoft Windows // Copyright (C) Microsoft Corporation, 1994 - 1998. // // File: scope.cpp // // Contents: implementation of the scope pane // // Classes: CScopePane // // History: 03-14-1998 stevebl Created // 07-16-1998 rahulth Added calls to IGPEInformation::PolicyChanged // //--------------------------------------------------------------------------- #include "precomp.hxx" #include #include // Comment this line to stop trying to set the main snapin icon in the // scope pane. #define SET_SCOPE_ICONS 1 // Un-comment the next line to persist snap-in related data. (This really // shouldn't be necessary since I get all my info from my parent anyway.) // #define PERSIST_DATA 1 /////////////////////////////////////////////////////////////////////////////// // IComponentData implementation DEBUG_DECLARE_INSTANCE_COUNTER(CScopePane); CScopePane::CScopePane() { HKEY hKey; DWORD dwDisp; DEBUG_INCREMENT_INSTANCE_COUNTER(CScopePane); m_bIsDirty = FALSE; m_fRSOP = FALSE; m_pScope = NULL; m_pConsole = NULL; m_pIPropertySheetProvider = NULL; m_fLoaded = FALSE; m_fExtension = FALSE; m_pIGPEInformation = NULL; m_pIRSOPInformation = NULL; } CScopePane::~CScopePane() { DEBUG_DECREMENT_INSTANCE_COUNTER(CScopePane); ASSERT(m_pScope == NULL); ASSERT(CResultPane::lDataObjectRefCount == 0); } #include //+-------------------------------------------------------------------------- // // Member: CScopePane::CreateNestedDirectory // // Synopsis: Ensures the existance of a path. If any directory along the // path doesn't exist, this routine will create it. // // Arguments: [lpDirectory] - path to the leaf directory // [lpSecurityAttributes] - security attributes // // Returns: 1 on success // 0 on failure // // History: 3-17-1998 stevebl Copied from ADE // // Notes: Originally written by EricFlo // //--------------------------------------------------------------------------- UINT CScopePane::CreateNestedDirectory (LPTSTR lpDirectory, LPSECURITY_ATTRIBUTES lpSecurityAttributes) { TCHAR szDirectory[MAX_PATH]; LPTSTR lpEnd; // // Check for NULL pointer // if (!lpDirectory || !(*lpDirectory)) { SetLastError(ERROR_INVALID_DATA); return 0; } // // First, see if we can create the directory without having // to build parent directories. // if (CreateDirectory (lpDirectory, lpSecurityAttributes)) { return 1; } // // If this directory exists already, this is OK too. // if (GetLastError() == ERROR_ALREADY_EXISTS) { return ERROR_ALREADY_EXISTS; } // // No luck, copy the string to a buffer we can munge // HRESULT hr; hr = StringCchCopy(szDirectory, sizeof(szDirectory)/sizeof(szDirectory[0]), lpDirectory); if (FAILED(hr)) { SetLastError(HRESULT_CODE(hr)); return 0; } // // Find the first subdirectory name // lpEnd = szDirectory; if (szDirectory[1] == TEXT(':')) { lpEnd += 3; } else if (szDirectory[1] == TEXT('\\')) { // // Skip the first two slashes // lpEnd += 2; // // Find the slash between the server name and // the share name. // while (*lpEnd && *lpEnd != TEXT('\\')) { lpEnd++; } if (!(*lpEnd)) { return 0; } // // Skip the slash, and find the slash between // the share name and the directory name. // lpEnd++; while (*lpEnd && *lpEnd != TEXT('\\')) { lpEnd++; } if (!(*lpEnd)) { return 0; } // // Leave pointer at the beginning of the directory. // lpEnd++; } else if (szDirectory[0] == TEXT('\\')) { lpEnd++; } while (*lpEnd) { while (*lpEnd && *lpEnd != TEXT('\\')) { lpEnd++; } if (*lpEnd == TEXT('\\')) { *lpEnd = TEXT('\0'); if (!CreateDirectory (szDirectory, NULL)) { if (GetLastError() != ERROR_ALREADY_EXISTS) { return 0; } } *lpEnd = TEXT('\\'); lpEnd++; } } // // Create the final directory // if (CreateDirectory (szDirectory, lpSecurityAttributes)) { return 1; } if (GetLastError() == ERROR_ALREADY_EXISTS) { return ERROR_ALREADY_EXISTS; } // // Failed // return 0; } STDMETHODIMP CScopePane::Initialize(LPUNKNOWN pUnknown) { ASSERT(pUnknown != NULL); HRESULT hr; AFX_MANAGE_STATE(AfxGetStaticModuleState()); // MMC should only call ::Initialize once! ASSERT(m_pScope == NULL); pUnknown->QueryInterface(IID_IConsoleNameSpace, reinterpret_cast(&m_pScope)); ASSERT(hr == S_OK); hr = pUnknown->QueryInterface(IID_IPropertySheetProvider, (void **)&m_pIPropertySheetProvider); hr = pUnknown->QueryInterface(IID_IConsole, reinterpret_cast(&m_pConsole)); ASSERT(hr == S_OK); hr = m_pConsole->QueryInterface (IID_IDisplayHelp, reinterpret_cast(&m_pDisplayHelp)); ASSERT(hr == S_OK); #ifdef SET_SCOPE_ICONS LPIMAGELIST lpScopeImage; hr = m_pConsole->QueryScopeImageList(&lpScopeImage); ASSERT(hr == S_OK); // Load the bitmaps from the dll CBitmap bmp16x16; CBitmap bmp32x32; bmp16x16.LoadBitmap(IDB_16x16); bmp32x32.LoadBitmap(IDB_32x32); // Set the images lpScopeImage->ImageListSetStrip(reinterpret_cast(static_cast(bmp16x16)), reinterpret_cast(static_cast(bmp32x32)), 0, RGB(255,0,255)); lpScopeImage->Release(); #endif return S_OK; } STDMETHODIMP CScopePane::CreateComponent(LPCOMPONENT* ppComponent) { ASSERT(ppComponent != NULL); CComObject* pObject; HRESULT hr = CComObject::CreateInstance(&pObject); if ( FAILED(hr) ) { return hr; } ASSERT(pObject != NULL); m_pResultPane = pObject; // Store IComponentData pObject->SetIComponentData(this); return pObject->QueryInterface(IID_IComponent, reinterpret_cast(ppComponent)); } STDMETHODIMP CScopePane::Notify(LPDATAOBJECT lpDataObject, MMC_NOTIFY_TYPE event, LPARAM arg, LPARAM param) { ASSERT(m_pScope != NULL); HRESULT hr = S_OK; UINT i; // Since it's my folder it has an internal format. // Design Note: for extension. I can use the fact, that the data object doesn't have // my internal format and I should look at the node type and see how to extend it. if (event == MMCN_PROPERTY_CHANGE) { // perform any action needed as a result of result property changes hr = OnProperties(param); } else if ( event == MMCN_REMOVE_CHILDREN ) { // // In RSoP, we may get called to refresh the scope pane when the query // is re-executed -- if this happens, current nodes will be removed and // we must reset all of our cached information. We reset the relevant // information below // if ( ((HSCOPEITEM)arg != NULL) && m_fRSOP && (m_pIRSOPInformation != NULL) ) { m_pIRSOPInformation->Release(); m_pIRSOPInformation = NULL; } } else { INTERNAL* pInternal = ExtractInternalFormat(lpDataObject); MMC_COOKIE cookie = 0; if (pInternal != NULL) { cookie = pInternal->m_cookie; FREE_INTERNAL(pInternal); } else { // only way we could not be able to extract our own format is if we're operating as an extension m_fExtension = TRUE; } if (m_fRSOP) { WCHAR szBuffer[MAX_DS_PATH]; if (m_pIRSOPInformation == NULL) { IRSOPInformation * pIRSOPInformation; hr = lpDataObject->QueryInterface(IID_IRSOPInformation, reinterpret_cast(&pIRSOPInformation)); if (SUCCEEDED(hr)) { m_pIRSOPInformation = pIRSOPInformation; m_pIRSOPInformation->AddRef(); /* extract the namespace here */ hr = m_pIRSOPInformation->GetNamespace(GPO_SECTION_USER, szBuffer, sizeof(szBuffer) / sizeof(szBuffer[0])); if (SUCCEEDED(hr)) { m_szRSOPNamespace = szBuffer; } pIRSOPInformation->Release(); } } } else { if (m_pIGPEInformation == NULL) { IGPEInformation * pIGPEInformation; hr = lpDataObject->QueryInterface(IID_IGPEInformation, reinterpret_cast(&pIGPEInformation)); if (SUCCEEDED(hr)) { GROUP_POLICY_OBJECT_TYPE gpoType; hr = pIGPEInformation->GetType(&gpoType); if (SUCCEEDED(hr)) { if (gpoType == GPOTypeDS) { WCHAR szBuffer[MAX_PATH]; do { AFX_MANAGE_STATE (AfxGetStaticModuleState()); hr = pIGPEInformation->GetFileSysPath(GPO_SECTION_USER, szBuffer, MAX_PATH); if (FAILED(hr)) break; m_pIGPEInformation = pIGPEInformation; m_pIGPEInformation->AddRef(); m_szFileRoot = szBuffer; m_szFileRoot += L"\\Documents & Settings"; CreateNestedDirectory (((LPOLESTR)(LPCOLESTR)(m_szFileRoot)), NULL); //initialize the folder data. for (i = IDS_DIRS_START; i < IDS_DIRS_END; i++) { m_FolderData[GETINDEX(i)].Initialize (i, (LPCTSTR) m_szFileRoot); } ConvertOldStyleSection (m_szFileRoot); } while (0); } else { // force this to fail hr = E_FAIL; } } pIGPEInformation->Release(); } } } if (SUCCEEDED(hr)) { switch(event) { case MMCN_EXPAND: { hr = OnExpand(cookie, arg, param); } break; case MMCN_SELECT: hr = OnSelect(cookie, arg, param); break; case MMCN_CONTEXTMENU: hr = OnContextMenu(cookie, arg, param); break; default: //perform the default action hr = S_FALSE; break; } } } return hr; } STDMETHODIMP CScopePane::Destroy() { SAFE_RELEASE(m_pScope); SAFE_RELEASE(m_pDisplayHelp); SAFE_RELEASE(m_pConsole); SAFE_RELEASE(m_pIPropertySheetProvider); SAFE_RELEASE(m_pIGPEInformation); SAFE_RELEASE(m_pIRSOPInformation); return S_OK; } STDMETHODIMP CScopePane::QueryDataObject(MMC_COOKIE cookie, DATA_OBJECT_TYPES type, LPDATAOBJECT* ppDataObject) { ASSERT(ppDataObject != NULL); CComObject* pObject = NULL; CComObject::CreateInstance(&pObject); ASSERT(pObject != NULL); if (!pObject) return E_UNEXPECTED; // Save cookie and type for delayed rendering pObject->SetID (m_FolderData[GETINDEX(cookie)].m_scopeID); pObject->SetType(type); pObject->SetCookie(cookie); return pObject->QueryInterface(IID_IDataObject, reinterpret_cast(ppDataObject)); } /////////////////////////////////////////////////////////////////////////////// //// IPersistStreamInit interface members STDMETHODIMP CScopePane::GetClassID(CLSID *pClassID) { ASSERT(pClassID != NULL); // Copy the CLSID for this snapin *pClassID = CLSID_Snapin; return S_OK; } STDMETHODIMP CScopePane::IsDirty() { return ThisIsDirty() ? S_OK : S_FALSE; } STDMETHODIMP CScopePane::Load(IStream *pStm) { #ifdef PERSIST_DATA ASSERT(pStm); // UNDONE - Read data from the stream here. return SUCCEEDED(hr) ? S_OK : E_FAIL; #else return S_OK; #endif } STDMETHODIMP CScopePane::Save(IStream *pStm, BOOL fClearDirty) { #ifdef PERSIST_DATA ASSERT(pStm); // UNDONE - Write data to the stream here. // on error, return STG_E_CANTSAVE; #endif if (fClearDirty) ClearDirty(); return S_OK; } STDMETHODIMP CScopePane::GetSizeMax(ULARGE_INTEGER *pcbSize) { ASSERT(pcbSize); // UNDONE - set the size of the string to be saved ULONG cb = 0; // Set the size of the string to be saved ULISet32(*pcbSize, cb); return S_OK; } STDMETHODIMP CScopePane::InitNew(void) { return S_OK; } /////////////////////////////////////////////////////////////////////////////// //// Notify handlers for IComponentData HRESULT CScopePane::OnAdd(MMC_COOKIE cookie, LPARAM arg, LPARAM param) { return E_UNEXPECTED; } HRESULT CScopePane::OnExpand(MMC_COOKIE cookie, LPARAM arg, LPARAM param) { if (arg == TRUE) //MMC never sends arg = FALSE (for collapse) { // Did Initialize get called? ASSERT(m_pScope != NULL); EnumerateScopePane(cookie, param); } return S_OK; } HRESULT CScopePane::OnSelect(MMC_COOKIE cookie, LPARAM arg, LPARAM param) { return E_UNEXPECTED; } HRESULT CScopePane::OnContextMenu(MMC_COOKIE cookie, LPARAM arg, LPARAM param) { return S_OK; } HRESULT CScopePane::OnProperties(LPARAM param) { if (param == NULL) { return S_OK; } ASSERT(param != NULL); return S_OK; } void CScopePane::EnumerateScopePane(MMC_COOKIE cookie, HSCOPEITEM pParent) { AFX_MANAGE_STATE (AfxGetStaticModuleState()); CString szFullPathname; CString szParent; SCOPEDATAITEM scopeItem; FILETIME ftCurr; LONG i; int cChildren = 0; DWORD myDocsFlags = REDIR_DONT_CARE; DWORD myPicsFlags = REDIR_DONT_CARE; memset(&scopeItem, 0, sizeof(SCOPEDATAITEM)); CHourglass hourglass; //this may take some time, so put up an hourglass GetSystemTimeAsFileTime (&ftCurr); //set the common members for the scope pane items scopeItem.mask = SDI_STR | SDI_PARAM | SDI_CHILDREN; #ifdef SET_SCOPE_ICONS scopeItem.mask |= SDI_IMAGE | SDI_OPENIMAGE; scopeItem.nImage = IMG_CLOSEDBOX; scopeItem.nOpenImage = IMG_OPENBOX; #endif scopeItem.relativeID = pParent; scopeItem.displayname = MMC_CALLBACK; if (m_fExtension) { switch(cookie) { case NULL: //getting the folder // if we're an extension then add a root folder to hang everything off of if (m_fRSOP) { // make sure that nodes don't get enumerated if they contain no data if (FAILED(m_pResultPane->TestForRSOPData(cookie))) { if (m_pIRSOPInformation) { m_pIRSOPInformation->Release(); m_pIRSOPInformation = NULL; } return; } } scopeItem.lParam = IDS_FOLDER_TITLE; //use resource id's as cookies scopeItem.cChildren = 1; m_pScope->InsertItem(&scopeItem); break; case IDS_FOLDER_TITLE: for (i = IDS_LEVEL1_DIRS_START; i < IDS_LEVEL1_DIRS_END; i++) { BOOL fInsert = TRUE; if (m_fRSOP) { if (FAILED(m_pResultPane->TestForRSOPData(i))) { fInsert = FALSE; } } if (fInsert) { scopeItem.lParam = i; m_FolderData[GETINDEX(i)].Initialize(i, (LPCTSTR) m_szFileRoot ); if (i == IDS_MYDOCS && !m_fRSOP) { // // Show the My Pictures folder only if it does not follow MyDocs. // and only if there is no registry setting overriding the hiding behavior // for My Pics // if (AlwaysShowMyPicsNode()) { cChildren = 1; m_FolderData[GETINDEX(i)].m_bHideChildren = FALSE; } else { m_FolderData[GETINDEX(IDS_MYPICS)].Initialize(IDS_MYPICS, (LPCTSTR) m_szFileRoot ); m_FolderData[GETINDEX(i)].LoadSection(); m_FolderData[GETINDEX(IDS_MYPICS)].LoadSection(); myDocsFlags = m_FolderData[GETINDEX(i)].m_dwFlags; myPicsFlags = m_FolderData[GETINDEX(IDS_MYPICS)].m_dwFlags; if (((REDIR_DONT_CARE & myDocsFlags) && (REDIR_DONT_CARE & myPicsFlags)) || ((REDIR_FOLLOW_PARENT & myPicsFlags) && (!(REDIR_DONT_CARE & myDocsFlags))) ) { cChildren = 0; m_FolderData[GETINDEX(i)].m_bHideChildren = TRUE; } else { cChildren = 1; m_FolderData[GETINDEX(i)].m_bHideChildren = FALSE; } } } scopeItem.cChildren = cChildren; //only My Docs will possibly have children m_pScope->InsertItem(&scopeItem); m_FolderData[GETINDEX(i)].SetScopeItemID(scopeItem.ID); } if (IDS_MYDOCS == i && m_fRSOP && SUCCEEDED(m_pResultPane->TestForRSOPData(IDS_MYPICS))) { // In RSOP mode we put My Pictures after My Documents // instead of under it. Otherwise the results pane // for My Documents would contain a folder along with // the data and it would look very odd. scopeItem.lParam = IDS_MYPICS; scopeItem.cChildren = 0; m_pScope->InsertItem(&scopeItem); m_FolderData[GETINDEX(IDS_MYPICS)].Initialize (IDS_MYPICS, (LPCTSTR) m_szFileRoot ); m_FolderData[GETINDEX(IDS_MYPICS)].SetScopeItemID(scopeItem.ID); } } break; case IDS_MYDOCS: //of all levels 1 folder, only MyDocs has children if (!m_fRSOP && !(m_FolderData[GETINDEX(IDS_MYDOCS)].m_bHideChildren)) { scopeItem.lParam = IDS_MYPICS; scopeItem.cChildren = 0; m_pScope->InsertItem(&scopeItem); m_FolderData[GETINDEX(IDS_MYPICS)].Initialize (IDS_MYPICS, (LPCTSTR) m_szFileRoot ); m_FolderData[GETINDEX(IDS_MYPICS)].SetScopeItemID(scopeItem.ID); } break; } } } STDMETHODIMP CScopePane::GetSnapinDescription(LPOLESTR * lpDescription) { // UNDONE OLESAFE_COPYSTRING(*lpDescription, L"description"); return S_OK; } STDMETHODIMP CScopePane::GetProvider(LPOLESTR * lpName) { // UNDONE OLESAFE_COPYSTRING(*lpName, L"provider"); return S_OK; } STDMETHODIMP CScopePane::GetSnapinVersion(LPOLESTR * lpVersion) { // UNDONE OLESAFE_COPYSTRING(*lpVersion, L"version"); return S_OK; } STDMETHODIMP CScopePane::GetSnapinImage(HICON * hAppIcon) { // UNDONE return E_NOTIMPL; } STDMETHODIMP CScopePane::GetStaticFolderImage(HBITMAP * hSmallImage, HBITMAP * hSmallImageOpen, HBITMAP * hLargeImage, COLORREF * cMask) { // UNDONE return E_NOTIMPL; } STDMETHODIMP CScopePane::GetHelpTopic(LPOLESTR *lpCompiledHelpFile) { LPOLESTR lpHelpFile; lpHelpFile = (LPOLESTR) CoTaskMemAlloc (MAX_PATH * sizeof(WCHAR)); if (!lpHelpFile) { DbgMsg((TEXT("CScopePane::GetHelpTopic: Failed to allocate memory."))); return E_OUTOFMEMORY; } ExpandEnvironmentStringsW (L"%SystemRoot%\\Help\\gpedit.chm", lpHelpFile, MAX_PATH); *lpCompiledHelpFile = lpHelpFile; return S_OK; } STDMETHODIMP CScopePane::GetDisplayInfo(SCOPEDATAITEM* pScopeDataItem) { AFX_MANAGE_STATE(AfxGetStaticModuleState()); LONG i; ASSERT(pScopeDataItem != NULL); if (pScopeDataItem == NULL) return E_POINTER; if (IDS_FOLDER_TITLE == pScopeDataItem->lParam) { m_szFolderTitle.LoadString(IDS_FOLDER_TITLE); pScopeDataItem->displayname = (unsigned short *)((LPCOLESTR)m_szFolderTitle); } else { pScopeDataItem->displayname = L"???"; if (-1 != (i = GETINDEX(pScopeDataItem->lParam))) pScopeDataItem->displayname = (unsigned short*)((LPCOLESTR)(m_FolderData[i].m_szDisplayname)); } ASSERT(pScopeDataItem->displayname != NULL); return S_OK; } STDMETHODIMP CScopePane::CompareObjects(LPDATAOBJECT lpDataObjectA, LPDATAOBJECT lpDataObjectB) { if (lpDataObjectA == NULL || lpDataObjectB == NULL) return E_POINTER; // Make sure both data object are mine INTERNAL* pA; INTERNAL* pB; HRESULT hr = S_FALSE; pA = ExtractInternalFormat(lpDataObjectA); pB = ExtractInternalFormat(lpDataObjectB); if (pA != NULL && pB != NULL) hr = ((pA->m_type == pB->m_type) && (pA->m_cookie == pB->m_cookie)) ? S_OK : S_FALSE; FREE_INTERNAL(pA); FREE_INTERNAL(pB); return hr; } // Scope item property pages: STDMETHODIMP CScopePane::CreatePropertyPages(LPPROPERTYSHEETCALLBACK lpProvider, LONG_PTR handle, LPDATAOBJECT lpIDataObject) { AFX_MANAGE_STATE(AfxGetStaticModuleState()); HRESULT hr = S_FALSE; INTERNAL* pInternal = ExtractInternalFormat(lpIDataObject); if (! pInternal) return S_FALSE; DWORD cookie = pInternal->m_cookie; LONG i; BOOL fShowPage = FALSE; AFX_OLDPROPSHEETPAGE * pPsp; AFX_OLDPROPSHEETPAGE * pPspSettings; CFileInfo* pFileInfo; //it is one of the folders i = GETINDEX (cookie); pFileInfo = &(m_FolderData[i]); if (!pFileInfo->m_pRedirPage) //make sure that the property page is not already up. { pFileInfo->m_pRedirPage = new CRedirect(cookie); pFileInfo->m_pRedirPage->m_ppThis = &(pFileInfo->m_pRedirPage); pFileInfo->m_pRedirPage->m_pScope = this; pFileInfo->m_pRedirPage->m_pFileInfo = pFileInfo; fShowPage = TRUE; pPsp = (AFX_OLDPROPSHEETPAGE *)&(pFileInfo->m_pRedirPage->m_psp); //create the settings page; pFileInfo->m_pSettingsPage = new CRedirPref(); pFileInfo->m_pSettingsPage->m_ppThis = &(pFileInfo->m_pSettingsPage); pFileInfo->m_pSettingsPage->m_pFileInfo = pFileInfo; pPspSettings = (AFX_OLDPROPSHEETPAGE *)&(pFileInfo->m_pSettingsPage->m_psp); } if (fShowPage) //show page if it is not already up. { hr = SetPropPageToDeleteOnClose (pPsp); if (SUCCEEDED (hr)) hr = SetPropPageToDeleteOnClose (pPspSettings); if (SUCCEEDED(hr)) { HPROPSHEETPAGE hProp = CreateThemedPropertySheetPage(pPsp); HPROPSHEETPAGE hPropSettings = CreateThemedPropertySheetPage(pPspSettings); if (NULL == hProp || NULL == hPropSettings ) hr = E_UNEXPECTED; else { lpProvider->AddPage(hProp); lpProvider->AddPage (hPropSettings); hr = S_OK; } } } FREE_INTERNAL(pInternal); return hr; } // Scope item property pages: STDMETHODIMP CScopePane::QueryPagesFor(LPDATAOBJECT lpDataObject) { // scope panes don't have property pages in RSOP mode if (m_fRSOP) { return S_FALSE; } //the only property sheets we are presenting right now are those //for built-in folder redirection INTERNAL* pInternal = ExtractInternalFormat(lpDataObject); if (! pInternal) return S_FALSE; MMC_COOKIE cookie = pInternal->m_cookie; HRESULT hr = S_FALSE; CError error; if (CCT_SCOPE == pInternal->m_type) { if (SUCCEEDED(m_FolderData[GETINDEX(cookie)].LoadSection())) hr = S_OK; else { error.ShowConsoleMessage (m_pConsole, IDS_SECTIONLOAD_ERROR, m_FolderData[GETINDEX(cookie)].m_szDisplayname); hr = S_FALSE; } } FREE_INTERNAL(pInternal); return hr; } BOOL CScopePane::IsScopePaneNode(LPDATAOBJECT lpDataObject) { BOOL bResult = FALSE; INTERNAL* pInternal = ExtractInternalFormat(lpDataObject); if (! pInternal) return bResult; if (pInternal->m_type == CCT_SCOPE) bResult = TRUE; FREE_INTERNAL(pInternal); return bResult; } /////////////////////////////////////////////////////////////////////////////// // IExtendContextMenu implementation // STDMETHODIMP CScopePane::AddMenuItems(LPDATAOBJECT pDataObject, LPCONTEXTMENUCALLBACK pContextMenuCallback, LONG * pInsertionAllowed) { //we do not have any commands on the menu. return S_OK; } STDMETHODIMP CScopePane::Command(long nCommandID, LPDATAOBJECT pDataObject) { //we do not have any commands on the menu return S_OK; }