Parameter Conversion and Wrapper Methods
Where an object cannot be automatically converted by the VM, or is not converted in the desired manner, then it should first be converted to a more fundamental type in the image. The idiom used is to implement an #asParameter method for that object to answer an object which will be correctly coerced by the VM when passed directly to an appropriately declared external method. This is also more flexible, because polymorphic conversion can be performed as required.
Here is an example from UserLibrary, which is the class representing User32.DLL (one of the base Win32™ DLLs).
childWindowFromPointEx: hwnd pt: aPOINTL uFlags: flags
"Answers the handle of the window that contains the specified point.
HWND hwndParent, // handle to parent window
POINT pt, // structure with point coordinates
UINT uFlags // skipping flags
<stdcall: handle ChildWindowFromPointEx handle POINTL dword>
We might invoke this by evaluating an expression such as:
childWindowFromPointEx: View desktop asParameter
pt: (300@400) asParameter uFlags: 0.
It is good practice to write helper methods that wrap external library calls into more flexible, object-oriented, and easily used methods. Such wrapper methods should perform any useful #asParameter conversions, and should also convert any return values to appropriate objects (e.g. window handles should normally be wrapped in an appropriate View subinstance). Where a wrapper method exists, this should be used in preference to the underlying external method, and should generally be the only sender of the external library selector.
Our example, UserLibrary>>childWindowFromPointEx:pt:uFlags:, is wrapped by View>>subViewFromPoint:flags: in the following manner:
subViewFromPoint: aPoint flags: cwpFlags
"Answers the View in the receiver beneath aPoint (in the receivers coordinates)."
| handleFound viewFound |
handleFound := UserLibrary default
childWindowFromPointEx: self asParameter pt: aPoint asParameter uFlags: cwpFlags.
and: [handleFound ~= self handle
and: [(viewFound := self class withHandle: handleFound) notNil
and: [viewFound isManaged]]])
In this example the coordinate argument, a Point, is converted to a POINTL by sending it #asParameter. This allows it to be either a POINTL, or a Point, or some other object which implements #asParameter to return a POINTL. The routine also "improves" on the underlying Win32™ function by always answering a sub view, regardless of the depth of nesting, and not answering the same window. When modifying the functionality in this way, one should consider providing a more basic wrapping function (perhaps prefixed with #basic...), as this the "raw" form is sometimes needed by subclasses and other closely related classes.