Douglas Hodges, Erik Christensen, and Scot Gellock
Updated: July 15, 1996
Summary
In a componentized product where the application is built from a shell and a set of components (e.g., a form component, a query editing component, etc.), the controlling shell needs to be able to provide a way for the various components to invoke a common set of builders and wizards. For example, the user should be presented with the same URL picker anywhere in the product. Furthermore, if the shell has an extensible add-in model for builders and wizards, then the shell needs to manage the extensible set of builders. This document describes the IBuilderWizardManager interface as a mechanism for accomplishing this.
Contents
Introduction
IBuilderWizardManager Interface
GetBuilder
DoesBuilderExist
MapObjectToBuilderCLSID
MapBuilderCATIDToCLSID
EnableModeless
IProvidePropertyBuilder Interface
MapPropertyToBuilder
ExecuteBuilder
A very compelling feature of the Microsoft® Access development environment is the extensive use of wizards and builders. Wizards and builders are normal modal UI that help the user perform some operation. The two terms builders and wizards are often used interchangeably. For the purpose of this discussion, the term "wizard" is a general term that refers to all types of modal UI that assist the user in performing a task. The term "builder," on the other hand, is used to describe a specific type of wizard that helps the user edit a property value or object. A builder takes as input an initial or current value, offers the user some modal UI, and then returns a new value. A builder is often used to help the user edit the value of a property of an ActiveX control from a property page. In this case, when the user selects a given property (e.g., a URL property), if there is a builder that can help the user edit the property value, then the control presents the user with a [...] button to launch the builder (e.g., a URL picker). Another important scenario where the user can be presented with a builder is upon instantiating a control in a container (form). The container first instantiates and initializes the control and then invokes the builder passing the IUnknown of the control as the input argument to the builder.
In a componentized product where the application is built from a shell and a set of components (e.g., a form component, a query editing component, etc.), the controlling shell needs to be able to provide a way for the various components to invoke a common set of builders and wizards. For example, the user should be presented with the same URL picker anywhere in the product. Furthermore, if the shell has an extensible add-in model for builders and wizards, then the shell needs to manage the extensible set of builders. For example, a form component needs to be able to ask the shell if a builder has been installed for a given control CLSID when the user inserts an instance of the control. In order to allow a component or ActiveX control to invoke a builder managed by the shell, the shell implements a service SID_SBuilderWizardManager that exposes the IBuilderWizardManager interface.
A shell that implements a property browser (i.e., a grid-style property sheet), queries for the IPerPropertyBrowsing interface on an ActiveX control to see if the control can perform custom editing of a property via one of its property pages. This works fine for things that are easily expressed in a property page. There are a class of problems, however, that do not work very well with this metaphor. A typical example is a control wanting to bring up a file open dialog box to choose a file or directory. There's no way to use the standard open dialog box in conjunction with a property page in order to get this to happen (short of trickery). There are more complex scenarios where the control may want to launch another application to set the property (such as an SQL editor to set an SQL statement property) or to invoke a builder managed by the shell via IBuilderWizardManager. A control that wants to support the editing of a property via a builder can implement the IProvidePropertyBuilder interface. When the property browser has the focus on a property, it calls IProvidePropertyBuilder::MapPropertyToBuilder, which returns S_OK or S_FALSE depending on whether the control wants to edit the property via a builder. The control may use a standard builder that is managed by the IBuilderWizardManager or it may use an internal builder that only the control can execute. If S_OK, then the property browser adds the [...] button. Once the user presses the [...] button, if the control uses a standard builder then the property browser executes the builder directly (using its own IBuilderWizardManager functionality), or if the control uses an internal builder, then the property browser calls IProvidePropertyBuilder::ExecuteBuilder to allow the control to do whatever it wants to edit the property. The property browser will assume that the editing is modal for the duration of the call to execute the control's internal builder and must disable the shell UI appropriately prior to calling ExecuteBuilder.
A standard shell that implements a grid-style property page has several options as to how to give the user assistance in editing a property value. Some shells have special knowledge of properties like color and font and have intrinsic builders for these properties. Some controls will implement IPerPropertyBrowsing and will let the user use another property page to edit the property. Other controls will implement IProvidePropertyBuilder and will let the user use either a standard or internal builder to edit a property. Which of these options to use is an interesting issue, particularly when there is a multiple selection. Most shells will give these various options the following priority:
// {95FC88C2-9FCB-11cf-A405-00AA00C00940} DEFINE_GUID(SID_SBuilderWizardManager, 0x95fc88c2, 0x9fcb, 0x11cf, 0xa4, 0x5, 0x0, 0xaa, 0x0, 0xc0, 0x9, 0x40); // {95FC88C3-9FCB-11cf-A405-00AA00C00940} DEFINE_GUID(IID_IBuilderWizardManager, 0x95fc88c3, 0x9fcb, 0x11cf, 0xa4, 0x5, 0x0, 0xaa, 0x0, 0xc0, 0x9, 0x40); typedef enum tagBLDPROMPTOPT { BLDPROMPTOPT_PROMPTIFMULTIPLE = 0 , BLDPROMPTOPT_PROMPTALWAYS = 1 , BLDPROMPTOPT_PROMPTNEVER = 2 } BLDPROMPTOPT; typedef enum tagBLDGETOPT { BLDGETOPT_FAUTOMAPGUID = 0x00000001 , BLDGETOPT_FAUTOMAPENABLEPROMPT = 0x00000002 , BLDGETOPT_FAUTOMAPPROMPTALWAYS = 0x00000004 , BLDGETOPT_FOBJECTBUILDER = 0x00000008 , BLDGETOPT_FNOINTRINSICS = 0x80000000 } BLDGETFLAGS; [object, uuid(95FC88C3-9FCB-11cf-A405-00AA00C00940), pointer_default(unique)] interface IBuilderWizardManager : IUnknown { HRESULT DoesBuilderExist ( [in] REFGUID rguidBuilder ); HRESULT MapObjectToBuilderCLSID ( [in] REFCLSID rclsidObject, [in] DWORD dwPromptOpt, [in] HWND hwndPromptOwner, [out] CLSID *pclsidBuilder ); HRESULT MapBuilderCATIDToCLSID ( [in] REFGUID rguidBuilder, [in] DWORD dwPromptOpt, [in] HWND hwndPromptOwner, [out] CLSID *pclsidBuilder ); HRESULT GetBuilder ( [in] REFGUID rguidBuilder, [in] DWORD grfGetOpt, [in] HWND hwndPromptOwner, [out] IDispatch **ppdispApp, [out] HWND *pwndBuilderOwner, [in] REFIID riidBuilder, [out] IUnknown **ppunkBuilder ); HRESULT EnableModeless ( [in] BOOL fEnable ); }
A BuilderWizardManager client retrieves a pointer to the IDispatch of a builder by calling the GetBuilder method. They request a builder by providing either the CLSID of a specific builder or a builder classification GUID (Component Category CATID). By default the pguidBuilder is expected to be a CLSID that identifies a particular builder's OLE server. It is reasonable for the BuilderWizardManager to return the IDispatch for a builder identified by a CLSID that is implemented internally, i.e., one that is not actually able to be created by CoCreateInstance. If the pguidBuilder is not a valid CLSID and the caller has set the BLDGETOPT_AUTOMAPGUID flag, then the builder CATID will be mapped to a CLSID by internally calling MapBuilderCATIDToCLSID(). The additional flags -- BLDGETOPT_FAUTOMAPENABLEPROMPT and BLDGETOPT_FAUTOMAPPROMPTALWAYS -- control whether the user is prompted to choose if there is more than one potential builder to be called.
The builders that are returned via the GetBuilder method are always expected to run as modal builders. The client of the BuilderWizardManager is responsible for disabling the application appropriately prior to invoking the builder, and then re-enabling the application after the builder method returns. This can be achieved by one of several mechanisms. If the client has special knowledge that the application is already disabled, then the client does not need to do anything. (Note: A common case of this will be a control that implements IProvidePropertyBuilder::ExecuteBuilder. The semantics of ExecuteBuilder require the caller of ExecuteBuilder to disable the application. If the control wants to invoke a special builder via the BuilderWizardManager as part of its implementation of ExecuteBuilder, then the control already knows that the application is disabled; it can simply call GetBuilder and then Invoke without any special effort to disable the application.) If the client is internal to the application that implements BuilderWizardManager, then the client can use the private mechanisms of the application. If the client is a standard in-place OLE object then, it can achieve this by the normal mechanism of calling IOleInPlaceFrame::EnableModeless. If the client is registered with the IComponentManager, then it can call IComponentManager:: OnComponent[Enter|Exit]State to manage the modality of the application. But if the client is not an in-place object and otherwise has no other means to disable the application, then it can call IBuilderWizardManager::EnableModeless method to achieve the same result.
The client of the BuilderWizardManager must know the interface that the builder implements and the signature of the method that it needs to call on the builder OLE server. Different types of builders will have different signatures. Most builders will expect at least the following parameters:
The following are some important types of builders:
Argument | Argument type | Argument description |
RguidBuilder | REFGUID | [IN] normally the CLSID of a specific builder. If BLDGETOPT_AUTOMAPGUID is specified then this GUID may be a builder classification GUID (CATID). |
GrfExecOpt | DWORD | [IN] group of bit flags. Values are taken from the BLDGETOPT enumeration. |
HwndPromptOwner | HWND | [IN] Optional owner HWND for all dialogs in case the user is to be prompted to choose which builder to run. This is only used if both BLDGETOPT_FAUTOMAPGUID and BLDGETOPT_FAUTOMAPENABLEPROMPT flags are specified.
If the caller of GetObject is within a modal dialog box, then the caller needs to pass the hwnd of the modal dialog box as the hwndPromptOwner. If the caller is not within a modal dialog box, then the caller should just pass NULL, in which case the BuilderWizardManager will use the hwnd for the application frame window. |
PpdispApp | IDispatch** | [OUT] IDispatch of the Application that the caller can pass to the invoke call on the builder. This interface pointer is returned as an Addref'ed pointer that the caller is responsible for releasing. *ppdispApp may be NULL if the BuilderWizardManager does not actually have an Application IDispatch* available.
ppdispApp may be NULL, if the caller is not interested in getting the Application IDispatch*. |
PhwndBuilderOwner | HWND* | [OUT] calculated hwnd that the caller should use as the owner hwnd for invoking the builder. This pointer may be NULL if the caller is not interested in receiving the calculated hwnd.
if hwndPromptOwner==NULL then *phwndBuilderOwner will be set to the hwnd of the Application main frame window. if hwndPromptOwner!=NULL then *phwndBuilderOwner will be set to hwndPromptOwner. This is the case when the builder is invoked from within another modal dialog box. It is the caller's responsibility to pass the proper hwndPromptOwner. |
RiidBuilder | REFIID | [IN] interface the client wants to get on the builder OLE server. The client needs to know what interface to ask for based on the type of builder it wants to get. |
PpunkBuilder | IUnknown** | [OUT] interface pointer of builder |
Return Value | S_OK if the builder interface is successfully returned.
S_FALSE if the dialog UI is canceled. E_NOINTERFACE if the builder is not supported. If the builder automation server could not be instantiated, then any error HRESULT returned from CoCreateInstance will be returned. |
DoesBuilderExist returns S_OK or S_FALSE to indicate whether a builder exists for a particular builder CLSID or builder classification GUID. This method would be used, for example, by a property page to know whether to enable the [...] builder button for a property.
Argument | Argument type | Argument description |
RguidBuilder | REFGUID | [IN] Either a builder classification GUID (CATID) or builder CLSID. |
Return Value | S_OK if builder exists.
S_FALSE if builder does not exist. |
Given the CLSID of an OLE object/control, return the CLSID of a builder that can help the user set properties on the control. The caller can control whether the user is prompted to choose if more than one builder is available for a particular control CLSID. If the user is not to be prompted and there is more than one builder available, then the BuilderWizardManager will pick the default builder at its own discretion. A control container will call this method when inserting a control to find out if there exists a control creation builder/wizard to run. If so, then GetBuilder would be called to actually get the builder.
The builder manager's implementation of MapObjectToBuilderCLSID may have private knowledge of how to do the mapping for special CLSID's or it may use a general mechanism that looks in the registry under the object's CLSID key for the ObjectBuilderWizard keyword to find the builder classification GUID (CATID) appropriate for the object. This method will always return the CLSID of a particular builder.
The following are legal flags to pass for the dwPromptOpt parameter:
Argument | Argument type | Argument description |
RclsidObject | REFCLSID | [IN] CLSID of the object. |
DwPromptOpt | DWORD | [IN] flag to control whether the user is prompted to choose the builder. Values are taken from the BLDPROMPTOPT enumeration. |
HwndPromptOwner | HWND | [IN] Owner HWND for all dialog boxes and windows. May be NULL in which case the BuilderWizardManager will use the hwnd for the application frame window.
NOTE: this method call may bring up a modal dialog box to allow the user to choose which builder to use. |
PclsidBuilder | CLSID* | [OUT] Address at which to return the CLSID of a specific builder. |
Return Value | S_OK if the clsid maps to a builder.
S_FALSE if there is no builder for the clsid. |
Given the component category GUID (CATID) of a builder return the CLSID of a specific builder. The caller can control whether the user is prompted to choose if more than one builder is available for a particular builder CATID. If the user is not to be prompted and there is more than one builder available, then the BuilderWizardManager will pick the default builder at its own discretion. Most clients will not need to call this method directly; they will be able to call IBuilderWizardManager::GetBuilder and specify the BLDGETOPT_FAUTOMAPGUID flag. Advanced callers may have reason to retrieve the CLSID for a builder rather than immediately cause the builder to be instantiated.
The builder manager's implementation of MapBuilderCATIDToCLSID may have private knowledge of how to do the mapping for special CATIDs or it may use a general mechanism that looks in the registry. This method will always return the CLSID of a particular builder.
Argument | Argument type | Argument description |
RguidBuilder | REFGUID | [IN] normally the CATID of a builder.
If the GUID is actually a CLSID of a specific builder and this builder is supported by the BuilderWizardManager (i.e., the builder is properly installed for the given application), then this method will return S_OK and the *pclsidBuilder will be set to the rguidBuilder IN parameter. |
dwPromptOpt | DWORD | [IN] flag to control whether the user is prompted to choose the builder. Values are taken from the BLDPROMPTOPT enumeration. |
hwndPromptOwner | HWND | [IN] Owner HWND for all dialog boxes and windows. May be NULL, in which case the BuilderWizardManager will use the hwnd for the application frame window.
NOTE: this method call may bring up a modal dialog box to allow the user to choose which builder to use. |
pclsidBuilder | CLSID* | [OUT] Address at which to return the CLSID of a specific builder. |
Return Value | S_OK if the CATID maps to a builder.
S_FALSE if there is no builder for the CATID. |
The builders that are returned via the GetBuilder method are always expected to run as modal builders. The client of the BuilderWizardManager is responsible for disabling the application appropriately prior to invoking the builder, and then re-enabling the application after the builder method returns. This can be achieved by one of several mechanisms. If the client is internal to the application that implements BuilderWizardManager, then the client can use the private mechanisms of the application. If the client is a standard in-place OLE object, then it can achieve this by the normal mechanism of calling IOleInPlaceFrame::EnableModeless. If the client is registered with the IComponentManager, then it can call IComponentManager:: OnComponent[Enter|Exit]State to manage the modality of the application. But if the client is not an in-place object and otherwise has no other means to disable the application, then it can call IBuilderWizardManager::EnableModeless method to achieve the same result.
This method has the same semantics as IOleInPlaceFrame::EnableModeless.
Argument | Argument type | Argument description |
fEnable | BOOL | [IN] TRUE if modeless dialog boxes should be enabled, FALSE if they should be disabled. |
Return Value | S_OK if successful.
This method cannot fail. |
// {95FC88C1-9FCB-11cf-A405-00AA00C00940} DEFINE_GUID(IID_IProvidePropertyBuilder, 0x95fc88c1, 0x9fcb, 0x11cf, 0xa4, 0x5, 0x0, 0xaa, 0x0, 0xc0, 0x9, 0x40); typedef enum tagCTLBLDTYPE { CTLBLDTYPE_FSTDPROPBUILDER = 0x00000001 , CTLBLDTYPE_FINTERNALBUILDER = 0x00000002 , CTLBLDTYPE_FEDITSOBJDIRECTLY = 0x00000004 } CTLBLDTYPE; [object, uuid(95FC88C1-9FCB-11cf-A405-00AA00C00940), pointer_default(unique)] interface IProvidePropertyBuilder : IUnknown { HRESULT MapPropertyToBuilder ( [in] LONG dispid, [in, out] LONG *pdwCtlBldType, [in, out] BSTR *pbstrGuidBldr [out, retval] VARIANT_BOOL *pfRetVal ); HRESULT ExecuteBuilder ( [in] LONG dispid, [in] BSTR bstrGuidBldr, [in] IDispatch *pdispApp, [in] LONG hwndBldrOwner, [in, out] VARIANT *pvarValue, [out, retval] VARIANT_BOOL *pfRetVal ); }
When the user gives focus to a property in a property browser (e.g., a grid-style "All" page property sheet), the property browser can ask the control if it supports custom editing for the property via a builder or other custom modal UI. The control should set *pfRetVal equal to VARIANT_TRUE if a builder is available, otherwise VARIANT_FALSE. If the *pfRetVal == VARIANT_TRUE, then the property browser should display a [...] button. The control may use a standard Property Builder (CTLBLDTYPE_FSTDPROPBUILDER) that is managed by the IBuilderWizardManager or it may use an internal builder (CTLBLDTYPE_FINTERNALBUILDER) that only the control can execute. There are two types of internal builders:
The control indicates the type of builder used via the pdwCtlBldType parameter. If *pfRetVal == VARIANT_TRUE, then the property browser adds the [...] button. Once the user presses the [...] button, then the builder is invoked. If the control uses a standard Property Builder, then the property browser executes the builder directly (using its own IBuilderWizardManager functionality). The control may return a CATID or a specific CLSID to identify the builder. The property browser should call CLSIDFromString to convert the *pbstrGuidBldr from a string to a GUID. (To be OLE automation compatible we cannot return a GUID, but instead must return a string.) The property browser will call IBuilderWizardManager:: GetBuilder(guidBuilder, BLDGETOPT_FAUTOMAPGUID, ) to get the builder and then will directly call IDispatch::Invoke on the builder passing the standard parameters for a Property Builder:
If the control uses an internal builder, then the property browser will invoke the builder by calling IProvidePropertyBuilder::ExecuteBuilder to allow the control to do whatever it wants to edit the property. Most builders should return the new value for the property without directly updating the object. This enables a grid style property page to use the builder in a multiple selection situation. If the internal builder directly edits the property of the object, then the CTLBLDTYPE_EDITSOBJDIRECTLY bit should be set in * pdwCtlBldType. Such a builder can only be used on a single selection.
Argument | Argument type | Argument description |
dispid | LONG | [IN] DISPID to identify a property. |
pdwCtlBldType | LONG * | [IN, OUT] Indicates whether the builder for the control is an internal builder or a standard builder. The acceptable values are taken from the CTLBLDTYPE enumeration. The value passed in should be zero. This value should be an OUT only paramater, but in order to be OLE automation compatible it is declared to be an IN/OUT parameter. The input value is ignored. |
pbstrGuidBldr | BSTR * | [IN, OUT] If the control uses a standard builder (*pdwCtlBldType == CTLBLDTYPE_STDPROPBUILDER), then *pbstrGuidBldr is a builder CATID or a specific builder CLSID. If the control uses an internal builder (*pdwCtlBldType == CTLBLDTYPE_INTERNALBUILDER), then *pbstrGuidBldr can be any value as determined by the control, including CLSID_NULL if the GUID is not needed. The value passed in should be an empty bstr. This value should be an OUT only paramater, but in order to be OLE automation-compatible it is declared to be an IN/OUT parameter. The input value is freed and ignored. |
pfRetVal | VARIANT_BOOL | [OUT, RETVAL] This VARIANT_BOOL is the return value used by the builder to indicate whether the a builder is available. VARIANT_TRUE = builder available. VARIANT_FALSE = there is no builder available. |
Return Value | S_OK or E_INVALIDARG. |
A property browser uses the ExecuteBuilder method to ask a control that uses an internal builder to execute the builder to perform its custom editing for a property. This method returns the value that is the result of the builder's normally not setting this value into the property of the control directly. This enables a grid-style property page to use the builder in a multiple selection situation. Such a builder can only be used on a single selection. It is the responsibility of the caller (normally the grid-style property page) to set the property of the control. The execution of the builder must be modal. The caller is responsible for properly disabling all windows prior to calling the method.
Argument | Argument type | Argument description |
dispid | LONG | [IN] This dispid parameter is an optional argument and is normally set to NULL. It is only used when the builder being invoked is of type CTLBLDTYPE_EDITSOBJDIRECTLY. In this case the dispid identifies the property of the object being edited. This type of builder can only be invoked on a single selection. |
bstrGuidBldr | BSTR | [IN] The guid that was passed from the control via MapPropertToBuilder (BSTR *pbstrGuidBldr). This can be any value as determined by the control, including an empty string if the guid is not needed. |
pdispApp | IDispatch* | [IN] IDispatch of Application. This argument is needed for most builders. This parameter may be NULL if the caller does not actually have an Application IDispatch* available. |
hwndBldrOwner | LONG | [IN] Owner HWND for all dialog boxes and windows. |
pvarValue | VARIANT* | [IN/OUT] Pointer to VARIANT which contains current property value. |
pfRetVal | VARIANT_BOOL | [OUT, RETVAL] This VARIANT_BOOL is the return value used by the builder to indicate whether the user of the builder committed the action (pressing the OK button if one exist on the builder's UI) or cancels the action (pressing the Cancel button if one exist on the builder's UI). VARIANT_TRUE = builder was successfully executed by the user. VARIANT_FALSE = the user has decided to cancel the builder's invocation and the property value was unchanged. |
Return Value | S_OK if the builder executed properly.
E_NOINTERFACE if the builder is not supported. If the builder automation server could not be instantiated or invoked, then any error HRESULT returned from CoCreateInstance or IDispatch::Invoke will be returned. |