2 Fragen betreffend Menüs und Subclassing



  • Hallo,
    ich habe 2 Problem mit einer MDI-Anwendung. Und zwar geht es um folgendes:
    Ich habe in meinem CMainFrame die Funktion OnInitMenuPopup überschrieben. Dort rufe ich dann das Menü ab, das der Benutzer öffnet, und gehe alle Menüeinträge dieses Menüs rekursiv durch und stelle die auf Ownerdraw. Das klappt auch, die Menüs werden so gezeichnet wie von mir erwartet. Leider ergibt sich dadurch ein Problem: Im Menü "Fenster" werden die Einträge für die einzelnen MDI-Fenster nicht mehr automatisch verwaltet. Sprich - wenn ich ein MDI-Fenster schliesse, habe ich dessen Eintrag trotzdem immer noch unter "Fenster". Weiss jemand, woran das liegen könnte, und wie ich es beheben kann?

    Mein zweites Problem ist etwas komplexer. Und zwar rufe ich in der CWinApp-Klasse in der OnInitInstance die Funktion HookWindowsHookEx auf, um die Menüs Subclassen zu können. Mit Hookprozedur werden dann die Ränder der Menüs flach dargestellt. Das funktioniert auch, ABER: wenn ich irgend einen beliebigen Dialog aus meiner Anwendung heraus öffne, dann sind desse Systemmenü und alle Kontextmenüs ebenfalls flach, aber nicht Ownerdraw. Wie bringe ich es fertig, dass entweder nur das Hauptmenü meiner Anwendung subclassed ist, oder aber alle Menüs Ownerdraw sind?

    Code habe ich im Moment nicht zur Hand, habe ich vergessen. Werde den aber noch posten, falls nötig. Wenn aber sonst einer eine Idee hat wäre das genial!
    Ich danke schon im Voraus.
    Grüsse


  • Mod

    zu 1. Kann es sein, dass Du die Basisklasse zu OnInitMenuPopup nicht aufrufst?
    zu 2. Was für einen Hook benutzt Du denn? Du weißt ansich doch um welches Menü und welches Fenstr es sich handelt... Warum nimmtst Du überhaupt einen Hook? Was für einen Hook? Geht das nicht normal mit Subclassing?



  • Hallo Martin,
    zu 1: doch, die Basisklasse rufe ich auf.
    Ich habs auf 2 arten versucht:
    - bevor meine Menüs auf Ownerdraw gestellt werden (zu beginn von OnInitMenuPopup)
    - nachdem sie schon auf Ownerdraw sind (vor dem verlassen von OnInitMenuPopup)

    beides klappt nicht, Verhalten wie beschrieben.

    zu 2: was heisst für dich "Normal mit Subclassing"?
    Der eingesetzte Hook ist:

    SetWindowsHookEx(GWL_CALLWNDPROC, this->CallWndProc, theApp->m_hInstance, GetCurrentThreadId());

    Bringt dir das was?

    Grüsse


  • Mod

    Warum bitte dieser Holzhammer?
    Du kannst doch in der MFC einfach die entsprechenden Meldungen in Deiner CMainFrame Klasse genauso abhandeln.



  • Wie jetzt? Du meinst, ich kann z.B. die Meldung OnNcPaint() normal in meiner CMainFrame abhandeln und das funktioniert?
    So habe ich es noch nie versucht, aber es wäre angenehm einfach!


  • Mod

    Hier wären zwei Ansätze, beide basieren auf der Kenntnis der Menu Fensterklasse #32768.
    http://msdn.microsoft.com/en-us/library/ms633574(VS.85).aspx

    1. Bei WM_INITMENUPOUP FindWndow durchführen und ein Fenster dieser Klasse finden. Aufgrund der Konstruktion ist es wie beim Highlander: Es kann nur eines geben! 🕶
    Das funktioniert easy für genau das Fenster in dem Du es möchtest.
    2. Du verwendest den ganz offziellen Weg und machst Superclassing. Du registrierst eine eigene Klasse #32768 mit Deiner eigenen Fensterprozedur.
    Im Fall 2 müsstest Du evtl. das entsprechende Fenster für das dieses menü geöfnet wirdermitteln und dann die weiteren Schritte wahlweise zulassen oder nicht.

    Just my 2 cent's.



  • Hallo Martin,
    na danke erstmal! Super dass du mir hilfst.
    Nun habe ich aber noch eine Frage.
    zu 1: du sagst, bei OnInitMenuPopup soll ich ein Fenster der Klasse #32768 finden. Das ansich ist kein Problem, aber was mache ich dann mit dem gefundenen Fenster? Irgendwie hab' ich das noch nicht so ganz verstanden.
    Aber ich denke, Variante 1 sollte schon passen. Scheint mir wesentlich einfacher als extra ne eigene Klasse zu bauen 😉

    Grüsse



  • So, hier mal 2 Bilder, wie meine Menüs aussehen.
    Also, ich hab in der Hookprozedur die Nachricht WM_NCPAINT überschrieben. Wie du siehst, funktioniert das auch. Nun soll aber, wenn das Systemmenü oder ein anderes Menü, das nicht das Menü meiner Anwendung ist, gezeichnet wird, die standard-Prozedur aufgerufen werden (CallWindowProc).
    Und ich weiss nicht, wie ich feststellen kann, welches Menü gezeichnet wird. kannst du mir da weiterhelfen?

    Bild 1: http://tpl-el.com/files/menu.jpg

    Bild 2: http://tpl-el.com/files/menu2.jpg

    Grüsse


  • Mod

    Wenn Du das Fensterhandle hast kannst Du genau dieses Fenster Subclassen und nur dort WM_NCPAINT überschreiben.

    Wenn Du in einem Hook alle WM_NCPAINT gebehandelst, dann erwischt Du doch alle Fenster. Dich interessieren doch aber nur die des Menüs.



  • Ja, da hast du wohl recht! Das Fensterhandle hab ich auch, das rufe ich mit AfxGetMainWnd() in der Hookprozedur ab.

    Hier mal mein Code:

    LRESULT CALLBACK CTestApp::CallWndProc(int nCode, WPARAM wParam, LPARAM lParam)
    {
    	CWPSTRUCT* pCwp = (CWPSTRUCT*)lParam;
    	while(nCode == HC_ACTION)
    	{
    		HWND hWnd = pCwp->hwnd;
    		UINT uMsg = pCwp->message;
    		CWnd* pFrame;
    		TCHAR pszClassName[10];
    		int nCount;
    		if((uMsg != WM_CREATE) && (uMsg != 0x01E2))
    			break;
    		pFrame = CWnd::GetForegroundWindow();
    		if(pFrame == NULL)
    			break;
    		nCount = ::GetClassName(hWnd, pszClassName, sizeof(pszClassName) / sizeof(pszClassName[0]));
    		if((nCount != 6) || (_tcscmp(pszClassName, _T("#32768")) != 0))
    			break;
    		if(::GetProp(pCwp->hwnd, oldProp) != NULL)
    			break;
    		WNDPROC wp = (WNDPROC)(LONG_PTR)::GetWindowLong(pCwp->hwnd, GWL_WNDPROC);
    		if(wp == NULL)
    			break;
    		ASSERT(wp != SubclassMenuProc);
    		if(!SetProp(pCwp->hwnd, oldProp, wp))
    			break;
    		if(!SetWindowLong(pCwp->hwnd, GWL_WNDPROC, (DWORD)(DWORD_PTR)SubclassMenuProc))
    		{
    			::RemoveProp(hWnd, oldProp);
    			break;
    		}
    		else
    			::SetProp(AfxGetMainWnd()->GetSafeHwnd(), newProp, (HANDLE)TRUE);
    	}
    	return CallNextHookEx(HookProc, nCode, wParam, lParam);
    }
    

    Der sollte doch schon stimmen, oder?

    So sieht meine InitInstance aus:

    [code]
    BOOL CTestApp::InitInstance()
    {
    ...
    if(HookProc == NULL)
    HookProc = ::SetWindowsHookEx(WH_CALLWNDPROC, CTestApp::CallWndProc, theApp.m_hInstance, ::GetCurrentThreadId());
    }

    Grüsse


Anmelden zum Antworten