Funktion "scanCode2ascii(..)" gibt manchmal Unsinn zurück



  • Hallo zusammen,
    schon wieder ich.

    Und ein weiteres Problem mit dem Auslesen eines Barcodelesers.

    Den Barcodeleser lese ich über einen Tastatur-Hook aus, um damit meine Anwendung fernzusteuern.
    Ich habe also kein Edit-Ctrl! Teilweise zumindest.

    Über die Hook-DLL bekomme ich die Scan Codes der Tastatur.
    Die wandle ich über eine Function "scan2ascii(DWORD scancode, USHORT* result)" in mein benötigtes ASCII-Zeichen um.

    Nun gibt mir diese Function manchmal falsche Werte als ASCII-Zeichen zurück.
    Ich kann jedenfalls sehen, dass alle Parameter (wParam, lParam, scanCode, ..) alle noch korrekt sind.
    Nur die Function gibt was Anderes zurück.

    static int scan2ascii(DWORD scancode, USHORT* result)
    {
    	static HKL layout = GetKeyboardLayout(0);
    	static BYTE State[256];
    
    	if (GetKeyboardState(State) == FALSE)
    		return -1;
    
    	UINT vk = MapVirtualKeyEx(scancode, 1, layout);
    	return ToAsciiEx(vk, scancode, State, result, 0, layout);
    }
    

    Aufgerufen wird das so:

    long CMainFrame::OnHookLowKeyboard(WPARAM wParam, LPARAM lParam)
    {// "wParam, lParam" --> "pkbhs->vkCode, pkbhs->scanCode"
    	CString str;
    	USHORT result = 0;
    	// Convert the virtual key code into a scancode (as required by GetKeyNameText).
    	UINT scanCode = MapVirtualKeyEx(wParam, 0, GetKeyboardLayout(0));
    	switch (wParam)
    	{
    		// Certain keys end up being mapped to the number pad by the above function,
    		// as their virtual key can be generated by the number pad too.
    		// If it's one of the known number-pad duplicates, set the extended bit:
    	case VK_INSERT:
    	case VK_DELETE:
    	case VK_HOME:
    	case VK_END:
    	case VK_NEXT:  // Page down
    	case VK_PRIOR: // Page up
    	case VK_LEFT:
    	case VK_RIGHT:
    	case VK_UP:
    	case VK_DOWN:
    		scanCode |= 0x100; // Add extended bit
    		break;
    	}
    	
    	int b = scan2ascii(scanCode, &result);// HP: Die gesendeten Codes entsprechen leider nicht direkt dem Ascii-Zeichen. --> Hier umwandeln in "result"
    	TRACE(_T("MainFrm:OnHookLow: vkCode=%d, scanCode=%d (lParam=%d), scan2ascii Rückgabe: %d, result: %d **********################## *****************\n"), wParam, scanCode, lParam, b, result);
    	...
    

    Hier nun zwei TRACE-Ausgaben. eine mit korrektem Ergebnis, die andere mit einem falschen.
    Das falsche liefert aber nicht immer dieselben Zeichen zurück. Aber i.d.R. sind es nicht druckbare Zeichen.
    Womit kann das zusammenhängen?

    Hier eine korrekte TRACE-Ausgabe:

    • DllHookLow0: nCode=0, vkCode=187, scanCode=27
    • MainFrm:OnHookLow: vkCode=187, scanCode=27 (lParam=27), scan2ascii Rückgabe: 1, result: 43
    • MainFrm:OnHookLow: ********* Inhalt ergänzen +, (wParam: 187, lParam: 27 --> ZeichenCode: +)
    • DllHookLow0: nCode=0, vkCode=160, scanCode=42
    • MainFrm:OnHookLow: vkCode=160, scanCode=42 (lParam=42), scan2ascii Rückgabe: 0, result: 0
    • MainFrm:OnHookLow: Es wurde ein Ctrl oder Shift erkannt (160). 160,161 = Shift. Wiederholte Zeichen überspringen. Bei FALSE merken: 0
    • MainFrm:OnHookLow: Steuer-Zeichen 160 merken

    Und hier Fehler: (selber Barcode wurde gescannt)

    • DllHookLow0: nCode=0, vkCode=187, scanCode=27
    • MainFrm:OnHookLow: vkCode=187, scanCode=27 (lParam=27), scan2ascii Rückgabe: 1, result: 29 !!! Da kommt der Fehler !!!
    • DllHookLow0: nCode=0, vkCode=160, scanCode=42
    • MainFrm:OnHookLow: vkCode=160, scanCode=42 (lParam=42), scan2ascii Rückgabe: 0, result: 0
    • MainFrm:OnHookLow: Es wurde ein Ctrl oder Shift erkannt (160). 160,161 = Shift. Wiederholte Zeichen überspringen. Bei FALSE merken: 0
    • MainFrm:OnHookLow: Steuer-Zeichen 160 merken

    Das gewünschte und korrekte Zeichen, die 43 (+ Zeichen) wurde einfach zu einer 29, ein "GroupSymbol". (Also die Dezimal-Darstellung des ASCII-Zeichens)

    Und wie gesagt, manchmal kann ich mehrere Scans ohne Fehler durchführen. Manchmal sind sogar mehrere Zeichen verhundst. Es beschränkt sich aber immer auf die ersten Zeichen, die übertragen wurden.

    Vielen Dank schon mal für Eure Mühe.



  • @elmut19
    Deine Ausgabe mittels TRACE ist fehlerhaft.

    Probiere doch mal folgendes

    TRACE(_T("MainFrm:OnHookLow: vkCode=%d, scanCode=%d (lParam=%d), scan2ascii Rückgabe: %d, result: %d **********################## *****************\n"), wParam, scanCode, lParam, b, (b == 1) ? (result & 0xFF) : result);
    

    ToAsciiEx gibt die Anzahl der geschriebenen Bytes in lpChar (hier result) zurück. Ist der Rückgabewert 1, so hat ToAsciiEx nur ein Byte in result geschrieben. Das andere Byte hat die Funktion gar nicht angefasst und deswegen steht da noch ein alter Wert drin.

    Lies dir nochmal genau die Doku dazu durch:
    https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-toasciiex



  • @Quiche-Lorraine
    Danke Dir für die Antwort.

    Ich gebe diesen Rückgabewert schon im TRACE aus. Es ist eine "1".

    Aber mir fällt noch was Weiteres auf (im Moment, beim Rumexperimentieren).

    Der Fehler scheint nicht aufzutreten, wenn der Focus z.B. auf einer anderen Anwendung, wie dem Notepad liegt. (Bin mir noch nicht ganz sicher)
    Wenn irgendwie meine eigene Anwendung den Focus hat, könnte sich vielleicht meine Hook-Auswertung mit dem vielleicht schon verfügbaren Edit-Ctrl beissen?
    In folgendem Bild hatte z.B. das Edit-Ctrl den Fokus, und der BC-Scanner hat im Grunde zweimal dort reingepastet.
    So wurde einmal der gescannte String "50022", der direkt über den Scanner da reingekommen sein muss schon ausgewertet, während dieser String ein weiteres Mal aber als "[13]%0022", noch im Edit-Ctrl zu sehen war.

    Ein paar Minuten später ...
    Ich habe dann Folgendes ausprobiert:
    Zuvor hatte ich in meinem Code explizit das Foreground Window auf meine App gesetzt.
    Zusätzlich hatte ich dann auch noch den Fokus auf mein Edit-Ctrl gesetzt, bevor ich meine Strings gesendet habe.
    Jetzt habe ich das mal weggenommen.

    if (cnt > 0) {// HP: Es ist eine gültige Zeichenkette vorhanden
    	if (!g_FlagIsInBcDialog && !g_bComesFromStartButton) {// HP: Der Dialog ist noch nicht geöffnet.  --   26.09.19: "!g_bComesFromStartButton" ergänzt.
    		// HP: "SendMessage" würde hier warten, bis der Aufgerufene beendet ist.--> "PostMessage" verwenden!
    		ShowWindow(g_hwndMain, SW_NORMAL);// HP: Das Haupt-Fenster auf normale Grösse und in Vordergrund bringen.
    		// HP: Message, normalerweise durch Tool-Button ausgelöst, muss nun den Dialog öffnen.
    		PostMessage(g_hwndMain, WM_COMMAND, ID_START_BARCODEREADER, 0);// Message, normalerweise durch Tool-Button ausgelöst. 01.10.19: vorher war es ID_START_MAIN
    		pView->m_bDoStart = FALSE;
    		g_dwBC_LastReaderTime = 0;
    		TRACE(_T("StarterThread: Message %d gesendet an %d, %s\n"), WM_COMMAND, g_hwndMain, g_cBC_ReaderContent);
    	}
    	else {// HP: Hier ist der Dialog schon geöffnet.
    
    		// HP, 26.09.19: Dieses Flag "!g_bComesFromStartButton" wird nur dann gesetzt, wenn der Dialog mit einer Anlage aufgerufen wurde.
    		//     Das bedeutet, dass es nicht über die "Anlagen-Erkennung", also Scanner-Fernsteuerung, erfolgte.
    		//     Für diesen Fall soll auch nichts an den Dialog gesendet werden (doppelte Erfassung).
    		if (!g_bComesFromStartButton) {
    			// ---------------------------------------------------------------------
    !!! Das durch nachf. ersetzt			//::SetForegroundWindow(g_hwndMain);
    			::SetForegroundWindow(0);
    			Sleep(50);
    			HWND hEdit;
    			int ii = 0;
    			if (g_hwndBcStart) {// HP: Handle wurde durch den Dialog selbst, bei seinem Aufruf ermittelt. (oben unter "if(!g_FlagIsInBcDialog)")
    				hEdit = ::GetDlgItem(g_hwndBcStart, IDC_EDIT_BARCODE);
    !!!! jetzt raus genommen			//	::SetFocus(hEdit);
    			}
    			// ---------------------------------------------------------------------
    

    Es scheint!!!! erstmal besser zu laufen.
    Dazu muss ich aber noch testen.

    Aber vielleicht fällt jemand was dazu ein? Damit ich das ev. verstehe.

    ... Und wieder einige Zeit später:
    Es hat wohl doch nicht zum erhofften Ziel geführt.
    Weiss jemand Rat?
    Kann auch der Debugger dran schuld sein, da ich gerade ausschliesslich damit teste?



  • @elmut19
    Jetzt erschlägst du mich ein wenig. 🙂

    Einen Schritt zurück. Ist dir der Fehler klar? Hast du ihn behoben und getestet?

    Du könntest beispielsweise auch folgendes ausprobieren

    static int scan2ascii(DWORD scancode, USHORT* result)
    {
    	static HKL layout = GetKeyboardLayout(0);
    	static BYTE State[256];
       
            *result = 0;  // <-- Rückgabewert auf einen definierten Zustand setzen
    	if (GetKeyboardState(State) == FALSE)
    		return -1;
    
    	UINT vk = MapVirtualKeyEx(scancode, 1, layout);
    	return ToAsciiEx(vk, scancode, State, result, 0, layout);
    }
    

    Du musst aber trotzdem auf den Rückgabewert von ToAsciiEx() achten!



  • @Quiche-Lorraine
    Guten Morgen Quiche-Lorraine.

    Vielen Dank nochmals, dass Du mir hilfst.
    Und nein! Der Fehler ist mir leider noch nicht klar.

    Das mit dem "result" muss eigentlich alles OK sein, da das in der aufrufenden Methode, als lokale Var, mit "0" initialisiert wird (USHORT result = 0;).
    Diese Methode wird ja für jeden Tastendruck neu aufgerufen. Und für Fehler im "State" habe ich zur Unterscheidung extra die "-1" genommen.

    Ich habe auch auf diesen Rückgabewert immer angesehen.
    Eine "2" ist mir dabei noch nie aufgefallen.

    Dass der Fehler sporadisch auftritt macht es leider sehr schwer.
    Mal geht es ewig lange gut und dann schlägt´s wieder zu.

    Ich würde ganz gerne verstehen, was im Hintergrund so alles passieren kann.

    Nun gerade wieder der Fall:
    Zunächst, wie es hätte sein sollen, im TRACE:

    • DllHookLow0: nCode=0, vkCode=187, scanCode=27
    • MainFrm:OnHookLow: vkCode=187, scanCode=27 (lParam=27), scan2ascii Rückgabe: 1, result: 43
    • MainFrm:OnHookLow: ********* Inhalt ergänzen +, (wParam: 187, lParam: 27 --> ZeichenCode: +)

    Und so siehts wieder mit Fehler aus:
    "Rückgabe" ist das, was "ToAsciiEx(..)" zurück gibt.

    • DllHookLow: nCode=0, vkCode=187, scanCode=27
    • MainFrm:OnHookLow: vkCode=187, scanCode=27 (lParam=27), scan2ascii Rückgabe: 1, result: 29
    • MainFrm:OnHookLow: ######### Nicht druckbares Zeichen, nicht eingefügt! (wParam: 187, lParam: 27 --> ZeichenCode: )

    Hinzu kommt zu der ganzen Sache:
    Wenn ich nun explizit eine beliebigen Editor fokusiere (Notepad++, z.B.) und dort auch meinen gescannten Barcode rein lade (und nicht in meine Anwendung oder den Desktop), dann scheint der Fehler noch nie aufgetreten zu sein.
    Jedenfalls kann ich mich nicht dran erinnern, dass es einmal so gewesen wäre.



  • Inzwischen habe ich mir auch "State" und "layout" angesehen:

    static int scan2ascii(DWORD scancode, USHORT* result)
    {
    	static HKL layout = GetKeyboardLayout(0);
    	static BYTE State[256];
    
    	if (GetKeyboardState(State) == FALSE)
    		return -1;
    
    	UINT vk = MapVirtualKeyEx(scancode, 1, layout);
    
    	// HP: debug only -------------------------------------------------------------
    	int i = 0;
    	CString b;
    	CString xxx = _T("");
    	for (i = 0; i < 256; i++) {
    		b.Format(_T("%d"), State[i]);
    		xxx += b;
    	}
    	// HP: Ende debug only --------------------------------------------------------
    
    	TRACE(_T("++++++++ scan2ascii: State: %s \n"), xxx);
    	TRACE(_T("++++++++ scan2ascii: vkCode=%d, scanCode=%d, Layout: high=%d, low=%d **********################## *****************\n"), vk, scancode, HIBYTE(layout), LOBYTE(layout));
    
    	return ToAsciiEx(vk, scancode, State, result, 0, layout);
    }
    

    "layout" ist immer gleich. Und ich nehme an, daher auch korrekt.
    "State" verhält sich aber beim Auftreten eines solchen Fehlers merkwürdig.
    Während bei fehlerfreiem Empfang der Daten immer alles gleich ist, ändert sich was am "State" um einen Fehler herum.

    Diesmal wurde das 4. Zeichen, eigentlich eine "9", zu einem ")" gemacht.

    Zunächst ein paar fehlerfreie Zeichen:

    • DllHookLow: nCode=0, vkCode=49, scanCode=2
    • ++++++++ scan2ascii: State: 0000000001000000010000000000000000000000000000000010110111000000011101101100100000011001100000000000000000000000000000000000000000000000000000001000000000000000111000000000000000000000000101100000000000000000000000000000000000000000000000001001001000010000
    • ++++++++ scan2ascii: vkCode=67, scanCode=46, Layout: high=4, low=7 **********################## *****************
    • MainFrm:OnHookLow: vkCode=67, scanCode=46 (lParam=46), scan2ascii Rückgabe: 1, result: 99 **********################## *****************
    • MainFrm:OnHookLow: ********* Zeichen mit Shift hinzufügen +C, 67, 46 --> C
    • DllHookLow: nCode=0, vkCode=57, scanCode=10
    • ++++++++ scan2ascii: State: 0000000001000000010000000000000000000000000000000010110111000000011101101100100000011001100000000000000000000000000000000000000000000000000000001000000000000000111000000000000000000000000101100000000000000000000000000000000000000000000000001001001000010000
    • ++++++++ scan2ascii: vkCode=49, scanCode=2, Layout: high=4, low=7 **********################## *****************
    • MainFrm:OnHookLow: vkCode=49, scanCode=2 (lParam=2), scan2ascii Rückgabe: 1, result: 49 **********################## *****************
    • MainFrm:OnHookLow: ********* Inhalt ergänzen +C1, (wParam: 49, lParam: 2 --> ZeichenCode: 1)
    • DllHookLow: nCode=0, vkCode=49, scanCode=2
    • ++++++++ scan2ascii: State: 0000000001000000010000000000000000000000000000000010110111000000011101101100100000011001100000000000000000000000000000000000000000000000000000001000000000000000111000000000000000000000000101100000000000000000000000000000000000000000000000001001001000010000
    • ++++++++ scan2ascii: vkCode=57, scanCode=10, Layout: high=4, low=7 **********################## *****************
    • MainFrm:OnHookLow: vkCode=57, scanCode=10 (lParam=10), scan2ascii Rückgabe: 1, result: 57 **********################## *****************
    • MainFrm:OnHookLow: ********* Inhalt ergänzen +C19, (wParam: 57, lParam: 10 --> ZeichenCode: 9)
    • DllHookLow: nCode=0, vkCode=54, scanCode=7
    • ++++++++ scan2ascii: State: 0000000001000000010000000000000000000000000000000010110111000000011101101100100000011001100000000000000000000000000000000000000000000000000000001000000000000000111000000000000000000000000101100000000000000000000000000000000000000000000000001001001000010000
    • ++++++++ scan2ascii: vkCode=49, scanCode=2, Layout: high=4, low=7 **********################## *****************
    • MainFrm:OnHookLow: vkCode=49, scanCode=2 (lParam=2), scan2ascii Rückgabe: 1, result: 49 **********################## *****************
    • MainFrm:OnHookLow: ********* Inhalt ergänzen +C191, (wParam: 49, lParam: 2 --> ZeichenCode: 1)

    Nun mit Fehler: (gleicher Ausschnitt)

    • DllHookLow: nCode=0, vkCode=49, scanCode=2
    • ++++++++ scan2ascii: State: 010000001000000011000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000001100000000000000000000000012900000000000000000000000000000000000000000000000000001001001000010000
    • ++++++++ scan2ascii: vkCode=49, scanCode=2, Layout: high=4, low=7 **********################## *****************
    • MainFrm:OnHookLow: vkCode=49, scanCode=2 (lParam=2), scan2ascii Rückgabe: 1, result: 49 **********################## *****************
    • MainFrm:OnHookLow: ********* Inhalt ergänzen +C1, (wParam: 49, lParam: 2 --> ZeichenCode: 1)
    • DllHookLow: nCode=0, vkCode=57, scanCode=10
    • ++++++++ scan2ascii: State: 01000000100000001281000000000000000000000000000000000000000000000000110000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000012911000000000000000000000000100000000000000000000000000000000000000000000000000001001001000010000
    • ++++++++ scan2ascii: vkCode=57, scanCode=10, Layout: high=4, low=7 **********################## *****************
    • MainFrm:OnHookLow: vkCode=57, scanCode=10 (lParam=10), scan2ascii Rückgabe: 1, result: 41 **********################## *****************
    • MainFrm:OnHookLow: ********* Inhalt ergänzen +C1), (wParam: 57, lParam: 10 --> ZeichenCode: ))
    • DllHookLow: nCode=0, vkCode=49, scanCode=2
    • ++++++++ scan2ascii: State: 010000001000000001000000000000000000000000000000010000000129000000001100000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000111000000000000000000000000100000000000000000000000000000000000000000000000000001001001000010000
    • ++++++++ scan2ascii: vkCode=49, scanCode=2, Layout: high=4, low=7 **********################## *****************
    • MainFrm:OnHookLow: vkCode=49, scanCode=2 (lParam=2), scan2ascii Rückgabe: 1, result: 49 **********################## *****************
    • MainFrm:OnHookLow: ********* Inhalt ergänzen +C1)1, (wParam: 49, lParam: 2 --> ZeichenCode: 1)

    Wenn alles korrekt läuft, scheint der "State", zumindest innerhalb der Zeichenkette, identisch zu bleiben.
    Bei einer neuen Übertragung kann sich das wohl ändern.

    In dem Fehlerfall hat sich was an dieser Bit-Kette um den Fehler herum geändert.
    Eigentlich sollten ja nur "1" und "0" drin sein. Dort gibts aber merkwürdige Zahlen!

    Was kann mir denn das sagen?
    "GetKEyboardState(..)" gibt mir ja schliesslich ein TRUE zurück.
    Kann ich da noch mehr auswerten?



  • Wieso sollen da nur 0 oder 1 drin stehen, s. GetKeyboardState?
    Das Highbit (128) ist gesetzt, wenn die Taste gedrückt ist, das Lowbit (1) gilt nur für Toggle-Keys.

    Ich frage mich jedoch, brauchst du überhaupt den aktuellen Status aller Tasten für die Umrechnung nach Ascii?
    Der Barcodeleser sollte doch unabhängig von gleichzeitigen Nutzereingaben funktionieren.
    Probiere mal einfach ein leeres State-Array (also alles Nullen) zu übergeben (d.h. den Aufruf von GetKeyboardState(State) wegzulassen).



  • @Th69
    Danke Th69.

    Ich habe es mal ausprobiert, den "State" mit Nullen zu initialisieren.
    Jedenfalls hat es ein korrektes Ergebnis geliefert.
    Jetzt muss ich das Zeug noch eine Weile testen.

    Ich hoffe, das war es.
    Ich melde mich wieder am Freitag zurück.
    Aber vielen Dank!

    .... Und Freitag:
    Hatte nur für 3 kurze Versuche Zeit zum Testen.
    Diese hatten allerdings funktioniert.



  • Bis heute hatte ich keine Ausfälle dieser Art mehr.
    Den "KeyboardState" auf "NULL" zu setzen hat dann wohl geholfen.
    Vielen Dank nochmal Th69!


Log in to reply