Windows Aero - app crash on 32-bit build (x64 works fine)



  • I've run into this issue and I'm unable to find out what's wrong about it.
    I'm dynamically loading the library dwmapi.dll which provides the DwmExtendFrameIntoClientArea function (Windows Vista, Seven and 8 only). Now, the issue only happens on the 32-bit build (Using MSVC2012 compiler) where the 32-bit app would crash before calling the DwmExtendFrameIntoClientArea function. The project compiles fine on both x64 and x86 builds. I'm posting here the code and I hope someone will be able to find what's wrong.

    @
    /* WARNING: Aero is only available on Windows Vista, Windows 7 and Windows 8

    • trying to use Aero on Windows XP will fail the DLL loading
      */

    #include <QApplication>
    #include <QtWidgets>
    #include <QLibrary>

    int main(int argc, char *argv[])
    {
    QApplication GUI_test(argc, argv);
    QMainWindow mainWin;
    // We need an erased background for the Aero glass to show up
    mainWin.setAttribute(Qt::WA_TranslucentBackground);

    // Loading the dwmapi.dll library which provides the Aero feature
    QLibrary dwmapi_dll("dwmapi.dll");
    if (dwmapi_dll.load()){
        // DLL loaded successfully, prepare for calling DwmExtendFrameIntoClientArea
    
        // The next is a replacement for the MARGINS struct declared in the Windows API
        // Using a replacement so it's not necessary to include the Windows API headers
        struct MARGINS {
            /// Setting any of these to -1 extends the frame to the whole window
            int L;
            int R;
            int T;
            int B;
        };
    
        /// HRESULT WINAPI DwmExtendFrameIntoClientArea(HWND, MARGIN);
        // Since the windows API headers are not included the HRESULT and
        // WINAPI types are not available so the function will be cast to void
        typedef void (*pDWM_EFICA) (HWND, MARGINS);
    
        // Getting a function pointer
        pDWM_EFICA DwmExtendFrameIntoClientArea = (pDWM_EFICA) dwmapi_dll.resolve("DwmExtendFrameIntoClientArea");
        // Preparing the margins - setting all to -1 so Aero will be extended to the whole window
        MARGINS m = {-1, -1, -1, -1};
        if (DwmExtendFrameIntoClientArea){
            // Drawing with Aero; if composition is not enabled a non transparent background is drawn
            // usually it is the background color defined in the Windows theme, or the application defined window background
            DwmExtendFrameIntoClientArea((HWND)mainWin.winId(), m); /**** HERE THE 32-BIT BUILD CRASHES *****/
        }
    
        // The DLL is not necessary anymore, unloading it
        dwmapi_dll.unload();
    } /* else {
        // Either the DLL could not be loaded because of too low user privileges
        // Or the user is using an unsupported OS (like Windows XP and before)
        // Ignoring the cause right now, it will not cause any further fault in the application
    }*/
    
    // Showing the window (if successfully loaded the dwmapi DLL, Aero will be already drawn at this point)
    mainWin.show();
    return GUI_test.exec&#40;&#41;;
    

    }
    @



  • Please guys, can anyone have a look and tell me anything about this?


  • Moderators

    From what I can tell at first glance you have 2 things wrong.
    First is the method signature. It should be
    @
    typedef void (pDWM_EFICA) (HWND, const MARGINS);
    @
    and then pass &m.

    The other thing is that you call this api before window is shown and event loop is started. At this point winId probably returns a null HWND or some garbage. You should move it to more appropriate place like the showEvent.

    Also some notes:

    • the doc for winId says that this value can change at runtime so you should hunt for the event of type QEvent::WinIdChange and probably re-run the margin setting with the new HWND.
    • before calling DwmExtendFrameIntoClientArea you should check if the user is running DWM with "DwmIsCompositionEnabled":http://msdn.microsoft.com/en-us/library/windows/desktop/aa969518.aspx It will be off if the user is using basic theme (by setting it manually, viewed via remote desktop, the display driver can't handle aero etc.).
    • The setting can change at runtime (see reasons above), so you should also watch for the WM_DWMCOMPOSITIONCHANGED message in the "nativeEvent":http://qt-project.org/doc/qt-5.1/qtwidgets/qwidget.html#nativeEvent and adjust your drawing accordingly.


  • I know that I'm not checking for composition to be enabled or being disabled at runtime, but I'm testing this on my PC which I know it's always enabled. In a final project though I would implement those checks too.

    I've tried using a const MARGINS * parameter, but nothing changes. I've put some QMessageBox after loading the DLL and before calling the ExtendFrameIntoClientArea function; on the x64 build it all goes fine, messages show and then the window shows as expected; on x86 build messages show, but as soon as I close the message before calling the ExtendFrameIntoClientArea function the application crashes.
    I don't think that calling the function before showing the window is a problem. The only thing that matters is that when calling the function the window is created and has a background that needs to be drawn. As soon as I create a QMainWindow I get these, so windows has an area where to draw the Aero glass. Anyway, i tried showing the window before I even load the DLL, and then apply Aero, the result is the same app crash when I call the ExtendFrameIntoClientArea function, and as same as before, only crashes on 32-bit build.

    So... do you any other idea about what's happening?
    Meanwhile, I'll try posting in the microsoft forums too, maybe they know more about similar issues.


  • Moderators

    Ok, I got back to my dev machine and run the thing. Yeah, it's like you said - runs in 64bit and crashes in 32.

    I thought it might by a QLibrary problem but using naked LoadLibrary from winapi gives same function pointers and also crashes.
    So the other possibility was that 32bit winId returns wrong value, but I checked with Spy++ and HWND is the same as returned by winId.

    Then I tried the same with statically linked dwmapi and including dwmapi.h. Curiously it runs fine this way so... I don't know really.
    Sorry, I don't have other ideas.



  • So it works fine with the statically linked lib? Strange.. but you know, that's Microsoft code... :D

    Thank you very much for your help!


  • Moderators

    These tests I made with Qt5.1.1.
    I'm using similar code (dynamic load) in one of my work projects that uses Qt 4.8.0 and it works fine in both 32 and 64 bit versions.
    So maybe Qt5 is now doing something funky under the covers and it bugs out in 32bit version.
    I'd suggest to report it.



  • If I'm not asking too much, Chris, could you please post your Qt 4.8.0 code where you're using Aero? I'll try to use it to understand if I'm doing something wrong or if it might be a Qt issue.
    BTW, confirmed to work when statically linking the lib, but that's no good for deploying on non-Aero capable Windows versions.


  • Moderators

    Well I can't post company code but the idea is basically the same: load library, get function address and run.
    I'll try to run your code on a Qt4.8 setup when I get to it, or if you have it handy you can try it yourself. If it runs it probably means a bug in Qt5 32bit.



  • That's OK. If you're still willing to try my code let me know how's it working on a Qt 4.8 build. Still, I think that's some Qt issue because I checked other people's code about Aero and I've seen none having different code for 32 and 64 bit versions. If you confirm that the code's working I may report this issue to the Qt devels.


Log in to reply
 

Looks like your connection to Qt Forum was lost, please wait while we try to reconnect.