The first being that most useful frameworks are quite large. In addition to learning the native platform APIs, you must also learn the hierarchy and usage conventions of the core classes. Careful thought is often necessary before adding a new feature to a framework, so that it is consistent with the existing class design. On the implementation side, it's much more difficult to modify portions of a complex class hierarchy because of the formal and implicit dependencies. Of course, even a perfectly designed base class can be subverted when derived classes make assumptions about callback and usage conventions.
Polymorphism causes it's own problems.
In this case, the code to manage windows is spread among 4 different locations, making debugging window layering and interaction much more difficult. Overengineering a framework for use in some mythical future project is often a waste of time, because you simply can't predict the future. Don't spend weeks writing extremely general class libraries if you don't need them right now . Invariably you'll find fault with a fundamental design decision, and you'll need to rewrite those parts of the code anyway. In the end, any software design is based on certain assumptions, and as code accumulates, more assumptions and subtle interactions are added.