Creating an MMC Property Page

banner art

Previous Next

Creating an MMC Property Page

You can use the Active Template Library (ATL) and Microsoft® Visual Studio® .NET to create a plug-in property page that can be displayed in the Microsoft Management Console (MMC) used to administer the server.

  • Note   It is not recommended that you use .NET to create property pages for the server. The Microsoft Windows® .NET Framework does not contain a managed implementation of the IPropertyPage interface.

To create a property page using ATL, complete the following steps:

  1. Start Microsoft Visual Studio .NET and, on the File menu, click New, click Project, and then click Visual C++ Projects.

  2. Enter a name for the project.

  3. Click Application Settings on the ATL Project Wizard dialog box.

  4. Clear the Attributed check box and click Finish.

  5. On the Solution Explorer page of the development environment, right-click the proxy stub node, click Remove and click OK. The wizard creates the proxy stub code and identifies it by adding PS to the project name identified in step 2. Proxy stub code is not required for the property page.

  6. Select Add Class from the Project menu, select ATL in the left pane of the Add Class wizard, and select ATL Property Page in the right pane.

  7. Click Open. The ATL Property Page wizard is displayed.

  8. Supply a name for the class.

  9. Click Options, select the apartment threading model, and indicate that the object cannot be aggregated.

  10. Click Strings and specify a title for the property page. The server displays the title in the property page tab.

  11. Click Finish. The wizard generates a class derived from IPropertyPageImpl and CDialogImpl. This is illustrated by the following class declaration. CDialogImpl enables an object to create a dialog box, and IPropertyPageImpl allows an object to manage a property page.

    class ATL_NO_VTABLE CSamplePropPage :
    public CComObjectRootEx<CComSingleThreadModel>,
    public CComCoClass<CContextSamplePropPage,
    public IPropertyPageImpl<CSamplePropPage>,
    public CDialogImpl<CSamplePropPage>

  12. The wizard also generates a dialog box resource. Use the dialog editor to add controls to the dialog resource, or add code to your class to create the controls at run time. The dialog box resource does not include a frame, command buttons or tabs. These features are provided by MMC when it creates the property page frame. Set the style property to child.

  13. After you have added controls to your dialog box, you can add message handling to the default message map created by the wizard. In the following example, when the user changes the state of the edit control identified by IDC_EDIT_PATH, the OnPathChange function is executed. The OnPathChange function calls the IPropertyPageImpl::SetDirty method to indicate to the server that the Apply button on the dialog box must be enabled.


LRESULT OnPathChange (WORD wNotifyCode, WORD wID, HWND hWndCtl, BOOL& bHandled) { SetDirty( TRUE ); return 0; }

The CHAIN\_MSG\_MAP macro is used to pass any message not handled by your class to the **IPropertyPageImpl** interface.
  1. Handle the WM_INITDIALOG message to initialize the dialog box when it is displayed for the first time. Be sure you include the message in a MESSAGE_HANDLER macro in the message map for your class.

  2. Add an administration interface to your plug-in so that the plug-in and the property page can communicate. For example, if your dialog box contains an edit control that an administrator can use to input a string, your plug-in administration interface can implement methods that specify and retrieve the string. This is illustrated by the following interface definition in the plug-in .idl file. You must include the plug-in header file in the implementation of your property page COM object.

    class ATL_NO_VTABLE CPluginAdmin :
    public IDispatchImpl<IPluginAdmin, &IID_IPluginAdmin,
    public CComObjectRootEx<CComMultiThreadModel>

    // Specify and retrieve the log file path. STDMETHOD( put_String )( BSTR bstrString ); STDMETHOD( get_String )( BSTR *pbstrString );

  3. Identify the property page to the plug-in by specifying the CLSID of the property page for the MMCMoniker property of the plug-in. If you have created multiple property pages, specify the CLSIDs in a comma delimited list. This is illustrated by the following registry script example.

    ForceRemove Properties
    val Author = s 'Company Name'
    val Name = s 'Sample Plugin'
    val Copyright = s 'Copyright 2002. All rights reserved.'
    val MMCMoniker = s 'CLSID:{2B8E10C4-4DFE-4e4f-9BCC-2F01F611244B}',

  4. Retrieve pointers sent to the property page object from MMC. You can do this in the function that handles the WM_INITDIALOG message, or create a different function. You can retrieve the pointers from the m_ppUnk member of the IPropertyPageImpl interface. MMC sends the following IUnknown pointers to your property page object:

    • m_ppUnk[0] contains a pointer to the plug-in's administration interface.
    • m_ppUnk[1] contains a pointer to the IWMSServer interface.
    • m_ppUnk[2] contains a pointer to an IWMSPublishingPoint interface.

    The size of the array is contained in the m_nObjects data member. The following example illustrates how to retrieve the plug-in's administration interface.

    if( 0 == m_nObjects )

{ return( E_UNEXPECTED ); }

m_pPluginAdmin = m_ppUnk[0];

if( ! m_pPluginAdmin ) { return( E_FAIL ); }

return( S_OK );

  1. Implement the IPropertyPageImpl::Apply method to update the controls on the property page. For example, you can use the plug-in administration interface (defined in step 15 and retrieved in step 16) to update a text box that enables a user to enter a string.

    CSamplePropPage::Apply( void )
    HRESULT hr = S_OK;

    // Retrieve values from the dialog resource controls. hr = RetrieveDialogInformation();

    if( SUCCEEDED( hr ) ) { if( !m_pPluginAdmin ) { return( E_FAIL ); } else { // Check the old value and set if it has changed. if( _wcsicmp( m_bstrOrigString, m_bstrString ) ) { m_pPluginAdmin-> put_String( m_bstrString ); } } }

    if( SUCCEEDED( hr ) ) SetDirty( FALSE );

    return( hr ); }

See Also

Previous Next