• Status: Solved
  • Priority: Medium
  • Security: Public
  • Views: 563
  • Last Modified:

CTempWnd, CTempMenu

I have a structure with several pieces in it of type CmMenu* (a class derived from CMenu).  A pointer to this structure gets passed into a function and when I examine its contents in the debugger I find that one of the CmMenu* pieces is showing as being a CTempMenu and another piece that should be a CWnd* is of type CTempWnd.  What are these temp types?  I don't find mention of them in the documentation, but I'm still looking.  How do they get created?  Are they safe to use?  They don't seem to be, since when I return from the function my CmMenu* doesn't save the changes I make to it inside the function.
0
appleby
Asked:
appleby
1 Solution
 
chensuCommented:
Don't worry. They are defined internally in MFC as follows.

class CTempMenu : public CMenu
{
      DECLARE_DYNCREATE(CTempMenu)
      DECLARE_FIXED_ALLOC(CTempMenu);
};

class CTempWnd : public CWnd
{
      DECLARE_DYNCREATE(CTempWnd)
      DECLARE_FIXED_ALLOC(CTempWnd);
};

You can find more code in the winmenu.cpp and wincore.cpp.
0
 
applebyAuthor Commented:
The most important part of my question you did not answer, but thank you for the information provided.  I still need to know if they are safe to use, or more clearly, are they just temporary and do you lose any changes you make when you return from using them?
0
 
vachoohoCommented:
CTempMenu, CtempWnd and other temp objects constructed by MFC framework during the calls to some functions
such as AfxGetMainWnd(), GetDC() etc.

They are realy temporary objects and can not be used outside the scope they'd received. MFC framework will delete all those objects when an application will have an idle time.
Functions which returns such kind of objects all has the special note in MSDN.

example:
this code is incorrect and will not work
void CWndMy::OnCommand1()
{
  m_pMenu = GetMenu();
}

void CWndMy::OnCommand2()
{
  if(m_pMenu != NULL)
    m_pMenu->AppendMenu(...);
}
in OnCommand2 handler m_pMenu could be destroyed and deleted
you can not use it!

0
[Webinar] Kill tickets & tabs using PowerShell

Are you tired of cycling through the same browser tabs everyday to close the same repetitive tickets? In this webinar JumpCloud will show how you can leverage RESTful APIs to build your own PowerShell modules to kill tickets & tabs using the PowerShell command Invoke-RestMethod.

 
applebyAuthor Commented:
I see.  Thanks that clears it up.

The code where I'm getting this is after passing this structure into a function then calling some CMenu operations on the CmMenu part of the structure, like DeleteMenu().  Can you suggest a way I can do that without it creating a CTempMenu?  I need the menu items to really get deleted, and stay deleted after returning from the function.
0
 
ZoppoCommented:
Hi appleby

in fact vachooho tells the truth, but the question of loosing any changes is still not answered:

Because CWnd/CMenu are just wrapper classes for API function anything you call against CWnd/CMenu is wrapped to an API call using the CWnd's/CMenu's HWND/HMENU, so there's no danger that changes are lost.

Only danger (even with CWnd/CMenu pointers) is that one should never store a CWnd/CMenu pointer returned by (nearly, exception i.e. CWnd::FromHandlePermanent) any MFC function for later use, because most of the objects these pointers are pointing to are temporary and the framework deletes these objects when processing WM_IDLE messages. It's always a safe strategy to store the attached HWND/HMENU handle.

Example:

CWnd* pParentWnd;
HMENU hMainMenu;

----------------------------------------
CMyView::OnInitialUpdate(...)
{
...
pParentWnd = AfxGetMainFrame();
hMainMenu = pParentWnd->GetMenu()->GetSafeHmenu();
...
}

CMyView::OnAnyMessage(...)
{
...
CMenu *pMenu;
pMenu = pParentWnd->GetMenu(); // this may crash, because pParentWnd might not point to an existing object
pMenu = CMenu::FromHandle( hMainMenu ); // this is safe, cannot crash except the menu has been destroyed earlier
...
}
----------------------------------------


hope that helps,

ZOPPO
0
 
applebyAuthor Commented:
Yes, this is what I need to solve my problem.  Thank you Zoppo.  Repost an answer for the credit.
0
 
ZoppoCommented:
You're welcome ...   :)
0
 
applebyAuthor Commented:
Maybe I closed this out too soon.  :)  
I am grabbing the HMENU from my CMenu and passing that around instead of a pointer to the menu.  But within the functions, I still don't get the changes saved when I return.  Inside the function I create a temporary menu from the handle, do my work on it, then return the handle.  When I return, I get back exactly what I sent without the modifications.  What am I doing wrong?  
0
 
ZoppoCommented:
Can you post the code which does the changes ?
0
 
applebyAuthor Commented:
menus is a pointer to a class which contains member variable m_tasks, which is an HMENU.
(m_tasks was originally a CmMenu* but I changed it in trying to get this working.)

In a global function, this call is made:

      DestroyChildren( menus->m_tasks );

And DestroyChildren is defined like this, calling CMenu::DeleteMenu within it.

      extern void DestroyChildren( HMENU men )
      {
            CmMenu* tmpMen = (CmMenu*)CMenu::FromHandle(men);
            if ( !men ) return;

            for ( UINT i=0; i<tmpMen->GetMenuItemCount(); )
            {
                  tmpMen->DeleteMenu(i, MF_BYPOSITION);
            }
      }


You can see in the debugger that it does get the right number of menu items and deletes them with success, according to the return values, but when the function returns the menu is still in its original form.

I'm probably missing some vital code you want to look at - let me know.
0
 
ZoppoCommented:
Does DeleteMenu() return TRUE?

Have you tried to call CMenu::DrawMenuBar() after DestroyChildren() ? Perhaps the menu items are deleted correct and only the displayed menu is wrong.

furthermore one little hint:

instead of:
for ( UINT i=0; i<tmpMen->GetMenuItemCount(); )
{
 tmpMen->DeleteMenu(i, MF_BYPOSITION);
}

you can write:
while ( tmpMen->GetMenuItemCount() )
{
 tmpMen->DeleteMenu(0, MF_BYPOSITION);
}

it's easier to read this.

ZOPPO
0
 
applebyAuthor Commented:
Yes, DeleteMenu() does return true.  I wasn't calling DrawMenuBar() after the DestroyChildren() call, (btw, you mean CWnd::DrawMenuBar() I believe, there is none for CMenu) but it makes no difference when I try it.


0
 
ZoppoCommented:
Can you post some code where the menu is created and m_tasks is set? Are you sure m_tasks is the handle to that menu the window uses?
0
 
applebyAuthor Commented:
In the OnInitialUpdate() of the view class for the class m_tasks belongs to, this code allocates space and loads the menu.  "Tasks" is a submenu on the menu when it first gets loaded.  (it is this way in the resource editor to start with.)  Then when DestroyChildren gets called as above, it removes all the items under the "Tasks" submenu, or is supposed to.

      m_pMenu = new CmMenu();
      m_pMenu->LoadMenu(IDR_FPW);
      SetMenu(m_pMenu);
      
      frame->m_tasks = m_pMenu->FindSubMenu("Tasks")->GetSafeHmenu();

I'm sure it is the right handle, because inside the DeleteMenu() function, it sees the right number of items on the menu.  That is the only menu that has 9 items on it.  
0
 
applebyAuthor Commented:
I also just checked the handle by comparing it to the m_hMenu of the CMenu* object and they are the same.
0
 
ZoppoCommented:
I do not see any problems in your code.

Is the function DestroyChildren called from another thread or module(dll)?
Have you tried to get the menu anew in DestroyChildren, i.e.

DestroyChildren( frame->GetMenu()->m_hMenu );

void DestroyChildren( HMENU men )
{
  CMenu* tmpMen = CMenu::FromHandle(men);
  tmpMen = tmpMen->FindSubMenu( "Tasks " );
  if ( !tmpMen ) return;

  for ( UINT i=0; i<tmpMen->GetMenuItemCount(); )
  {
     tmpMen->DeleteMenu(i, MF_BYPOSITION);
  }
}

0
 
ZoppoCommented:
sorry, unfortunately I have to go home now (hurra, it's friday evening!!!), so I can't discuss anything more until monday morning...

have a nice weekend and try to forget your problem one or two nights, it sometimes helps...

ZOPPO
0

Featured Post

Free Tool: Subnet Calculator

The subnet calculator helps you design networks by taking an IP address and network mask and returning information such as network, broadcast address, and host range.

One of a set of tools we're offering as a way of saying thank you for being a part of the community.

Tackle projects and never again get stuck behind a technical roadblock.
Join Now