Rätselhaftes Verhalten von SendInput auf unterschiedlichen Rechnern



  • Ich beschäftige mich zur Zeit ein wenig mit der Möglichkeit Zeichenketten an einen fremden Prozess zu schicken. Dazu benutzte ich die Funktion SendInput in der folgenden Art und Weise.

    bool SendStringToWindow(HWND hTargetWnd, DWORD TargetProcessID, DWORD MyThreadID, bool c)
    {
      INPUT Input[10];
      size_t Size = 0;
    
      AttachThreadInput(MyThreadID, TargetProcessID, TRUE);
      SetForegroundWindow(hTargetWnd);
      ZeroMemory(Input, sizeof(Input));
    
      Input[Size].type = INPUT_KEYBOARD;
      Input[Size].ki.wVk = VK_SHIFT;
      Input[Size].ki.wScan = VirtualKey2ScanCode(VK_CAPITAL);		/// Nix anderes als MapVirtualKey()
      Size++; 
      if (c)
      {
    	SendInput(Size, Input, sizeof(INPUT));
    	if (SendInputDelay > 0)
          Sleep(SendInputDelay);
    	Size = 0;
      }  
      Input[Size].type = INPUT_KEYBOARD;
      Input[Size].ki.wVk = VkKeyScanEx('k', KeyboardLayout);
      Size++;  
      if (c)
      {
    	SendInput(Size, Input, sizeof(INPUT));
    	if (SendInputDelay > 0)
          Sleep(SendInputDelay);
    	Size = 0;
      }  
      Input[Size].type = INPUT_KEYBOARD;
      Input[Size].ki.wVk = VkKeyScanEx('k', KeyboardLayout);
      Input[Size].ki.dwFlags = KEYEVENTF_KEYUP;
      Size++;
      if (c)
      {
    	SendInput(Size, Input, sizeof(INPUT));
    	if (SendInputDelay > 0)
          Sleep(SendInputDelay);
    	Size = 0;
      }  
      Input[Size].type = INPUT_KEYBOARD;
      Input[Size].ki.wVk = VkKeyScanEx('s', KeyboardLayout);
      Size++;  
      if (c)
      {
    	SendInput(Size, Input, sizeof(INPUT));
    	if (SendInputDelay > 0)
          Sleep(SendInputDelay);
    	Size = 0;
      }  
      Input[Size].type = INPUT_KEYBOARD;
      Input[Size].ki.wVk = VkKeyScanEx('s', KeyboardLayout);
      Input[Size].ki.dwFlags = KEYEVENTF_KEYUP;
      Size++;  
      if (c)
      {
    	SendInput(Size, Input, sizeof(INPUT));
    	if (SendInputDelay > 0)
          Sleep(SendInputDelay);
    	Size = 0;
      }    
      Input[Size].type = INPUT_KEYBOARD;
      Input[Size].ki.wVk = VK_SHIFT;
      Input[Size].ki.wScan = VirtualKey2ScanCode(VK_CAPITAL);
      Input[Size].ki.dwFlags = KEYEVENTF_KEYUP;
      Size++;
      if (c)
      {
    	SendInput(Size, Input, sizeof(INPUT));
    	if (SendInputDelay > 0)
          Sleep(SendInputDelay);
    	Size = 0;
      }
      else // Macht die Sache noch schlimmer
      {
    	SendInput(Size, Input, sizeof(INPUT)); 
    	if (SendInputDelay > 0)
    	  Sleep(SendInputDelay);
      }
      AttachThreadInput(MyThreadID, TargetProcessID, FALSE);
      if (AttachThreadInputDelay > 0)
        Sleep(AttachThreadInputDelay);
      return Success;
    }
    

    Auf den Rückgabewert von SendInput achte ich natürlich, aber der lieferte bisher immer nur die Anzahl der Zeichen (Size) zurück. Ebenso achte ich auf den Rückgabewert von AttachThreadInput, aber auch der funktioniert immer.

    Das Problem an dem Code ist das abhängig von den Delay's sowie dem Parameter c man ganz unterschiedliche Verhalten vom SendInput beobachten kann. Habe ich die Delay's so eingestellt das sie auf meinem Rechner funktionieren, funktioniert der Funktionsausruf leider nicht immer auf den anderen Rechnern. Ich konnte dann beobachten dass manchmal das erste Zeichen nicht gesendet wurde und manchmal das letzte Zeichen nicht (Codebeispiel). Meistens ist das Verhalten dann so dass die Funktion 4-5 Mal ohne Probleme klappt und dann einmal nicht.

    Mittels Spy++ und Co. habe ich mir mal angeschaut was der fremde Prozess geschickt bekommt.
    Wenn funktioniert hat sieht es folgendermaßen aus:

    WM_KEYDOWN      mVirtKey: VK_SHIFT  cRepeat:1  ScanCode:2A  fExtended:0  fAltDown:0  fRepeat:0  fUp:0
    WM_KEYDOWN      mVirtKey: 'K'       cRepeat:1  ScanCode:00  fExtended:0  fAltDown:0  fRepeat:0  fUp:0
    WM_KEYUP        mVirtKey: 'K'       cRepeat:1  ScanCode:00  fExtended:0  fAltDown:0  fRepeat:1  fUp:1
    WM_KEYDOWN      mVirtKey: 'S'       cRepeat:1  ScanCode:00  fExtended:0  fAltDown:0  fRepeat:0  fUp:0
    WM_KEYUP        mVirtKey: 'S'       cRepeat:1  ScanCode:00  fExtended:0  fAltDown:0  fRepeat:0  fUp:1
    WM_KEYUP        mVirtKey: VK_SHIFT  cRepeat:1  ScanCode:2A  fExtended:0  fAltDown:0  fRepeat:0  fUp:1
    

    Und so sieht es aus wenn es nicht funktioniert hat:

    WM_KEYDOWN      mVirtKey: VK_SHIFT  cRepeat:1  ScanCode:2A  fExtended:0  fAltDown:0  fRepeat:0  fUp:0
    WM_KEYDOWN      mVirtKey: 'K'       cRepeat:1  ScanCode:00  fExtended:0  fAltDown:0  fRepeat:0  fUp:0
    WM_KEYUP        mVirtKey: 'K'       cRepeat:1  ScanCode:00  fExtended:0  fAltDown:0  fRepeat:0  fUp:1
    WM_KEYDOWN      mVirtKey: 'S'       cRepeat:1  ScanCode:00  fExtended:0  fAltDown:0  fRepeat:0  fUp:0
    WM_LBUTTONDWON  ...
    

    Die WM_LBUTTONDWON kommt dadurch Zustände dass das Program, an denen ich die Daten schicke, einen Mausklick simuliert sobald ich die Taste S drücke. Was mir aufgefallen ist dass sich die fRepeat Flag's bei der Nachricht WM_KEYUP mVirtKey: 'K' unterscheiden. Nun ja gemäß MSDN ist das Flag im fehlerfreien Fall richtig gesetzt worden, im fehlerbehafteten Fall aber nicht. Warum dem so ist, habe ich keine Ahnung ebenso warum dies nur auf gewissen Rechnern passiert.

    Hat jemand eine Ahnung was das Problem dahinter sein könnte ???

    Nachtrag:
    Ich habe ein Workaround gefunden, was die Probleme ein wenig abmildert so das dass Verhalten nicht mehr ganz so schlimm von den Delay's abhängt. Und zwar habe ich meine Funktion SendStringToWindow() immer in einer WndProc() aufgerufen sobald ich einen Button gedrückt habe.

    LRESULT MyWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
    {
    	switch(uMsg)
    	{
    	case WM_COMMAND:
    	{
    		switch (LOWORD(wParam))
    		{
    		case IDC_BUTTON_SENDSTRING:
    			SendStringToWindow();
    			return 0;
    		}
    	}
    	}
    	return 0;
    }
    

    Wenn ich nun hingehe und die Funktion SendStringToWindow() in einen eigenen Thread aufrufe, verschwinden so einige Probleme auf den Rechnern. Und siehe da, die Delay's können auf einmal wesentlich niedriger eingestellt werden ohne dass es zu Problemen kommt. Nichtsdestotrotz bleibt mir das Verhalten von SendInput rätselhaft.



  • Trotz meines Workaround's taucht der Fehler bei gewissen Programmen auf gewissen Rechnern immer noch auf. Es hat den Anschein dass gerade bei wiederholten Tastenanschläge mit Ctrl, Alt, Shift, Tab das Problem auftaucht.

    Beispielsweise kommt bei Tab,Tab,Strg+c (Strg+c = Taste Strg drücken, Taste c drücken, Taste c loslassen, Taste Strg loslassen) öfters Tab,Tab,Strg,c an. Oder wie in meinem Codebeispiel wird aus Shift+k+s die Kombination Shift+k,s. Dummerweise kommt bei Shift+k,Shift+s auch des öfteren Shift+k,s an. Gefühlsmäßig habe ich den Verdacht dass beim Senden von Tastenkürzel die Zeit, in der die Tasten Ctrl, Alt, Shift gedrückt sind, manchmal zu klein ist und auch manchmal zu groß ist.

    Ich werde mir wohl den Quelltext von Autohotkey mal näher anschauen müssen, da ich dort die Probleme nicht finden konnte. Aber so auf dem ersten Blick senden die die Zeichen nicht immer mit SendInput/keybd_event sondern auch mit WM_KEYDOWN/WM_KEYUP, obwohl ja davon abgeraten wird.


Anmelden zum Antworten