COleBusyDialog customization - c++

I would like to customize COleBusyDialog dialog before I call DoModal on it. I am fine with buttons and layout. However I would like to have different static text inside it. How can I change it?
I see it has public member COleBusyDialog::m_bz which is used to customize it:
typedef struct tagOLEUIBUSY
{
DWORD cbStruct;
DWORD dwFlags;
HWND hWndOwner;
LPCTSTR lpszCaption;
LPFNOLEUIHOOK lpfnHook;
LPARAM lCustData;
HINSTANCE hInstance;
LPCTSTR lpszTemplate;
HRSRC hResource;
HTASK hTask;
HWND *lphWndDialog;
} OLEUIBUSY, *POLEUIBUSY, *LPOLEUIBUSY;
I am new to mfc and I don't know if i have to create new dialog in resources and assign it to this struct or is there simple way to modify the text.

Derive from COleMessageFilter and overide OnBusyDialog. To use your customized message filter, replace AfxGetThread()->m_pMessageFilter. Then delete the previous message filter and call Register() on the new one.

Related

Wrong parent HWND in modal dialog

Why do i get desktop as a parent HWND for my modal dialog here?
class CSaveProfileAsDlg:
public CSimpleDialog<IDD_DLG_RESOURCE>
{
....
LRESULT OnInitDialog(UINT, WPARAM, LPARAM, BOOL&)
{
...
HWND parent = GetParent(); // or GetAncestor(m_hWnd, GA_PARENT);
assert(parent != GetDesktopWindow()); // not ok
...
}
....
}
//somewhere in code
//m_hWnd is some valid HWND
assert(m_hWnd != GetDesktopWindow()); //ok
CSaveProfileAsDlg dlg;
dlg.DoModal(m_hWnd /*as a parent wnd*/);
I can "solve" it by passing corret HWND in CSaveProfileAsDlg ctor, but i'd like to have correct solution.
Thank you!
The documentation is very confusing but I think I found the problem. DoModal internally calls ::DialogBox(), one parameter of which takes a HWND named hWndParent. From the documentation:
hWndParent [in, optional]
Type: HWND
A handle to the window that owns the dialog box.
The keyword here is the word "owns". The section about owned windows confirms this:
Dialog boxes and message boxes are owned windows by default. An application specifies the owner window when calling a function that creates a dialog box or message box.
So we actually talk about the owner window instead of its parent. This makes sense as the dialog is a free floating window and not part of a window hierarchy as "parenthood" would imply.
You can get the owning window by using:
HWND parent = ::GetWindow(m_hWnd, GW_OWNER);
I had a similiar issue. I was wondering why GetParent() return always a different CWnd*.
The right solution is simple, just pass the desired pWnd to the dlg Constructor.
It will be stored in the CDialog member variable m_pParentWnd.
Then you can always get the passed pWnd with this member variable in your Dialog.
//somewhere in code
//pWnd some valid CWnd pointer
CSaveProfileAsDlg dlg (pWnd); // relevant!
dlg.DoModal();
.
class CSaveProfileAsDlg:
public CSimpleDialog<IDD_DLG_RESOURCE>
{
....
LRESULT OnInitDialog(UINT, WPARAM, LPARAM, BOOL&)
{
...
CWnd* _pWnd = GetParent(); // not ok, returns probably CMainFrame or similiar
CWnd* pWnd = m_pParentWnd; // your parent Wnd
...
}
....
}
I've got two versions of the same application and one of them disables all parent popup windows as it should when DoModal is called. Second version disables only top-level CMainFrame and real parent is stay enabled so I can call modal dialog twice and more.
This happen in CWnd::GetSafeOwner_, line :
hWnd = ::GetLastActivePopup(hWnd);
Firsts version return real parent while second version return CMainFrame.
I've spent one day and could not find the cause of such behaviour. However I've found workaround:
When DoModal is called it Disable CMainFrame so I can disable it's children also:
afx_msg void OnEnable(BOOL bEnable);
ON_WM_ENABLE()
void CMainFrame::OnEnable(BOOL bEnable)
{
std::for_each(m_bars.begin(), m_bars.end(),
[=](const std::pair<EBar, BaseBar*>& bar)
{
bar.second->EnableWindow(bEnable);
});
}

How to get the widget window handle to passed to win32 api MessageBox in Qt

In short, I want to use win32Api in Qt5.1.1, And I have a simple example.
I want to pass the widget handle to the WinApi function int MessageBox(HWND hWnd, LPCTSTR lpText, LPCTSTR lpCaption, UINT uType).
The problem in the first parameter (HWND hWnd, ...).
My attempt: (Failed)
WId wind = (HWND)w.winId(); // `w` is the instance of my widget.
MessageBox(wind, "Hello world!", "Message", MB_OK);
How to obtain the widget(window) handle, to passed to MessageBox winapi function ?
If your in Qt, all Widgets should have a QWidget::winId() function. Simply call winId() and you'll get the windows handle.

Add visible window titles to combobox MFC

I want to add visible window titles to a combobox. Here is my source:
BOOL CALLBACK EnumWindowsProc(HWND hWnd, long lParam)
{
TCHAR buff[255];
CComboBox* pComboBox = (CComboBox*)GetDlgItem(IDC_COMBO_PROCESS);
if (IsWindowVisible(hWnd))
{
GetWindowText(hWnd, buff, 254);
pComboBox->AddString(buff);
}
return TRUE;
}
void CFindProcess::OnDropdownComboProcess()
{
EnumWindows(EnumWindowsProc, 0);
}
but I get error:
error C2660: 'GetDlgItem' : function does not take 1 arguments 60
How I can correctly add titles to combo?
MFC objects are thread-sensitive, GetDlgItem works well in the thread that created the object, probably the main UI thread. Function EnumWindows probably creates a worker thread to access the callback function, and that is why GetDlgItem failed to get a valid handle of the combobox.
To access the combobox properly in another thread, you have to use the static function: CWnd::FromHandle with the raw handle of the combobox object as follows:
BOOL CALLBACK EnumWindowsProc(HWND hWnd, long lParam)
{
if (IsWindowVisible(hWnd))
{ TCHAR szBuffer[255];
INT nLength = GetWindowText(hWnd, szBuffer, 254);
if (nLength>0)
{ // only add windows that has a caption
CComboBox *pComboBox = (CComboBox*)CWnd::FromHandle((HWND)lParam);
pComboBox->AddString(szBuffer);
}
}
return TRUE;
}
// call EnumWindows --------------------
CComboBox *pComboBox = (CComboBox *)GetDlgItem(IDC_COMBO1);
// passing the raw handle of the combobox as parameter
EnumWindows(EnumWindowsProc, (LPARAM)pComboBox->m_hWnd);
Firstly, your GetDlgItem has two parameters, and the first is a handle to the dialog box that contains the control.
So it expects a HWND parameter of the dialog that contains this control, I would presume that will be the HWND you pass as a parameter to your function.
CComboBox* pComboBox = (CComboBox*)GetDlgItem(hWnd,IDC_COMBO_PROCESS);
^^^^ added parameter
If you look at EnumWindows in MSDN, you'll see you have to pass a callback and it has a HWND parameter, if you look at what this parameter is for it says:
A handle to a top-level window.
This is exactly what you have to pass to GetDlgItem.
Also, you should check the return value of GetWindowText as this returns the number of characters written to the buff you passed it.
int ret = GetWindowText(hWnd, buff, 254);
if (ret > 0) pComboBox->AddString(buff); // only add non-empty strings.
In addition to what user #mfc has provided, I would not do UI update from a different thread. I believe EnumWindows does not create thread for enumeration. It would call the callbacks within the call-stack of current thread.
This, in turn, means that UI may freeze for a while. Thus, it is recommended to create a thread for enumeration. More over, I would not directly update UI from different thread. May be a vector of string, or a PostMessage (on each iteration) I would have used.
It is true that EnumWindows may perform quite fast. But when you move to enumerate other (kernel) objects like file, printers, users etc - the UI is definitely going to freeze. So, better practice writing multithreaded code. Initially writing MT-code would be a pain, but later you'd love it, appreciate it, and cannot live without it.

CWnd::CreateDlgIndirect leaves m_hWnd==NULL

A dialog I'm working on isn't displaying, using:
CWnd::CreateDlgIndirect(LPCDLGTEMPLATE lpDialogTemplate,CWnd* pParentWnd, HINSTANCE hInst)
The call to CreateDlgIndirect is in a lon-used base-class, which effectively takes the IDD of the dialog template in the resource file - it works fine for many other dialogs but I can't see what's different in my dialog. My dialog works fine when created in a more normal way, but I have to use the base class as it has loads of other functionality built in.
What I find when trawling through CWnd::CreateDlgIndirect in dlgcore.cpp, is that the plain Win32 API call is failing:
hWnd = ::CreateDialogIndirect(hInst, lpDialogTemplate,pParentWnd->GetSafeHwnd(), AfxDlgProc);
I can't step into that function for some reason, so all I see is the HWND is NULL.
Can anyone suggest what kind of problems might be causing this? I compared the two dialog resource templates and their properties are the same.
edit: I have one custom control on the dialog. When I remove this, it works. No idea why, what difference might this make?
One of the more obscure ways for CreateDialogXXX to fail is for a child control on the dialog to fail creation. Usually because the application has not initialized the common controls library before attempting to effect the dialog creation. See InitCommonControlsEx
One way to check this is to open the dialog in the resource editor, go to the dialog's properties, and find and turn on the DS_NOFAILCREATE flag. Usually called something obscure like "No Fail Create". Or add the DS_NOFAILCREATE directly to your dialog template in memory. This will allow the dialog to show, and the culprit should be evident by its absence.
In the case that the child control is an actual custom control - well the custom window class is either not registered correctly, or at all. Check the HINSTANCE used in registration - unless the CS_GLOBAL flag is specified, window classes are identified by (hInstance, ClassName) - this prevents window classes using the same name in different dlls conflicting.
A custom CWnd derived control fails, if you use the wrong HINSTANCE. When using a satelite DLL for the language resources, here's what I had to change:
// ----------------------------------------- //
// RegisterWndClass
// ----------------------------------------- //
/*static*/ BOOL MyCtrl::RegisterWndClass() {
WNDCLASS windowclass = {0};
// !! THIS IS THE IMPORTANT PART: !!
HINSTANCE hInst = AfxGetResourceHandle(); // NOT AfxGetInstanceHandle();
if(!(::GetClassInfo(hInst, MYCTRL_CLASSNAME, &windowclass))) {
//If not then we have to register the new class
windowclass.style = CS_DBLCLKS | CS_HREDRAW | CS_VREDRAW;
windowclass.lpfnWndProc = ::DefWindowProc;
windowclass.cbClsExtra = windowclass.cbWndExtra = 0;
windowclass.hInstance = hInst;
windowclass.hIcon = NULL;
windowclass.hCursor = AfxGetApp()->LoadStandardCursor(IDC_IBEAM);
windowclass.hbrBackground = ::GetSysColorBrush(COLOR_WINDOW);
windowclass.lpszMenuName = NULL;
windowclass.lpszClassName = MYCTRL_CLASSNAME;
if(!AfxRegisterClass(&windowclass)) {
AfxThrowResourceException();
return FALSE;
}
}
return TRUE;
}

How do I add an edit box to MFC CFolderDialog ("browse for folder" dialog)?

I currently have a CFolderDialog class that is used in my CDocManagerEx class for handling file operations as follows:
alt text http://img268.yfrog.com/img268/9271/filedialog.png
I don't know if I need to show the method implementation of this class (I found this from a project posted here), but here is the class definition if it helps:
class CFolderDialog
{
friend static int CALLBACK BrowseDirectoryCallback(
HWND hWnd, UINT uMsg, LPARAM lParam, LPARAM lpData);
public:
CFolderDialog( LPCTSTR lpszFolderName = NULL,
DWORD dwFlags = NULL/*BIF_RETURNONLYFSDIRS*/,
CWnd* pParentWnd = NULL);
virtual ~CFolderDialog();
virtual int DoModal();
CString GetPathName() const;
protected:
virtual void OnInitDialog();
virtual void OnSelChanged(ITEMIDLIST* pIdl);
virtual void CallbackFunction(HWND hWnd, UINT uMsg, LPARAM lParam);
void EnableOK(BOOL bEnable = TRUE);
void SetSelection(LPCTSTR pszSelection);
void SetSelection(ITEMIDLIST* pIdl);
void SetStatusText(LPCTSTR pszStatusText);
CString ShortName(const CString& strName);
public:
BROWSEINFO m_bi;
protected:
CString m_strInitialFolderName;
CString m_strFinalFolderName;
TCHAR m_szDisplayName[MAX_PATH];
TCHAR m_szPath[MAX_PATH];
HWND m_hDialogBox;
};
class CMyFolderDialog : public CFolderDialog
{
public:
CMyFolderDialog(LPCTSTR lpszFolderName = NULL,
DWORD dwFlags = NULL,
CWnd* pParentWnd = NULL,
LPCTSTR pszFileFilter = NULL);
virtual ~CMyFolderDialog();
protected:
virtual void OnSelChanged(ITEMIDLIST* pIdl);
protected:
CString m_strFileFilter;
};
My goal of this question is adding an edit control to the window just below the workspace where the directory is selected. What would be the easiest way to accomplish this?
If you just want an edit control that allows the user to type in the name of a directory entry, that is possible. The C++ class you're using is a wrapper round the Win32 SHBrowseForFolder() method, and that method supports having an edit box by setting the BIF_EDITBOX (or better, BIF_USENEWUI) in the ulFlags member of the BROWSEINFO structure.
Looking at the C++ class, it looks like the simplest way to achieve this will be to pass BIF_USENEWUI as the dwFlags member in the constructor call. (Though I'd be tempted to just call SHBrowseForFolder directly and not bother with the C++ class.)
Do note the warning about this flag in MSDN, though: OleInitialize() or CoInitialize() must have been called before bringing up the dialog with this flag.
More generally, if you want an edit control that you can use for your own purposes, allowing the user to enter anything, that's more of a problem: there's no way to extend the dialog used by SHBrowseForFolder() with custom controls. If you want to do that, you'd end up having to re-implement the whole dialog, which isn't a good idea.
Also, as a final note, if you can limit yourself to Vista (and beyond) then there's another way to have a directory selection dialog: use the new IFileDialog COM interface, with the FOS_PICKFOLDERS flag.
Maybe some of the ideas in this will do what you want?
http://www.codeproject.com/KB/dialog/browse_for_folder_dialog.aspx
or this
http://www.codeguru.com/cpp/w-p/files/browserfunctionsdialogs/article.php/c4443