Geoff Chappell - Software Analyst
This function queries an inner object for a desired interface.
HRESULT SHWeakQueryInterface ( IUnknown *punkOuter, IUnknown *punkInner, REFIID riid, PVOID *ppv);
The punkOuter argument is an interface pointer to an outer object.
The punkInner argument is an interface pointer to an inner object that is to be queried for a desired interface.
The riid argument is a reference to the IID of the desired interface.
The ppv argument is the address of a variable that is to receive a pointer to the desired interface.
The function returns zero for success, else an error code.
In all cases of failure, the variable at ppv is cleared to NULL.
If either punkOuter or punkInner is NULL, the function has nothing to do, and fails (returning E_NOINTERFACE). Failure at querying the object punkInner for the desired interface becomes failure for the function. Once the function has obtained a pointer to the desired interface for the object at punkInner, it releases the object at punkOuter.
The SHWeakQueryInterface function seems intended to ease some bookkeeping with COM aggregation. An outer (aggregating) object contains an inner (aggregable) object such that the inner object’s interfaces seem to be those of the outer object. Part of the technique is that the inner object implements an explicit IUnknown interface that gives access to its other interfaces, but has all its other interfaces delegate their IUnknown methods to an outer IUnknown interface provided by the outer object. If the outer object ever queries the inner object’s explicit IUnknown interface for an interface other than IUnknown, the inner object’s delegation (of the implied AddRef for the queried interface) will have incremented the outer IUnknown interface’s reference count. An outer object that is not awake to this will never be deletable. Proper reference counting therefore demands the following rule, which Microsoft’s page Aggregation (COM) expresses as:
The outer object must call its controlling IUnknown Release method if it queries for a pointer to any of the inner object’s interfaces.
Using the SHWeakQueryInterface function conveniently deals with both steps as one.
The function is not, however, restricted to helping with aggregation. The object represented by punkOuter can be any (old) object that is to be released once a desired interface is obtained from the (new) object represented by punkInner.
Moreover, punkOuter and punkInner can refer to the same object (not just in theory but in Microsoft’s actual practice). The effect is then that the caller of SHWeakQueryInterface obtains the desired interface without adding a reference, and without the responsibility of calling the object’s Release method when done.
Of course, this usage is unsafe in general, most notably when the desired interface has a tear-off implementation. It is well suited, however, to another situation that can occur in containment when the inner and outer objects are designed to act as a pair, such that the inner object may query the outer object for an interface. While the reference is held, the outer object is not deletable. This is a problem especially if the reference is wanted for the whole of the inner object’s lifetime and the inner object lives until the outer object is to be destroyed. A solution is for the inner object to release its reference to the desired interface, but use the interface anyway, through what is sometimes called a weak reference. The interface is maintained because the inner object’s existence is anyway dependent on the outer object’s and the interface is agreed not to have a tear-off implementation. Although this does not seem to be Microsoft’s typical use of SHWeakQueryInterface, it may be what gives the function its name.
The SHWeakQueryInterface function is exported from SHLWAPI.DLL as ordinal 267 in version 5.00 and higher.
Though this function dates from as long ago as 1999, it was still not documented by Microsoft as late as the January 2007 edition of the Windows Vista Software Development Kit (SDK).
Before Windows Vista, the SHWeakQueryInterface function was imported by BROWSEUI.DLL, SHDOCVW.DLL and SHELL32.DLL. Since SHDOCVW is beyond debate as being part of Internet Explorer, the only way that this function can be exempt from needing to be documented among the Settlement Program Interfaces is that Microsoft does not count SHLWAPI as part of any Windows Operating System Product for the terms of the settlement. Whatever may have been Microsoft’s argument, it is made irrelevant in Windows Vista. Where use of the function by BROWSEUI and SHDOCVW is transferred to or reproduced in the new DLL named IEFRAME, the latter does not import the function from SHLWAPI but instead calls its own copy.
Windows Vista also sees this function acquire new use, in TWEXT.DLL, which is the Time Warp namespace extension and is responsible for the Previous Versions tab in the Properties dialog for files. As it happens, TWEXT shows the purest of all known uses of the function. Its creatable TimeWarpFolder class is implemented as an outer object for an inner object of whose implementation details TWEXT has no special knowledge. When the TimeWarpFolder queries the inner object for its IShellFolder, IShellFolder2, IShellFolder3 and IPersistFolder3 interfaces, it uses SHWeakQueryInterface to keep its own reference count in order.