Sunday, January 01, 2006

Getting the resize handling code implemented for Windows turned out to be rather time consuming. Writing the code was easy enough. It was getting it all to link properly that drove me mad. I spent numerous hours this New Year's weekend fighting what appeared to be a linker error in VC++ 2003.NET -- errors related to undefined .ctor and member functions at link time in my mixed-mode (managed/unmanaged) DLL.

The problem manifested itself once I added the managed helper class that was designed to receive Form Resize events. On doing so, the linker started to complain that the Button Click event helper class that had previously linked without problems had unresolved references. This was happening even though both were of these classes were coded to the exact same pattern, both were being built and linked into the DLL in exactly the same manner, and both were used in the same way in their respective contexts.

In the end, I was forced to put the implementation of these helper classes into a single source file in order resolve the linker issue. When I migrate to .NET 2005, I'll likely split this file into separate compilation units, as keeping all of the handlers required for all of the widgets in the same file might become difficult to maintain. But for now, I am past the problem. Time to move on :-) Here's the code:

// Button Clicks

#include "buttoncallbackhelper.h"
#include "windowsbuttonimpl.h"

#pragma managed

ButtonCallbackHelper::ButtonCallbackHelper(WindowsButtonImpl *impl)
m_impl = impl;

void ButtonCallbackHelper::OnButtonClick(Object *sender, System::EventArgs* e)
if (m_impl)

// Window resize

#include "windowresizehelper.h"
#include "windowswindowimpl.h"

WindowResizeHelper::WindowResizeHelper(WindowsWindowImpl *impl)
m_impl = impl;

void WindowResizeHelper::OnResizeHandler(Object *sender, System::EventArgs* e)
if (m_impl && sender) {
Control *control = dynamic_cast<Control *>(sender);
if (control) {
Rectangle r = control->ClientRectangle;
int x, y, width, height;

x = r.Left;
y = r.Top;
width = r.Right - r.Left;
height = (r.Bottom - r.Top);
m_impl->HandleResize(x, y, width, height);

As you can see, handling a button click is simple -- all we need to know is that it occured, and pass it up to the containing WindowsButtonImpl instance for handling. In the case of handling a window resize, it is a bit more complicated -- dynamic_cast the Object that generated the event to an instance of Control, retrieve the client rectangle, compute the x, y, width, and height values from this result, and then pass them along to the WindowsWindowImpl instance associated with the Control, which sends them on to whatever listeners in the layout engine have registered for the event.

Because the callback handlers needed to be coded as managed, I implemented these helper classes to maintain a pointer to the parent, non-managed abstract class. In the case of the Button helper class, the abstract class is WindowsButtonImpl. The constructor for the helper class takes that pointer as an argument. When the Click callback is triggered, it uses the member variable to find the parent class and calls it with the event, and it is then percolated up to the layout engine.

The final touch needed was to add __declspec(dllexport) prefixes to the generated component header and wrapper functions -- in testing component execution, it was clear that the JavaScript engine was not finding the component interfaces because they were not being exported. A simple #define EXPORT that resolves to __declspec(dllexport) on Windows and to nothing elsewhere, plus some code changes to the component wrapper generator was all that was required.