MDI und Callback Funktion



  • SERVUS.

    ich habe eine MDI application programmiert. das problem ist nun aber, dass alle CHILDREN - WINDOWS die gleiche CALLBACK FUNKTION haben. dass heisst, wenn ich mehrere fenster öffne, und in einem etwas ändern möchte, dann findet die gleiche veränderung in allen anderen offenen fenstern auch statt.

    wie kann ich diesem problem herr werden?
    gibts dazu nen guten LINK irgendwo?

    DANKE.
    STICK.



  • Ist doch klar: Wenn du für alle Fenster die gleiche Funktion zuweist, reagieren alle Fenster gleich. Jede Callback-Funktion eines Child-Windows muß einen individuellen Namen haben, z. Bsp: LRESULT CALLBACK ChildProc01, LRESULT CALLBACK ChildProc02. Die Fuktionsnamen werden bei den Fensterparametern eingegeben und hinterher extra implementiert:

    LRESULT CALLBACK ChildProc01(HWND hWnd, UINT msg, LPARAM lParam, WPARAM wParam)
    {
    ...
    }



  • SERVUS.

    das ist mir schon bewusst, dass verschiedenen fenstern verschiedene funktionen zugewiesen werden sollten.
    ich will ja aber auch das GLEICHE fenster beliebig oft aufrufen, also wird dieses fenster immer wieder die gleiche CALLBACK FUNKTION haben. wenn ich aber dann in einem der fenster zum beispiel ZOOME, dann will ich nicht, dass in den anderen fenstern vom gleichen typ auch hereingezoomt wird. also wird es sich dann doch um eine andere CALLBACK FUNKTION handeln.

    kann man denn eine art ARRAY mit CALLBACK FUNKTIONEN machen, deren anzahl dann davon abhängt, wie oft ich das fenster geöffnet habe?

    ich will nämlich nicht am anfang eine bestimmte anzahl von CALLBACK FUNKTIONEN festlegen, weil einfach gar nicht geklärt ist, wie oft das fenster vom user geöffnet werden wird.

    ich hoffe, ich habe mich halbwegs verständlich ausgedrückt.

    DANKE SCHONMAL FÜR INSPIRATIONEN.
    STICK.



  • Das müßte über das Fensterhandle gehen. Jedes Fenster bekommt beim Öffnen ein eigenes Handle zugewiesen, auch, wenn es das gleiche Fenster ist. Das Handle kann mit "GetModuleHandle" ermittelt werden.

    "If this parameter is NULL, GetModuleHandle returns a handle of the file used to create the calling process."

    Vielleicht, wenn die Handles in einem Array gespeichert werden...(fragt sich halt, wie groß das Array sein soll, wenn man das Fenster "beliebig" oft öffnen kann 😕)



  • HALLO.

    ich muss ehrlich sagen, dass ich nicht 100% weis, mit der GetModuleHandle funktion umzugehen.
    ich denke einbinden muss ich GetModuleHandle in die CreateWindowEx, kann ich aber auch dort die erhaltenen daten in ein array einfügen?

    ich dachte, vielleicht so, aber so richtig klappts nicht:

    HWND       hChild;
    HINSTANCE  test[4];
    int g_iNumChild = 0;
    
    hChild = CreateWindowEx(WS_EX_MDICHILD,g_szChildClassName,"OpenGL window", 
    WS_CHILD | WS_VISIBLE | WS_OVERLAPPEDWINDOW,0,0,350,350,
    g_hMDIClient,NULL, test[g_iNumChild]=GetModuleHandle(NULL),&cs);
    
    g_iNumChild++;
    

    DANKE.
    STICK.



  • Nur auf die Schnelle:

    Wie schon gesagt: GetModuleHandle liefert das Handle des geforderten Moduls zurück. Diesen Wert hannst Du dann zwischenspeichern, z. B.
    int Window[20]
    ...
    Window[i] = GetModulHandle (NULL)
    ...

    Schau ruhig auch im MSDN nach.



  • man könnte da doch sicher auch mit SetWindowLong arbeiten, oder?



  • SERVUS.

    Elektronix schrieb:

    int Window[20]
    ...
    Window[i] = GetModulHandle (NULL)

    wie stellst du dir eine umwandlung von einer INTEGER ZAHL in eine HINSTANCE vor? mein compiler macht da nicht mit.
    (error C2440: '=' : 'struct HINSTANCE__ *' kann nicht in 'int' konvertiert werden)

    grüsse.
    STICK.



  • Gibt denn der Aufruf der CALLBACK Funktion nicht schon das passende Handle genau fuer diese eine Instanz des Clients zurueck? Da muss nur noch fuer jede Instanz von diesem Client ein eigener Speicherbereich fuer die Dauer seines "Lebens" bereitgestellt werden.

    (Wenn man also z.B. 5 VERSCHIDENE Clients vordefiniert, so kann man von jedem Client beliebig viele Instanzen zur Laufzeit erzeugen. Und jede (Instanz) soll unabhaengig von der anderen (Instanz) des gleichen Clients sein.

    Wenn das so ist, dann funktioniert es so (fuer jede Client-Callback-Funktion):

    global :

    typedef struct tagMDDATA
    {
    	stDISC stDisc;
    	CSBAR  sbMD;  // 
    	CTABC  tcMD;
    	HWND   hTab;  // Handle des TabControl
    	CREDIT reMD;
    } MDDATA, * PMDDATA;
    

    in WM_CREATE:

    ...
    		// Platz für individuelle Daten
    		pMDData = (PMDDATA) HeapAlloc (GetProcessHeap (),
    		                               HEAP_ZERO_MEMORY, sizeof (MDDATA));
    
    		// Handle des Rahmen- und des Client-Fensters festhalten:
    		hwndClient = GetParent (hwnd);
    		hwndFrame  = GetParent (hwndClient);
    
    		// Statusbar erzeugen:
    		SetWindowLong (hwnd, 0, (long) pMDData);
    		pMDData->sbMD.Init (hwnd, NUM_FIELDS);   // VARIANTE 1
    		//pMDData->hwndSBar = sbMD.Init (hwnd);  // VARIANTE 2
    
    		// TabControl erzeugen:
    		pMDData->hTab = pMDData->tcMD.Init (hwnd, NUM_TABS);
    
    		// Ausgabefenster auf dem TabControl erzeugen:
    		pMDData->reMD.Init (pMDData->hTab, 0);
    		break;
    

    in WM_SIZE z.B.: so:

    case WM_SIZE:
    		pMDData = (PMDDATA) GetWindowLong (hwnd, 0);
    		GetClientRect (hwnd, &rect);
    
    		// Statusbar anzeigen:
    		pMDData->sbMD.ScaleSBar ();  // VARIANTE 1
    		pMDData->sbMD.WriteSBarField (0, TEXT("No disc"));
    		pMDData->sbMD.WriteSBarField (1, TEXT("No track"));
    		pMDData->sbMD.WriteSBarField (2, TEXT("mm:ss"));
    		pMDData->sbMD.WriteSBarField (3, TEXT("Power OFF"));
    		...
    

    usw.

    Sind hier nur Beispiele. Die Vorgehensweise hab' ich auch nur aus der MSDN - ist ganz gut beschrieben.

    Blackbird



  • Hab' noch was vergessen:

    case WM_DESTROY:
    		pMDData = (PMDDATA) GetWindowLong (hwnd, 0);
    		HeapFree (GetProcessHeap (), 0, pMDData);
    

    Die Types
    CSBAR sbMD;
    CTABC tcMD;
    CREDIT reMD;

    sind Klassen, die alle Methoden fuer die Controls enthalten, z.B.: fuer die Statusbar

    CSBAR::CSBAR (HWND hwnd, int iFields)
    {
    	if (!hwnd)
    		return;
    
    	m_hwnd = hwnd;
    	m_iFields = iFields;
    	m_hwndSbar = InitSBar ();
    };
    

    Hier wird das Handle der Instanz (eines Clients) an die Statusbar uebergeben:

    pMDData->sbMD.Init (hwnd, NUM_FIELDS);   // VARIANTE 1
    

    Blackbird



  • [quote]wie stellst du dir eine umwandlung von einer INTEGER ZAHL in eine HINSTANCE vor? mein compiler macht da nicht mit. [/quote]
    Hast Recht, war ein bißchen zu sehr aus dem Bauch geschossen 😃



  • AN BLACKBIRD:

    also ich muss leider sagen, dass ich nichts verstehe von dem, was du schreibst. hab auch irgendwie das gefühl, dass das nicht so zu meiner frage passt (?). weiss zum beispiel gar nicht, wozu ich WM_SIZE und so brauche und was die klassen der statusbar mit meinem problem zu tun haben.

    mein problem ist einfach so, dass ich ein MAIN MDI WINDOW habe, mit ner TOOLBAR. wenn ich auf den button drücke, kommt ein OpenGL window, das ein CHILD WINDOW ist. in der CALLBACK FUNKTION für dieses child fenster steht, dass beim drücken von F1 eingezoomt wird. nun soll dieses zoomen aber immer nur beim aktiven fenster erfolgen, nicht bei allen OpenGl-fenstern. man müsste also auch irgendwie abfragen, welches fenster gerade aktiv ist.

    DANKE schonmal, falls du noch irgendwelche ideen hast.
    STICK.



  • Deine Callback-Funktion hat eine WM_CREATE case-Falle?
    Wenn ja, dann muss dort das Handle (als Parameter HWND hwnd im Funktionsaufruf) dieser Instanz gespeichert werden. Und zwar in einem eigenen Speicherbereich:

    // Platz für individuelle Daten
            pMDData = (PMDDATA) HeapAlloc (GetProcessHeap (),
                                           HEAP_ZERO_MEMORY, sizeof (MDDATA));
    

    Die Struct pMMDDate ist hier nur Beispiel, sie kann auch aus nur einem Element (dem Handle) bestehen.

    Mit

    SetWindowLong (hwnd, 0, (long) pMDData);
            pMDData->sbMD.Init (hwnd, NUM_FIELDS);   // VARIANTE 1
    

    wird das Handle gespeichert, hier fuer eine Statusbar. Dann nochmals fuer ein Tab-Feld und ein Edit-Fenster. Ist hier nur ein Beispiel. Das kannst Du natuerlich viel einfacher machen.
    Einfach nur das Handle speichern.

    In Deinem Code in der case-Falle WM_COMMAND wird irgendwo die F1-Taste verarbeitet. Dort muss alles was Deine Instanz betrifft (zoomen), mit genau diesem Handle passieren. Und das holt man sich so, wie ich es in WM_SIZE gezeigt habe (zuerst die struct, darin ist das Handle):

    case WM_SIZE:
            pMDData = (PMDDATA) GetWindowLong (hwnd, 0);
    

    Der restliche Code zeigt nur beispielhaft, wie es geht.
    Den Speicherbereich immer freigeben, wenn die Instanz des Childs zerstoert wird.

    Blackbird



  • SERVUS.

    habe in meinem programm folgendes hinzugefügt, klappt aber leider immer noch nicht. vielleicht kann jemand den grund für folgende fehlermeldung finden:
    "error C2228: Der linke Teil von '.Init' muss eine Klasse/Struktur/Union sein"

    global:

    typedef struct tagMDDATA
    {
        HWND   hTab;
    } MDDATA, *PMDDATA;
    

    am anfang der Callbackfunktion habe ich folgende variable definiert:

    PMDDATA pMDData;
    

    WM_CREATE:

    pMDData = (PMDDATA) HeapAlloc (GetProcessHeap (),
              HEAP_ZERO_MEMORY, sizeof (MDDATA));
    SetWindowLong (hWnd, 0, (long) pMDData);	
    pMDData->hTab.Init (hWnd);
    

    WM_SIZE:

    pMDData = (PMDDATA) GetWindowLong (hWnd, 0);
    

    WM_CLOSE:

    pMDData = (PMDDATA) GetWindowLong (hWnd, 0);
    HeapFree (GetProcessHeap (), 0, pMDData);
    

    DANKE schonmal.
    STICK.



  • Du weißt inzwischen, ich bin auch Anfänger 🙂 Aber hast Du es schon mal mit
    cbWndExtra versucht?

    [quote]
    Specifies the number of extra bytes to allocate following the window instance. The operating system initializes the bytes to zero. If an application uses the WNDCLASS structure to register a dialog box created by using the CLASS directive in the resource file, it must set this member to DLGWINDOWEXTRA. [/quote]
    Also, den Status des Fensters in WndClassExtra speichern... ist halt so ne Idee...



  • Statt

    pMDData->hTab.Init (hWnd);
    

    einfach nur

    pMDData->hTab = hWnd;
    

    Weil: Deine Struct MDData enthaelt keine Klasse (wie in meinem Beispiel), sondern nur das Handle.

    Und wenn die F1-Taste verarbeitet wird, musst Du das gespeicherte Handle auch verwenden:

    case WM_KEYDOWN:
      pMDData = (PMDDATA) GetWindowLong (hWnd, 0); 
      if (pMDData->hTab == (HWND)lParam && VK_F1 == wParam)
      {
        MessageBox (pMDData->hTab, "F1 down", "Eingabebox", MB_OK); // nur Beispiel!
      }
    ...
    

    Wo die F1-Taste abgefangen wird; das kann bei Dir auch anders aussehen.

    Blackbird



  • SERVUS BLACKBIRD.

    danke für die antwort. ich werde deine hinweise mit einbauen und sag dann bescheid, ob alles funktioniert hat.

    STICK.



  • HALLO.

    ich hatte vor einer weile (23.01.2006) dieses problem mit MDI und verschiedenen CALLBACK FUNKTIONEN gepostet.

    leider bin ich seit dem nicht mehr dazu gekommen, das zu ende zu bringen. darum wollte ich jetzt noch einmal nachfragen, ob mir noch jemand helfen kann?? 🙄

    ich habe immer noch das problem, dass ich nicht weis, wie ich das gespeicherte HANDLE für meine verschiedenen CALLBACK FUNKTIONEN wieder verwenden kann. ich habe es jetzt am beispiel der F1 taste so gemacht, aber wenn ich F1 betätige, dann stürzt das program ab.

    case WM_KEYDOWN:			
    {
       pMDData = (PMDDATA) GetWindowLong (hWnd, 0);
       if (pMDData->hTab == (HWND)lParam && PAD[wParam] == TRUE)
          MessageBox (pMDData->hTab, "F1 down", "Eingabebox", MB_OK);
       return 0;				
    }
    

    DANKE. 🙂
    STICK.



  • An welcher Stelle genau stürzt er denn ab?



  • das programm macht nicht mehr weiter, wenn ich auf F1 drücke.

    STICK. 🙂


Anmelden zum Antworten