Autor Thema: point & click  (Gelesen 13006 mal)

Arnold

  • Member
Re: point & click
« Antwort #15 am: Montag, 10. Oktober 2011, 10:33 »
Hallo,

inspiriert von diesem Thema habe ich an meinem Windows-Rechner den bereits vorhandenen Programmaufruf ersetzt durch ein selbst geschreibenes C-Programm (anstelle des GUILE-Script-Starts den Lilypond in die Registry geschreiben hat).
Das Fenster zu finden, in dem die gesuchte Datei bereits geöffnet ist, und dieses in den Vordergrung zu holen, war ein Kinderspiel - den WindowsClass-Name des von mir benutzten Notepad ausfindig machen, dann alle diese Fenster nach einer passenden Kopfzeile absuchen.
Viel länger habe ich gesucht, bis ich den Sprung an die gewünschte Stelle hingebracht habe.
Ach ja, die LYW-Spezifika kommen aus der Zeit, da ich in einem Betriebssystem keine UTF8-Editor hatte, sondern stattdessen meine Dateien in UTF-16 geschrieben habe, per Programm die Ausgangsdatei und alle über \include angezogenen Dateien in UTF-8 ausgeleitet bzw. aktualisiert habe. Diese Quasi-Make-Prozedur konnte natürlich das Änderungsdatum aller Dateien auswerten und entscheiden, ob die PDF-Datei bereits aktuell ist. Heute liegt die Haupaufgabe darin, das betroffene Acrobat-Reader-Fenster zu schließen.
Aber jetzt erst einmal wieder zum Spung in den Texteditor:
/* LilyGotoEditor.c */
/*
 * Originally compiled with the 'free' Borland C++ command line compiler
 * which is: Borland C++ 5.5 for Win32 Copyright (c) 1993, 2000 Borland
 *           Turbo Incremental Link 5.00 Copyright (c) 1997, 2000 Borland
 *
 * command line: bcc32 -We LilyGotoEditor.c
 */

#define UNICODE

#include <windows.h>
#include <stdio.h>
#include <wchar.h>
#include <io.h>
#include <sys/types.h>
#include <sys/stat.h>


#define MyClassName L"LilyGotoEditorInquiryWin"
int ARGC;
wchar_t **ARGV;
int Tiefe = 1, Cntr, TCntr, VisibleOnly = 1;
int ScanModus = 0;
int wirdBeendet = 0;
int SuchTitelGrossKleinBeachten;
HWND GefundenesFenster = NULL;
wchar_t *SuchKlasse = NULL, *SuchTitel = NULL;
HINSTANCE myInst, myPrevInst;

int OrdinaryFileExists(wchar_t *F)
{ struct _stat st;
  if (_waccess(F, 4) < 0) return 0;
  if (_wstat(F, &st) < 0) return 0;
  if (st.st_mode & S_IFREG) return 1;
  return 0; // e.g. is a directory
}

BOOL CALLBACK MainWinEnumFunct(HWND hwnd, LPARAM lParam)
{ int ct, visi;
  wchar_t WindowTitle[270], ClassName[270];
  TCntr++;
  if (!IsWindow(hwnd)) return FALSE;
  WindowTitle[0] = ClassName[0] = 0;
  GetWindowText(hwnd, WindowTitle, 269);
  GetClassName(hwnd, ClassName, 269);
  WindowTitle[269] = 0; ClassName[269] = 0;
  visi = IsWindowVisible(hwnd);
  if (VisibleOnly && (!visi)) return TRUE;
  Cntr++;
  if (ScanModus) {
    for (ct = 0; ct < lParam; ct++) fprintf(stderr, "   ");
    fprintf(stderr, "[0x%.8lx]%s '%S' \"%S\"\n",
      (long) hwnd, VisibleOnly ? "" : visi ? " visib" : " invis",
      ClassName, WindowTitle);
  } else { /* SuchModus */
    if (!GefundenesFenster) {
      int passt = 1;
      if (SuchKlasse != NULL) {
        if (wcscmp(SuchKlasse, ClassName)) passt = 0;
      }
      if (SuchTitel != NULL) {
        if (SuchTitelGrossKleinBeachten) {
          if (wcscmp(SuchTitel, WindowTitle)) passt = 0;
        } else {
          if (_wcsicmp(SuchTitel, WindowTitle)) passt = 0;
        }
      }
      if (passt) GefundenesFenster = hwnd;
    }
  }
  /* sleep(1); */
  if (hwnd == NULL) return FALSE;
  if (Tiefe - 1 > lParam)
    EnumChildWindows(hwnd, MainWinEnumFunct, lParam + 1);
  return TRUE;
}

HWND SucheUnterFenster(HWND fenster, wchar_t *WindowClass, wchar_t *WindowTitle, int CaseSensitive)
{ ScanModus = 0;
  SuchKlasse = WindowClass; SuchTitel = WindowTitle;
  SuchTitelGrossKleinBeachten = CaseSensitive;
  GefundenesFenster = NULL;
  TCntr = Cntr = 0;
  EnumChildWindows(fenster, MainWinEnumFunct, 0L);
  return(GefundenesFenster);
}

HWND SucheFenster(wchar_t *WindowClass, wchar_t *WindowTitle, int CaseSensitive)
{ ScanModus = 0;
  SuchKlasse = WindowClass; SuchTitel = WindowTitle;
  SuchTitelGrossKleinBeachten = CaseSensitive;
  GefundenesFenster = NULL;
  TCntr = Cntr = 0;
  EnumWindows(MainWinEnumFunct, 0L);
  return(GefundenesFenster);
}

void ZaehleFenster(void)
{ ScanModus = 1;
  fprintf(stderr, "Tiefe = %d\n", Tiefe);
  TCntr = Cntr = 0;
  EnumWindows(MainWinEnumFunct, 0L);
  fprintf(stderr, "%d von %d Fenster bei Tiefe %d gelistet\n",
    Cntr, TCntr, Tiefe);
}


void SplitArg(wchar_t *arg, int *Line, int *Char, int *Column,
              wchar_t *Basename, wchar_t *FilePath, wchar_t *Lead)
{ int m, lg, ct, NameStart, NameEnd;
  NameStart = NameEnd = -1;
  Basename[0] = FilePath[0] = Lead[0] = 0;
  *Line = *Char = *Column = 0;
  m = 0; for (ct = wcslen(arg) - 1; ct >= 0; ct--) {
    if (arg[ct] == L':') {
      ++m;
      switch(m) {
        case 1: swscanf(&(arg[ct + 1]), L"%d", Column); break;
        case 2: swscanf(&(arg[ct + 1]), L"%d", Char); break;
        case 3: swscanf(&(arg[ct + 1]), L"%d", Line);
                NameEnd = ct; ct = -9;
      }
    }
  }
  for (ct = 0; (ct >= 0) && arg[ct]; ct++) {
    Lead[ct] = arg[ct]; Lead[ct + 1] = 0;
    if (arg[ct] == L':') {
      NameStart = ct + 1;
      if (arg[ct + 1] == L'/') {
        ++NameStart;
        wcscat(Lead, L"/");
        if (arg[ct + 2] == L'/') {
          ++NameStart;
          wcscat(Lead, L"/");
        }
      }
      ct = -9;
    }
  }
  lg = NameEnd - NameStart;
  if (lg > 0) {
    wcsncpy(FilePath, &(arg[NameStart]), lg);
    FilePath[lg] = 0;
    for (ct = 0; FilePath[ct]; ct++)
      if (FilePath[ct] == L'/') FilePath[ct] = L'\\';
  }
  for (ct = wcslen(FilePath) - 1; ct >= 0; ct--) {
    switch(FilePath[ct]) {
      case L'/': case L'\\': case L':':
        wcscpy(Basename, &(FilePath[ct + 1]));
        ct = -9;
    }
  }
  if (!Basename[0]) wcscpy(Basename, FilePath);
}

LRESULT CALLBACK
ShowInputs(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
  switch(message) {
    case WM_SYSKEYDOWN:
      fprintf(stderr, "\nWM_SYSKEYDOWN, wParam = 0x%.4x, lParam = 0x%.8lx", wParam, lParam);
      break;
    case WM_SYSKEYUP:
      fprintf(stderr, "\nWM_SYSKEYUP, wParam = 0x%.4x, lParam = 0x%.8lx", wParam, lParam);
      break;
    case WM_SYSCHAR:
      fprintf(stderr, "\nWM_SYSCHAR, wParam = 0x%.4x, lParam = 0x%.8lx", wParam, lParam);
      break;
    case WM_SYSDEADCHAR:
      fprintf(stderr, "\nWM_SYSDEADCHAR, wParam = 0x%.4x, lParam = 0x%.8lx", wParam, lParam);
      break;
    case WM_KEYDOWN:
      fprintf(stderr, "\nWM_KEYDOWN, wParam = 0x%.4x, lParam = 0x%.8lx", wParam, lParam);
      break;
    case WM_KEYUP:
      fprintf(stderr, "\nWM_KEYUP, wParam = 0x%.4x, lParam = 0x%.8lx", wParam, lParam);
      break;
    case WM_CHAR:
      fprintf(stderr, "\nWM_CHAR, wParam = 0x%.4x, lParam = 0x%.8lx", wParam, lParam);
      break;
    case WM_DEADCHAR:
      fprintf(stderr, "\nWM_DEADCHAR, wParam = 0x%.4x, lParam = 0x%.8lx", wParam, lParam);
      break;
    case WM_DESTROY:
      PostQuitMessage(0);
      wirdBeendet = 1;
      return 0;
  }
  return(DefWindowProc(hwnd, message, wParam, lParam));
}

void WatchKeyCodes(void)
{ MSG msg;
  int to;
  to = 100;
  fprintf(stderr, "\n\nSie haben jetzt ein paar Sekunden Zeit,\num Tastatureingaben im Fang-Fenster zu analysieren ...");
  do {
    if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
      TranslateMessage(&msg);
      DispatchMessage(&msg);
      to = 50;
    } else {
      --to; Sleep(50);
    }
  } while (to);
}

void ErzeugePruefFenster(void)
{ WNDCLASS wndclass;

  if (!myPrevInst) {
    wndclass.style = CS_HREDRAW | CS_VREDRAW;
    wndclass.lpfnWndProc = ShowInputs;
    wndclass.cbClsExtra = 0;
    wndclass.cbWndExtra = 0;
    wndclass.hInstance = myInst;
    wndclass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
    wndclass.hCursor = LoadIcon(NULL, IDC_ARROW);
    wndclass.hbrBackground = (HBRUSH) COLOR_WINDOW - 1;
    wndclass.lpszMenuName = NULL;
    wndclass.lpszClassName = MyClassName;
    RegisterClass(&wndclass);
  }
  GefundenesFenster = CreateWindow(MyClassName,
                                   L"LilyGotoEditor - Tastenfänger",
                                   WS_OVERLAPPEDWINDOW,
                                   CW_USEDEFAULT, CW_USEDEFAULT,
                                   250, 80,
                                   NULL, NULL,
                                   myInst,
                                   NULL);
  fprintf(stderr, "\nCreated Window 0x%.8p\n", GefundenesFenster);
  ShowWindow(GefundenesFenster, SW_RESTORE);
  UpdateWindow(GefundenesFenster);
}

void SetupHelper(int level)
{ char Zeile[100];
  AllocConsole();
  fprintf(stderr, "Scanning Windows ...\n");
  Tiefe = level;
  while (1) {
    if (wirdBeendet) { GefundenesFenster = NULL; wirdBeendet = 0; }
    if (GefundenesFenster == NULL) ErzeugePruefFenster();
    ZaehleFenster();
    SetFocus(GefundenesFenster);
    WatchKeyCodes();
    fprintf(stderr, "\nBitte RETURN um Scan zu wiederholen ..");
    fprintf(stderr, "\n(Fenster schliessen um zu beenden)    ");
    fgets(Zeile, 100, stdin);
  }
}

void SendKeyStrokeInputs(wchar_t *s)
{ INPUT inputToSend[100];
  wchar_t cmd[32];
  long lParam, wParam;
  int lg, ct;
  wchar_t info[1000];
  // swprintf(info, L"SendKeyStrokeInputs(\"%s\")\n", s);
  // fprintf(stderr, "SendKeyStrokeInputs(\"%S\")\n", s);
  lg = ct = 0;
  while (s[ct] && (lg < 100)) {
    if (s[ct] == L'(') {
      lParam = wParam = 0; cmd[0] = 0;
      swscanf(&(s[ct + 1]), L"%s%x%x", cmd, &wParam, &lParam);
      if (!_wcsicmp(cmd, L"KEYDOWN")) {
        inputToSend[lg].ki.dwFlags = 0;
        goto keyupdown;
      } else if (!_wcsicmp(cmd, L"KEYUP")) {
        inputToSend[lg].ki.dwFlags = KEYEVENTF_KEYUP;
keyupdown:
        inputToSend[lg].ki.wVk = wParam;
        inputToSend[lg].ki.wScan = (lParam & 0x1ff0000) >> 16;
        inputToSend[lg].ki.time = 0;
        inputToSend[lg].ki.dwExtraInfo = 0;
        ++lg;
      }
      while (s[ct] && (s[ct] != L')')) ++ct;
    }
    if (s[ct]) ct++;
  }

  if (lg) SendInput(lg, inputToSend, sizeof(INPUT));
  // fprintf(stderr, "%d Input-Aktionen gesendet ...\n", lg);
  // swprintf(&(info[wcslen(info)]), L"\n%d Input-Aktionen waren im Sendepuffer", lg);
  for (ct = 0; ct < lg; ct++) {
    swprintf(&(info[wcslen(info)]), L"\n { %ld, %lx, %lx }",
      (long) inputToSend[ct].ki.dwFlags,
      (long) inputToSend[ct].ki.wVk,
      (long) inputToSend[ct].ki.wScan);
  }
  // MessageBox(NULL, info, L"TRACE", MB_ICONINFORMATION | MB_OK);
}

void SendUnicodeKeyInput(wchar_t *s)
{ INPUT inputToSend[100];
  int lg, ct;
  lg = wcslen(s);
  if (lg > 100) lg = 100;
  if (!s) return;
  for (ct = 0; ct < lg; ct++) {
    inputToSend[ct].type = INPUT_KEYBOARD;
    inputToSend[ct].ki.wVk = 0;
    inputToSend[ct].ki.wScan = s[ct];
    inputToSend[ct].ki.dwFlags = KEYEVENTF_UNICODE;
    inputToSend[ct].ki.time = 0;
    inputToSend[ct].ki.dwExtraInfo = 0;
  }
  SendInput(lg, inputToSend, sizeof(INPUT));
}

void LilyGotoEditor(wchar_t *arg)
{ int Line, Char, Column;
  int m, ct, NameStart, NameEnd;
  int erg1, /* erg2, */ erg3 /*, erg4 */ ;
  wchar_t Basename[MAX_PATH], FilePath[MAX_PATH], Lead[MAX_PATH];
  HWND fenster, fenster2, fenster3;
  wchar_t FensterTitel[MAX_PATH + 100];

  // 1. Die Aufrufzeile entschlüsseln ...
  SplitArg(arg, &Line, &Char, &Column, Basename, FilePath, Lead);
  /* { wchar_t InfoText[1000];
    swprintf(InfoText, L"Aufrufparameter = »%s«", arg);
    swprintf(&(InfoText[wcslen(InfoText)]), L"\nLine = %d\nChar = %d\nColumn = %d", Line, Char, Column);
    swprintf(&(InfoText[wcslen(InfoText)]), L"\nBasename = »%s«\nFilePath = »%s«\nLead = »%s«",
      Basename, FilePath, Lead);
    MessageBox(NULL, InfoText, L"LilyGotoEditor", MB_ICONINFORMATION | MB_OK);
  } */
  if (Line <= 0) {
    wchar_t InfoText[1000];
    swprintf(InfoText, L"In der Aufrufzeile (»%s«) konnte keine gültige Zeilennummer gefunden werden!\r\n"
      L"\r\nAlso kann auch nicht dorthin gesprungen werden.", arg);
    MessageBox(NULL, InfoText, L"LilyGotoEditor", MB_ICONERROR | MB_OK);
    return;
  }

  // 2. Suche, ob schon eine Editor-Fenter offen ist - ich benutze »Notepad«
  swprintf(FensterTitel, L"%sw - Editor", Basename);                    // LYW - special
  if ((fenster = SucheFenster(L"Notepad", FensterTitel, 0)) == NULL) {  // LYW - special
    swprintf(FensterTitel, L"%s - Editor", Basename);
    fenster = SucheFenster(L"Notepad", FensterTitel, 0);
  }                                                                     // LYW - special
  /* { wchar_t InfoText[1000];
    swprintf(InfoText, L"FensterTitel = »%s«\nFenster = 0x%p", FensterTitel, fenster);
    MessageBox(NULL, InfoText, L"LilyGotoEditor", MB_ICONINFORMATION | MB_OK);
  } */

  // 3. Falls kein Editor-Fenster offen ist, die Datei öffnen (und Tschüß, klick bitte nochmal)
  if (fenster == NULL) {
    wchar_t InfoText[1000];
    swprintf(InfoText, L"Datei »%s« ist offenbar noch nicht mit »Notepad« geöffnet!\r\n\r\n"
                       L"Bitte die Datei\r\n»%s«\r\nöffnen (z. Bsp. durch OK) und dann die Aktion wiederholen.",
      Basename, FilePath);
    if (MessageBox(NULL, InfoText, L"LilyGotoEditor", MB_ICONEXCLAMATION | MB_OKCANCEL) == IDOK) {
      swprintf(InfoText, L"%sw", FilePath);
      if (OrdinaryFileExists(InfoText))                             // LYW - special
        swprintf(InfoText, L"start notepad \"%sw\"", FilePath);     // LYW - special
       else {                                                       // LYW - special
        if (!OrdinaryFileExists(FilePath)) {
          swprintf(InfoText, L"Leider keine Datei »%s« gefunden!\r\n\r\nAlso kann ich sie auch nicht öffnen.", FilePath);
          MessageBox(NULL, InfoText, L"LilyGotoEditor", MB_ICONERROR | MB_OK);
          return;
        }
        swprintf(InfoText, L"start notepad \"%s\"", FilePath);
      }                                                             // LYW - special
      _wsystem(InfoText);
    }
    return;
  }

  // 4. Das Editorfenster i_r_g_e_n_d_w_i_e nach vorne bringen
  /* erg4 = */ ShowWindow(fenster, SW_RESTORE);
  erg1 = SetForegroundWindow(fenster);
  /* erg2 = */ SetWindowPos(fenster, HWND_TOP, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW);
  erg3 = (SetFocus(fenster) == NULL);
  BringWindowToTop(fenster);
  /* { wchar_t InfoText[1000];
    swprintf(InfoText, L"ShowWindow(): %d\nSetForgroundWindow(): %d\nSetWindowPos(): %d\nSetFocus(): %d",
      erg4, erg1, erg2, erg3);
    MessageBox(NULL, InfoText, L"LilyGotoEditor", MB_ICONINFORMATION | MB_OK);
  } */
  if (!erg1 || !erg3) {
    wchar_t InfoText[1000];
    swprintf(InfoText, L"Habe Probleme festgestellt beim Versuch, das Editier-Fenster der Datei »%s« in den Vordergund zu rufen!", Basename);
    MessageBox(NULL, InfoText, L"LilyGotoEditor", MB_ICONINFORMATION | MB_OK);
    return;
  }

  Sleep(700);  // 0,7 Sekunden Verschnaufpause
 
  // 5. Dialog öffnen »zu Zeile springen« - diesen Einsprung habe ich durch probieren gefunden
  PostMessage(fenster, WM_COMMAND, 24, (LPARAM) Line);

  // 6. Warten bis der Dialog bereit ist (ein anderes Fenster aktiv ist)
  for (ct = 30; ct > 0; ct--) {
    fenster2 = GetForegroundWindow();
    if ((fenster2 != NULL) && (fenster2 != fenster)) ct = 0;
     else Sleep(25);
  }
  Sleep(100);

  // 7. Meine Zeilennummer übergeben
  { wchar_t Nr[50]; swprintf(Nr, L"%d", Line); SendUnicodeKeyInput(Nr); }
  Sleep(300);

  // 8. Den »Wechseln«-Button des Dialogs suchen ...
  fenster3 = SucheUnterFenster(fenster2, L"Button", L"Wechseln", 1);

  // 9. ... und dem einen Mausklick vorgaukeln
  if (fenster3 != NULL) {
    SendMessage(fenster3, WM_LBUTTONDOWN, MK_LBUTTON, 0x00050005);
    SendMessage(fenster3, WM_LBUTTONUP, 0, 0x00050005);
  }
  /*
  { wchar_t M[1000]; swprintf(M, L"fenster2 = 0x%p\nfenster = 0x%p\nfenster3 = 0x%p", fenster2, fenster, fenster3);
    MessageBox(NULL, M, L"TRACE", MB_ICONINFORMATION | MB_OK);
  }
  */
  Sleep(350);

  // 10. Und jetzt das eigentliche Text-Edit-Fenster des Editors suchen
  //     - es gibt derer drei. Hoffentlich bleibt die Reihenfolge immer gleich,
  //       sonst muß ich vielleicht auch noch Fenstergröße und -position auswerten.
  fenster2 = SucheUnterFenster(fenster, L"Edit", L"", 1);
  if (fenster2 == NULL) fenster2 = fenster;

  // 11. Und dem eine ausreichende Anzahl an »Cursor Rechts«-Tastendrücken vorgaukeln
  for (ct = 0; ct < Char; ct++) {
    SendMessage(fenster2, WM_KEYDOWN, 0x0027, 0x014d0001);
    SendMessage(fenster2, WM_KEYUP, 0x0027, 0xc14d0001);
  }

  // Fine. Wenn alles gutgegangen ist, sind wir im Editor an der gewünschten Position angekommen.

  // Fazit: War wesentlich widerspenstiger als ich es mir dachte.
}





int WINAPI WinMain(HINSTANCE hInstance,
   HINSTANCE hPrevInstance,
   LPSTR plszCmdParam,
   int nCmdShow)
{ int ct, p;
  ARGV = CommandLineToArgvW(GetCommandLineW(), &ARGC);
  myInst = hInstance;
  myPrevInst = hPrevInstance;

  // AllocConsole();
 
  for (ct = 1; ct < ARGC; ct++) {
    if (ARGV[ct][0] == L'-') {
      for (p = 1; ARGV[ct][p]; p++) switch(ARGV[ct][p]) {
        case '1': SetupHelper(1); break;
        case '2': SetupHelper(2); break;
        case '3': SetupHelper(3); break;
        case '4': SetupHelper(4); break;
      }
    } else {
      LilyGotoEditor(ARGV[ct]);
    }
  }
  // Sleep(15000);
  return 0;
}
Natürlich ist diese Lösung maßgeschneidert auf mein Setup und deshalb selten von anderen ohne Änderung nutzbar. Daher macht es auch keinen Sinn, eine EXE-Datei anzuhängen.
Und wirklich tragfähig wäre eine solche Lösung erst, wenn es einen »allgemein akzeptierten Schnipsel-Jäger-Editor« für Lilypond gäbe, also so etwas wie eine »Integrierte Entwicklungsumgebung« (mit Verwendungsnachweis), die dann auch eine vordefinierte Schnittstelle zum von außen initiierten Springen an eine bestimmte Position bietet.