How much do you expect from a framework layer?

If a framework exposes functionality provided by a lower layer, how hard should the framework try to insulate you from all the quirks and limitations of the lower layer?

Instinctively, of course, you would say, “The framework should insulate me completely.” But be careful what you ask for. If a framework insulated you completely, then every limitation of the underlying layer needs to be worked around in some manner or other. This would mean writing a lot of code to emulate missing functionality or removing a limitation, just in case somebody using the framework actually runs into that limitation.

Let’s take for example the ToolTip.AutoPopDelay property. The ToolTip class is a Windows Forms wrapper around the Common Controls ToolTip window class. If you look at the documentation for the TTM_SETDELAYTIME message, you’ll see that the delay time (iTime) is passed in the low word of the lParam parameter. Consequently, it is limited to a 16-bit value, and in this case, it’s a signed 16-bit value since negative values for iTime have special meaning (as noted in the documentation).

Since the maximum value for a signed 16-bit integer is 32767, the maximum value you can set for the delay time is a little over 32 seconds.

So if you try to set your ToolTip.AutoPopDelay to something longer, like 60 seconds, you will find that the delay time is not properly set, since the ToolTip class merely passes the delay value through to the underlying control. And until you understood the underlying control, you would never understand why.

Comments (10)
  1. Marc Miller says:

    I think this is a bug — the implementers of ToolTip.AutoPopDelay had the perfectly reasonable option of making that property type a short.

  2. Anonymous says:

    they could also have thrown an exception of some description when a bung value got passed in, but I’m not sure if that’s good design or not.

    Would a short change on 64 bit the same way that the low word of lParam would? That is, on windows on ia64, would a short still be equivalent to the low word of lParam?

    I don’t know.

  3. null says:

    But if I am insulated from that which lies below, currently, the implementation can change to what will lie below in future.

  4. pdq says:

    To completely insulate you from the calling layer, invalid or special inputs must be handled also. This should generate an error or transform it into what you really wanted, if possible.

  5. Raymond Chen says:

    And suppose someday the 16-bit limitation on tooltip timeouts is lifted. The framework would continue to enforce a 16-bit limit even thought the limitation would no longer exist. Maybe that’s what you want, I don’t know.

  6. That’s what documentation is for, not?
    When describing the function, make a note that the maximum value for the delay is 32766 (32767 would be -1). If the performance loss isn’t too great, add a simple value check in the framework, which can optionally be ignored using some kind of config call.

  7. Mike Dunn says:

    And suppose someday the 16-bit limitation on tooltip timeouts is lifted.

    I’d rather have it work right now, within whatever limitations exist now. If it might someday in the future work better, great, but that does me no good in the present.
    I think it’s perfectly reasonable to enforce a range limit on the parameter, with an exception or whatever .NET does (I don’t code with .NET so I don’t know the practices well). Sure, you can document it, but the exception will work for everyone, including people who don’t read the docs or don’t see the note in the Remarks section about the range limit.

  8. Matt says:

    Well, since nobody else has, I’ll make the reference to The Law of Leaky Abstractions:

  9. Raymond Chen says:

    Part of the issue is whether you want any particular framework to be an all-encompassing "protect me from the real world" framework or whether you want it to be a lightweight "just make my life a little easier" framework. Different people want different degrees of heftiness in their frameworks. If you’re a heavyweight framework person, then you want every last quirk of the underlying technology hidden from you, at great expense if necessary. If you’re a lightweight framework person, then you want a little syntactic sugar and not much else; don’t waste your time emulating missing functionality; that’s an extra 50K I don’t want to pay for.

  10. Shane King says:

    Throwing an exception for invalid inputs is a good middle ground though. It’s also good practice. A boundary API like a framework should always do at least minimal sanity checking.

    If the underlying implementation changes, then it’s the framework’s job to be updated to allow the new range of values.

Comments are closed.