/////////////////////////////////////////////////////////////////////////////// // // Copyright (c) Microsoft Corp. All rights reserved. // // FILE // // snapwork.cpp // // SYNOPSIS // // Defines classes for implementing an MMC Snap-In. // // MODIFICATION HISTORY // // 02/19/2000 Original version. // /////////////////////////////////////////////////////////////////////////////// #include #include namespace SnapIn { void AfxThrowLastError() { DWORD error = GetLastError(); AfxThrowOleException(HRESULT_FROM_WIN32(error)); } ////////// // Clipboard formats we support. ////////// const CLIPFORMAT CCF_ID_NODETYPE = (CLIPFORMAT) RegisterClipboardFormatW(CCF_NODETYPE); const CLIPFORMAT CCF_ID_SZNODETYPE = (CLIPFORMAT) RegisterClipboardFormatW(CCF_SZNODETYPE); const CLIPFORMAT CCF_ID_DISPLAY_NAME = (CLIPFORMAT) RegisterClipboardFormatW(CCF_DISPLAY_NAME); const CLIPFORMAT CCF_ID_SNAPIN_CLASSID = (CLIPFORMAT) RegisterClipboardFormatW(CCF_SNAPIN_CLASSID); ////////// // Helper function that returns the length of a string in bytes. ////////// inline ULONG wcsbytelen(PCWSTR sz) throw () { return (wcslen(sz) + 1) * sizeof(WCHAR); } ////////// // Helper functions that writes data to an HGLOBAL ////////// HRESULT WriteDataToHGlobal( HGLOBAL& dst, const VOID* data, ULONG dataLen ) throw () { if (GlobalSize(dst) < dataLen) { HGLOBAL newGlobal = GlobalReAlloc(dst, dataLen, 0); if (!newGlobal) { return E_OUTOFMEMORY; } dst = newGlobal; } memcpy(dst, data, dataLen); return S_OK; } ////////// // Helper function that loads a string resource. ////////// ULONG LoadString( HMODULE module, UINT id, PCWSTR* string ) throw () { HRSRC resInfo = FindResourceW( module, MAKEINTRESOURCEW((id >> 4) + 1), RT_STRING ); if (resInfo) { HGLOBAL resData = LoadResource( module, resInfo ); if (resData) { PCWSTR sz = (PCWSTR)LockResource(resData); if (sz) { // Skip forward to our string. for (id &= 0xf; id > 0; --id) { sz += *sz + 1; } *string = sz + 1; return *sz; } } } *string = NULL; return 0; } } // Static member of ResourceString WCHAR ResourceString::empty; ResourceString::ResourceString(UINT id) throw () { PCWSTR string; ULONG length = LoadString( _Module.GetResourceInstance(), id, &string ); sz = new (std::nothrow) WCHAR[length + 1]; if (sz) { memcpy(sz, string, length * sizeof(WCHAR)); sz[length] = L'\0'; } else { sz = ∅ } } /////////////////////////////////////////////////////////////////////////////// // // Methods for manipulating a generic IDataObject (i.e., not necessarily one of // ours). // /////////////////////////////////////////////////////////////////////////////// VOID WINAPI ExtractData( IDataObject* dataObject, CLIPFORMAT format, PVOID data, DWORD dataLen ) { HGLOBAL global; ExtractData( dataObject, format, dataLen, &global ); memcpy(data, global, dataLen); GlobalFree(global); } VOID WINAPI ExtractData( IDataObject* dataObject, CLIPFORMAT format, DWORD maxDataLen, HGLOBAL* data ) { if (!dataObject) { AfxThrowOleException(E_POINTER); } FORMATETC formatetc = { format, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL }; STGMEDIUM stgmedium = { TYMED_HGLOBAL, NULL }; stgmedium.hGlobal = GlobalAlloc(GPTR, maxDataLen); if (!stgmedium.hGlobal) { AfxThrowOleException(E_OUTOFMEMORY); } HRESULT hr = dataObject->GetDataHere(&formatetc, &stgmedium); if (SUCCEEDED(hr)) { *data = stgmedium.hGlobal; } else { GlobalFree(stgmedium.hGlobal); AfxThrowOleException(hr); } } VOID WINAPI ExtractNodeType( IDataObject* dataObject, GUID* nodeType ) { ExtractData( dataObject, CCF_ID_NODETYPE, nodeType, sizeof(GUID) ); } // Convert an IDataObject to its corresponding SnapInDataItem. SnapInDataItem* SnapInDataItem::narrow(IDataObject* dataObject) throw () { if (!dataObject) { return NULL; } SnapInDataItem* object; HRESULT hr = dataObject->QueryInterface( __uuidof(SnapInDataItem), (PVOID*)&object ); if (FAILED(hr)) { return NULL; } object->Release(); return object; } // Default implementations of various functions used by GetDataHere. This way // derived classes don't have to implement these if they're sure MMC will never // ask for them. const GUID* SnapInDataItem::getNodeType() const throw () { return &GUID_NULL; } const GUID* SnapInDataItem::getSnapInCLSID() const throw () { return &GUID_NULL; } PCWSTR SnapInDataItem::getSZNodeType() const throw () { return L"{00000000-0000-0000-0000-000000000000}"; } // By default we compare column items as case sensitive strings. int SnapInDataItem::compare( SnapInDataItem& item, int column ) throw () { return wcscmp(getDisplayName(column), item.getDisplayName(column)); } ////////// // Do nothing implementations of all the notifications, etc. ////////// HRESULT SnapInDataItem::addMenuItems( SnapInView& view, LPCONTEXTMENUCALLBACK callback, long insertionAllowed ) { return S_FALSE; } HRESULT SnapInDataItem::createPropertyPages( SnapInView& view, LPPROPERTYSHEETCALLBACK provider, LONG_PTR handle ) { return S_FALSE; } HRESULT SnapInDataItem::queryPagesFor() throw () { return S_FALSE; } HRESULT SnapInDataItem::getResultViewType( LPOLESTR* ppViewType, long* pViewOptions ) throw () { return S_FALSE; } HRESULT SnapInDataItem::onButtonClick( SnapInView& view, MMC_CONSOLE_VERB verb ) { return S_FALSE; } HRESULT SnapInDataItem::onContextHelp( SnapInView& view ) { return S_FALSE; } HRESULT SnapInDataItem::onDelete( SnapInView& view ) { return S_FALSE; } HRESULT SnapInDataItem::onDoubleClick( SnapInView& view ) { return S_FALSE; } HRESULT SnapInDataItem::onExpand( SnapInView& view, HSCOPEITEM itemId, BOOL expanded ) { return S_FALSE; } HRESULT SnapInDataItem::onMenuCommand( SnapInView& view, long commandId ) { return S_FALSE; } HRESULT SnapInDataItem::onPropertyChange( SnapInView& view, BOOL scopeItem ) { return S_FALSE; } HRESULT SnapInDataItem::onRefresh(SnapInView& view) { return S_FALSE; } HRESULT SnapInDataItem::onRename( SnapInView& view, LPCOLESTR newName ) { return S_FALSE; } HRESULT SnapInDataItem::onSelect( SnapInView& view, BOOL scopeItem, BOOL selected ) { return S_FALSE; } HRESULT SnapInDataItem::onShow( SnapInView& view, HSCOPEITEM itemId, BOOL selected ) { return S_FALSE; } HRESULT SnapInDataItem::onToolbarButtonClick( SnapInView& view, int buttonId ) { return S_FALSE; } HRESULT SnapInDataItem::onToolbarSelect( SnapInView& view, BOOL scopeItem, BOOL selected ) { return S_FALSE; } HRESULT SnapInDataItem::onViewChange( SnapInView& view, LPARAM data, LPARAM hint ) { return S_FALSE; } ////////// // IUnknown ////////// STDMETHODIMP_(ULONG) SnapInDataItem::AddRef() { return InterlockedIncrement(&refCount); } STDMETHODIMP_(ULONG) SnapInDataItem::Release() { LONG l = InterlockedDecrement(&refCount); if (l == 0) { delete this; } return l; } STDMETHODIMP SnapInDataItem::QueryInterface(REFIID iid, void ** ppvObject) { if (ppvObject == NULL) { return E_POINTER; } else if (iid == __uuidof(SnapInDataItem) || iid == __uuidof(IUnknown) || iid == __uuidof(IDataObject)) { *ppvObject = this; } else { *ppvObject = NULL; return E_NOINTERFACE; } AddRef(); return S_OK; } ////////// // IDataObject ////////// STDMETHODIMP SnapInDataItem::GetData( FORMATETC *pformatetcIn, STGMEDIUM *pmedium ) { return E_NOTIMPL; } STDMETHODIMP SnapInDataItem::GetDataHere( FORMATETC *pformatetc, STGMEDIUM *pmedium ) { if (pmedium == NULL) { return E_POINTER; } if (pmedium->tymed != TYMED_HGLOBAL) { return DV_E_TYMED; } ULONG dataLen; const VOID* data; if (pformatetc->cfFormat == CCF_ID_NODETYPE) { dataLen = sizeof(GUID); data = getNodeType(); } else if (pformatetc->cfFormat == CCF_ID_DISPLAY_NAME) { dataLen = wcsbytelen(getDisplayName()); data = getDisplayName(); } else if (pformatetc->cfFormat == CCF_ID_SZNODETYPE) { dataLen = wcsbytelen(getSZNodeType()); data = getSZNodeType(); } else if (pformatetc->cfFormat == CCF_ID_SNAPIN_CLASSID) { dataLen = sizeof(GUID); data = getSnapInCLSID(); } else { return DV_E_CLIPFORMAT; } return WriteDataToHGlobal(pmedium->hGlobal, data, dataLen); } STDMETHODIMP SnapInDataItem::QueryGetData( FORMATETC *pformatetc ) { return E_NOTIMPL; } STDMETHODIMP SnapInDataItem::GetCanonicalFormatEtc( FORMATETC *pformatectIn, FORMATETC *pformatetcOut ) { return E_NOTIMPL; } STDMETHODIMP SnapInDataItem::SetData( FORMATETC *pformatetc, STGMEDIUM *pmedium, BOOL fRelease ) { return E_NOTIMPL; } STDMETHODIMP SnapInDataItem::EnumFormatEtc( DWORD dwDirection, IEnumFORMATETC **ppenumFormatEtc ) { return E_NOTIMPL; } STDMETHODIMP SnapInDataItem::DAdvise( FORMATETC *pformatetc, DWORD advf, IAdviseSink *pAdvSink, DWORD *pdwConnection ) { return E_NOTIMPL; } STDMETHODIMP SnapInDataItem::DUnadvise( DWORD dwConnection ) { return E_NOTIMPL; } STDMETHODIMP SnapInDataItem::EnumDAdvise( IEnumSTATDATA **ppenumAdvise ) { return E_NOTIMPL; } SnapInDataItem::~SnapInDataItem() throw () { } PCWSTR SnapInPreNamedItem::getDisplayName(int column) const throw () { return name; } HRESULT SnapInView::displayHelp(PCWSTR contextHelpPath) { CComPtr displayHelp; HRESULT hr = console->QueryInterface( __uuidof(IDisplayHelp), (PVOID*)&displayHelp ); if (FAILED(hr)) { return hr; } return displayHelp->ShowTopic(const_cast(contextHelpPath)); } void SnapInView::updateAllViews( SnapInDataItem& item, LPARAM data, INT_PTR hint ) const { CheckError(master->getConsole()->UpdateAllViews(&item, data, hint)); } void SnapInView::deleteResultItem(const SnapInDataItem& item) const { HRESULTITEM itemId; CheckError(resultData->FindItemByLParam((LPARAM)&item, &itemId)); CheckError(resultData->DeleteItem(itemId, 0)); } void SnapInView::updateResultItem(const SnapInDataItem& item) const { if (resultData != NULL) { HRESULTITEM itemId; CheckError(resultData->FindItemByLParam((LPARAM)&item, &itemId)); CheckError(resultData->UpdateItem(itemId)); } } bool SnapInView::isPropertySheetOpen(const SnapInDataItem& item) const { HRESULT hr = sheetProvider->FindPropertySheet( (MMC_COOKIE)&item, const_cast(this), const_cast(&item) ); CheckError(hr); return hr == S_OK; } IToolbar* SnapInView::attachToolbar(size_t index) { // Make sure we have a controlbar. if (!controlbar) { AfxThrowOleException(E_POINTER); } // Get the entry for this index. ToolbarEntry& entry = toolbars[index]; // Create the toolbar if necessary. if (!entry.toolbar) { // Create the toolbar. CComPtr unk; CheckError(controlbar->Create(TOOLBAR, this, &unk)); CComPtr newToolbar; CheckError(unk->QueryInterface(__uuidof(IToolbar), (PVOID*)&newToolbar)); const SnapInToolbarDef& def = *(entry.def); // Add the bitmaps. CheckError(newToolbar->AddBitmap( def.nImages, def.hbmp, 16, 16, def.crMask )); // Add the buttons. CheckError(newToolbar->AddButtons( def.nButtons, def.lpButtons)); // All went well, so save it away. entry.toolbar = newToolbar; } // Attach the toolbar to the controlbar ... CheckError(controlbar->Attach(TOOLBAR, entry.toolbar)); return entry.toolbar; } void SnapInView::detachToolbar(size_t index) throw () { if (toolbars[index].toolbar) { // We don't care if this fails, because there's nothing we can do about // it anyway. controlbar->Detach(toolbars[index].toolbar); } } void SnapInView::reSort() const { CheckError(resultData->Sort(sortColumn, sortOption, 0)); } void SnapInView::formatMessageBox( UINT titleId, UINT formatId, BOOL ignoreInserts, UINT style, int* retval, ... ) { ResourceString title(titleId); ResourceString format(formatId); HRESULT hr; if (ignoreInserts) { hr = console->MessageBox( format, title, style, retval ); } else { va_list marker; va_start(marker, retval); PWSTR text; DWORD nchar = FormatMessageW( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_STRING, format, 0, 0, (PWSTR)&text, 4096, &marker ); va_end(marker); if (!nchar) { AfxThrowLastError(); } hr = console->MessageBox( text, title, style, retval ); LocalFree(text); } CheckError(hr); } void SnapInView::setImageStrip( UINT smallStripId, UINT largeStripId, BOOL scopePane ) { ////////// // Load the bitmaps. ////////// HBITMAP smallStrip, largeStrip; smallStrip = LoadBitmap( _Module.GetModuleInstance(), MAKEINTRESOURCE(smallStripId) ); if (smallStrip) { largeStrip = LoadBitmap( _Module.GetModuleInstance(), MAKEINTRESOURCE(largeStripId) ); } if (!smallStrip || !largeStrip) { AfxThrowLastError(); } ////////// // Set the image strip. ////////// HRESULT hr; IImageList* imageList; if (scopePane) { hr = console->QueryScopeImageList(&imageList); } else { hr = console->QueryResultImageList(&imageList); } if (SUCCEEDED(hr)) { hr = imageList->ImageListSetStrip( (LONG_PTR*)smallStrip, (LONG_PTR*)largeStrip, 0, RGB(255, 0, 255) ); imageList->Release(); } DeleteObject(smallStrip); DeleteObject(largeStrip); CheckError(hr); } STDMETHODIMP_(ULONG) SnapInView::AddRef() { return InternalAddRef(); } STDMETHODIMP_(ULONG) SnapInView::Release() { ULONG l = InternalRelease(); if (l == 0) { delete this; } return l; } STDMETHODIMP SnapInView::Initialize(LPCONSOLE lpConsole) { HRESULT hr; hr = lpConsole->QueryInterface( __uuidof(IConsole2), (PVOID*)&console ); if (FAILED(hr)) { return hr; } hr = lpConsole->QueryInterface( __uuidof(IHeaderCtrl2), (PVOID*)&headerCtrl ); if (FAILED(hr)) { return hr; } hr = lpConsole->QueryInterface( __uuidof(sheetProvider), (PVOID*)&sheetProvider ); if (FAILED(hr)) { return hr; } hr = lpConsole->QueryInterface( __uuidof(IResultData), (PVOID*)&resultData ); if (FAILED(hr)) { return hr; } return S_OK; } STDMETHODIMP SnapInView::Destroy(MMC_COOKIE cookie) { resultData.Release(); sheetProvider.Release(); headerCtrl.Release(); console.Release(); nameSpace.Release(); return S_OK; } STDMETHODIMP SnapInView::GetResultViewType( MMC_COOKIE cookie, LPOLESTR* ppViewType, long* pViewOptions ) { return ((SnapInDataItem*)cookie)->getResultViewType( ppViewType, pViewOptions ); } STDMETHODIMP SnapInView::GetDisplayInfo( RESULTDATAITEM* pResultDataItem ) { if (pResultDataItem->mask & RDI_STR) { SnapInDataItem* item = (SnapInDataItem*)(pResultDataItem->lParam); pResultDataItem->str = const_cast(item->getDisplayName(pResultDataItem->nCol)); } return S_OK; } STDMETHODIMP SnapInView::Initialize(LPUNKNOWN pUnknown) { HRESULT hr; CComPtr initNameSpace; hr = pUnknown->QueryInterface( __uuidof(IConsoleNameSpace2), (PVOID*)&initNameSpace ); if (FAILED(hr)) { return hr; } CComPtr initConsole; hr = pUnknown->QueryInterface( __uuidof(IConsole), (PVOID*)&initConsole ); if (FAILED(hr)) { return hr; } hr = internalInitialize(initNameSpace, this); if (FAILED(hr)) { return hr; } return Initialize(initConsole); } STDMETHODIMP SnapInView::CreateComponent(LPCOMPONENT* ppComponent) { HRESULT hr; CComObject* newComponent; hr = CComObject::CreateInstance(&newComponent); if (FAILED(hr)) { return hr; } CComPtr newView(newComponent); hr = newView.p->internalInitialize(nameSpace, this); if (FAILED(hr)) { return hr; } (*ppComponent = newView)->AddRef(); return S_OK; } STDMETHODIMP SnapInView::Destroy() { return Destroy((MMC_COOKIE)0); } STDMETHODIMP SnapInView::GetDisplayInfo(SCOPEDATAITEM* pScopeDataItem) { if (pScopeDataItem->mask & SDI_STR) { SnapInDataItem* item = (SnapInDataItem*)(pScopeDataItem->lParam); pScopeDataItem->displayname = const_cast(item->getDisplayName()); } return S_OK; } STDMETHODIMP SnapInView::QueryDataObject( MMC_COOKIE cookie, DATA_OBJECT_TYPES type, LPDATAOBJECT* ppDataObject ) { if (!IS_SPECIAL_COOKIE(cookie)) { (*ppDataObject = (SnapInDataItem*)cookie)->AddRef(); return S_OK; } else { return S_FALSE; } } STDMETHODIMP SnapInView::Notify( LPDATAOBJECT lpDataObject, MMC_NOTIFY_TYPE event, LPARAM arg, LPARAM param ) { // Extract the SnapInDataItem. SnapInDataItem* item; if (IS_SPECIAL_DATAOBJECT(lpDataObject)) { item = NULL; } else { item = SnapInDataItem::narrow(lpDataObject); } HRESULT hr = S_FALSE; try { if (item) { // If we have a SnapInDataItem, dispatch the notification ... switch (event) { case MMCN_BTN_CLICK: hr = item->onButtonClick(*this, (MMC_CONSOLE_VERB)param); break; case MMCN_CONTEXTHELP: hr = item->onContextHelp(*this); break; case MMCN_DELETE: hr = item->onDelete(*this); break; case MMCN_DBLCLICK: hr = item->onDoubleClick(*this); break; case MMCN_EXPAND: hr = item->onExpand(*this, (HSCOPEITEM)param, (BOOL)arg); break; case MMCN_REFRESH: hr = item->onRefresh(*this); break; case MMCN_RENAME: hr = item->onRename(*this, (LPOLESTR)param); break; case MMCN_SELECT: hr = item->onSelect(*this, LOWORD(arg), HIWORD(arg)); break; case MMCN_SHOW: hr = item->onShow(*this, (HSCOPEITEM)param, (BOOL)arg); break; case MMCN_VIEW_CHANGE: hr = item->onViewChange(*this, arg, param); break; } } else { // ... otherwise, handle it ourselves. switch (event) { case MMCN_COLUMN_CLICK: sortColumn = (int)arg; sortOption = (int)param; break; case MMCN_PROPERTY_CHANGE: { hr = ((SnapInDataItem*)param)->onPropertyChange(*this, arg); break; } } } } CATCH_AND_SAVE(hr); return hr; } STDMETHODIMP SnapInView::CompareObjects( LPDATAOBJECT lpDataObjectA, LPDATAOBJECT lpDataObjectB ) { return SnapInDataItem::narrow(lpDataObjectA) == SnapInDataItem::narrow(lpDataObjectB) ? S_OK : S_FALSE; } STDMETHODIMP SnapInView::AddMenuItems( LPDATAOBJECT lpDataObject, LPCONTEXTMENUCALLBACK piCallback, long *pInsertionAllowed ) { SnapInDataItem* item = SnapInDataItem::narrow(lpDataObject); if (!item) { return S_FALSE; } HRESULT hr; try { hr = item->addMenuItems(*this, piCallback, *pInsertionAllowed); } CATCH_AND_SAVE(hr); return hr; } STDMETHODIMP SnapInView::Command( long lCommandID, LPDATAOBJECT lpDataObject ) { SnapInDataItem* item = SnapInDataItem::narrow(lpDataObject); if (!item) { return S_FALSE; } HRESULT hr; try { hr = item->onMenuCommand(*this, lCommandID); } CATCH_AND_SAVE(hr); return hr; } STDMETHODIMP SnapInView::ControlbarNotify( MMC_NOTIFY_TYPE event, LPARAM arg, LPARAM param ) { // If we don't have a controlbar, there's nothing we can do. if (!controlbar) { return S_FALSE; } // Get the IDataObject. IDataObject* lpDataObject; switch (event) { case MMCN_BTN_CLICK: lpDataObject = (IDataObject*)arg; break; case MMCN_SELECT: lpDataObject = (IDataObject*)param; break; default: lpDataObject = NULL; } // Convert to a SnapInDataItem. if (IS_SPECIAL_DATAOBJECT(lpDataObject)) { return S_FALSE; } SnapInDataItem* item = SnapInDataItem::narrow(lpDataObject); if (!item) { return S_FALSE; } // Dispatch the notification. HRESULT hr = S_FALSE; try { switch (event) { case MMCN_BTN_CLICK: hr = item->onToolbarButtonClick(*this, param); break; case MMCN_SELECT: hr = item->onToolbarSelect(*this, LOWORD(arg), HIWORD(arg)); break; } } CATCH_AND_SAVE(hr); return hr; } STDMETHODIMP SnapInView::SetControlbar( LPCONTROLBAR pControlbar ) { releaseToolbars(); controlbar = pControlbar; return S_OK; } STDMETHODIMP SnapInView::CreatePropertyPages( LPPROPERTYSHEETCALLBACK lpProvider, LONG_PTR handle, LPDATAOBJECT lpDataObject ) { SnapInDataItem* item = SnapInDataItem::narrow(lpDataObject); if (!item) { return S_FALSE; } HRESULT hr; try { hr = item->createPropertyPages(*this, lpProvider, handle); } CATCH_AND_SAVE(hr); return hr; } STDMETHODIMP SnapInView::QueryPagesFor( LPDATAOBJECT lpDataObject ) { SnapInDataItem* item = SnapInDataItem::narrow(lpDataObject); return item ? item->queryPagesFor() : S_FALSE; } STDMETHODIMP SnapInView::GetWatermarks( LPDATAOBJECT lpIDataObject, HBITMAP *lphWatermark, HBITMAP *lphHeader, HPALETTE *lphPalette, BOOL *bStretch ) { return E_NOTIMPL; } HRESULT SnapInView::Compare( LPARAM lUserParam, MMC_COOKIE cookieA, MMC_COOKIE cookieB, int* pnResult ) { *pnResult = ((SnapInDataItem*)cookieA)->compare( *(SnapInDataItem*)cookieB, *pnResult ); return S_OK; } const SnapInToolbarDef* SnapInView::getToolbars() const throw () { static SnapInToolbarDef none; return &none; } SnapInView::SnapInView() throw () : master(NULL), toolbars(NULL), numToolbars(0), sortColumn(0), sortOption(RSI_NOSORTICON) { } SnapInView::~SnapInView() throw () { releaseToolbars(); delete[] toolbars; if (master && master != this) { master->Release(); } } HRESULT SnapInView::internalInitialize( IConsoleNameSpace2* consoleNameSpace, SnapInView* masterView ) throw () { nameSpace = consoleNameSpace; master = masterView; if (master != this) { master->AddRef(); } const SnapInToolbarDef* defs = master->getToolbars(); // How many new toolbars are there? size_t count = 0; while (defs[count].nImages) { ++count; } if (count) { // Allocate memory ... toolbars = new (std::nothrow) ToolbarEntry[count]; if (!toolbars) { return E_OUTOFMEMORY; } // ... and save the definitions. We don't actually create the toolbars // now. We do this as we need them. for (size_t i = 0; i < count; ++i) { toolbars[i].def = defs + i; } } numToolbars = count; return S_OK; } void SnapInView::releaseToolbars() throw () { if (controlbar) { for (size_t i = 0; i < numToolbars; ++i) { if (toolbars[i].toolbar) { controlbar->Detach(toolbars[i].toolbar); toolbars[i].toolbar.Release(); } } } } SnapInPropertyPage::SnapInPropertyPage( UINT nIDTemplate, UINT nIDHeaderTitle, UINT nIDHeaderSubTitle, bool EnableHelp ) : CHelpPageEx(nIDTemplate, 0, nIDHeaderTitle, nIDHeaderSubTitle, EnableHelp), notify(0), param(0), owner(false), applied(false), modified(FALSE) { if (!nIDHeaderTitle) { m_psp.dwFlags |= PSP_HIDEHEADER; } } SnapInPropertyPage::SnapInPropertyPage( LONG_PTR notifyHandle, LPARAM notifyParam, bool deleteHandle, UINT nIDTemplate, UINT nIDCaption, bool EnableHelp ) : CHelpPageEx(nIDTemplate, nIDCaption, EnableHelp), notify(notifyHandle), param(notifyParam), owner(deleteHandle), applied(false), modified(FALSE) { } SnapInPropertyPage::~SnapInPropertyPage() throw () { if (owner && notify) { MMCFreeNotifyHandle(notify); } } void SnapInPropertyPage::addToMMCSheet(IPropertySheetCallback* cback) { // Swap our callback for the MFC supplied one. mfcCallback = m_psp.pfnCallback; m_psp.pfnCallback = propSheetPageProc; m_psp.lParam = (LPARAM)this; HRESULT hr = MMCPropPageCallback(&m_psp); if (SUCCEEDED(hr)) { HPROPSHEETPAGE page = CreatePropertySheetPage(&m_psp); if (page) { hr = cback->AddPage(page); if (FAILED(hr)) { DestroyPropertySheetPage(page); } } else { // GetLastError() doesn't work with CreatePropertySheetPage. hr = E_UNEXPECTED; } } if (FAILED(hr)) { delete this; } CheckError(hr); } void SnapInPropertyPage::DoDataExchange(CDataExchange* pDX) { pDX->m_bSaveAndValidate ? getData() : setData(); } BOOL SnapInPropertyPage::OnApply() { // If we've been modified, ... if (modified) { try { // Save the changes. saveChanges(); } catch (CException* e) { // Bring up a message box. reportException(e); // We're in an indeterminate state, so we can't cancel. CancelToClose(); // Block the apply. return FALSE; } // Notify MMC if necessary. if (notify) { MMCPropertyChangeNotify(notify, param); } // Set our flags. applied = true; modified = FALSE; } return TRUE; } BOOL SnapInPropertyPage::OnWizardFinish() { try { saveChanges(); } catch (CException* e) { // Bring up a message box. reportException(e); // Disable all the buttons. Something's wrong, so we'll only let the user // cancel. ::PostMessageW( ::GetParent(m_hWnd), PSM_SETWIZBUTTONS, 0, (LPARAM)(DWORD)PSWIZB_DISABLEDFINISH ); return FALSE; } return TRUE; } void SnapInPropertyPage::OnReset() { // If we've been modified, ... if (modified) { // ... discard the changes. discardChanges(); modified = FALSE; } } void SnapInPropertyPage::SetModified(BOOL bChanged) { modified = bChanged; CHelpPageEx::SetModified(bChanged); } void SnapInPropertyPage::getData() { } void SnapInPropertyPage::setData() { } void SnapInPropertyPage::saveChanges() { } void SnapInPropertyPage::discardChanges() { } void SnapInPropertyPage::enableControl(int controlId, bool enable) { ::EnableWindow(::GetDlgItem(m_hWnd, controlId), (enable ? TRUE : FALSE)); } void SnapInPropertyPage::fail(int controlId, UINT errorText, bool isEdit) { failNoThrow(controlId, errorText, isEdit); AfxThrowUserException(); } void SnapInPropertyPage::failNoThrow(int controlId, UINT errorText, bool isEdit) { // Give the offending control the focus. HWND ctrl = ::GetDlgItem(m_hWnd, controlId); ::SetFocus(ctrl); if (isEdit) { ::SendMessage(ctrl, EM_SETSEL, 0, -1); } // Bring up a message box. reportError(errorText); } void SnapInPropertyPage::initControl(int controlId, CWnd& control) { if (control.m_hWnd == NULL) { if (!control.SubclassWindow(::GetDlgItem(m_hWnd, controlId))) { AfxThrowNotSupportedException(); } } } void SnapInPropertyPage::onChange() { SetModified(); } void SnapInPropertyPage::reportError(UINT errorText) { // Bring up a message box. MessageBox( ResourceString(errorText), ResourceString(getErrorCaption()), MB_ICONWARNING ); } void SnapInPropertyPage::reportException(CException* e) { // Get the error message. WCHAR errorText[256]; e->GetErrorMessage(errorText, sizeof(errorText)/sizeof(errorText[0])); e->Delete(); // Bring up a message box. MessageBox( errorText, ResourceString(getErrorCaption()), MB_ICONERROR ); } void SnapInPropertyPage::setLargeFont(int controlId) { static CFont largeFont; CWnd* ctrl = GetDlgItem(controlId); if (ctrl) { // If we don't have the large font yet, ... if (!(HFONT)largeFont) { // ... create it. largeFont.CreatePointFont( 10 * _wtoi(ResourceString(IDS_LARGE_FONT_SIZE)), ResourceString(IDS_LARGE_FONT_NAME) ); } ctrl->SetFont(&largeFont); } } void SnapInPropertyPage::showControl(int controlId, bool show) { CWnd* ctrl = GetDlgItem(controlId); if (ctrl) { show ? ctrl->ModifyStyle(0, WS_VISIBLE) : ctrl->ModifyStyle(WS_VISIBLE, 0); } } void SnapInPropertyPage::getValue( int controlId, LONG& value, UINT errorText ) { WCHAR buffer[32]; int len = GetDlgItemText(controlId, buffer, 32); // We'll fail anything that's longer than 30 characters. This is an // arbitrary bound. We just need to make sure that buffer is long enough to // hold any valid integer plus a little whitespace. if (len == 0 || len > 30) { fail(controlId, errorText); } // Skip any leading whitespace. PWSTR sz = buffer; while (*sz == L' ' || *sz == L'\t') { ++sz; } // Save the first non-whitespace character. WCHAR first = *sz; // Convert the integer. value = wcstol(sz, &sz, 10); // Skip any trailing whitespace. while (*sz == L' ' || *sz == L'\t') { ++sz; } // Make sure all went well. if ((value == 0 && first != L'0') || *sz != L'\0' || value == LONG_MIN || value == LONG_MAX) { fail(controlId, errorText); } } void SnapInPropertyPage::setValue( int controlId, LONG value ) { WCHAR buffer[12]; SetDlgItemText(controlId, _ltow(value, buffer, 10)); } void SnapInPropertyPage::getValue( int controlId, bool& value ) { value = IsDlgButtonChecked(controlId) != 0; } void SnapInPropertyPage::setValue( int controlId, bool value ) { CheckDlgButton(controlId, (value ? BST_CHECKED : BST_UNCHECKED)); } void SnapInPropertyPage::getValue( int controlId, CComBSTR& value, bool trim ) { HWND hwnd = ::GetDlgItem(m_hWnd, controlId); int len = ::GetWindowTextLength(hwnd); SysFreeString(value.m_str); value.m_str = SysAllocStringLen(NULL, len); if (!value) { AfxThrowMemoryException(); } ::GetWindowTextW(hwnd, value, len + 1); if (trim) { SdoTrimBSTR(value); } } void SnapInPropertyPage::setValue( int controlId, PCWSTR value ) { SetDlgItemText(controlId, value); } void SnapInPropertyPage::getRadio( int firstId, int lastId, LONG& value ) { value = GetCheckedRadioButton(firstId, lastId); value = value ? value - firstId : -1; } void SnapInPropertyPage::setRadio( int firstId, int lastId, LONG value ) { CheckRadioButton(firstId, lastId, firstId + value); } UINT CALLBACK SnapInPropertyPage::propSheetPageProc( HWND hwnd, UINT uMsg, LPPROPSHEETPAGE ppsp ) throw () { SnapInPropertyPage* page = (SnapInPropertyPage*)(ppsp->lParam); UINT retval = page->mfcCallback(hwnd, uMsg, ppsp); if (uMsg == PSPCB_RELEASE) { delete page; } return retval; }