Home > Software design >  How to show a QDialog on a custom Windows Desktop (HDESK)
How to show a QDialog on a custom Windows Desktop (HDESK)

Time:06-16

Solution

After several tests and good leads from the community, I can now confirm that it is mandatory to apply SetThreadDesktop before creating the QApplication. This is the only way to allow Qt to display QWidgets in the new desktop.

However, SwitchDesktop can be called before or after the QApplication, it doesn't matter as long as SetThreadDesktop has been called before the creation of the QApplication.

Thanks to all of you! I hope this topic will be useful for other people.

Problem

I'm working on a Qt project on Windows where i need to create a custom desktop (HDESK) using API such as CreateDesktop, OpenDesktop and SwitchDesktop. On this new Desktop, i need to show a QDialog (using exec or show depending on my needs) but the QDialog is not displayed on the new desktop but on the "default" one.

My code looks something like this :

/* Starting the QApplication from the main function */
QApplication qApp;
qApp.exec();
/* [...] An HEVENT is fired or a Win32 COM call is made telling me than i need to display the QDialog */
/* Creating the new desktop and switching to it */
HDESK hNewDesktop=NULL;

hNewDesktop=CreateDesktop(L"_MyNewDesktopName", NULL, NULL, DF_ALLOWOTHERACCOUNTHOOK, GENERIC_ALL, NULL);
SwitchDesktop(hNewDesktop);

/* Creating the QDialog and showing it */
QDialog *pqdlgMyDialog=NULL;

pqdlgMyDialog=new QDialog(NULL);
pqdlgMyDialog.show(); // or .exec() depending on the my needs

Doing this, create and display a new desktop with a black background but the QDialog is displayed on the default desktop (the one we see when we start Windows).

I have to tried to set a parent to my QDialog doing something like this but it does not work either :

QWindow *pqwParentWindow=NULL;
QWidget *pwqParentWidget=NULL;
HWND hwndParentWindow=NULL;

hwndParentWindow=GetTopWindow(NULL); // I have also tried GetDesktopWindow, FindWindowEx etc.
pqwParentWindow=QWindow::fromWinId((WId)hwndParentWindow);
pwqParentWidget=QWidget::createWindowContainer(pqwParentWindow);
[...]
QDialog *pqdlgMyDialog=NULL;
pqdlgMyDialog=new QDialog(pwqParentWidget);
pqdlgMyDialog.show(); // or .exec() depending on the my needs

If someone has an idea, i'm willing to try everything ! I have done a lot of reading of Qt Documentation and MSDN, looking for stuff such as "Is a QApplication linked to a desktop" but without success...

CodePudding user response:

HWNDs are tied to threads and threads with HWNDs are tied to desktops.

The SetThreadDesktop function will fail if the calling thread has any windows or hooks on its current desktop (unless the hDesktop parameter is a handle to the current desktop).

I don't know if QApplication creates a hidden window or if you have another HWND there but it is easy to demonstrate that SetThreadDesktop will fail if there is already a window on the thread:

HDESK g_hNew;

DWORD CALLBACK TrySwitch(LPVOID Param)
{
    if (Param)
    {
        CreateWindow(L"EDIT", L"Existing window", WS_VISIBLE|WS_OVERLAPPEDWINDOW, 0, 0, 200, 200, 0, 0, 0, 0);
    }
    if (SetThreadDesktop(g_hNew))
    {
        SwitchDesktop(g_hNew);
        CreateWindow(L"EDIT", L"New window", WS_VISIBLE|WS_OVERLAPPEDWINDOW, 0, 0, 200, 200, 0, 0, 0, 0);
    }
    Sleep(3333);
    return 0;
}

void Example()
{
    HDESK hIn = OpenInputDesktop(DF_ALLOWOTHERACCOUNTHOOK, false, DESKTOP_SWITCHDESKTOP);
    g_hNew = CreateDesktop(L"_MyNewDesktopName", NULL, NULL, DF_ALLOWOTHERACCOUNTHOOK, GENERIC_ALL, NULL);
    WaitForSingleObject(CreateThread(NULL, 0, TrySwitch, (void*) TRUE, 0, NULL), 3333); // Leaks thread handle and I don't care
    WaitForSingleObject(CreateThread(NULL, 0, TrySwitch, (void*) FALSE, 0, NULL), 3333); // Leaks thread handle and I don't care
    SwitchDesktop(hIn);
    CloseDesktop(hIn);
    CloseDesktop(g_hNew);
} 
  • Related