0

I working on an MFC class which derives from CWnd and creates a hidden window in its constructor. The object itself is constructed inside a derived CWinApp::InitInstance function.

if (
    this->CWnd::Create(
        nullptr,
        nullptr,
        WS_DISABLED, // Even disabled it will receive broadcast messages.
        {0, 0, 0, 0},
        CWnd::GetDesktopWindow(),
        fakeWindowId
    ) == FALSE
)
      throw runtime_error{"failed to create window"};

When I run this code in a debug build it triggers the following assertion:

Debug Assertion Failed!

Program: C:\WINDOWS\SYSTEM32\mfc140ud.dll File: f:\dd\vctools\vc7libs\ship\atlmfc\src\mfc\wincore.cpp Line: 571

For information on how your program can cause an assertion failure, see the Visual C++ documentation on asserts.

(Press Retry to debug the application)

If I ignore the assertion, the code works fine and no ill effect is observable. How do I address this assertion?

I'm registering the window as follows as well:

BOOL HiddenWindow::PreCreateWindow(CREATESTRUCTW& cs)
{
    if ( ! CWnd::PreCreateWindow(cs))
        return FALSE;
    cs.dwExStyle &= ~WS_EX_CLIENTEDGE;
    WNDCLASSEXW wc;
    ZeroMemory(&wc, sizeof(WNDCLASSEXW));
    wc.cbSize = sizeof(WNDCLASSEXW);
    const auto instance{AfxGetInstanceHandle()};

    if (GetClassInfoExW(instance, this->className_.c_str(), &wc) == FALSE)
    {
        wc.lpszClassName = this->className_.c_str();

        if ( ! RegisterClassExW(&wc))
        {
            Logger::Fatal(
                "Registering the window for copy data message failed! Messages will not be "
                    "copied, error code {}.",
                GetLastError()
            );
            return FALSE;
        }
    }
    else
        Logger::Debug(
            "There is already a window registered under the class name '{}'.",
            toString(this->className_)
        );
    cs.lpszClass = _wcsdup(this->className_.c_str());
    return TRUE;
}
JadziaMD
  • 2,690
  • 5
  • 31
  • 42
  • 1
    *"Press Retry to debug the application"* - that's the *first* thing you do, whenever you hit a debug assertion. Looking at the source it appears, that MFC requires certain preconditions be met, one of them being, that the window in question has been subclassed. This has not been done when running the constructor. You'll have to postpone window creation to a later point, after subclassing has completed. (Subclassing is an artifact MFC uses to attach its C++ class instances to native windows, wiring up Windows messages to its handlers implemented as class members.) – IInspectable May 01 '19 at 07:03
  • I'm already running it in the debugger. ;) Postponing the window creation to after the constructor still triggered the same assertion. I moved it to a function called "create" and called it after constructing the object. – JadziaMD May 01 '19 at 07:17
  • @JadziaMD you need to do what the first comment suggests. Then Visual Studio will show you the line with the ASSERT if the source code of MFC and that will give you a valuable hint about what has gone wrong. – Jabberwocky May 01 '19 at 07:42
  • @Jabberwocky There's not a retry button to click, since it's in the output window. I'm running it in the Visual Studio debugger, so I have break or continue. Clicking break will only go to my code. I've already opened the MFC file and the assertion doesn't help me ```ASSERT(oldWndProc != NULL);``` – JadziaMD May 01 '19 at 07:46
  • First, you seem to create non child window. Then what is fakeWindowId? In non child windows it should be menu handle, or null otherwise. Second, why would you `RegisterClassExW` in `PreCreateWindow`? Why your class name in `CWnd::Create` is nullptr? It should be the name of a class already registered. – Jovibor May 01 '19 at 07:56
  • @jovibor ```fakeWindowId``` is just a made up number to fit the signature. I don't know why the original author of the code I'm trying to fix did that. It's the same reason for the registration, that's where the original author had it. I suspect it's because the application is a hybrid MFC and CEF application. – JadziaMD May 01 '19 at 08:01
  • Try to null it then. Window id's are only valid for child windows with `WS_CHILD` flag. Also, try to set `parentwnd` to `this` instead of `GetDesktopWindow` and see if it helps. – Jovibor May 01 '19 at 08:06
  • 1
    Also see this similar thread: https://stackoverflow.com/questions/13370578/why-is-cwndcreateex-failing-to-create-my-window Your assertion is also within a `_AfxCbtFilterHook` func. – Jovibor May 01 '19 at 08:12
  • @jovibor I removed the fakeWindowId, passing NULL now. However, when replacing the parent window with this, I get the error code . Returning back to pasing in the CWnd::GetDesktopWindow() I no longer get that error code. I've removed the registration in the PreCreateWindow, and now it's registered before the function call to create the window. The class name is now passed to the CWnd::Create, and the assertion still triggers. I looked at the link you sent, and it is in the same area, but I don't see how it would be connected to what I'm doing. – JadziaMD May 01 '19 at 09:45
  • @Jovibor I forgot to paste in the error code. It's 0x0000057E – JadziaMD May 01 '19 at 09:58
  • @JadziaMD did you read this comment, might shed some light? https://stackoverflow.com/questions/13370578/why-is-cwndcreateex-failing-to-create-my-window#comment62261343_13463520 And as i can see this assert might be related to some threading issue. – Jovibor May 01 '19 at 10:07
  • @Jovibor Yeah I did, and I'm not making any AFX hook calls. I've not done anything with MFC since I took a semester on it 20 years ago, so I'm likely forgetting something. This object is being created in the CWinApp::InitInstance override function. – JadziaMD May 01 '19 at 10:13
  • By default, a failed assertion displays the [Assertion Failed Dialog Box](https://docs.microsoft.com/en-us/visualstudio/debugger/assertion-failed-dialog-box), giving you the option to instantly move to the failed assertion in the debugger. If all you get is output in the debug output pane of Visual Studio, your code called [_CrtSetReportMode](https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/crtsetreportmode) with the `_CRTDBG_MODE_DEBUG` flag (as opposed to `_CRTDBG_MODE_WNDW`). As far as I understand, the earliest you can do what you are doing is after `Create` returned. – IInspectable May 01 '19 at 20:20
  • @jov: This cannot be a threading issue. Everything runs on a single thread. MFC sets up a local CBT hook on the calling thread. When a native window gets created, that hook gets called just before control passes back to the system, which then calls the window procedure with the various `WM_CREATE` messages. After that, `CreateWindowExW` returns to the caller. There is no context switch involved anywhere. – IInspectable May 01 '19 at 20:23
  • @IInspectable Is it possible to get the output sent to a file? I get a lot of dialogue windows otherwise. – JadziaMD May 02 '19 at 11:16
  • This is possible, as explained in the link to the documentation I posted previously. But unless you are running automated tests (where you do not want to, or cannot interact with the GUI), it's best to just let failed assertions show a dialog. Every failed assertion is a bug, and it should be as hard as possible to ignore bugs. – IInspectable May 02 '19 at 18:33

1 Answers1

-1

So, I was unable to ever figure out what caused the MFC assertion. The solution was to remove the MFC window entirely and replace it with a Win32 window underneath the class, i.e. CreateWindowExW, GetClassinfoExW, and RegisterClassExW.

JadziaMD
  • 2,690
  • 5
  • 31
  • 42
  • I don't know why this was downvoted. No other solution was proposed to address the cause of the assertion, and this did. – JadziaMD May 08 '19 at 06:54