How to show a QDialog on a custom Windows Desktop (HDESK) - windows

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...

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

SetWindowsHookEx for WH_KEYBOARD_LL handler PostThreadMessage message not received?

I setup a handler to catch keystrokes for a short time while using a private message loop for the window. And while it works, if I try the PostThreadMessage the window never receives the message?
The callback looked like (I tried other messages to, this one sample was with a private message):
LRESULT CALLBACK LLKeyboardHookProc(int ncode, WPARAM wp, LPARAM lp)
{
if (ncode == HC_ACTION) {
if (wp==WM_KEYDOWN) {
KBDLLHOOKSTRUCT *kbs=reinterpret_cast<KBDLLHOOKSTRUCT*>(lp);
if (kbs->vkCode==VK_ESCAPE) {
PostThreadMessage (GetCurrentThreadId(), UWM_MYCLOSEMESSAGE, 0, 0);
}
}
}
return CallNextHookEx(0, ncode, wp, lp);
}
HHOOK kbhook = SetWindowsHookEx(WH_KEYBOARD_LL, LLKeyboardHookProc, GetModuleHandle(NULL), 0);
The message loop for the WS_EX_LAYERED window looks like:
while (IsWindow(hwnd)) {
MSG msg;
while (::PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
::TranslateMessage(&msg);
::DispatchMessage(&msg);
}
// let MFC do its idle processing
LONG lIdle=0;
while (AfxGetApp()->OnIdle(lIdle++));
WaitMessage();
}
// clean up
UnhookWindowsHookEx(kbhook);
I changed it to use a volatile variable for now to close the window but I'd really like to send it a message. Using a global HWND wouldn't be thread-safe without locks so didn't want to go that way.
P.S. I did check the PostThreadMessage result and it was not FALSE (no debug messages when I added them) so it seemed to post it somewhere.
PostThreadMessage() posts a thread message to the queue, not a window message. Your message loop is ignoring thread messages, only dispatching window messages. A window will NEVER see a thread message, so you need to handle such messages directly in your message loop instead, eg:
MSG msg;
while (::PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
if (msg.hwnd == NULL && msg.message == UWM_MYCLOSEMESSAGE) { // <-- add this
// do something...
}
else {
::TranslateMessage(&msg);
::DispatchMessage(&msg);
}
}
However, as Hans Passant stated in a comment:
Never use PostThreadMessage() when you also created a window, something as simple as resizing the window causes the message to get lost.
Raymond Chen has posted several blog articles on this subject:
Thread messages are eaten by modal loops
Watching thread messages disappear
Rescuing thread messages from modal loops via message filters
Why do messages posted by PostThreadMessage disappear?
You could try using a WH_MSGFILTER hook, like Raymond demonstrated. But the best way to solve this is to simply not post a thread message at all, post a window message instead. Have your keyboard hook use PostMessage() (or even SendMessage()) to an HWND that your app owns, and then you can handle the message in that window's message procedure.

CoInitializeSecurity error

I have an .exe that loads 1.dll and 1.dll loads 1_1.dll.
In the .exe I create multiple threads, from one of them I call a function that calls upon a 1.dll function that between other things .. calls a function from 1_1.dll that fails in doing this:
// Initialize COM.
HRESULT hr = CoInitializeEx(NULL, COINIT_MULTITHREADED);
if( FAILED(hr) )
{
//m_iStatus = ERROR_COINITIALIZE_EX;
return;
}
// Set general COM security levels.
hr = CoInitializeSecurity(
NULL,
-1,
NULL,
NULL,
RPC_C_AUTHN_LEVEL_PKT_PRIVACY,
RPC_C_IMP_LEVEL_IMPERSONATE,
NULL,
0,
NULL);
if( FAILED(hr) )
{
CoUninitialize();
//m_iStatus = ERROR_COINITIALIZE_SEC;
return;
}
It falis on the call to CoInitializeSecurity with this message:
Security must be initialized before any interfaces are marshalled or unmarshalled. It cannot be changed once initialized.
Can anyone expalin me what is happening here, what am I doing wrong and how should I fix this?
CoInitializeSecurity function...
... is called exactly once per process, either explicitly or implicitly
The only case DLL might need to call CoInitializeSecurity is when it is loaded into process, which is known to not initialize COM on its own. An the process is at all basically a thin host for the DLL. That is, almost never.
It is .EXE's task to do CoInitializeSecurity.

MFC Custom HtmlHelp

I'm doing maintenance on a legacy MFC application. When the user selects Help → Help from the main menu along the top of the app, I need my custom OnHelp() to be called. From my research, I've learned that MFC normally intercepts this command automatically and brings up your help file itself. But you can override this command and intercept this message yourself. I have this in my message map:
BEGIN_MESSAGE_MAP(MyApp, MyBaseApp)
//{{AFX_MSG_MAP(MyApp)
ON_COMMAND(ID_APP_ABOUT, OnAppAbout)
ON_COMMAND(ID_HELP, OnHelp)
//}}AFX_MSG_MAP
// Standard file based document commands
END_MESSAGE_MAP()
The "About" OnAppAbout() gets called, but not my OnHelp() (MFC still intercepts it and brings up the help by itself). My implementation is pretty straightforward:
void MyApp::OnHelp()
{
// This never gets called
MessageBox( NULL, "HtmlHelp: MyApp", "Hey", MB_OK );
CString csHelpFile;
csHelpFile.Format( "%s/MyHelp.chm", MyDoc::GetHelpPath() );
::HtmlHelp(
NULL,
csHelpFile,
HELP_WM_HELP,
NULL );
}
I know it's not getting called because my MessageBox never appears (I can't use Visual Studio to debug this; message boxes only). I also tried to wedge it into the CMDIFrameWnd, with the message map and similar implementation, with no success either (a different item from the Help menu item is implemented here and works fine). Any idea what I need to do to hook into my own custom help function?
You need to add ON_WM_HELPINFO() into your CMainFrame's message map. Define afx_msg BOOL OnHelpInfo(HELPINFO* pHelpInfo); in your header file for the Main Frame, and implement it in the cpp file:
BOOL CMainFrame::OnHelpInfo(HELPINFO* pHelpInfo)
{
#ifdef _DEBUG
MessageBox( NULL, "HtmlHelp: MyApp", "Hey", MB_OK );
#endif
CString csHelpFile;
csHelpFile.Format( "%s/MyHelp.chm", MyDoc::GetHelpPath() );
::HtmlHelp(
NULL,
csHelpFile,
HELP_WM_HELP,
NULL );
return CFrameWnd::OnHelpInfo(pHelpInfo);
}

Win32 font resource leak

I have a small matter of GDI leak and i wanted to know someone's opinion on how to solve this.Say i have a class that enfolds data specific to creating and handling a window ex:
class Wnd {
HWND hWnd;
HFONT hFont;
LOGFONT LogFont;
//etc
public:
//constructors and member functions
//The following function atempts to change the font of the window
//pointed to by the hWnd parameter
void ChangeFont (const LOGFONT& lf) {
std::memcpy (&LogFont,&lf,sizeof(LOGFONT));
hFont=CreateFontIndirect (&LogFont);
SendMessage (hWnd,WM_SETFONT,(WPARAM) hFont,(LPARAM) 1);
}
~Wnd () {
//i don't think this would work since i haven't used the SelectObject function
DeleteObject ((HGDIOBJ) hFont);
}
};
So the main question is , at destruction time how do i release the memory allocated to the
hFont parameter?Should i get a device context of the window and use the SelectObject () function so that after that i could release it calling the function for the old font and use DeleteObject () to free the memory?Thanks a lot.
So the main question is , at destruction time how do i release the
memory allocated to the hFont parameter?
You use DeleteObject() per documentation for CreateFontIndirect() and the WM_SETFONT message.
Should i get a device context of the window and use the SelectObject
() function so that after that i could release it calling the function
for the old font and use DeleteObject () to free the memory?
This should not be necessary, as long as your painting routine correctly restores the old font somehow after the routine is done with the font.

timed MessageBox in console application

I use MessageBox function in Win32 console application.
Application does not not use MFC, not even event loop.
I need to make a wrapper, MessageBoxTimed(), that exits
(and dialog box disappears) after N seconds, if user did not press any button.
Is there more or less simple way to do this ?
This will not be trivial. Since the MessageBox() function itself is modal, you will likely need to start another thread that waits for the predefined number of seconds, and is interrupt-able if the message box is dismissed manually.
If the timer expires, use the FindWindow() API to find the handle of the message box and then simulate a click of the OK button, or perhaps more appropriately a keypress of the ESC button.
EDIT: Actually, not too bad. This isn't fully tested, may need some additional cleanup, but is enough to get you started.
#include <Windows.h>
class TimedMB
{
public:
TimedMB() : timeout_(0), caption_(0)
{
interrupt_ = CreateEvent(NULL, FALSE, FALSE, NULL);
}
~TimedMB()
{
CloseHandle(interrupt_);
}
static DWORD WINAPI timer(LPVOID param)
{
TimedMB* mb = reinterpret_cast<TimedMB*>(param);
if(WAIT_TIMEOUT == WaitForSingleObject(mb->interrupt_, mb->timeout_))
{
HWND message_box = FindWindow(NULL, mb->caption_);
if(::IsWindow(message_box))
{
PostMessage(message_box, WM_COMMAND, IDCANCEL, 0);
}
}
return 0;
}
void DisplayMessageBox(const char* msg, const char* caption, DWORD timeout)
{
timeout_ = timeout;
caption_ = caption;
CreateThread(NULL, 0, &TimedMB::timer, this, 0, NULL);
::MessageBox(NULL, msg, caption, MB_OKCANCEL);
::SetEvent(interrupt_);
}
private:
HANDLE interrupt_;
DWORD timeout_;
const char* caption_;
};
int main()
{
TimedMB mb;
mb.DisplayMessageBox("Hello There!", "My Message Box", 5000);
}
If you need to dismiss it automatically, I'd avoid using MessageBox at all. Instead, I'd just put together a dialog that closes itself after the specified period of time. If memory serves, you can do this pretty easily by setting a time when you display the pseudo-message box dialog. When the time goes off or the user clicks "ok" (or "close", etc.) you close the window and cancel the timer.
Don't do this. Modal dialogs should be closed by user intervention. Deviating from this pattern is just confusing and non-standard. If you want a message windows that closes itself, then use a balloon window.

Resources