HINSTANCE & HWND Corrupted when passed around functions


Keywords:c++ 


Question: 

I have a funny error occurring at runtime where my HINSTANCE and HWND variables are getting corrupted. I have posted the debugging output below and the line of code where the runtime error occurs. *Note: even though the screen capture kindof shows the HWND isn't corrupted, it doesn't point to the right/valid window anymore

Why is this problem occurring and how can I fix it?

WindowTiler.exe!WindowLayoutComponent::init(const IEventArgs & evtArgs) Line 26 C++
[External Code]
WindowTiler.exe!EventDelegate::operator()(const IEventArgs & evtArgs) Line 21 C++
WindowTiler.exe!IApp::eventHandler(const int & evtId, const IEventArgs & evtArgs) Line 20 C++
WindowTiler.exe!Win32App::wndProc(HWND__ * hwnd, unsigned int message, unsigned int wParam, long lParam) Line 18 C++
[External Code]
[Frames below may be incorrect and/or missing, no symbols loaded for user32.dll]
WindowTiler.exe!Win32App::initInstance(const Win32AppInit & evtArgs) Line 154 C++
WindowTiler.exe!Win32App::init(const IEventArgs & evtArgs) Line 100 C++
WindowTiler.exe!App::init(const IEventArgs & evtArgs) Line 45 C++
WindowTiler.exe!WinMain(HINSTANCE__ * hInstance, HINSTANCE__ * hPrevInstance, char * lpCmdLine, int nCmdShow) Line 16 C++
[External Code]

Status WindowLayoutComponent::init(const IEventArgs& evtArgs)
{
    auto args = (const WinEventArgs&)evtArgs;

    HWND btn = CreateWindowEx(WS_EX_TRANSPARENT, _T("Button"), _T("Test"), WS_VISIBLE | WS_CHILD | WS_EX_TRANSPARENT,
        30, 30, 50, 50, args.hwnd, NULL, args.hinstance, 0); // LINE 26

    return S_SUCCESS;
}

enter image description here

Edit: There are 2 IEventArgs objects involved. One passed to App::init(const IEventArgs & evtArgs) and another different object created in Win32App::wndProc and passed to IApp::eventHandler(const int & evtId, const IEventArgs & evtArgs).

LRESULT CALLBACK Win32App::wndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    int wmId, wmEvent;
    PAINTSTRUCT ps;
    HDC hdc;

    WinEventArgs args { hinstance, hwnd, wParam, lParam };
    eventHandler(message, args);
    ...

1 Answer: 

After extensive debugging I have found the problem and solved it. I'm surprised the compiler didn't catch this error. It's actually quite interesting what went wrong. The problem was here...

struct IEventArgs
{

};

struct WinEventArgs : public IEventArgs
{
    WinEventArgs() = delete; 
    WinEventArgs(const HINSTANCE& hinstance, const HWND& hwnd, const WPARAM& wParam, const LPARAM& lParam) :
        hinstance(hinstance), hwnd(hwnd), wParam(wParam), lParam(lParam)
    {}

    const HINSTANCE& hinstance;
    const HWND& hwnd;
    const WPARAM& wParam;
    const LPARAM& lParam;
};

class EventDelegate
{
public:
        // the problem is here: the parameter should be 'const IEventArgs&'
    typedef std::function<Status(IEventArgs)> EDelegate;

    EventDelegate(EDelegate delegate, GUID gUidContext);

    Status operator()(const IEventArgs& evtArgs)
    {
        return delegate(evtArgs);
    }

private:
    GUID gUidContext;
    EDelegate delegate;
};

I've produced a simple example of the problem that people can reproduce. Whilst I've fixed it, what exactly was going wrong, was it that a copy was being made and I hadn't implemented a copy constructor?

struct IEventArgs {  };

struct WinEventArgs : public IEventArgs
{
    WinEventArgs(const int& a, const int& b) : a(a), b(b) { }
    const int& a;
    const int& b;
};

class Component
{
public:
    void test(const IEventArgs& evtArgs)
    {
        const WinEventArgs& args = static_cast<const WinEventArgs&>(evtArgs);
        printf("a=1 is: %d, b=2 is: %d\n", args.a, args.b);
    }
};

int main()
{
    WinEventArgs args(1,2);
    Component cmp;

    // Note Component::test's parameter is 'const IEventArgs&' and that the std::function parameter is just 'IEventArgs'
    std::function<void(IEventArgs)> func = std::bind(&Component::test, cmp, std::placeholders::_1);
    func(args);

    // When std::function parameter is 'const IEventArgs&' the cast inside Component::test succeeds
    std::function<void(const IEventArgs&)> func2 = std::bind(&Component::test, cmp, std::placeholders::_1);
    func2(args);

    return 0;
}