Uploaded image for project: 'Qt'
  1. Qt
  2. QTBUG-120198

Process abruptly terminates while executing static destructor in Qt6Multimedia.dll

    XMLWordPrintable

Details

    • Bug
    • Resolution: Fixed
    • P2: Important
    • 6.5.6, 6.7.1, 6.8.0 FF
    • 6.6.0
    • Multimedia
    • None
    • Dell Latitude 5521
      11th Gen Intel(R) Core(TM) i7-11850H @ 2.50GHz
      Windows 10 Pro
      OS Build 19045.3693
    • Windows
    • 44f1e17d2 (dev), 073e01142 (6.7), ea027cb82 (6.6), 199ca6500 (tqtc/lts-6.5), 2964b974c (dev), b573c337a (6.7), 6e817fc7a (6.6), 2e581b275 (tqtc/lts-6.5), 5fcfa339b (dev), 2fd9cc557 (6.7), 7c3a328c0 (6.6), c8e219be2 (tqtc/lts-6.5), 7efc9f9b6 (dev)
    • Multimedia Wk 13-14

    Description

      Since migrating our app from Qt 5.15.14 to Qt 6.6.0, we've discovered that certain code that it usually executed during application shutdown is no longer being called. This includes:

      • DllMain() functions in our DLLs that handle the DLL_PROCESS_DETACH case.
      • Destructors for C++ static objects.

      Normally, these calls are made as part of the Windows 'exit' processing that goes on after an application leaves its 'main' function. Windows normally unloads each DLL in turn, calling its DllMain() function, and then calls 'atexit' handlers, which includes a dynamically generated handler for each static C++ object, that runs its destructor.

      Since moving to Qt 6, none of this code is executed any more - but only if our app makes use of Qt6Multimedia classes, such as QMediaPlayer. If we do use QMediaPlayer, then the process abruptly terminates before these exit steps get called.

      I've been able to identify the line of code at which this termination is occurring. It's happening on the call to UnregisterEndpointNotificationCallback() in the ~QWindowsMediaDevices() destructor, in qwindowsmediadevices.cpp:

      QWindowsMediaDevices::~QWindowsMediaDevices()
      {
          if (m_deviceEnumerator) {
              m_deviceEnumerator->UnregisterEndpointNotificationCallback(m_notificationClient.Get());
          }
          if (m_warmUpAudioClient) {
              HRESULT hr = m_warmUpAudioClient->Stop();
              if (FAILED(hr)) {
                  qWarning() << "Failed to stop audio engine" << hr;
              }
          }
      
          m_deviceEnumerator.Reset();
          m_notificationClient.Reset();
          m_warmUpAudioClient.Reset();    CoUninitialize();
      } 

      To reproduce this, open and build the Qt 6 media player example app at C:\Qt\Examples\Qt-6.6.0\multimedia\player. Set a breakpoint on the call to UnregisterEndpointNotificationCallback() in C:\Qt\6.6.0\Src\qtmultimedia\src\multimedia\windows\qwindowsmediadevices.cpp. When the breakpoint is reached, press F10 to step over the call. The process will immediately terminate, with no identifiable error or exception.

      I'm not sure why this call to UnregisterEndpointNotificationCallback() is killing the process in this way. The code in question is being executed within a static destructor, destroying the 'devicesHolder', and the stack looks like this:

      QWindowsMediaDevices::`scalar deleting destructor' - Qt6Multimediad
      QWindowsMediaDevices::~QWindowsMediaDevices - qwindowsmediadevices.cpp
      `anonymous namespace'::DevicesHolder::~DevicesHolder - qplatformmediadevices.cpp
      `anonymous namespace'::`dynamic atexit destructor for 'devicesHolder'' - Qt6Multimediad
      initterm_e - ucrtbased
      initterm_e - ucrtbased
      initterm_e - ucrtbased
      execute_onexit_table - ucrtbased
      __scrt_dllmain_uninitialize_c - utility.cpp
      dllmain_crt_process_detach - dll_dllmain.cpp
      dllmain_crt_dispatch - dll_dllmain.cpp
      dllmain_dispatch - dll_dllmain.cpp
      _DllMainCRTStartup - dll_dllmain.cpp
      RtlActivateActivationContextUnsafeFast - ntdll
      LdrShutdownProcess - ntdll
      RtlExitUserProcess - ntdll 

      It may have something to do with accessing COM objects in a static destructor, but I'm not really sure.

      If I debug this in Visual Studio, I can use "Set Next Statement" to skip over the call to UnregisterEndpointNotificationCallback(). It doesn't get much further if I do this - it crashes soon after on the call to  m_warmUpAudioClient.Reset(). The only way I've found to avoid the crash is to use "Set Next Statement" to step over this line of code in DevicesHolder::~DevicesHolder():

       delete nativeInstance;

      This prevents it from executing the problematic ~QWindowsMediaDevices() destructor, and the remainder of the app exit processing occurs as it should. Of course, I can only do this in the debugger, and even if I were to actually change the code to remove that 'delete' call and rebuild the DLL, it would be introducing a memory leak.

      I'd really appreciate it if someone could look into why this is happening, as the lack of normal exit handler execution in our DLLs is quite a big issue for us.

      Attachments

        Issue Links

          No reviews matched the request. Check your Options in the drop-down menu of this sections header.

          Activity

            People

              johanseg Jøger Hansegård
              roballan Rob Allan
              Votes:
              0 Vote for this issue
              Watchers:
              5 Start watching this issue

              Dates

                Created:
                Updated:
                Resolved: