Source Files
The source files for the AppExt project are listed in alphabetical order. This source code is intended only as a supplement to existing Microsoft documentation.
Note
THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR PURPOSE. MICROSOFT CORPORATION SHALL NOT BE LIABLE FOR ANY TECHNICAL OR EDITORIAL ERRORS OR OMISSIONS CONTAINED HEREIN.
The source file, About.h, that supports the Help About snap-in for the extension to the property pages of the Applications snap-in is listed below.
//About.cpp #include "About.h" #include "resource.h" #include "globals.h" #include <crtdbg.h> CSnapinAbout::CSnapinAbout() : m_cref(0) { OBJECT_CREATED m_hSmallImage = (HBITMAP)LoadImage(g_hinst, MAKEINTRESOURCE(IDB_SMBMP), IMAGE_BITMAP, 16, 16, LR_LOADTRANSPARENT); m_hLargeImage = (HBITMAP)LoadImage(g_hinst, MAKEINTRESOURCE(IDB_LGBMP), IMAGE_BITMAP, 32, 32, LR_LOADTRANSPARENT); m_hSmallImageOpen = (HBITMAP)LoadImage(g_hinst, MAKEINTRESOURCE(IDB_SMOPEN), IMAGE_BITMAP, 16, 16, LR_LOADTRANSPARENT); m_hAppIcon = LoadIcon(g_hinst, MAKEINTRESOURCE(IDI_ICON1)); } CSnapinAbout::~CSnapinAbout() { if (m_hSmallImage != NULL) FreeResource(m_hSmallImage); if (m_hLargeImage != NULL) FreeResource(m_hLargeImage); if (m_hSmallImageOpen != NULL) FreeResource(m_hSmallImageOpen); if (m_hAppIcon != NULL) FreeResource(m_hAppIcon); m_hSmallImage = NULL; m_hLargeImage = NULL; m_hSmallImageOpen = NULL; m_hAppIcon = NULL; OBJECT_DESTROYED } /////////////////////// // IUnknown implementation /////////////////////// STDMETHODIMP CSnapinAbout::QueryInterface(REFIID riid, LPVOID *ppv) { if (!ppv) return E_FAIL; *ppv = NULL; if (IsEqualIID(riid, IID_IUnknown)) *ppv = static_cast<ISnapinAbout *>(this); else if (IsEqualIID(riid, IID_ISnapinAbout)) *ppv = static_cast<ISnapinAbout *>(this); if (*ppv) { reinterpret_cast<IUnknown *>(*ppv)->AddRef(); return S_OK; } return E_NOINTERFACE; } STDMETHODIMP_(ULONG) CSnapinAbout::AddRef() { return InterlockedIncrement((LONG *)&m_cref); } STDMETHODIMP_(ULONG) CSnapinAbout::Release() { if (InterlockedDecrement((LONG *)&m_cref) == 0) { // we need to decrement our object count in the DLL delete this; return 0; } return m_cref; } /////////////////////////////// // Interface ISnapinAbout /////////////////////////////// STDMETHODIMP CSnapinAbout::GetSnapinDescription( /* [out] */ LPOLESTR *lpDescription) { _TCHAR szDesc[MAX_PATH]; LoadString(g_hinst, IDS_SNAPINDESC, szDesc, (sizeof szDesc)/(sizeof szDesc[0])); return AllocOleStr(lpDescription, szDesc); } STDMETHODIMP CSnapinAbout::GetProvider( /* [out] */ LPOLESTR *lpName) { return AllocOleStr(lpName, _T("Copyright ) Microsoft Corporation"));; } STDMETHODIMP CSnapinAbout::GetSnapinVersion( /* [out] */ LPOLESTR *lpVersion) { return AllocOleStr(lpVersion, _T("1.0"));; } STDMETHODIMP CSnapinAbout::GetSnapinImage( /* [out] */ HICON *hAppIcon) { *hAppIcon = m_hAppIcon; if (*hAppIcon == NULL) return E_FAIL; else return S_OK; } STDMETHODIMP CSnapinAbout::GetStaticFolderImage( /* [out] */ HBITMAP *hSmallImage, /* [out] */ HBITMAP *hSmallImageOpen, /* [out] */ HBITMAP *hLargeImage, /* [out] */ COLORREF *cMask) { *hSmallImage = m_hSmallImage; *hLargeImage = m_hLargeImage; *hSmallImageOpen = m_hSmallImageOpen; *cMask = RGB(0, 128, 128); if (*hSmallImage == NULL || *hLargeImage == NULL || *hSmallImageOpen == NULL) return E_FAIL; else return S_OK; } // this allocates a chunk of memory using CoTaskMemAlloc and copies our chars into it HRESULT CSnapinAbout::AllocOleStr(LPOLESTR *lpDest, _TCHAR *szBuffer) { MAKE_WIDEPTR_FROMTSTR_ALLOC(wszStr, szBuffer); *lpDest = wszStr; return S_OK; }
The source file, AppExt.cpp, that supports the extension to the property pages of the Applications snap-in is listed below.
//AppExt.cpp #ifndef ISOLATION_AWARE_ENABLED // enable control styles #define ISOLATION_AWARE_ENABLED 1 #define _WIN32_FUSION 0x0100 #endif #include "AppExt.h" #include "resource.h" #include "globals.h" #include "guids.h" #include <crtdbg.h> #include <afxres.h> #include "commctrl.h" #include "utility.h" using namespace std; // we need to do this to get around MMC.IDL - it explicitly defines // the clipboard formats as WCHAR types... #define _T_CCF_DISPLAY_NAME _T("CCF_DISPLAY_NAME") #define _T_CCF_NODETYPE _T("CCF_NODETYPE") #define _T_CCF_SNAPIN_CLASSID _T("CCF_SNAPIN_CLASSID") // These are the clipboard formats that we must supply at a minimum. // mmc.h actually defined these. We can make up our own to use for // other reasons. We don't need any others at this time. UINT CPropSheetExtension::s_cfDisplayName = RegisterClipboardFormat(_T_CCF_DISPLAY_NAME); UINT CPropSheetExtension::s_cfNodeType = RegisterClipboardFormat(_T_CCF_NODETYPE); UINT CPropSheetExtension::s_cfSnapInCLSID = RegisterClipboardFormat(_T_CCF_SNAPIN_CLASSID); CPropSheetExtension::CPropSheetExtension() : m_cref( 0 ), m_pXmlDom( NULL ) { OBJECT_CREATED } CPropSheetExtension::~CPropSheetExtension() { OBJECT_DESTROYED } /////////////////////// // IUnknown implementation /////////////////////// STDMETHODIMP CPropSheetExtension::QueryInterface(REFIID riid, LPVOID *ppv) { if (!ppv) return E_FAIL; *ppv = NULL; if (IsEqualIID(riid, IID_IUnknown)) *ppv = static_cast<IExtendPropertySheet*>(this); else if (IsEqualIID(riid, IID_IExtendPropertySheet)) *ppv = static_cast<IExtendPropertySheet*>(this); else if (IsEqualIID(riid, IID_ISnapinHelp2)) *ppv = static_cast<ISnapinHelp2*>(this); if (*ppv) { reinterpret_cast<IUnknown *>(*ppv)->AddRef(); return S_OK; } return E_NOINTERFACE; } STDMETHODIMP_(ULONG) CPropSheetExtension::AddRef() { return InterlockedIncrement((LONG *)&m_cref); } STDMETHODIMP_(ULONG) CPropSheetExtension::Release() { ULONG ul = InterlockedDecrement((LONG *)&m_cref); if (ul == 0) { // we need to decrement our object count in the DLL delete this; return 0; } return ul; } INT_PTR CALLBACK CPropSheetExtension::DialogProc( HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam ) { LRESULT lr = ERROR_SUCCESS; CPropSheetExtension* pThis = NULL; if( uMsg == WM_INITDIALOG ) { // get a pointer to the PROPSHEETPAGE structure: LPPROPSHEETPAGE pPage = reinterpret_cast<LPPROPSHEETPAGE>( lParam ); // obtain pointer to parent object: pThis = reinterpret_cast<CPropSheetExtension*>( pPage->lParam ); // pass pointer to parent object to this window: SetWindowLong( hwndDlg, GWLP_USERDATA, (LONG)pThis ); } else { // obtain pointer to parent object from this window: pThis = reinterpret_cast<CPropSheetExtension*>( GetWindowLong( hwndDlg, GWLP_USERDATA ) ); } switch ( uMsg ) { case WM_INITDIALOG: { // enable themes: InitCommonControls(); if ( pThis == NULL ) { // invalid parent object pointer! exit! abort! EndDialog( hwndDlg, IDABORT ); break; } // // TODO: get properties data: // // // an example of a checkbox value: // IXMLDOMNode* pElmExampleProp = NULL; // find the "Reg" property element: HRESULT hr = pThis->m_pXmlDom->selectSingleNode( L"/Application/Properties/Reg[@id='exampleSetting']", &pElmExampleProp ); if ( hr == S_OK ) { BSTR bstrValue; // get the registry property value: GetAttribute( pElmExampleProp, _T( "value" ), bstrValue ); int nResult = _wcsicmp( bstrValue, _T( "1" ) ); bool bChecked = ( nResult == STRING_MATCH ); // set the state of the control: CheckDlgButton( hwndDlg, IDC_CHECK1, bChecked ? BST_CHECKED : BST_UNCHECKED ); pElmExampleProp->Release(); ::SysFreeString(bstrValue); } else { // set the default state of the control: CheckDlgButton( hwndDlg, IDC_CHECK1, BST_UNCHECKED ); } break; } case WM_SETCURSOR: { // activate the property page when the client area is clicked: switch ( HIWORD( lParam ) ) { case WM_LBUTTONDOWN: case WM_RBUTTONDOWN: case WM_MBUTTONDOWN: { if ( (HWND)wParam == hwndDlg ) { PostMessage( hwndDlg, WM_SETFOCUS, 0, 0 ); } } } break; } case WM_CTLCOLORDLG: { // prevent the default background dialog color from being painted: return NULL; } case WM_NOTIFY: { NMHDR* pNmhdr = reinterpret_cast<NMHDR*>( lParam ); if ( pNmhdr->code == PSN_HELP ) { // // TODO: specify the path to the help file for this interface: // ::MMCPropertyHelp( L"example.chm::/step_three.htm" ); } } case WM_COMMAND: { WORD wCommand = HIWORD( wParam ); switch ( wCommand ) { // TODO: list all change events here: case BN_CLICKED: // buttons, checkboxes, radiobuttons case EN_CHANGE: // editboxes case CBN_SELCHANGE: // comboboxes { // notify parent that a property has been changed: SendMessage( GetParent( hwndDlg ), PSM_CHANGED, (WPARAM)hwndDlg, 0 ); break; } default: { wCommand = wCommand; } } break; } case WM_DESTROY: { // free the DOM: pThis->m_pXmlDom->Release(); pThis->m_pXmlDom = NULL; break; } case PSM_QUERYSIBLINGS: { if ( wParam == 0xFF00 ) { // // set the "name" attribute (if it has not been defined yet): // IXMLDOMNode* pElmApp = NULL; // get the "Application" element: HRESULT hr = pThis->m_pXmlDom->selectSingleNode( L"/Application", &pElmApp ); if ( hr == S_OK ) { BSTR bstrName; hr = GetAttribute( pElmApp, _T( "name" ), bstrName ); if ( hr == S_FALSE ) { // "name" attribute not found, create: SetAttribute( pThis->m_pXmlDom, pElmApp, _T( "name" ), _T( "<<APPLICATION_NAME>>" ) ); } IXMLDOMNode* pElmProps = NULL; // get the "Properties" element: hr = pThis->m_pXmlDom->selectSingleNode( L"/Application/Properties", &pElmProps ); if ( hr == S_FALSE ) { // "Properties" element not found, create: hr = AddElement( pThis->m_pXmlDom, pElmApp, _T( "Properties" ), &pElmProps ); } if ( SUCCEEDED( hr ) ) { // // TODO: set properties data: // // // an example of a checkbox value setting a registry property: // IXMLDOMNode* pElmExampleProp = NULL; // find the "Reg" property element: hr = pThis->m_pXmlDom->selectSingleNode( L"/Application/Properties/Reg[@id='exampleSetting']", &pElmExampleProp ); if ( hr == S_FALSE ) { // "Reg" element not found, create: hr = AddElement( pThis->m_pXmlDom, pElmProps, _T( "Reg" ), &pElmExampleProp ); if ( hr == S_OK ) { // Set the property id. This identifies the property // for later retreival/modification by this interface. // The property id is not used during configuration. SetAttribute( pThis->m_pXmlDom, pElmExampleProp, _T( "id" ), _T( "exampleSetting" ) ); } } if ( hr == S_OK ) { // set the registry property type: SetAttribute( pThis->m_pXmlDom, pElmExampleProp, _T( "type" ), _T( "REG_DWORD" ) ); // set the registry hive: SetAttribute( pThis->m_pXmlDom, pElmExampleProp, _T( "hive" ), _T( "HKEY_CURRENT_USER" ) ); // set the registry key path: SetAttribute( pThis->m_pXmlDom, pElmExampleProp, _T( "key" ), _T( "Software\\Microsoft\\Group Policy\\Preferences\\Applications\\Example" ) ); // set the registry property name: SetAttribute( pThis->m_pXmlDom, pElmExampleProp, _T( "name" ), _T( "Setting" ) ); // get the state of the control: bool bChecked = ( IsDlgButtonChecked( hwndDlg, IDC_CHECK1 ) == BST_CHECKED ); // set the registry property value: SetAttribute( pThis->m_pXmlDom, pElmExampleProp, _T( "value" ), bChecked ? _T( "1" ) : _T( "0" ) ); pElmExampleProp->Release(); } pElmProps->Release(); } pElmApp->Release(); } } break; } default: { } } lr = DefWindowProc( hwndDlg, uMsg, wParam, lParam ); return lr; } /////////////////////////////// // Interface IExtendPropertySheet /////////////////////////////// HRESULT CPropSheetExtension::CreatePropertyPages( /* [in] */ LPPROPERTYSHEETCALLBACK lpProvider, /* [in] */ LONG_PTR handle, /* [in] */ LPDATAOBJECT lpIDataObject) { CLIPFORMAT cfApmXMLDOM = (CLIPFORMAT) ::RegisterClipboardFormatW( CCF_AUTOPROF_XMLDOM ); FORMATETC formatetc = { cfApmXMLDOM, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL }; STGMEDIUM stgmedium = { TYMED_HGLOBAL, NULL }; stgmedium.hGlobal = GlobalAlloc( GMEM_SHARE, sizeof( m_pXmlDom ) ); HRESULT hr = lpIDataObject->GetDataHere( &formatetc, &stgmedium ); bool bHaveDom = false; bool bAddPage = false; if ( SUCCEEDED( hr ) ) { LPBYTE pbNewData = reinterpret_cast<LPBYTE>( ::GlobalLock( stgmedium.hGlobal ) ); if ( pbNewData == NULL ) { hr = E_UNEXPECTED; } else { ::CopyMemory( &m_pXmlDom, pbNewData, sizeof( m_pXmlDom ) ); if ( !::GlobalUnlock( stgmedium.hGlobal ) ) { hr = HRESULT_FROM_WIN32( GetLastError() ); } else { bHaveDom = true; } } } if ( stgmedium.hGlobal != NULL ) { if ( ::GlobalFree( stgmedium.hGlobal ) != NULL ) { hr = HRESULT_FROM_WIN32( GetLastError() ); } } if ( SUCCEEDED( hr ) ) { IXMLDOMNode* pAttExtId = NULL; // get the "extid" attribute: hr = m_pXmlDom->selectSingleNode( L"/Application/@extid", &pAttExtId ); if ( hr == S_OK ) { BSTR bstrExtId; hr = pAttExtId->get_text( &bstrExtId ); if ( SUCCEEDED( hr ) ) { // uses case insensitive comparison: int nResult = _wcsicmp( bstrExtId, g_szwExtId ); if ( nResult == STRING_MATCH ) { // this is the correct extid, create property page: bAddPage = true; } else { // this is some other extid, skip property page creation: bAddPage = false; } SysFreeString( bstrExtId ); } pAttExtId->Release(); } else { // "extid" attribute was not present: hr = E_UNEXPECTED; } } if ( SUCCEEDED( hr ) ) { if ( bAddPage ) { PROPSHEETPAGE psp; HPROPSHEETPAGE hPage = NULL; psp.dwSize = sizeof(PROPSHEETPAGE); psp.dwFlags = PSP_DEFAULT | PSP_HASHELP; // | PSP_USETITLE; // | PSP_USEICONID; psp.hInstance = g_hinst; psp.pszTemplate = MAKEINTRESOURCE(IDD_PROPPAGE_LARGE); psp.pfnDlgProc = DialogProc; psp.lParam = reinterpret_cast<LPARAM>(this); psp.pszTitle = NULL; // MAKEINTRESOURCE(IDS_TAB_GENERAL); psp.pszIcon = NULL; // MAKEINTRESOURCE(IDI_ICON_GENERAL); hPage = CreatePropertySheetPage(&psp); _ASSERT(hPage); hr = lpProvider->AddPage(hPage); } } if ( bHaveDom && !bAddPage ) { // since the property pages were not created, free the DOM: m_pXmlDom->Release(); m_pXmlDom = NULL; } return hr; } HRESULT CPropSheetExtension::QueryPagesFor( /* [in] */ LPDATAOBJECT lpDataObject) { return S_OK; } /****************************************************************************** ISnapinHelp ******************************************************************************/ // initialization, get full path to main chm file STDMETHODIMP CPropSheetExtension::GetHelpTopic( __in LPOLESTR* pszwCompiledHelpFile ) { HRESULT hr = S_FALSE; _ASSERT( pszwCompiledHelpFile ); // // TODO: specify the path to the compiled help file: // WCHAR szwHelpFile[] = L"C:\\example.chm"; hr = AllocOleStr( pszwCompiledHelpFile, szwHelpFile ); _ASSERT( SUCCEEDED(hr) ); return hr; } /****************************************************************************** ISnapinHelp2:ISnapinHelp ******************************************************************************/ // property return, get help topic from each item STDMETHODIMP CPropSheetExtension::GetLinkedTopics( __in LPOLESTR* pszwCompiledHelpFiles ) { // this only necessary if there is more than one help file, // see MSDN for more information. return S_FALSE; }
The source file, BaseSnap.cpp, that provides the operating system and Microsoft Managegment Console registration support for the extension to the property pages of the Applications snap-in is listed below.
//BaseSnap.cpp #include <objbase.h> #include <olectl.h> #include <initguid.h> #include "globals.h" #include "resource.h" #include "guids.h" #include "basesnap.h" #include "AppExt.h" #include "About.h" #include "Registry.h" #include "Extend.h" #include "commctrl.h" // our globals HINSTANCE g_hinst; BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, void* lpvReserved) { if ( fdwReason == DLL_PROCESS_ATTACH ) { g_hinst = hinstDLL; } return TRUE; } STDAPI DllGetClassObject(REFCLSID rclsid, REFIID riid, LPVOID *ppvObj) { if ((rclsid != CLSID_CPropSheetExtension) && (rclsid != CLSID_CSnapinAbout)) return CLASS_E_CLASSNOTAVAILABLE; if (!ppvObj) return E_FAIL; *ppvObj = NULL; // We can only hand out IUnknown and IClassFactory pointers. Fail // if they ask for anything else. if (!IsEqualIID(riid, IID_IUnknown) && !IsEqualIID(riid, IID_IClassFactory)) return E_NOINTERFACE; CClassFactory *pFactory = NULL; // make the factory passing in the creation function for the type of object they want if (rclsid == CLSID_CPropSheetExtension) pFactory = new CClassFactory(CClassFactory::CONTEXTEXTENSION); else if (rclsid == CLSID_CSnapinAbout) pFactory = new CClassFactory(CClassFactory::ABOUT); if (NULL == pFactory) return E_OUTOFMEMORY; HRESULT hr = pFactory->QueryInterface(riid, ppvObj); return hr; } STDAPI DllCanUnloadNow(void) { if (g_uObjects == 0 && g_uSrvLock == 0) return S_OK; else return S_FALSE; } CClassFactory::CClassFactory(FACTORY_TYPE factoryType) : m_cref(0), m_factoryType(factoryType) { OBJECT_CREATED } CClassFactory::~CClassFactory() { OBJECT_DESTROYED } STDMETHODIMP CClassFactory::QueryInterface(REFIID riid, LPVOID *ppv) { if (!ppv) return E_FAIL; *ppv = NULL; if (IsEqualIID(riid, IID_IUnknown)) *ppv = static_cast<IClassFactory *>(this); else if (IsEqualIID(riid, IID_IClassFactory)) *ppv = static_cast<IClassFactory *>(this); if (*ppv) { reinterpret_cast<IUnknown *>(*ppv)->AddRef(); return S_OK; } return E_NOINTERFACE; } STDMETHODIMP_(ULONG) CClassFactory::AddRef() { return InterlockedIncrement((LONG *)&m_cref); } STDMETHODIMP_(ULONG) CClassFactory::Release() { if (InterlockedDecrement((LONG *)&m_cref) == 0) { delete this; return 0; } return m_cref; } STDMETHODIMP CClassFactory::CreateInstance(LPUNKNOWN pUnkOuter, REFIID riid, LPVOID * ppvObj) { HRESULT hr; void* pObj; if (!ppvObj) return E_FAIL; *ppvObj = NULL; // Our object does does not support aggregation, so we need to // fail if they ask us to do aggregation. if (pUnkOuter) return CLASS_E_NOAGGREGATION; if (CONTEXTEXTENSION == m_factoryType) { pObj = new CPropSheetExtension(); } else { pObj = new CSnapinAbout(); } if (!pObj) return E_OUTOFMEMORY; // QueryInterface will do the AddRef() for us, so we do not // do it in this function hr = ((LPUNKNOWN)pObj)->QueryInterface(riid, ppvObj); if (FAILED(hr)) delete pObj; return hr; } STDMETHODIMP CClassFactory::LockServer(BOOL fLock) { if (fLock) InterlockedIncrement((LONG *)&g_uSrvLock); else InterlockedDecrement((LONG *)&g_uSrvLock); return S_OK; } ////////////////////////////////////////////////////////// // // Exported functions // // // Server registration // STDAPI DllRegisterServer() { HRESULT hr = SELFREG_E_CLASS; _TCHAR szName[256]; _TCHAR szSnapInName[256]; LoadString(g_hinst, IDS_NAME, szName, (sizeof szName)/(sizeof szName[0])); LoadString(g_hinst, IDS_SNAPINNAME, szSnapInName, (sizeof szSnapInName)/(sizeof szSnapInName[0])); _TCHAR szAboutName[256]; LoadString(g_hinst, IDS_ABOUTNAME, szAboutName, (sizeof szAboutName)/(sizeof szAboutName[0])); // register our CoClasses hr = RegisterServer(g_hinst, CLSID_CPropSheetExtension, szName); if SUCCEEDED(hr) hr = RegisterServer(g_hinst, CLSID_CSnapinAbout, szAboutName); // place the registry information for SnapIns if SUCCEEDED(hr) hr = RegisterSnapin(CLSID_CPropSheetExtension, szSnapInName, CLSID_CSnapinAbout); return hr; } STDAPI DllUnregisterServer() { if (UnregisterServer(CLSID_CPropSheetExtension) == S_OK) return UnregisterSnapin(CLSID_CPropSheetExtension); else return E_FAIL; }
The source file, Registry.cpp, that provides the registry support for the extension to the property pages of the Applications snap-in is listed below.
//Registry.cpp #include <objbase.h> #include <assert.h> #include "Registry.h" #include "Extend.h" #include "GUIDs.h" #include "globals.h" // if not standalone comment out next line //#define STANDALONE // list all nodes that are extendable here // List the GUID and then the description // terminate with a NULL, NULL set. EXTENSION_NODE _ExtendableNodes[] = { {NULL, NULL} }; // list all of the nodes that we extend // Application Result Node {C8535E2E-148D-494D-8E9A-71FC46649B5E} EXTENDER_NODE _NodeExtensions[] = { { PropertySheetExtension, // GUID of node that this plugin extends (application node): { 0xC8535E2E, 0x148d, 0x494d, { 0x8e, 0x9a, 0x71, 0xfc, 0x46, 0x64, 0x9b, 0x5e } }, // plugin GUID (also defined in guids.h, line 31): { 0xA2E38A7B, 0xD9B2, 0x4DFC, { 0x85, 0x69, 0x1F, 0x53, 0x83, 0x06, 0x15, 0x90 } }, // plugin uid name: _T( "<<PLUGIN_NAME>>" ) }, { DummyExtension, NULL, NULL, NULL } }; //////////////////////////////////////////////////////// // // Internal helper functions prototypes // // Set the given key and its value. BOOL setKeyAndValue(const _TCHAR* pszPath, const _TCHAR* szSubkey, const _TCHAR* szValue) ; // Set the given key and its value in the MMC Snapin location BOOL setSnapInKeyAndValue(const _TCHAR* szKey, const _TCHAR* szSubkey, const _TCHAR* szName, const _TCHAR* szValue); // Set the given valuename under the key to value BOOL setValue(const _TCHAR* szKey, const _TCHAR* szValueName, const _TCHAR* szValue); BOOL setSnapInExtensionNode(const _TCHAR* szSnapID, const _TCHAR* szNodeID, const _TCHAR* szDescription); // Delete szKeyChild and all of its descendants. LONG recursiveDeleteKey(HKEY hKeyParent, const _TCHAR* szKeyChild) ; //////////////////////////////////////////////////////// // // Constants // // Size of a CLSID as a string //const int CLSID_STRING_SIZE = 39 ; ///////////////////////////////////////////////////////// // // Public function implementation // // // register the plugin with the Microsoft Group Policy Preferences Snap-In: // LRESULT RegisterPlugin() { HKEY hKey; // TODO: replace "<<APPLICATION_NAME>>" with appropriate text: const TCHAR szKeyPath[] = _T( "SOFTWARE\\Microsoft" ) _T( "\\Plugins\\Applications\\<<APPLICATION_NAME>>" ); // TODO: replace "<<PLUGIN_NAME>>" with appropriate text: const TCHAR szPluginName[] = _T( "<<PLUGIN_NAME>>" ); // TODO: replace this with the plugin's extid (guids.h, line 24): const TCHAR szExtId[] = _T( "{5F6A652F-1FA2-4A5C-B4DB-48D5D8095F47}" ); // TODO: replace this with the snapin GUID (guids.h, line 29): const TCHAR szSnapin[] = _T( "{A2E38A7B-D9B2-4dfc-8569-1F5383061590}" ); // Create and open key and subkey. LRESULT lr = RegCreateKeyEx( HKEY_LOCAL_MACHINE, szKeyPath, 0, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &hKey, NULL ) ; if ( lr == ERROR_SUCCESS ) { lr = RegSetValueEx( hKey, _T( "" ), 0, REG_SZ, (BYTE*)szPluginName, (DWORD)( _tcslen( szPluginName ) + 1 ) * sizeof( TCHAR ) ); if ( lr == ERROR_SUCCESS ) { lr = RegSetValueEx( hKey, _T( "ExtId" ), 0, REG_SZ, (BYTE*)szExtId, (DWORD)( _tcslen( szExtId ) + 1 ) * sizeof( TCHAR ) ); } if ( lr == ERROR_SUCCESS ) { lr = RegSetValueEx( hKey, _T( "Snapin" ), 0, REG_SZ, (BYTE*)szSnapin, (DWORD)( _tcslen( szSnapin ) + 1 ) * sizeof( TCHAR ) ); } RegCloseKey( hKey ); } return lr; } // // Register the component in the registry. // HRESULT RegisterServer(HMODULE hModule, // DLL module handle const CLSID& clsid, // Class ID const _TCHAR* szFriendlyName) // IDs { // Get server location. _TCHAR szModule[512] ; DWORD dwResult = ::GetModuleFileName(hModule, szModule, sizeof(szModule)/sizeof(_TCHAR)) ; assert(dwResult != 0) ; // Get CLSID LPOLESTR wszCLSID = NULL ; HRESULT hr = StringFromCLSID(clsid, &wszCLSID) ; assert(SUCCEEDED(hr)) ; MAKE_TSTRPTR_FROMWIDE(pszCLSID, wszCLSID); // Build the key CLSID\\{...} _TCHAR szKey[64] ; _tcscpy_s(szKey, _countof(szKey), _T("CLSID\\")) ; _tcscat_s(szKey, _countof(szKey), pszCLSID) ; // Add the CLSID to the registry. setKeyAndValue(szKey, NULL, szFriendlyName) ; // Add the server filename subkey under the CLSID key. setKeyAndValue(szKey, _T("InprocServer32"), szModule) ; // set the threading model _tcscat_s(szKey, _countof(szKey), _T("\\InprocServer32")); setValue(szKey, _T("ThreadingModel"), _T("Apartment")); // Free memory. CoTaskMemFree(wszCLSID); // register the plugin with the Microsoft Group Policy Preferences Snap-In: /* LRESULT */ RegisterPlugin(); return S_OK ; } // // Remove the component from the registry. // LONG UnregisterServer(const CLSID& clsid) // IDs { // Get CLSID LPOLESTR wszCLSID = NULL ; (VOID) StringFromCLSID(clsid, &wszCLSID) ; // Build the key CLSID\\{...} _TCHAR szKey[64] ; _tcscpy_s(szKey, _countof(szKey), _T("CLSID\\")) ; MAKE_TSTRPTR_FROMWIDE(pszT, wszCLSID); _tcscat_s(szKey, _countof(szKey), pszT) ; // Delete the CLSID Key - CLSID\{...} LONG lResult = recursiveDeleteKey(HKEY_CLASSES_ROOT, szKey) ; assert((lResult == ERROR_SUCCESS) || (lResult == ERROR_FILE_NOT_FOUND)) ; // Subkey may not exist. // Free memory. CoTaskMemFree(wszCLSID) ; // TODO: replace "<<APPLICATION_NAME>>" with appropriate text: const TCHAR szKeyPath[] = _T( "SOFTWARE\\Microsoft" ) _T( "\\Plugins\\Applications\\<<APPLICATION_NAME>>" ); // unregister the plugin with the Microsoft Group Policy Preferences Snap-In: lResult = recursiveDeleteKey( HKEY_LOCAL_MACHINE, szKeyPath ); return S_OK ; } // // Register the snap-in in the registry. // HRESULT RegisterSnapin(const CLSID& clsid, // Class ID const _TCHAR* szNameString, // NameString const CLSID& clsidAbout) // Class Id for About Class { // Get CLSID LPOLESTR wszCLSID = NULL ; LPOLESTR wszAboutCLSID = NULL; LPOLESTR wszExtendCLSID = NULL; LPOLESTR wszNodeCLSID = NULL; EXTENSION_NODE *pExtensionNode; EXTENDER_NODE *pNodeExtension; _TCHAR szKeyBuf[1024] ; HKEY hKey; HRESULT hr = StringFromCLSID(clsid, &wszCLSID) ; if (IID_NULL != clsidAbout) hr = StringFromCLSID(clsidAbout, &wszAboutCLSID); MAKE_TSTRPTR_FROMWIDE(pszCLSID, wszCLSID); MAKE_TSTRPTR_FROMWIDE(pszAboutCLSID, wszAboutCLSID); // Add the CLSID to the registry. setSnapInKeyAndValue(pszCLSID, NULL, _T("NameString"), szNameString) ; #ifdef STANDALONE setSnapInKeyAndValue(pszCLSID, _T("StandAlone"), NULL, NULL); #endif if (IID_NULL != clsidAbout) setSnapInKeyAndValue(pszCLSID, NULL, _T("About"), pszAboutCLSID); // register each of the node types in _ExtendableNodes as an extendable node for (pExtensionNode = &(_ExtendableNodes[0]);*pExtensionNode->szDescription;pExtensionNode++) { hr = StringFromCLSID(pExtensionNode->GUID, &wszExtendCLSID); MAKE_TSTRPTR_FROMWIDE(pszExtendCLSID, wszExtendCLSID); setSnapInExtensionNode(pszCLSID, pszExtendCLSID, pExtensionNode->szDescription); CoTaskMemFree(wszExtendCLSID); } // register each of the node extensions for (pNodeExtension = &(_NodeExtensions[0]);*pNodeExtension->szDescription;pNodeExtension++) { hr = StringFromCLSID(pNodeExtension->guidNode, &wszExtendCLSID); MAKE_TSTRPTR_FROMWIDE(pszExtendCLSID, wszExtendCLSID); _tcscpy_s(szKeyBuf, _countof(szKeyBuf), _T("SOFTWARE\\Microsoft\\MMC\\NodeTypes\\")); _tcscat_s(szKeyBuf, _countof(szKeyBuf), pszExtendCLSID); switch (pNodeExtension->eType) { case NameSpaceExtension: _tcscat_s(szKeyBuf, _countof(szKeyBuf), _T("\\Extensions\\NameSpace")); break; case ContextMenuExtension: _tcscat_s(szKeyBuf, _countof(szKeyBuf), _T("\\Extensions\\ContextMenu")); break; case ToolBarExtension: _tcscat_s(szKeyBuf, _countof(szKeyBuf), _T("\\Extensions\\ToolBar")); break; case PropertySheetExtension: _tcscat_s(szKeyBuf, _countof(szKeyBuf), _T("\\Extensions\\PropertySheet")); break; case TaskExtension: _tcscat_s(szKeyBuf, _countof(szKeyBuf), _T("\\Extensions\\Task")); break; case DynamicExtension: _tcscat_s(szKeyBuf, _countof(szKeyBuf), _T("\\Dynamic Extensions")); default: break; } // Create and open key and subkey. long lResult = RegCreateKeyEx(HKEY_LOCAL_MACHINE , szKeyBuf, 0, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &hKey, NULL) ; if (lResult != ERROR_SUCCESS) { return FALSE ; } hr = StringFromCLSID(pNodeExtension->guidExtension, &wszNodeCLSID); assert(SUCCEEDED(hr)); MAKE_TSTRPTR_FROMWIDE(pszNodeCLSID, wszNodeCLSID); // Set the Value. if (pNodeExtension->szDescription != NULL) { RegSetValueEx(hKey, pszNodeCLSID, 0, REG_SZ, (BYTE *)pNodeExtension->szDescription, (DWORD)(_tcslen(pNodeExtension->szDescription)+1)*sizeof(_TCHAR)) ; } RegCloseKey(hKey) ; CoTaskMemFree(wszExtendCLSID); CoTaskMemFree(wszNodeCLSID); } // Free memory. CoTaskMemFree(wszCLSID) ; if (IID_NULL != clsidAbout) CoTaskMemFree(wszAboutCLSID); return S_OK ; } // // Unregister the snap-in in the registry. // HRESULT UnregisterSnapin(const CLSID& clsid) // Class ID { _TCHAR szKeyBuf[1024]; LPOLESTR wszCLSID = NULL; // Get CLSID HRESULT hr = StringFromCLSID(clsid, &wszCLSID); MAKE_TSTRPTR_FROMWIDE(pszCLSID, wszCLSID); // Load the buffer with the Snap-In Location _tcscpy_s(szKeyBuf, _countof(szKeyBuf), _T("SOFTWARE\\Microsoft\\MMC\\SnapIns")); // Copy keyname into buffer. _tcscat_s(szKeyBuf, _countof(szKeyBuf), _T("\\")); _tcscat_s(szKeyBuf, _countof(szKeyBuf), pszCLSID); // Delete the CLSID Key - CLSID\{...} LONG lResult = recursiveDeleteKey(HKEY_LOCAL_MACHINE, szKeyBuf); assert((lResult == ERROR_SUCCESS) || (lResult == ERROR_FILE_NOT_FOUND)) ; // Subkey may not exist. //Uncomment following for loop to unregister all extendable node types //Note that if a snap-in's extendable node types are unregistered, //any extension snap-ins for these node types will have to be re-registered //in order to rebuild their entries under the SOFTWARE\Microsoft\MMC\NodeTypes key /* // Unregister each of the node types in _ExtendableNodes as an extendable node // Note that this snap-in does not register any extendable node types EXTENSION_NODE *pNode; LPOLESTR wszExtendCLSID = NULL; for (pNode = &(_ExtendableNodes[0]);*pNode->szDescription;pNode++) { hr = StringFromCLSID(pNode->GUID, &wszExtendCLSID); MAKE_TSTRPTR_FROMWIDE(pszExtendCLSID, wszExtendCLSID); // Load the buffer with the Snap-In Location _tcscpy_s(szKeyBuf, _countof(szKeyBuf), _T("SOFTWARE\\Microsoft\\MMC\\NodeTypes\\")); // Copy keyname into buffer. _tcscat_s(szKeyBuf, _countof(szKeyBuf), pszExtendCLSID); recursiveDeleteKey(HKEY_LOCAL_MACHINE, szKeyBuf); CoTaskMemFree(wszExtendCLSID); } */ // free the memory CoTaskMemFree(wszCLSID); return S_OK; } // // Delete a key and all of its descendants. // LONG recursiveDeleteKey(HKEY hKeyParent, // Parent of key to delete const _TCHAR* lpszKeyChild) // Key to delete { // Open the child. HKEY hKeyChild ; LONG lRes = RegOpenKeyEx(hKeyParent, lpszKeyChild, 0, KEY_ALL_ACCESS, &hKeyChild) ; if (lRes != ERROR_SUCCESS) { return lRes ; } // Enumerate all of the descendants of this child. FILETIME time ; _TCHAR szBuffer[256] ; DWORD dwSize = 256 ; while (RegEnumKeyEx(hKeyChild, 0, szBuffer, &dwSize, NULL, NULL, NULL, &time) == S_OK) { // Delete the descendants of this child. lRes = recursiveDeleteKey(hKeyChild, szBuffer) ; if (lRes != ERROR_SUCCESS) { // Cleanup before exiting. RegCloseKey(hKeyChild) ; return lRes; } dwSize = 256 ; } // Close the child. RegCloseKey(hKeyChild) ; // Delete this child. return RegDeleteKey(hKeyParent, lpszKeyChild) ; } // // Create a key and set its value. // - This helper function was borrowed and modifed from // Kraig Brockschmidt's book Inside OLE. // BOOL setKeyAndValue(const _TCHAR* szKey, const _TCHAR* szSubkey, const _TCHAR* szValue) { HKEY hKey; _TCHAR szKeyBuf[1024] ; // Copy keyname into buffer. _tcscpy_s(szKeyBuf, _countof(szKeyBuf), szKey) ; // Add subkey name to buffer. if (szSubkey != NULL) { _tcscat_s(szKeyBuf, _countof(szKeyBuf), _T("\\")) ; _tcscat_s(szKeyBuf, _countof(szKeyBuf), szSubkey ) ; } // Create and open key and subkey. long lResult = RegCreateKeyEx(HKEY_CLASSES_ROOT , szKeyBuf, 0, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &hKey, NULL) ; if (lResult != ERROR_SUCCESS) { return FALSE ; } // Set the Value. if (szValue != NULL) { RegSetValueEx(hKey, NULL, 0, REG_SZ, (BYTE *)szValue, (DWORD)(_tcslen(szValue)+1)*sizeof(_TCHAR)) ; } RegCloseKey(hKey) ; return TRUE ; } // // Open a key value and set it // BOOL setValue(const _TCHAR* szKey, const _TCHAR* szValueName, const _TCHAR* szValue) { HKEY hKey; _TCHAR szKeyBuf[1024] ; // Copy keyname into buffer. _tcscpy_s(szKeyBuf, _countof(szKeyBuf), szKey) ; // Create and open key and subkey. long lResult = RegCreateKeyEx(HKEY_CLASSES_ROOT , szKeyBuf, 0, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &hKey, NULL) ; if (lResult != ERROR_SUCCESS) { return FALSE ; } // Set the Value. if (szValue != NULL) { RegSetValueEx(hKey, szValueName, 0, REG_SZ, (BYTE *)szValue, (DWORD)(_tcslen(szValue)+1)*sizeof(_TCHAR)) ; } RegCloseKey(hKey) ; return TRUE ; } // // Create a key and set its value. // - This helper function was borrowed and modifed from // Kraig Brockschmidt's book Inside OLE. // BOOL setSnapInKeyAndValue(const _TCHAR* szKey, const _TCHAR* szSubkey, const _TCHAR* szName, const _TCHAR* szValue) { HKEY hKey; _TCHAR szKeyBuf[1024] ; // Load the buffer with the Snap-In Location _tcscpy_s(szKeyBuf, _countof(szKeyBuf), _T("SOFTWARE\\Microsoft\\MMC\\SnapIns")); // Copy keyname into buffer. _tcscat_s(szKeyBuf, _countof(szKeyBuf), _T("\\")) ; _tcscat_s(szKeyBuf, _countof(szKeyBuf), szKey) ; // Add subkey name to buffer. if (szSubkey != NULL) { _tcscat_s(szKeyBuf, _countof(szKeyBuf), _T("\\")) ; _tcscat_s(szKeyBuf, _countof(szKeyBuf), szSubkey ) ; } // Create and open key and subkey. long lResult = RegCreateKeyEx(HKEY_LOCAL_MACHINE , szKeyBuf, 0, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &hKey, NULL) ; if (lResult != ERROR_SUCCESS) { return FALSE ; } // Set the Value. if (szValue != NULL) { RegSetValueEx(hKey, szName, 0, REG_SZ, (BYTE *)szValue, (DWORD)(_tcslen(szValue)+1)*sizeof(_TCHAR)) ; } RegCloseKey(hKey) ; return TRUE ; } BOOL setSnapInExtensionNode(const _TCHAR* szSnapID, const _TCHAR* szNodeID, const _TCHAR* szDescription) { HKEY hKey; _TCHAR szSnapNodeKeyBuf[1024] ; _TCHAR szMMCNodeKeyBuf[1024]; // Load the buffer with the Snap-In Location _tcscpy_s(szSnapNodeKeyBuf, _countof(szSnapNodeKeyBuf), _T("SOFTWARE\\Microsoft\\MMC\\SnapIns\\")); // add in the clisid into buffer. _tcscat_s(szSnapNodeKeyBuf, _countof(szSnapNodeKeyBuf), szSnapID) ; _tcscat_s(szSnapNodeKeyBuf, _countof(szSnapNodeKeyBuf), _T("\\NodeTypes\\")); _tcscat_s(szSnapNodeKeyBuf, _countof(szSnapNodeKeyBuf), szNodeID) ; // Load the buffer with the NodeTypes Location _tcscpy_s(szMMCNodeKeyBuf, _countof(szMMCNodeKeyBuf), _T("SOFTWARE\\Microsoft\\MMC\\NodeTypes\\")); _tcscat_s(szMMCNodeKeyBuf, _countof(szMMCNodeKeyBuf), szNodeID) ; // Create and open the Snapin Key. long lResult = RegCreateKeyEx(HKEY_LOCAL_MACHINE , szSnapNodeKeyBuf, 0, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &hKey, NULL) ; if (lResult != ERROR_SUCCESS) { return FALSE ; } // Set the Value. if (szDescription != NULL) { RegSetValueEx(hKey, NULL, 0, REG_SZ, (BYTE *)szDescription, (DWORD)(_tcslen(szDescription)+1)*sizeof(_TCHAR)) ; } RegCloseKey(hKey) ; // Create and open the NodeTypes Key. lResult = RegCreateKeyEx(HKEY_LOCAL_MACHINE , szMMCNodeKeyBuf, 0, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &hKey, NULL) ; if (lResult != ERROR_SUCCESS) { return FALSE ; } // Set the Value. if (szDescription != NULL) { RegSetValueEx(hKey, NULL, 0, REG_SZ, (BYTE *)szDescription, (DWORD)(_tcslen(szDescription)+1)*sizeof(_TCHAR)) ; } RegCloseKey(hKey) ; return TRUE ; }
The source file, Utility.cpp, that provides the MSXML utility functions written for the extension to the property pages of the Applications snap-in is listed below.
//Utility.cpp #include "utility.h" using namespace std; HRESULT GetAttribute( IXMLDOMNode* pParent, LPCTSTR szName, BSTR& bstrValue ) { HRESULT hr = E_FAIL; if ( pParent != NULL ) { IXMLDOMNamedNodeMap* pNodeMap = NULL; hr = pParent->get_attributes( &pNodeMap ); if ( hr == S_OK ) { IXMLDOMNode* pAttribute = NULL; BSTR bstrName = ::SysAllocString(szName); hr = pNodeMap->getNamedItem( bstrName, &pAttribute ); if ( hr == S_OK ) { hr = pAttribute->get_text( &bstrValue ); pAttribute->Release(); } ::SysFreeString(bstrName); pNodeMap->Release(); } } return hr; } HRESULT SetAttribute( IXMLDOMDocument* pDom, IXMLDOMNode* pParent, LPCTSTR szName, LPCTSTR szValue ) { HRESULT hr = E_FAIL; IXMLDOMAttribute* pAttribute = NULL; BSTR bstrName = ::SysAllocString(szName); hr = pDom->createAttribute( bstrName, &pAttribute ); if ( hr == S_OK ) { IXMLDOMNamedNodeMap* pNodeMap = NULL; BSTR bstrValue = ::SysAllocString( szValue ); hr = pAttribute->put_text( bstrValue ); if ( SUCCEEDED( hr ) ) { hr = pParent->get_attributes( &pNodeMap ); if ( hr == S_OK ) { hr = pNodeMap->setNamedItem( pAttribute, NULL ); pNodeMap->Release(); } } ::SysFreeString(bstrValue); pAttribute->Release(); } ::SysFreeString(bstrName); return hr; } HRESULT AddElement( IXMLDOMDocument* pDom, IXMLDOMNode* pParent, LPCTSTR szName, IXMLDOMNode** ppNew ) { HRESULT hr = E_FAIL; *ppNew = NULL; BSTR bstrName = ::SysAllocString(szName); BSTR bstrNsUri = ::SysAllocString(L""); hr = pDom->createNode( _variant_t((long)NODE_ELEMENT), bstrName, bstrNsUri, ppNew ); if ( hr == S_OK ) { hr = pParent->appendChild( *ppNew, NULL ); // release object if append failed: if ( hr != S_OK ) { (*ppNew)->Release(); *ppNew = NULL; } } ::SysFreeString(bstrName); ::SysFreeString(bstrNsUri); return hr; } HRESULT AllocOleStr( LPOLESTR* lppDest, LPCWSTR szwBuffer ) { HRESULT hr = E_OUTOFMEMORY; _ASSERT( lppDest ); // must be allocated with CoTaskMemAlloc(), caller frees *lppDest = reinterpret_cast<LPOLESTR>( ::CoTaskMemAlloc( ( ::lstrlenW( szwBuffer ) + 1 ) * sizeof( WCHAR ) ) ); if ( *lppDest != NULL ) { hr = S_OK; ::lstrcpyW( *lppDest , szwBuffer ); } return hr; }