IHTMLDocument2.write() memoryleak - mfc

In MFC CDHtmlDialog, I am updating HTML content using IHTMLDocument2's write function.
My Dialog is a modelless dialog. I am using same dialog object throughout the application.
Everytime I write something to the embedded browser, and look at memory
usage in task manager, the used memory seems to go up by about 750 KB per
IHTMLDocument2::write/close. If I comment out the call to
m_spHtmlDoc->write(pSA); the memory usage is more normal.
I am using below code to update the HTML Content.
IMPLEMENT_DYNCREATE(CEzHtmlChildDlg, CDHtmlDialog)
CEzHtmlChildDlg::CEzHtmlChildDlg(CWnd* pParent /*=NULL*/)
: CDHtmlDialog(CEzHtmlChildDlg::IDD, 0, pParent)
{
CDHtmlDialog::Create(CEzHtmlChildDlg::IDD, pParent);
}
CEzHtmlChildDlg::~CEzHtmlChildDlg()
{
}
void CEzHtmlChildDlg::DoDataExchange(CDataExchange* pDX)
{
CDHtmlDialog::DoDataExchange(pDX);
}
BOOL CEzHtmlChildDlg::OnInitDialog()
{
SetHostFlags(m_dwHostFlags
| DOCHOSTUIFLAG_DIALOG // MSHTML does not enable selection of the text in the form
| DOCHOSTUIFLAG_DISABLE_HELP_MENU // MSHTML does not add the Help menu item to the container's menu.
| DOCHOSTUIFLAG_SCROLL_NO // MSHTML does not have scroll bars.
| DOCHOSTUIFLAG_DISABLE_SCRIPT_INACTIVE // MSHTML does not execute any script until fully activated.
| DOCHOSTUIFLAG_THEME // Specifies that the hosted browser should use themes for pages it displays
// | DOCHOSTUIFLAG_NOTHEME
);
CDHtmlDialog::OnInitDialog();
// disable drop targets
// Don't allow user to drop shortcut onto this window and have the browser
// navigate to show that web site
m_pBrowserApp->put_RegisterAsDropTarget(VARIANT_FALSE);
EnableAutomation();
LPDISPATCH pDisp = GetIDispatch(FALSE);
SetExternalDispatch(pDisp);
return TRUE; // return TRUE unless you set the focus to a control
}
BOOL CEzHtmlChildDlg::PreTranslateMessage(MSG* pMsg)
{
// TODO: Add your specialized code here and/or call the base class
if (pMsg->message == WM_KEYDOWN &&
(pMsg->wParam == VK_ESCAPE || pMsg->wParam == VK_RETURN || pMsg->wParam == VK_F5))
return true;
if (pMsg->message == WM_RBUTTONDOWN || pMsg->message == WM_RBUTTONDBLCLK)
return TRUE;
return CDHtmlDialog::PreTranslateMessage(pMsg);
}
BEGIN_MESSAGE_MAP(CEzHtmlChildDlg, CDHtmlDialog)
END_MESSAGE_MAP()
BEGIN_DHTML_EVENT_MAP(CEzHtmlChildDlg)
END_DHTML_EVENT_MAP()
// CEzHtmlChildDlg message handlers
void CEzHtmlChildDlg::SetHTML(CString strHtml)
{
// Create a safearray to store the HTML text
SAFEARRAY *pSA;
SAFEARRAYBOUND saBound = {1, 0};
pSA = SafeArrayCreate(VT_VARIANT, 1, &saBound);
CComVariant varDummy;
// Copy the HTML into the one and only element
VARIANT *pVar;
CComBSTR bstrHTML = strHtml; // Load the text
varDummy = bstrHTML; // .. into a variant
SafeArrayAccessData(pSA, (void**)&pVar); // Access safearray data
pVar[0] = varDummy; // Set the text data
SafeArrayUnaccessData(pSA); // Release access
// Write the HTML as the document's new text
m_spHtmlDoc->write(pSA); // Overwrite HTML
m_spHtmlDoc->close(); // Update browser
SafeArrayDestroy(pSA); // Finished with the safearray
}
m_spHtmlDoc is a member variable of CDhtmlDialog.
What is the mistake I am doing while writing to document?,

Related

MFC get modal dialog list

I am working on unit test with MFC. My software is a SDI.
I have 2 threads : the first one is the graphic thread and the second one a unit test procedure which reproduce a user behavior.
It is working well when there isn't modal dialog, I use SendMessage (to simulate click on button, or change text, etc.) which is blocking until the message has been treated so I haven't any sync issue.
But when the button opens a Modal CDialog I can't use SendMessage because the unit test thread is going to be blocked as long as the modal dialog is opened. So I use PostMessage and Sleep. And now my problem is to get the pointer of the current opened CDialog.
Here is the code of the unit test
bool UnitTestBoite()
{
// Here I am in unit test thread
CFrameWnd *pMainFrame = (CFrameWnd *)AfxGetMainWnd();
// Post Message to notify the button ID_INSERER_BOITE is clicked
pMainFrame->PostMessageW(WM_COMMAND, MAKELONG(ID_INSERER_BOITE, 0), 0);
// In the handler of ID_INSERER_BOITE
// there is something like CDialog dlg(pMainFrame, IDD_BASE_BOITE); dlg.DoModal();
Sleep(1000);
UINT myModalTemplateId = IDD_BASE_BOITE;
...
To get modal dialog pointer I tried :
CWnd* pChild = pMainFrame->GetWindow(GW_CHILD);
while (pChild != nullptr)
{
// Retrieve template ID
UINT nResourceId = GetWindowLong(pChild->GetSafeHwnd(), GWL_ID);
if (nResourceId == myModalTemplateId )
break;
pChild = pChild->GetWindow(GW_HWNDNEXT);
}
if (pChild == nullptr)
return false;
or
HWND hwndForeGround = ::GetForegroundWindow();
// Retrieve template ID
UINT nResourceId = GetWindowLong(hwndForeGround, GWL_ID);
if (nResourceId != myModalTemplateId )
return false;
or
CWnd *pModal = pMainFrame_->GetForegroundWindow();
// Retrieve template ID
UINT nResourceId = GetWindowLong(pModal->GetSafeHwnd(), GWL_ID);
if (nResourceId != myModalTemplateId )
return false;
None of these code snippet worked...
The last solution I have thought was to make all my CDialog classes inherits from a custom class and register all opened CDialog, but it is a bit invasive...
Is there an "elegant" means to do that ?
Thank for reading,
Lucas.

CInvalidArgumentException when checking class in PreTranslateMessage

The case: I'd like to make shortcuts with numpad so users can use my application fast. I implemented this in PreTranslateMessage and this worked.
But the case is that I have an Edit Control where the user should enter some number. So at the time the user has focus on a Edit Control (CEdit), the shortcuts should be disabled.
To cover this, I added
CWnd* pControl;
pControl = this->GetFocus();
if(!(pControl->IsKindOf(RUNTIME_CLASS(CEdit)))){
But now whenever my application dialog loses focus, it closes (see video) and I get the following exeption:
This is the full code:
// Handles keypresses for fast acces of functions
BOOL COpenFilesDlg::PreTranslateMessage(MSG *pMsg){
CWnd* pControl;
pControl = this->GetFocus();
if(!(pControl->IsKindOf(RUNTIME_CLASS(CEdit)))){ //when this statement is commented the program doesn't crash
if(pMsg->message == WM_KEYDOWN)
{
if((pMsg->wParam == 0x31 || pMsg->wParam == VK_NUMPAD1))
someFunction();
else if((pMsg->wParam == 0x33 || pMsg->wParam == VK_NUMPAD3)){
someOtherFunction();
}
}
}
return CDialog::PreTranslateMessage(pMsg);
}
Now is my question: Why does my program crash when it is not in focus and how do I check if the focus is on a Edit Control in a proper way?
CWnd::GetFocus returns a pointer to the window that has the current focus, or NULL if there is no focus window.
pControl = this->GetFocus();
if ( pControl != NULL )
{
if(!(pControl->IsKindOf(RUNTIME_CLASS(CEdit))))
...
}
Another way is to compare pControl value with pointer to CEdit class member (or members) of the dialog class. For example, if CEdit m_edit is edit box class member, test:
if ( pControl == (CWnd*)&m_edit )
{
// focus is on m_edit control
}

Refreshing a CTreeCtrl when a CDockablePane is resized (double-click on title)

I have a CDockablePane which contain a custom control based on CTreeCtrl.
When I move the CDockablePane, everything is refreshed properly. When I resize the CDockablePane, everyting is refreshed properly. When I double-click on the "title" of the CDockablePane (which snap the CDockablePane to it's original position), the CDockablePane show white as snow, without the CTreeCtrl. I must hover with the mouse and things start to show one treeitem at the time.
I can't believe that this is not "built-in", I must be missing something.
Here is my OnSize and OnPaint function from my CDockablePane.
void CLegendeTreePane::OnSize(UINT nType, int cx, int cy)
{
CDockablePane::OnSize(nType, cx, cy);
if(m_pWndTree)
{
m_pWndTree->SetWindowPos(NULL, -1, -1, cx, cy, SWP_NOMOVE|SWP_NOACTIVATE|SWP_NOZORDER);
}
}
void CLegendeTreePane::OnPaint()
{
if(m_pWndTree)
m_pWndTree->AfficheItems();
CDockablePane::OnPaint();
}
So, what do I need to add/modify so the CDockablePane refresh everything when resized using the double-click on the title?
Thanks!
I encountered the same problem and found the workaround. Firs of all, it seems to happen on MDI Document application and not on SDI Document application. After comparing with a new project from VS2010 having dockable pane with no problem, I tryed to find why.
In my app, I was registering a new WNDCLASS and using it in the creation of my mainframe to be able to make sure only one app was running using the fonction FindWindow in my app. When I made this change, everything was now displaying correctly.
(Before)
BOOL CMDIAppApp::InitInstance()
{
//just before creating doc template
// Register our unique class name that we wish to use
WNDCLASSEX wndcls;
memset(&wndcls, 0, sizeof(WNDCLASSEX)); // start with NULL defaults
wndcls.cbSize = sizeof(WNDCLASSEX);
wndcls.lpfnWndProc = ::DefWindowProc;
wndcls.hInstance = AfxGetInstanceHandle();
wndcls.hIcon = LoadIcon(IDR_MAINFRAME); // or load a different icon
wndcls.hCursor = LoadCursor( IDC_ARROW );
wndcls.hbrBackground = (HBRUSH) (COLOR_WINDOW + 1);
wndcls.lpszMenuName = NULL;
// Specify our own class name for using FindWindow later
CString csClassName = _T("MyApp");
wndcls.lpszClassName = csClassName;
// Register new class and exit if it fails
if (!RegisterClassEx(&wndcls))
{
TRACE("Class Registration Failed\n");
return false;
}
// Register the application's document templates. Document templates
// serve as the connection between documents, frame windows and views
CMultiDocTemplate* pDocTemplate;
pDocTemplate = new CMultiDocTemplate(IDR_MDIAppTYPE,
RUNTIME_CLASS(CMDIAppDoc),
RUNTIME_CLASS(CChildFrame), // custom MDI child frame
RUNTIME_CLASS(CMDIAppView));
if (!pDocTemplate)
return FALSE;
AddDocTemplate(pDocTemplate);
}
and the in my CMainFrame using it
BOOL CMainFrame::PreCreateWindow(CREATESTRUCT& cs)
{
cs.lpszClass = _T("MyApp");
if( !CMDIFrameWndEx::PreCreateWindow(cs) )
return FALSE;
return TRUE;
}
(After)
Remove everything concerning the class registration in your App, it is not there we need to put it, all we have to do is modifying the PreCreateWindow fonction of CMainFrame, making sure the CMainFrame keep all its default windows settings and just changing the ClassName of CMainFrame once created
BOOL CMainFrame::PreCreateWindow(CREATESTRUCT& cs)
{
if( !CMDIFrameWndEx::PreCreateWindow(cs) )
return FALSE;
// TODO: Modify the Window class or styles here by modifying
// the CREATESTRUCT cs
// Check if our class is already defined
LPCTSTR pszClassName = _T("MyApp");
WNDCLASS wndcls;
if (!::GetClassInfo(AfxGetInstanceHandle(), pszClassName, &wndcls))
{
// Get the current requested window class
VERIFY(GetClassInfo(AfxGetInstanceHandle(), cs.lpszClass, &wndcls));
// We want to register this info with our name
wndcls.lpszClassName = pszClassName;
// Need to preset the icon otherwise the function GetIconWndClass
// calling us will overwrite our class.
LPCTSTR pszIcon = MAKEINTRESOURCE(IDR_MAINFRAME);
HINSTANCE hInst = AfxFindResourceHandle(pszIcon, ATL_RT_GROUP_ICON);
_ASSERTE(hInst!=NULL);
wndcls.hIcon = ::LoadIcon(hInst, pszIcon);
// Register our class now and check the outcome
if (!::RegisterClass(&wndcls))
{
_ASSERTE(!__FUNCTION__ "RegisterClass failed");
return FALSE;
}
}
// Now use our class
cs.lpszClass = pszClassName;
return TRUE;
}
Make sure to Unregister your class once your application exit, usually in
CMyApp::ExiInstance()
{
::UnregisterClass(_T("MyApp"));
}
I saw similar problems when I use the BCG.
When you create this pane use WS_CLIPCHILDREN. This will avid any redrawing problems that the parent tries to overwrite the childs client area...

Detecting modal dialogs in MFC

How can I programmatically detect if my MFC application currently is displaying a modal dialog or property sheet? Currently I'm using the following, but I feel that the code also triggers for modeless dialogs.
bool HasModalDialog(const CWnd* pWnd)
{
const CWnd* pChildWnd = pWnd ? pWnd->GetNextWindow(GW_HWNDPREV) : NULL;
while (pChildWnd)
{
if (pWnd == pChildWnd->GetTopLevelParent() &&
(pChildWnd->IsKindOf(RUNTIME_CLASS(CDialog)) ||
pChildWnd->IsKindOf(RUNTIME_CLASS(CPropertySheet))))
{
return true;
}
pChildWnd = pChildWnd->GetNextWindow(GW_HWNDPREV);
}
return false;
}
Usage:
HasModalDialog(AfxGetMainWnd())
Anyone got a alternative way of detecting modal dialogs?
Have you tried CWnd::GetLastActivePopup?
I haven't tested this to see if it'll work for modal dialogs only.
Edit 1: According to Raymond Chen, GetLastActivePopup should return the current active modal dialog.
Edit 2: Perhaps another method to retrieve the current modal window would be to modify your code to check for a disabled parent/owner - modal dialogs should always disable their owner before displaying.
I've tried many ways to solve that, why i needed that because i'm dealing with code that declare all the dialog as pointers to allocated in the heapmemory (TDialog* d = new TDialog) this was OWL code I converted it to MFC I want to delete those pointers automatically only if the dialog is modal it is not allocated in the heap, so i need to check for it my solution was easy to override the DoModal in my inherited class and set a flag isModal to true if it is not shown using DoModal the flag isModal will still null_ptr as it was initialized in the constructor
class : public CDialog
{
private:
bool isModal
public:
CMyDlg(int id, CWnd* parent = NULL) : CDialog(id, parent), isModal(false)
{
}
virtual INT_PTR DoModal()
{
isModal = true;
return CDialog::DoModal();//return __super::DoModal();
}
bool IsModal()
{
return isModal;
}
virtual void OnCancel()
{
if(isModal)
{
CDialog::OnCancel();
}
else
{
DestroyWindow();
}
}
virtual void OnOk()
{
if(isModal)
{
CDialog::OnCancel();
}
else
{
DestroyWindow();
}
}
virtual void PostNcDestroy()
{
delete this;
}
}
If you are only detecting windows within your application then you could derive your own CDialog and CPropertySheet and put a simple bool in there that keeps track of whether it is modal or not.
bool HasModalDialog(const CWnd* pWnd)
{
const CWnd* pChildWnd = pWnd ? pWnd->GetNextWindow(GW_HWNDPREV) : NULL;
while (pChildWnd)
{
if (pWnd == pChildWnd->GetTopLevelParent() )
{
if ( pChildWnd->IsKindOf(RUNTIME_CLASS(CMyDialog) )
{
return ((CMyDialog*)pChildWnd)->IsModal();
}
if ( pChildWnd->IsKindOf(RUNTIME_CLASS(CMyPropertySheet) )
{
return ((CMyPropertySheet*)pChildWnd)->IsModal();
}
}
pChildWnd = pChildWnd->GetNextWindow(GW_HWNDPREV);
}
return false;
}
There must be another way to do it but thats the only way I can think of off the top of my head.
I can't believe windows doesn't offer a function to do this; as calling EndDialog for a modeless dialog is undefined behaviour. But modal dialog box must use EndDialog. So if I don't want to create two seperate dialog procedures, how the heck will I know the right way to close the dialog.
Anyhow my simple solution is to to create a custom DialogBox style. Following is list of all dialog box styles
DS_FIXEDSYS = 0x0008
DS_SETFONT = 0x0040
DS_ABSALIGN = 0x0001
DS_CENTER = 0x0800
DS_CENTERMOUSE = 0x1000
DS_CONTEXTHELP = 0x2000
DS_CONTROL = 0x0400
DS_LOCALEDIT = 0x0020
DS_MODALFRAME = 0x0080
DS_NOFAILCREATE = 0x0010
DS_SETFOREGROUND = 0x0200
DS_SYSMODAL = 0x0002
DS_3DLOOK = 0x0004
DS_NOIDLEMSG = 0x0100
Close inspection shows that all dialog box styles reside on there own bits. Note that I ignored DS_SHELLFONT as it is simply a compound style of DS_SETFONT and DS_FIXEDSYS.
Thus our custom style just simply falls on its own bit.
JAV_DS_IS_MODELESS = 0x4000
Then when creating a modeless dialog box we set that style into it. Note that using DS_SYSMODAL style instead might be undefined according to MSDN documentation.
struct WindowContent
{
virtual void onClose(HWND hwnd) { closeWindow(hwnd,0); }
void closeWindow(HWND hwnd,int result);
}
BOOL CALLBACK dlgProc(HWND hwnd,UINT msg,WPARAM,LPARAM lparam)
{
WindowContent *content = (WindowContent*)GetWindowLongPtr(hwnd,GWL_USERDATA);
if(msg == WM_INITDIALOG
{
content = (WindowContent*)lparam;
SetWindowLongPtr(hwnd,GWL_USERDATA,content);
return 0;
}
if(msg == WM_CLOSE)
{
content->onClose(hwnd);
return 0;
}
return 0;
}
HWND createDialog(const char *dlg_template_name,WindowContent *content,HWND owner=NULL)
{
HWND dlg = CreateDialogParamA(NULL,dlg_template_name,owner,dlgProc,(LPARAM)content);
UINT old_style = GetWindowLong(dlg,GWL_STYLE);
SetWindowLong(dlg,GWL_STYLE,old_style|JAV_IS_MODELESS);
return 0;
}
void WindowContent::closeWindow(HWND hwnd,int result)
{
if( GetClassLong(hwnd,GCW_ATOM) == (int)WC_DIALOG )
{
if(GetWindowLong(hwnd,GWL_STYLE) & JAV_DS_IS_MODELESS) DestroyWindow(hwnd);
else EndDialog(hwnd,result);
}
else DestroyWindow(hwnd);
}

Dialog Boxes in MFC C++

I have 4 Dialog Boxes in 1 Project. Let's call them
IDD_DIALOG1
IDD_DIALOG2
IDD_DIALOG3
IDD_DIALOG4
When I compile my program the first window/dialog box I can see is IDD_DIALOG1, but I want to have IDD_DIALOG2 first.
My project do not have something like WinMain. It's clear MFC Application.
When you create your project you will have an application class too. In this class there will already be a default InitInstance method. For example:
BOOL CMFCApplication1App::InitInstance()
{
// InitCommonControlsEx() is required on Windows XP if an application
// manifest specifies use of ComCtl32.dll version 6 or later to enable
// visual styles. Otherwise, any window creation will fail.
INITCOMMONCONTROLSEX InitCtrls;
InitCtrls.dwSize = sizeof(InitCtrls);
// Set this to include all the common control classes you want to use
// in your application.
InitCtrls.dwICC = ICC_WIN95_CLASSES;
InitCommonControlsEx(&InitCtrls);
CWinApp::InitInstance();
AfxEnableControlContainer();
// Create the shell manager, in case the dialog contains
// any shell tree view or shell list view controls.
CShellManager *pShellManager = new CShellManager;
// Activate "Windows Native" visual manager for enabling themes in MFC controls
CMFCVisualManager::SetDefaultManager(RUNTIME_CLASS(CMFCVisualManagerWindows));
// Standard initialization
// If you are not using these features and wish to reduce the size
// of your final executable, you should remove from the following
// the specific initialization routines you do not need
// Change the registry key under which our settings are stored
// TODO: You should modify this string to be something appropriate
// such as the name of your company or organization
SetRegistryKey(_T("Local AppWizard-Generated Applications"));
CMFCApplication1Dlg dlg;
m_pMainWnd = &dlg;
INT_PTR nResponse = dlg.DoModal();
if (nResponse == IDOK)
{
// TODO: Place code here to handle when the dialog is
// dismissed with OK
}
else if (nResponse == IDCANCEL)
{
// TODO: Place code here to handle when the dialog is
// dismissed with Cancel
}
else if (nResponse == -1)
{
TRACE(traceAppMsg, 0, "Warning: dialog creation failed, so application is terminating unexpectedly.\n");
TRACE(traceAppMsg, 0, "Warning: if you are using MFC controls on the dialog, you cannot #define _AFX_NO_MFC_CONTROLS_IN_DIALOGS.\n");
}
// Delete the shell manager created above.
if (pShellManager != NULL)
{
delete pShellManager;
}
// Since the dialog has been closed, return FALSE so that we exit the
// application, rather than start the application's message pump.
return FALSE;
}
Part way down that method is this code:
CMFCApplication1Dlg dlg;
m_pMainWnd = &dlg;
INT_PTR nResponse = dlg.DoModal();
That is the main code you are interested in. So, if you want to start with a different dialogue, then #include the right header and change the code to the different dialogue class.
So in the above example, CMFCApplication1Dlg would be changed to something else, eg: CMyDialog2 (I do not know what the names of your dialogue classes are).