Resize cursor is showing on the border of a fixed dialog frame - mfc

It is a bit difficult to provide you with a minimal working example here but I am going to try and explain this issue that I have only just noticed.
The Context
So, I have a regular CDialogEx derived class, defined like this:
class CChristianLifeMinistryStudentsDlg : public CDialogEx
I have set it up so that the borders will not resize:
The main application (also CDialogEx based) has a fixed window. That behaves correct.
From the menu the user displays a resizable dialogue (an editor).
On this dialog is a button the user can press which will in turn display the popup modal dialog I am referring to.
What Happens
When this dialog is displayed I have noticed this when you hover the mouse over the dialog borders:
I don't understand why this is happening.
Cursor Management
In the "editor" that spawns this popup window I do have some cursor management like this:
BOOL CChristianLifeMinistryEditorDlg::OnSetCursor(CWnd* pWnd, UINT nHitTest, UINT message)
{
if (CPersistentWaitCursor::WaitCursorShown())
{
RestoreWaitCursor();
return TRUE;
}
return CDialogEx::OnSetCursor(pWnd, nHitTest, message);
}
But, I have tried temporarily to invoke this popup from my main application dialog which does not have an cursor management and the result is still the same.
Spy Results
As requested I have just used Spy to examine the window styles:
As anticipated we suddenly have WS_THICKFRAME set, when it was not in the resource editor!
So
In my RC file the dialog has the DS_MODALFRAME flag set but at runtime it ends up having the WS_THICKFRAME set. As far as I am aware I never make these changes for these affected dialog objects.
Update
I have found out the following:
BOOL CChristianLifeMinistryStudentsDlg::OnInitDialog()
{
LONG_PTR lStyle = GetWindowLongPtr(GetSafeHwnd(), GWL_STYLE);
if (lStyle & WS_THICKFRAME)
AfxMessageBox(_T("Thick"));
else if (lStyle & DS_MODALFRAME)
AfxMessageBox(_T("Modal"));
CDialogEx::OnInitDialog();
If I put the check code before the CDialogEx::OnInitDialog(); call the style is set as DS_MODALFRAME. But if I put the same check code after the CDialogEx::OnInitDialog(); call it is then changed to WS_THICKFRAME. Why?
OK
So, the CDialogEx::OnInitDialog method calls CWnd::LoadDynamicLayoutResource(LPCTSTR lpszResourceName). This in turn calls CWnd::InitDynamicLayout(). And in that method it does this:
if (!bIsChild && (pDialog != NULL || pPropSheet != NULL))
{
CRect rect;
GetClientRect(&rect);
ModifyStyle(DS_MODALFRAME, WS_POPUP | WS_THICKFRAME);
::AdjustWindowRectEx(&rect, GetStyle(), ::IsMenu(GetMenu()->GetSafeHmenu()), GetExStyle());
SetWindowPos(NULL, 0, 0, rect.Width(), rect.Height(), SWP_FRAMECHANGED | SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOOWNERZORDER);
}
There we go. So it is because I am using CDialogEx as my base class. Is this a bug in MFC?
Clarification
The "Editor" (parent window of the popup that owns the button) does use dynamic layout functonality:
But in this instance the popup does not need to. But it is because my popup is derived from CDialogEx that this is happening.
The plot thickens
So this is the MFC code that is always called with CDialog::OnInitDialog:
BOOL CWnd::LoadDynamicLayoutResource(LPCTSTR lpszResourceName)
{
if (GetSafeHwnd() == NULL || !::IsWindow(GetSafeHwnd()) || lpszResourceName == NULL)
{
return FALSE;
}
// find resource handle
DWORD dwSize = 0;
LPVOID lpResource = NULL;
HGLOBAL hResource = NULL;
if (lpszResourceName != NULL)
{
HINSTANCE hInst = AfxFindResourceHandle(lpszResourceName, RT_DIALOG_LAYOUT);
HRSRC hDlgLayout = ::FindResource(hInst, lpszResourceName, RT_DIALOG_LAYOUT);
if (hDlgLayout != NULL)
{
// load it
dwSize = SizeofResource(hInst, hDlgLayout);
hResource = LoadResource(hInst, hDlgLayout);
if (hResource == NULL)
return FALSE;
// lock it
lpResource = LockResource(hResource);
ASSERT(lpResource != NULL);
}
}
// Use lpResource
BOOL bResult = CMFCDynamicLayout::LoadResource(this, lpResource, dwSize);
// cleanup
if (lpResource != NULL && hResource != NULL)
{
UnlockResource(hResource);
FreeResource(hResource);
}
if (bResult)
{
InitDynamicLayout();
}
return bResult;
}
For some reason this call BOOL bResult = CMFCDynamicLayout::LoadResource(this, lpResource, dwSize); is return TRUE. As a result the dialog eventually calls InitDynamicLayout. In my other dialogs that are popups this does not happen. Instead, bResult ends up as FALSE and thus the frame is not resized.
So why does it think it worked?

Worked it out. I don't remember doing this but for some reason some of my controls on the dialog had dynamic properties set. For example:
I had to set all of these properties back to None. Then it behaved.
You can easily tell if a given dialog resource has any dynamic properties by opening your resource file in a text editor. For example:
IDD_DIALOG_OUR_CHRISTIAN_LIFE_AND_MINISTRY_MATERIAL AFX_DIALOG_LAYOUT
BEGIN
0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 10, 0,
0, 0, 0, 0,
50, 0, 0, 0,
50, 0, 0, 0,
0, 0, 0, 0,
0, 0, 10, 0,
0, 0, 0, 0,
50, 0, 0, 0,
50, 0, 0, 0,
0, 0, 0, 0,
0, 0, 10, 0,
0, 0, 0, 0,
50, 0, 0, 0,
50, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0
END
If something like the above is present then your dialog will be deemed as having a dynamic layout, and thus the settings for the dialog are modified:
ModifyStyle(DS_MODALFRAME, WS_POPUP | WS_THICKFRAME);
The resource will look like this when it has no dynamic control properties:
IDD_DIALOG_OUR_CHRISTIAN_LIFE_AND_MINISTRY_MATERIAL AFX_DIALOG_LAYOUT
BEGIN
0
END
I chose to manually reset each control via the IDE. However, I guess you could modify the text file manually.
As to why I had controls with dynamic properties in the first place, well, I can't tell you. I might have been fiddling in the past with the dialog and not realised the side effect to the border frame. Or, possibly, I may have copied controls from one resource on to another and it carried the dyanmic values.
The interesing side note is that whilst the MFC code set the border as thick, it did not change it sufficiently to enable dialog resizing. But that is another matter!
At least we now know the cause of the issue and how to easily identify the dialogs in the resource that have dynamic layouts.

Related

Setting a child window under Progman window to show video ender desktop icon

I'm trying to make a function similar to the wallpaper engine, by adding a child window to the Progman window. The function did allow me to add the video, however, the window size is fixed, which made me unable to fit the video to the entire desktop.
I tried to change the window size, but neither MoveWindow nor SetWindowPos worked. The Progman's hwnd Rect is just screen size (1920x1080), so it's not a boundary issue. The video window size is 640x480. I'm able to change the location of the window, meaning no permission issue, just can't change the size of it.
Here is the code.
LPCWSTR lpParam = L" videoTest2.mp4 -noborder -fs -loop 0";
STARTUPINFO si{ 0 };
PROCESS_INFORMATION pi{ 0 };
if (CreateProcess(L"ffplay.exe", (LPWSTR)lpParam, 0, 0, 0, 0, 0, 0, &si, &pi)) {
Sleep(200);
HWND hProgram = FindWindow(_T("Progman"), _T("Program Manager"));
HWND hFfplay = FindWindow(L"SDL_app", 0);
SetParent(hFfplay, hProgram);
// MoveWindow(hFfplay, 0, 0, 3840, 1080, true);
SetWindowPos(hFfplay, NULL, 0, 0, 3840, 1080, SWP_NOZORDER);
ShowWindow(hFfplay, SW_MAXIMIZE);
EnumWindows(EnumWindowProc, (LPARAM)SW_HIDE);
Sleep(1000);
EnumWindows(EnumWindowProc, (LPARAM)SW_SHOW);
}
else {
std::cout << GetLastError() << std::endl;
}
EnumWindowProc is just for showing and hiding system background images.

I can't set my custom ICO icons on toolbar buttons in Win32 application

I apologize for my poor English. I write a C ++ application in the Win32 API in Visual Studio 2017 Community on Windows 10. The application has a toolbar with buttons. I want to assign my custom functionality to each button. Therefore, each button must have its custom specific icon. (I added the icons to the application’s ResourceFiles folder.) The problem is that I can’t install my custom ICO icons on the buttons in the toolbar. When I run my application, the buttons are empty without icons. I re-read a lot of information on this topic on SO and on other sites. Still, I can’t install my icons on the buttons. I would be very grateful if anyone would help. Thank you in advance. Below is my code, with which I try to put my icons on the toolbar buttons:
#define IDM_INPUT 0
#define IDM_OUTPUT 1
#define IDM_TRIANGULATE 2
#define IDM_STOP 3
HWND CreateSimpleToolbar(HWND hWndParent)
{
// Declare and initialize the constants used in the function:
// - picture list id for buttons,
const int ImageListID = 0;
// - number of buttons,
const int numButtons = 4;
// - the size of each image for the button.
const int bitmapSize = 16;
const DWORD buttonStyles = BTNS_AUTOSIZE;
// Create the toolbar.
HWND hWndToolbar = CreateWindowEx(0, TOOLBARCLASSNAME, NULL,
WS_CHILD | TBSTYLE_WRAPABLE, 0, 0, 0, 0,
hWndParent, NULL, hInst, NULL);
if (hWndToolbar == NULL)
return NULL;
// Create a list of pictures for buttons.
g_hImageList = ImageList_Create(bitmapSize, bitmapSize,
ILC_COLOR16 | ILC_MASK, // Provide a transparent background.
numButtons, 0);
ImageList_AddIcon(g_hImageList, LoadIcon(hInst, MAKEINTRESOURCE(IDI_ICON1)));
ImageList_AddIcon(g_hImageList, LoadIcon(hInst, MAKEINTRESOURCE(IDI_ICON2)));
ImageList_AddIcon(g_hImageList, LoadIcon(hInst, MAKEINTRESOURCE(IDI_ICON3)));
ImageList_AddIcon(g_hImageList, LoadIcon(hInst, MAKEINTRESOURCE(IDI_ICON4)));
// Set a list of pictures for buttons.
SendMessage(hWndToolbar, TB_SETIMAGELIST,
(WPARAM)ImageListID,
(LPARAM)g_hImageList);
// Upload pictures for buttons.
SendMessage(hWndToolbar, TB_LOADIMAGES,
(WPARAM)IDB_STD_SMALL_COLOR,
(LPARAM)HINST_COMMCTRL);
// Initialize information about buttons.
TBBUTTON tbButtons[numButtons] =
{
{ MAKELONG(IDI_ICON1, ImageListID), IDM_INPUT, TBSTATE_ENABLED, buttonStyles, {0}, 0, 0 },
{ MAKELONG(IDI_ICON2, ImageListID), IDM_OUTPUT, TBSTATE_ENABLED, buttonStyles, {0}, 0, 0},
{ MAKELONG(IDI_ICON3, ImageListID), IDM_TRIANGULATE, TBSTATE_ENABLED, buttonStyles, {0}, 0, 0},
{ MAKELONG(IDI_ICON4, ImageListID), IDM_STOP, TBSTATE_ENABLED, buttonStyles, {0}, 0, 0}
};
// Add buttons.
SendMessage(hWndToolbar, TB_BUTTONSTRUCTSIZE, (WPARAM)sizeof(TBBUTTON), 0);
SendMessage(hWndToolbar, TB_ADDBUTTONS, (WPARAM)numButtons, (LPARAM)&tbButtons);
// Resize the toolbar and then show it.
SendMessage(hWndToolbar, TB_AUTOSIZE, 0, 0);
ShowWindow(hWndToolbar, TRUE);
return hWndToolbar;
}
IDI_ICON1, IDI_ICON2, IDI_ICON3 and IDI_ICON4 are the identifiers of the four ICO icon files resulting from the addition of these icons to the application resources.
If I check the result of the LoadIcon function in the debugger, I will see the message "Unable to read memories", although the returned HICON value itself is not zero.
Please tell me what needs to be fixed in the above code?

CMFCPropertySheet "Page" resources are not resizing with dynamic layout

I am really confused. :(
Here is a new property sheet:
#include "stdafx.h"
#include "resource.h"
#include "VisitsRotaMFCPropertySheet.h"
CVisitsRotaMFCPropertySheet::CVisitsRotaMFCPropertySheet()
:CResizingMFCPropertySheet(_T("VisitsRota"), AFX_IDS_APP_TITLE, nullptr, 0)
{
ConstructSheet();
}
CVisitsRotaMFCPropertySheet::~CVisitsRotaMFCPropertySheet()
{
}
BOOL CVisitsRotaMFCPropertySheet::OnInitDialog()
{
BOOL bResult = CResizingMFCPropertySheet::OnInitDialog();
m_Menu.LoadMenu(IDR_MENU);
SetMenu(&m_Menu);
return bResult;
}
void CVisitsRotaMFCPropertySheet::ConstructSheet()
{
m_psh.dwFlags |= PSH_NOAPPLYNOW;
AddPage(&m_ElderlyInfirmPage);
AddPage(&m_ShepherdingPage);
}
It is derived from CResizingMFCPropertySheet. This is the source for that class:
https://www.dropbox.com/s/fzpfo4c3dpt6l51/ResizingMFCPropertySheet.cpp?dl=0
Now, I have two pages in this window. Here is one for the definitions:
IDD_PAGE_ELDERLY_INFIRM DIALOGEX 0, 0, 420, 202
STYLE DS_SETFONT | DS_FIXEDSYS | WS_CHILD | WS_CAPTION
CAPTION "Elderly && Infirm"
FONT 8, "MS Shell Dlg", 400, 0, 0x1
BEGIN
GROUPBOX "Elders ...",IDC_STATIC,6,7,132,188
LISTBOX IDC_LIST_BOOKSTUDY,12,18,120,147,LBS_SORT | LBS_NOINTEGRALHEIGHT | WS_VSCROLL | WS_TABSTOP
PUSHBUTTON "Add",IDC_BUTTON_ADD_GROUP,12,172,35,18
PUSHBUTTON "Edit",IDC_BUTTON_EDIT_ELDER,55,172,35,18
PUSHBUTTON "Delete",IDC_BUTTON_DELETE_GROUP,97,172,35,18
GROUPBOX "Publishers ...",IDC_STATIC,144,7,132,188
LISTBOX IDC_LIST_ELDERY_INFIRM,150,18,120,147,LBS_SORT | LBS_NOINTEGRALHEIGHT | WS_VSCROLL | WS_TABSTOP
PUSHBUTTON "Add",IDC_BUTTON_ADD_ELDERLY,150,172,35,18
PUSHBUTTON "Edit",IDC_BUTTON_EDIT_ELDERLY,193,172,35,18
PUSHBUTTON "Delete",IDC_BUTTON_DELETE_ELDERLY,235,172,35,18
GROUPBOX "Report Settings ...",IDC_STATIC,281,7,132,188
LTEXT "Starting month:",IDC_STATIC,286,18,120,8
COMBOBOX IDC_COMBO_MONTH,286,31,120,12,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP
LTEXT "Number of months:",IDC_STATIC,286,49,78,12
COMBOBOX IDC_COMBO_NUM_MONTHS,376,49,30,96,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP
LTEXT "Number of publishers to visit each month:",IDC_STATIC_NUM_PUB,286,65,84,18
COMBOBOX IDC_COMBO_PUB_PER_MONTH,376,66,30,12,CBS_DROPDOWNLIST | CBS_SORT | WS_VSCROLL | WS_TABSTOP
LTEXT "Starting publisher:",IDC_STATIC,286,90,120,8
COMBOBOX IDC_COMBO_PUBLISHER,286,103,120,12,CBS_DROPDOWNLIST | CBS_SORT | WS_VSCROLL | WS_TABSTOP
END
It is correctly set up as a page and I have initially set the control data via the IDE:
IDD_PAGE_ELDERLY_INFIRM AFX_DIALOG_LAYOUT
BEGIN
0,
0, 0, 0, 100,
0, 0, 0, 100,
0, 100, 0, 0,
0, 100, 0, 0,
0, 100, 0, 0,
0, 0, 0, 100,
0, 0, 0, 100,
0, 100, 0, 0,
0, 100, 0, 0,
0, 100, 0, 0,
100, 0, 0, 100,
100, 0, 0, 0,
100, 0, 0, 0,
100, 0, 0, 0,
100, 0, 0, 0,
100, 0, 0, 0,
100, 0, 0, 0,
100, 0, 0, 0,
100, 0, 0, 0
END
I have adjusted my CDialog application to invoke the property sheet instead. The sheet itself sizes:
Why is the sheet control not automatically resizing? I just don't get it. My other application uses the same base class and yet all those property pages correctly resizing the controls etc using the dynamic layout features.
Update
I added this to one of my pages:
void CElderlyInfirmPage::OnSize(UINT nType, int cx, int cy)
{
CMFCPropertyPage::OnSize(nType, cx, cy);
AfxMessageBox(_T("Size"));
// TODO: Add your message handler code here
auto pManager = GetDynamicLayout();
if (pManager != nullptr)
{
AfxMessageBox(_T("Valid"));
}
}
It confirms that the "page" does not actually have a dynamic layout manager. Only the sheet does. So I think the problem is the fact that we can't use dynamic layout mechanism.
Update 2
I made some progress. Example:
It turns out that the property page doesn't seem to load the dynamic layout resources like it does for a dialog. I started to create it manually:
BOOL CElderlyInfirmPage::OnInitDialog()
{
CMFCPropertyPage::OnInitDialog();
// TODO: Add extra initialization here
ReadSettings();
InitMonthCombo();
// Init to THIS month
COleDateTime datNow = COleDateTime::GetCurrentTime();
m_cbMonth.SetCurSel(datNow.GetMonth()-1);
EnableDynamicLayout(TRUE);
auto pManager = GetDynamicLayout();
if (pManager != nullptr)
{
pManager->Create(this);
pManager->AddItem(IDC_COMBO_MONTH, CMFCDynamicLayout::MoveHorizontal(100), CMFCDynamicLayout::SizeNone());
pManager->AddItem(IDC_COMBO_NUM_MONTHS, CMFCDynamicLayout::MoveHorizontal(100), CMFCDynamicLayout::SizeNone());
pManager->AddItem(IDC_COMBO_PUB_PER_MONTH, CMFCDynamicLayout::MoveHorizontal(100), CMFCDynamicLayout::SizeNone());
pManager->AddItem(IDC_COMBO_PUBLISHER, CMFCDynamicLayout::MoveHorizontal(100), CMFCDynamicLayout::SizeNone());
}
return TRUE; // return TRUE unless you set the focus to a control
// EXCEPTION: OCX Property Pages should return FALSE
}
As you can see, the controls move now so it is progress. But the problem now is that I have a lot of IDC_STATIC controls on these pages and I don't want to change the ID numbers. This is because the application already has translations for localization and if I change the ID values I blow up the translations. So I am wondering if I can use the [CMFCDynamicLayout::LoadResource][3] method to load the complete settings from the RC file. But I can't work out how to call LoadResource here. I am sure it would be the answer to this question.
Update 3
I just traced the code and if you look here:
LRESULT CPropertySheet::HandleInitDialog(WPARAM, LPARAM)
{
LRESULT lResult = OnInitDialog();
CMFCDynamicLayout* pDynamicLayout = GetDynamicLayout();
if (pDynamicLayout != NULL)
{
CRect rectWindow;
GetWindowRect(rectWindow);
m_sizeMin = rectWindow.Size();
for (CWnd *pChild = GetWindow(GW_CHILD); pChild->GetSafeHwnd() != NULL; pChild = pChild->GetWindow(GW_HWNDNEXT))
{
HWND hwndChild = pChild->GetSafeHwnd();
if (!pDynamicLayout->HasItem(hwndChild))
{
if (pChild->SendMessage(WM_GETDLGCODE) & DLGC_BUTTON)
{
pDynamicLayout->AddItem(hwndChild, CMFCDynamicLayout::MoveHorizontalAndVertical(100, 100), CMFCDynamicLayout::SizeNone());
}
else if (IsLeftNavigationPane(hwndChild))
{
pDynamicLayout->AddItem(hwndChild, CMFCDynamicLayout::MoveNone(), CMFCDynamicLayout::SizeVertical(100));
}
else if (DYNAMIC_DOWNCAST(CPropertyPage, pChild) == NULL || CanAddPageToDynamicLayout())
{
pDynamicLayout->AddItem(hwndChild, CMFCDynamicLayout::MoveNone(), CMFCDynamicLayout::SizeHorizontalAndVertical(100, 100));
}
}
}
}
return lResult;
}
It does not seem to actually work with the layout properly.
I tried to use:
LoadDynamicLayoutResource(m_lpszTemplateName);
And I traced it. It eventually ended up here:
BOOL CMFCDynamicLayout::LoadResource(CWnd* pHostWnd, LPVOID lpResource, DWORD dwSize)
{
if (pHostWnd->GetSafeHwnd() == NULL || !::IsWindow(pHostWnd->GetSafeHwnd()) || lpResource == NULL)
{
return FALSE;
}
CMFCDynamicLayoutData layoutData;
BOOL bResult = layoutData.ReadResource(lpResource, (UINT)dwSize);
layoutData.ApplyLayoutDataTo(pHostWnd, FALSE);
return bResult;
}
It failed on the ApplyLayoutDataTo call, on the first if statement:
BOOL CMFCDynamicLayoutData::ApplyLayoutDataTo(CWnd* pHostWnd, BOOL bUpdate)
{
if (pHostWnd->GetSafeHwnd() == NULL || m_listCtrls.IsEmpty())
{
return FALSE;
}
ASSERT_VALID(pHostWnd);
pHostWnd->EnableDynamicLayout(FALSE);
pHostWnd->EnableDynamicLayout();
m_listCtrls.IsEmpty() was empty. So it hadn't read it in properly anyway.
I think I have no choice but to assign IDs to all my controls, even the static ones and manually build the dynamic layout up. Unless you have other ideas.
Dynamic layout is already be enabled for all classes derived from CDialog which call the default CDialog::OnInitDialog, which in turn uses CMFCDynamicLayout::LoadResource to read resizing information for child controls.
That include CMFCPropertyPage. The information is already loaded, so if you call EnableDynamicLayout it deletes the existing object and creates a new one. Just remove the call to EnableDynamicLayout.
This way pManager->Create(this); won't be necessary, but you can keep it in there. It won't do anything because pManager already created and the method knows not to create twice.
CPropertySheet does require EnableDynamicLayout and pManager->Create. PropertySheet cannot be designed in dialog editor, so MFC ignores resizing for its child windows. Dynamic resizing has to be implemented manually.
MCVE:
class CMyPage : public CMFCPropertyPage
{
CButton bn;
BOOL OnInitDialog()
{
CMFCPropertyPage::OnInitDialog();
//add test button dynamically
bn.Create(L"Test", WS_CHILD | WS_VISIBLE, CRect(0, 0, 100, 30), this, 301);
auto pManager = GetDynamicLayout();
if(pManager != nullptr)
{
pManager->AddItem(bn.GetDlgCtrlID(),
CMFCDynamicLayout::MoveHorizontal(100),
CMFCDynamicLayout::SizeNone());
}
return TRUE;
}
};
class CMySheet :public CMFCPropertySheet
{
public:
CMyPage Page1;
CMySheet()
{
Page1.Construct(IDD_PAGE1);
AddPage(&Page1);
}
static int CALLBACK XmnPropSheetCallback(HWND hWnd, UINT message, LPARAM lParam)
{
extern int CALLBACK AfxPropSheetCallback(HWND, UINT message, LPARAM lParam);
// XMN: Call MFC's callback
int nRes = AfxPropSheetCallback(hWnd, message, lParam);
if (message == PSCB_PRECREATE)
((LPDLGTEMPLATE)lParam)->style |= (DS_3DLOOK | DS_SETFONT
| WS_THICKFRAME | WS_SYSMENU | WS_POPUP | WS_VISIBLE | WS_CAPTION);
return nRes;
}
BOOL OnInitDialog()
{
BOOL res = CMFCPropertySheet::OnInitDialog();
EnableDynamicLayout(TRUE);//required for propertysheet
auto pManager = GetDynamicLayout();
if(pManager)
{
pManager->Create(this);
for(CWnd *child = GetWindow(GW_CHILD);
child; child = child->GetWindow(GW_HWNDNEXT))
{
if(child->SendMessage(WM_GETDLGCODE) & DLGC_BUTTON)
pManager->AddItem(*child,
CMFCDynamicLayout::MoveHorizontalAndVertical(100, 100),
CMFCDynamicLayout::SizeNone());
else
pManager->AddItem(*child,
CMFCDynamicLayout::MoveNone(),
CMFCDynamicLayout::SizeHorizontalAndVertical(100, 100));
}
}
return res;
}
INT_PTR DoModal()
{
// Hook into property sheet creation code
m_psh.dwFlags |= PSH_USECALLBACK;
m_psh.pfnCallback = XmnPropSheetCallback;
return CMFCPropertySheet::DoModal();
}
};
...
CMySheet sh;
sh.DoModal();

How to correctly retrieve Unicode text from a control?

Here is an example of Unicode: I avoided to use a win32 application for for brevity sake:
In main I created an edit control, a button to retrieve text from it and adds it to the listbox when pressed. So I used an object of struct MSG and blocked in a while loop peeking the messages from the message queue.
int main(){
// An edit control where I can add any unicode text to.
HWND hEdit = CreateWindowExW(WS_EX_CLIENTEDGE,
L"Edit", L"你好! 你好吗?", WS_BORDER | WS_VISIBLE | WS_SYSMENU,
200, 100, 250, 70, 0, 0, GetModuleHandle(NULL), NULL);
// A listobx to receive the content of the edit control when pressing the button get text.
HWND hLstBx = CreateWindowExW(WS_EX_CLIENTEDGE,
L"Listbox", NULL, WS_BORDER | WS_VISIBLE | WS_OVERLAPPEDWINDOW,
500, 100, 170, 400, 0, 0, GetModuleHandle(NULL), NULL);
// A button when pressed adds the content of the edit to the listbox.
HWND hGetText = CreateWindowExW(WS_EX_CLIENTEDGE,
L"Button", L"中文", WS_BORDER | WS_VISIBLE,
450, 300, 100, 70, 0, 0, GetModuleHandle(NULL), NULL);
// msg struct to pass to GetMessage to receive the messages from the queue.
MSG msg;
// blocking and getting messages from the queue.
while (GetMessageW(&msg, 0, 0, 0)) {
// some virtual keys translation.
TranslateMessage(&msg);
// sending the message to the specified window.
DispatchMessageW(&msg);
// Now after the messages sent to the target Window I check which control the message has been passed to, So if it is the button then:
if (msg.message == WM_LBUTTONDOWN &&
msg.hwnd == hGetText) {
std::wstring wstrBuff;
int txtLen = SendMessageW(hEdit, WM_GETTEXTLENGTH, 0, 0);
// SendMessageW(hEdit, WM_GETTEXT, txtLen + 1, (LPARAM)wstrBuff.c_str());
// MessageBoxW(0, wstrBuff.c_str(), 0, 0);
wchar_t lpTxt[MAX_PATH];
SendMessageW(hEdit, WM_GETTEXT, MAX_PATH, (LPARAM)lpTxt);
SendMessageW(hLstBx, LB_ADDSTRING, 0, (LPARAM)lpTxt);
MessageBoxW(0, lpTxt, L"你好! 你好吗?", 0);
//delete[]lpTxt;
}
}
std::wcout << std::endl << std::endl;
std::wcin.get();
return 0;
}
Every thing works fine except: If I un-comment the lines above I get run-time error facing the assertion message showing me the txtLen and the size of the text of the edit control. Is this because there's some string overlapping?
If I enter a small text it works fine but with a text about 14 characters I get the error.
Also is that the right way to pass std::wstring.c_str() to SendMessageW() to get the text?
One last question: How to correctly and effectively retrieve Unicode text from a control? How to use LPWSTR with dynamic memory: I don't want to exhaust stack.
NB: I saved the source file as utf8 /BOM otherwise I get unreadable characters. Thanks to the members who helped me about that.
Sending (LPARAM)wstrBuff.c_str() will return a pointer to read-only buffer with a single null symbol, not a buffer of txtLen + 1 symbols. If you are using latest VS (with C++17 standard support) you can modify your code to supply a proper pointer:
std::wstring wstrBuff;
wstrBuff.resize(txtLen + 1);
const LRESULT copied_symbols_count = SendMessageW
(
hEdit
, WM_GETTEXT
, static_cast<WPARAM>(wstrBuff.size())
, reinterpret_cast<LPARAM>(wstrBuff.data())
);
if(copied_symbols_count == txtLen)
{
assert(L'\0' == wstrBuff.back());
wstrBuff.pop_back(); // get rid of terminating null
}
else
{
wstrBuff.clear(); // something went wrong...
}
Note that C++17 standard adds non-const-qualified wstring::data() method that can be safely used to obtain a pointer to writable buffer.

Can't change position of button

In a C++ app I create a button using CreateWindowEx and later try to change its position using SetWindowPos, but the button doesn't appear where I want it.
What's interesting is that when I resize the window (with the mouse, not programatically), I can see for a split second a blank silhouette the same size of the button where the button is supposed to appear. This must be because I also call SetWindowPos in response to window resizing events. However the actual button stays at the same location. I'd post a screenshot but for some reason the silhouette never shows up in screenshots.
This is the code that changes the X position (the code that changes the Y position is almost identical):
HRESULT Control::put_Left(float left)
{
RECT windowRect;
::GetWindowRect(m_hWnd, &windowRect);
if (m_isTopLevel)
{
BOOL bResult = ::SetWindowPos(
m_hWnd,
nullptr,
static_cast<int>(DesktopDpi::GetInstance().DipsToPixelsX(left)),
windowRect.top,
0,
0,
SWP_NOSIZE | SWP_NOZORDER | SWP_NOREPOSITION
);
if (!bResult)
return HRESULT_FROM_WIN32(::GetLastError());
}
else
{
// NOTE: for a button this is the code that will execute, because a
// button is not a top-level window
HWND hWndParent = ::GetParent(m_hWnd);
if (hWndParent == nullptr)
return HRESULT_FROM_WIN32(::GetLastError());
POINT parentPos = {0, 0};
if (!::ClientToScreen(hWndParent, &parentPos))
return E_FAIL;
BOOL bResult = ::SetWindowPos(
m_hWnd,
nullptr,
static_cast<int>(DesktopDpi::GetInstance().DipsToPixelsX(left)),
windowRect.top - parentPos.y,
0,
0,
SWP_NOSIZE | SWP_NOZORDER | SWP_NOREPOSITION
);
if (!bResult)
return HRESULT_FROM_WIN32(::GetLastError());
}
return S_OK;
}
Are you sure the parent of the button isn't moving it back to the old position when it handles WM_SIZE? It sure sounds like it from your description.