Directory Services

Implementing the Property Page COM Object

A property sheet extension is a COM object implemented as an in-proc server. The property sheet extension must implement the IShellExtInit and IShellPropSheetExt interfaces. A property sheet extension is instantiated when the user displays the property sheet for an object of a class for which the property sheet extension has been registered.

Implementing IShellExtInit

After the property sheet extension COM object is instantiated, the IShellExtInit::Initialize method is called. IShellExtInit::Initialize supplies the property sheet extension with an IDataObject object that contains data pertinent to the directory object that the property sheet applies.

The IDataObject contains data in the CFSTR_DSOBJECTNAMES format. The CFSTR_DSOBJECTNAMES data format is an HGLOBAL that contains a DSOBJECTNAMES structure. The DSOBJECTNAMES structure contains directory object data that the property sheet extension applies.

The IDataObject also contains data in the CFSTR_DS_DISPLAY_SPEC_OPTIONS format. The CFSTR_DS_DISPLAY_SPEC_OPTIONS data format is an HGLOBAL that contains a DSDISPLAYSPECOPTIONS structure. The DSDISPLAYSPECOPTIONS contains configuration data for use by the extension.

If any value other than S_OK is returned from IShellExtInit::Initialize, the property sheet is not displayed.

The pidlFolder and hkeyProgID parameters of the IShellExtInit::Initialize method are not used.

The IDataObject pointer can be saved by the extension by incrementing the reference count of the IDataObject. This interface must be released when it is no longer required.

Implementing IShellPropSheetExt

After IShellExtInit::Initialize returns, the IShellPropSheetExt::AddPages method is called. The property sheet extension must add the page or pages during this method. A property page is created by filling in a PROPSHEETPAGE structure and then passing this structure to the CreatePropertySheetPage function. The property page is then added to the property sheet by calling the callback function passed to IShellPropSheetExt::AddPages in the lpfnAddPage parameter.

If any value other than S_OK is returned from IShellPropSheetExt::AddPages, the property sheet is not displayed.

If the property sheet extension does not need to add any pages to the property sheet, it should not call the callback function passed to IShellPropSheetExt::AddPages in the lpfnAddPage parameter.

The IShellPropSheetExt::ReplacePage method is not used.

Passing the Extension Object to the Property Page

Be aware that the property sheet extension object is independant from the property page. In many cases, it is desirable to be able to use the extension object, or some other object, from the property page. To do this, set the lParam member of PROPSHEETPAGE structure to the object pointer. The property page can then retrieve this value when it processes the WM_INITDIALOG message. For a property page, the lParam parameter of the WM_INITDIALOG message is a pointer to the PROPSHEETPAGE structure. Retrieve the object pointer by casting the lParam of the WM_INITDIALOG message to a PROPSHEETPAGE pointer and then retreiving the lParam of the PROPSHEETPAGE structure. The following code example shows how to accomplish this.

[C++]
case WM_INITDIALOG:
	{
		LPPROPSHEETPAGE pPage = (LPPROPSHEETPAGE)lParam;

		if(NULL != pPage)
		{
			CPropSheetExt *pPropSheetExt;
			pPropSheetExt = (CPropSheetExt*)pPage->lParam;

			if(pPropSheetExt)
			{
				return pPropSheetExt>OnInitDialog(wParam, lParam);
		}
	}
}
	break;

Consider the following. After IShellPropSheetExt::AddPages is called, the property sheet will release the property sheet extension object and never use it again. This means that the extension object are deleted before the property page is displayed. When the page attempts to access the object pointer, the memory will have been freed and the pointer will no longer be valid. A common way to correct this problem is to increment the reference count for the object when the page is added and then release the object when the property page dialog is destroyed. This actually creates another problem because the property page dialog does not get created until the first time the page is displayed. If the user never selects the extension page, the page never gets created and detroyed. This results in the extension object never getting released, so a memory leak occurs. The proper way to avoid this problem is to implement a property page callback function. To do this, add the PSP_USECALLBACK flag to the dwFlags member of the PROPSHEETPAGE structure and setting the pfnCallback member of the PROPSHEETPAGE structure to the address of a PropSheetPageProc function that you implement. When the PropSheetPageProc function receives the PSPCB_RELEASE notification, the ppsp parameter of the PropSheetPageProc contains a pointer to the PROPSHEETPAGE structure. The lParam of the PROPSHEETPAGE structure contains the extension pointer which can be used to release the object. The following code example shows how this can be accomplished.

[C++]
UINT CALLBACK CPropSheetExt::PageCallbackProc(  HWND hWnd,
												UINT uMsg,
												LPPROPSHEETPAGE ppsp)
{
	switch(uMsg)
	{
	case PSPCB_CREATE:
		// Must return TRUE to enable the page to be created.
		return TRUE;

	case PSPCB_RELEASE:
		{
			/*
			Release the object. This is called even if the page dialog was 
			never actually created.
			*/
			CPropSheetExt *pPropSheetExt = (CPropSheetExt*)ppsp->lParam;

			if(pPropSheetExt)
			{
				pPropSheetExt->Release();
		}
	}
		break;
}

	return FALSE;
}

Working With the Notification Object

Because the property sheet extension is displayed within a property sheet created by a component unknown to the extension, it is necessary to use a "manager" to handle the data transfer between the extension pages and the property sheet. This "manager" is called the notification object. The notification object acts as a moderator between the individual pages and the property sheet.

When the property sheet extension object is initialized, the extension must create a notification object by calling ADsPropCreateNotifyObj, passing the IDataObject obtained from IShellExtInit::Initialize and the directory object's name. It is not necessary to increment the reference count of the IDataObject interface because the notification object created by ADsPropCreateNotifyObj function will do this itself. The notification object handle provided by ADsPropCreateNotifyObj should be saved for later use. ADsPropCreateNotifyObj can be either called during IShellExtInit::Initialize or IShellPropSheetExt::AddPages. When the property sheet extension is shut down, it must send a WM_ADSPROP_NOTIFY_EXIT message to the notification object. This causes the notification object to destroy itself. This is best done when the PropSheetPageProc function receives the PSPCB_RELEASE notification.

The property sheet extension can obtain data in addition to that provided by the CFSTR_DSOBJECTNAMES clipboard format by calling ADsPropGetInitInfo. One of the advantages of using ADsPropGetInitInfo is that it provides an IDirectoryObject object used to programatically work with the directory object.

Note  Unlike most COM methods and functions, ADsPropGetInitInfo does not increment the reference count for the IDirectoryObject object. The IDirectoryObject must not be released unless the reference count is manually incremented first.

When the property page is first created, the extension should register the page with the notification object by calling ADsPropSetHwnd with the window handle of the page.

ADsPropCheckIfWritable is a utility function that the property sheet extension can use to determine if a property can be written.

Miscellaneous

The handle of the property page is passed to the page's dialog procedure. The property sheet is the direct parent of the property page, so the handle of the property sheet can be obtained by calling the GetParent function with the property page handle.

When the contents of the extension page changes, the extension should use the PropSheet_Changed macro to notify the property sheet that something has changed and the Apply pushbutton should be enabled.

Multiple-Selection Property Sheets

Beginning with Windows Server 2003 family operating systems, the Active Directory administrative MMC snap-ins support property sheet extensions for multiple directory objects. These property sheets are displayed when the properties are viewed for more than one item at a time. The primary difference between a single-selection property sheet extension and a multiple-selection property sheet extension is that the DSOBJECTNAMES structure supplied by the CFSTR_DSOBJECTNAMES clipboard format in IShellExtInit::Initialize will contain more than one DSOBJECT structure.

For multiple-selection property sheets, the system only binds to the first object in the DSOBJECT array. Because of this, ADsPropGetInitInfo only supplies the IDirectoryObject and writable attributes for the first object in the array. The other objects in the array are not bound to.

A multiple-selection property sheet extension is registered under the adminMultiselectPropertyPages attribute.

When creating a multiple-selection property sheet extension, you must link the DLL with Adprop.lib and not Dsprop.lib. Linking with Adprop.lib causes the Adprop.dll module to be used as opposed to Dsprop.dll. Adprop.dll is identical to Dsprop.dll except that Adprop.dll supports multiple-selection property sheets. Adprop.dll is only available with Windows Server 2003 family operating systems and later, so it is not available on Windows 2000 or Windows XP. Because of this, a multiple-selection property sheet extension will not work on Windows 2000 or Windows XP. If you are creating a single-selection property sheet extension designed to work on all platforms, link with Dsprop.lib. These requirements mean that you cannot implement both a single-selection and a multiple-selection property sheet extension in the same module and have it work on all platforms.

New with Windows Server 2003

The following features are new with Windows Server 2003.

If the property page encounters an error, ADsPropSendErrorMessage can be called with the appropriate error data. ADsPropSendErrorMessage will store all error messages in a queue. These messages will be displayed the next time ADsPropShowErrorDialog is called. When ADsPropShowErrorDialog returns, the queued messages are deleted.

Windows Server 2003 introduces the ADsPropSetHwndWithTitle function. This function is similar to ADsPropSetHwnd, but includes the page title. This enables the error dialog displayed by ADsPropShowErrorDialog to provide more useful data to the user. If the property sheet extension uses the ADsPropShowErrorDialog function, the extension should use ADsPropSetHwndWithTitle rather than ADsPropSetHwnd.

Use of these functions is not limited to multiple-selection property sheet extensions, but these functions are only implemented in Adprop.dll on Windows Server 2003, so they cannot be used by an extension used on Windows 2000 or Windows XP.

See Also

Example Code for Implementation of the Property Sheet COM Object