HWND nach mehrmaligem Aufruf plötzlich NULL



  • Hallo zusammen

    Mit einem Barcode-Scanner übergebe ich Daten an mein Programm.
    Dies soll auch möglich sein, wenn das Programm keinen Fokus hat und ev. auch der
    Dialog für den Empfang der Daten nicht geöffnet ist.

    Der Tastatur-Hook läuft. Und die Erkennung der Zeichen auch.
    Der gesammelte String wird an einen Thread übergeben, der damit den Dialog öffnen und den String an diesen übergeben soll.
    Das funktioniert i.d.R. ein- bis zweimal.
    Danach werden mir für den Dialog und/oder das Edit-Ctrl eine NULL als HWND zurückgegeben.

    Also "::GetForegroundWindow()" und/oder "::GetDlgItem(g_hwndBcStart, IDC_EDIT_BARCODE)" funktionieren nicht mehr.

    Was mache ich falsch?

    Hier den Quellcode Abschnitt im Thread.

    HWND g_hwndMain; // Handle der App. Wird beim App-Start ermittelt.
    HWND g_hwndBcStart; // Handle des Dialogs. Hier jedesmal neu ermittelt
    // "hEdit" Handle des Edit-Ctrls. Weiter unten deklariert u. ermittelt
    
    /* "g_hwndBcStart" u. "hEdit" werden manchmal zu "NULL" ermittelt */
    
    if (cnt > 0) {// HP: Es ist eine gültige Zeichenkette vorhanden
    	if (!g_FlagIsInBcDialog) {// HP: Der Dialog ist noch nicht geöffnet.
    		// HP: "SendMessage" würde hier warten, bis der Aufgerufene beendet ist.--> "PostMessage" verwenden!
    		PostMessage(g_hwndMain, WM_COMMAND, ID_START_MAIN, 0);// Message, die normalerweise durch Tool-Button ausgelöst.
    		pView->m_bDoStart = FALSE;
    		g_dwBC_LastReaderTime = 0;
    		TRACE(_T("StarterThread: Message gesendet an %d, %s\n"), g_hwndMain, g_cBC_ReaderContent);
    	}
    	else {// HP: Hier ist der Dialog schon geöffnet.
    		::SetForegroundWindow(g_hwndMain);
    		g_hwndBcStart = ::GetForegroundWindow();
    		HWND hEdit = ::GetDlgItem(g_hwndBcStart, IDC_EDIT_BARCODE);
    		::SetFocus(hEdit);
    
    		int iPos = 0;
    		WORD wCnt = 0;
    		CString csSeparator = _T("\t");
    		CString csToken = g_cBC_ReaderContent.Tokenize(csSeparator, iPos);
    		while (!csToken.IsEmpty() && (csToken != _T("\t"))) {
    			wCnt++;
    			TRACE(_T("StarterThread: *************  Token: %s **************\n"), csToken);
    			SendMessage(hEdit, WM_SETTEXT, FALSE, (LPARAM)((LPCTSTR)csToken));
    			csToken = g_cBC_ReaderContent.Tokenize(csSeparator, iPos);
    			SendMessage(hEdit, WM_KILLFOCUS, 0, 0);
    		}
    		g_cBC_ReaderContent.Empty();
    		pView->m_bDoStart = FALSE;
    		g_dwBC_LastReaderTime = 0;
    		TRACE(_T("StarterThread: Message an Edit-Ctrl gesendet: Main: %d, Dialog: %d, Edit: %d, %s\n"), g_hwndMain, g_hwndBcStart, hEdit, g_cBC_ReaderContent);
    	}
    }
    


  • @elmut19
    Noch eine etwas genauere Erklärung hierzu:

    Es werden z.B. 5 Barcodes von den Dialog erwartet.
    Wenn ich nur eine Teil sende, dann wartet der Dialog auf die restlichen Barcodes.

    Dieser Teil funktioniert i.d.R.

    Den Anwendungs-Fokus habe ich bei der ganzen Sache natürlich in einem ganz normalen Notepad,
    sonst würden die Tests ja keinen Sinn machen.
    Es ist ja der Test, dass meine Anwendung keinen Fokus besitzt.

    Wenn aber anschliessend der Dialog wieder geschlossen wird und ich den Vorgang
    mit neu gescannten Daten wiederhole, dann sind die HWND´s meist weg.

    Es kommt aber vor, dass z.B. nur der HWND des Edit-Ctrl´s weg ist oder auch beide.
    Manchmal bekomme ich sogar, bei wiederholtem Übertragen der Scan-Daten, den Dialog-HWND wieder zurück.



  • @elmut19
    Zu einem weiteren seltsamen Verhalten kommt es, wenn ich meiner Anwendung dabei den Focus gebe.
    Hier wird oftmals einfach ein Zeichen ignoriert oder ich habe sogar ein Zeichen mehr,
    nämlich mein Startzeichen des Scanners, das mir die Unterscheidung zur normalen Tastatur ermöglichen soll.
    Das konnte ich aber rausfiltern.

    Ausgenommen ist natürlich der Fall, dass ich den Dialog, wie eigentlich vorgesehen aufrufe und den Cursor ins Eingabefeld setze.
    Da gibts keine Schwierigkeiten.

    Kann sich irgend jemand das Ganze erklären?
    Oder hatte jemand Ähnliches schon mal erlebt?



  • ::SetForegroundWindow(g_hwndMain);
    g_hwndBcStart = ::GetForegroundWindow();
    

    Das verstehe ich nicht. Warum setzt du erst das Vordergrundfenster um dann das Handle des Vordergrundfensters per GetForegroundWindows zu holen? Kannst du nicht direkt über g_hwndMain drauf zugreifen? Welches Handle erwartest du denn bei dem Aufruf von GetForegroundWindows? Und wer erzeugt das Fenster? Kannst du dir das Handle beim Erzeugen nicht merken und damit arbeiten?

    PS:
    Das Erzeugen von Fenstern in nebenläufigen Threads ist nicht ganz unproblematisch, da gibt´s wohl ein paar Dinge zu beachten. Vielleicht erzeugst du den Dialog besser im Hauptthread und sendest aus dem nebenläufigen Thread nur Botschaften an den Dialog, um ihn zu steuern.



  • @DocShoe
    Hallo DocShoe,
    vielen Dank für Deine Antwort.
    Und ja, das sieht seltsam aus aber funktioniert.

    Die Sache ist die:
    Solange der Dialog nicht geöffnet ist, habe ich nur das Handle vom Hauptfenster.
    Dort setze ich die Message zum öffnen des Dialogs ab. Das ist dieselbe, wie sie durch meinen Button erzeugt wird.

    Wenn nun der Dialog schon geöffnet ist (zu Deiner Frage), dann kann aber jede andere Anwendung, die gerade aktiv ist, den Foreground besitzen.
    Mit "SetForeground" gebe ich diessen Foreground nun explizit meiner Anwendung.
    Diese hat aber ihr eigenes Fenster (den Dialog) im Foreground.
    Also bekomme ich auf meine Anfrag nach dem Foreground, den Foreground meiner Anwendung zurück. Und das ist der Dialog. Der drängelt sich ja immer vor.

    Heute Morgen dachte ich schon, das Problem hätte sich in Luft aufgelöst, da ich eine halbe Stunde rumprobiert hatte, ohne Fehler.
    Dann stand die Anwendung eine Stunde.
    Ich habe weiter getestet. Und nach zwei weiteren Versuchen waren die Handles wieder weg.
    Also zuerst nur der des Edit-Ctrls und beim nächsten Versuch der des Dialogs auch.

    Der Handle meiner Anwendung muss ja noch OK sein, denn der Dialog wird immer geöffnet.
    Das geht über "PostMessage(g_hwndMain, WM_COMMAND, ..."

    Ich bräuchte irgendwas wie einen "Refresh".

    Die Sache mit dem Verlegen in den Haupt-Thread muss ich durchdenken.
    .... --> Ich habe eigentlich keinen Haupt-Thread.
    Da gibt es nur den "run"-Thread. Das ist Windows!
    Dann gibt es noch meinen "MainFrm". Daraus werden alle anderen Threads erzeugt. Und es gibt da nix, was einem Thread gleich käme.

    Der Thread, aus dem ich das nun mache, ist gleich wie jeder andere meiner Threads, als "friend" deklariert.

    Aber was sollte da so problematisch sein?
    Ich bekomme doch von jeder beliebigen Anwendung, die gerade läuft den Handle!
    Und warum komme ich plötzlich nicht mehr an einige innere Handles dieser Anwendung ran?
    Es muss doch sowas wie einen "Refresh" geben! ???



  • @elmut19 sagte in HWND nach mehrmaligem Aufruf plötzlich NULL:

    ::SetForegroundWindow(g_hwndMain);

    Du solltest auf jeden Fall den (boolschen) Rückgabewert überprüfen (wie bei jeder WinAPI-Funktion)!

    Ich bin mir auch nicht sicher, ob das ganze komplett synchron verläuft (evtl. solltest du eine kleine Pause vor dem Aufruf von ::GetForegroundWindow() einbauen bzw. als Schleife mit evtl. Timout).



  • @Th69
    Hmm .... ?

    Vielen Dank Th69.
    Der Rückgabewert ist ja blöderweise schon mein Handle ....

    Aber ich werden es mal mit einer Timeout-Schleife probieren und mit warten zwischen "Set" u. "Get".
    Vielleicht braucht der Rechner ja etwas Zeit, um sein angewiesenes Handle in den "Foreground" zu setzen.

    .... Habs nun probiert: Leider ohne Erfolg!

    				::SetForegroundWindow(g_hwndMain);
    				Sleep(100);
    				int ii = 0;
    _NOCHMAL1:	g_hwndBcStart = ::GetForegroundWindow();
    				Sleep(50);
    				if ((g_hwndBcStart == 0) && (ii < 5)) {
    					ii++;
    					goto _NOCHMAL1;
    				}
    				ii = 0;
    _NOCHMAL2:	HWND hEdit = ::GetDlgItem(g_hwndBcStart, IDC_EDIT_BARCODE);
    				Sleep(50);
    				if ((g_hwndBcStart == 0) && (ii < 5)) {
    					ii++;
    					goto _NOCHMAL2;
    				}
    


  • @elmut19
    Nö, der Rückgabetyp von SetForegroundWindow ist BOOL. Den kann man sich zur Fehlerbehandlung auch schon mal angucken...



  • goto _NOCHMAL1🙄



  • Mal was ganz Anderes:
    Was genau möchtest du eigentlich erreichen, was ist die Aufgabe deiner Anwendung? Lass so Sachen wie Dialog nach vorn bringen etc. mal weg und beschreib´ uns, was du funktional haben möchtest.



  • @DocShoe

    Zunächst: Das mit "_NOCHMAL1" oder "NOCHMAL2" war ohne Einfluss.
    Habe auch meine copy/paste-Fehler gesehen. Danke.

    Nun mein Vorhaben:
    Mein Programm steuert mehrere Anlagen, die irgendwas verarbeiten.
    Dazu wird eine Anlage mit Produkten beladen. An den Produkten kleben bis zu n Etiketten, die hierzu erfasst werden sollen.
    Es können auch n-x Etiketten sein.

    Nun will ein Kunde die Etiketten sowie die zugehörige Anlage, die den Verarbeitungsprozess durchführt, über die gerade gesammelten Daten auch starten.
    Das soll verhindern, dass man plötzlich nicht mehr weiss, an welcher Anlage man gerade war.
    Mein Steuer-Programm erkennt somit auch gleich die Anlage und öffnet den Start-Dialog, der die Daten empfängt und nach einem Plausi-Test noch eine Übersichtsliste der gesammelten Daten zeigt.
    Danach kann dann die schon vorgewählte Anlage gestartet werden.

    Der Scanner arbeitet im Batchbetrieb, damit die Daten auch in 20m Entfernung gesammelt werden können.
    Am PC werden sie dann auf einen Rutsch übertragen.

    Klar. Ich fände es auch wesentlich angenehmer, wenn die Leute den Dialog selbst öffnen würden/müssten.
    Dabei funktioniert ja auch alles. Und man wüsste auch, dass das Eingabefeld den Fokus hat, und, und ...
    Es läge auch in der Verantwortung des Bedieners.

    Aber nun kommen dann immer wieder so lästige Anforderungen ....
    Auch von meinem Boss.
    Und am Besten wäre es, wenn man gar nicht mehr auf den Bildschirm schauen müsste.

    Und solche Anforderungen ziehen dann automatisch andere nach sich.
    So kann ich ab dieser Anforderung schon nicht mehr sicher sein, ob die Anwendung, die ich bedienen will, auch selbst den Fokus besitzt.
    Es würden also die erfassten Daten ev. ins Leere laufen.

    Und somit komme ich auf den riesigen Aufwand, nur um sich einen Klick auf einen Button zu ersparen.



  • Mal sehen, ob ich das richtig verstanden habe:

    • Es gibt eine Software von euch, die von einem Barcodeleser Etikettendaten liest
    • Es gibt eine zweite Software, über die Anlagen gestartet werden können. Diese Software möchtet ihr mit eurer Software fernsteuern (programmgesteuert einen Dialog öffnen, einen Etikettentext in ein Texteingabefeld eintragen und dann einen Button klicken)

    Stimmt das so in etwa?



  • @DocShoe
    Es ist alles in eine Software integriert.
    Aber ja, es läuft auf Fernsteuerung durch den Barcode-Scanner raus.

    Ich habe die Software durch einen "HookDll" ergänzt.
    Und in "MainFrm" ist die Empfänger-Methode für den Hook integriert.
    Diese filtert dann die einzelnen Zeichen raus, die eindeutig vom Scanner stammen.
    Das ist allerdings auch ein Sch....
    ... Und hat auch noch ein paar Macken.
    Diese Methode erkennt vom Scanner das eingestellte Startzeichen.
    Dann gehe ich davon aus, dass alle Scanner-Zeichen sehr schnell hintereinander kommen.
    Über einen Timeout, währenddessen sich nichts mehr am übertragenen String ändert,
    gehe ich davon aus, dass ich alle Daten vom Scanner angekommen sind.
    Dann setze ich ein Flag für meinen Thread, der die Daten übernimmt und entweder den Dialog
    damit aufruft oder, wenn der Dialog schon da ist (und einen Plausifehler oder fehlende Daten meldet),
    die weiteren Daten direkt ins Edit-Ctrl schreibt.

    ... Ob dieser Automatisierungsgrad in der Praxis auch praktikabel ist oder eher mehr Ärger bringt als Vorteile, kann man wohl erst am Beispiel sehen.
    Ich sehe jedenfalls mehr Ärger!
    Aber sag das mal ohne Beweis.



  • Wenn alles in einer Software integriert ist kannst du dir doch das Fensterhandle des Dialogs merken, oder nicht? Warum dann das Geraffel über Set/GetForeground Window?



  • @DocShoe
    Kann ich denn sicher gehen, dass der Dialog, sowie sein Edit-Ctrl immer denselben Handle bekommen?
    Bei jedem neuen Aufruf?
    Der Dialog verschwindet schlisslich immer wieder!



  • Na und? Dann setzt du beim Erzeugen des Dialogs das Handle halt neu. Und beim Schließen löschst du´s halt wieder, musst nur drauf achten, dass das threadsicher verriegelt wird.



  • Ähm, was mir gerade auffällt....
    Wenn das alles in einer Software passiert... warum dann der umständliche Weg über die GUI? Beim Drücken auf den Start Button werden Parameter aus der GUI ausgelesen und iwie an die Anlage geschickt. Kann das nicht der Thread alles selbst erledigen, ohne den Umweg über die GUI zu gehen?



  • @DocShoe
    Das habe ich mir auch schon alles überlegt.
    Aber wie gebe ich dann Fehlermeldungen aus?
    Wenn z.B. einmal weniger Etiketten gescannt werden sollen, als für die Anlage vorgegeben?
    Das kommt öfter mal vor. Und das soll dann bestätigt werden können.
    Also brauche ich irgend einen Dialog.
    Und mein Thread hat keine GUI.

    Aber inzwischen denke ich, dass es doch manchmal Probleme mit den aufeinanderfolgenden "Set/GetForeground" geben könnte.
    Ich bekomme gerade für den Dialog den selben Handle, wie für meine App.

    Aber warum geht/ging das öfters gut?

    Ich müsste den Dialog besser über seine ID suchen und darüber seinen Handle ermitteln.
    Aber wie? Habe da nix gefunden.

    Dieses "FindWindow(..)" verlangt ja blöderweise immer nach dem Fenster-Titel!
    Der ändert sich aber immer bei mir (andere Sprachen).

    Und 40 Minuten später...
    Nun habe ich mir überlegt, dass ich den Dialog seinen Handle selbst ermitteln lasse.
    Das kommt dann in eine globale Var.
    Die ersten Tests sind schon mal positiv.



  • @DocShoe sagte in HWND nach mehrmaligem Aufruf plötzlich NULL:

    ::SetForegroundWindow(g_hwndMain);
    g_hwndBcStart = ::GetForegroundWindow();
    

    Das verstehe ich nicht. Warum setzt du erst das Vordergrundfenster um dann das Handle des Vordergrundfensters per GetForegroundWindows zu holen? Kannst du nicht direkt über g_hwndMain drauf zugreifen? Welches Handle erwartest du denn bei dem Aufruf von GetForegroundWindows? Und wer erzeugt das Fenster? Kannst du dir das Handle beim Erzeugen nicht merken und damit arbeiten?

    Tja, das war´s wohl.
    Ich habe da mit meiner Version wohl ein zeitliches Fehlverhalten ausgelöst, das fälschlicherweise eine für mich teilweise funktionierende Version erzeugt hatte.
    Das hat sich dann, durch Deinen Hinweis, ein Sleep() einzubauen, bewiesen.
    Es ging dann gar nicht mehr.

    Wie oben gesagt, lasse ich mir nun den Handle vom Dialog selbst setzen, auf den ich dann, wenn der Dialog steht wieder zugreifen kann.
    Es funktioniert nu schon eine ganze Weile.

    Danke DocShoe!!!


  • Mod

    @elmut19 sagte in HWND nach mehrmaligem Aufruf plötzlich NULL:

    WM_KILLFOCUS

    Mal Grunsätzlich. WM_KILLFOCUS wird nur als Notification von Windows versendet.
    Man sollte es niemals selbst versenden...
    Dazu gibt es SetFocus!


Log in to reply