Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Sonstige Fragen zu Delphi (https://www.delphipraxis.net/19-sonstige-fragen-zu-delphi/)
-   -   Autotext-Programm - aber wie? (https://www.delphipraxis.net/171644-autotext-programm-aber-wie.html)

Marco Steinebach 16. Nov 2012 16:00

Autotext-Programm - aber wie?
 
Hallo zusammen,
ich will, eigentlich, nur ein kleines AutoText-Programm schreiben - also ich bin, in welchem Programm auch immer, schreibe beispielsweise
mfg
drücke, beispielsweise strg+leertaste
und anschließend steht
"mit freundlichen Grüßen"
da.

Mein erstes Problem:
wie kriege ich raus, welche Anwendung, als strg+leertaste gedrückt wurde, aktiv war?
Ich denke mir, das strg+Leertaste, als Hotkey definiert, meinem Programm ja mitteilen sollte, wann es startet - und ich, habe ich das Handle der aktiven Anwendung, mit
wm_settext
den zu ersetzenden Text schreiben kann.
Oder nicht? ;-)

Viele Grüße
Marco

Bummi 16. Nov 2012 16:40

AW: Autotext-Programm - aber wie?
 
Delphi-Quellcode:
function GetFocusedControlHandle: HWND;
var
   FG_Window_Handle:HWND;
   FocusedThreadID : DWORD;
begin

  FG_Window_Handle := GetForegroundWindow;

  FocusedThreadID := GetWindowThreadProcessID(FG_Window_Handle, nil) ;

  if AttachThreadInput(GetCurrentThreadID, FocusedThreadID, true) then
  Result := GetFocus;
  AttachThreadInput(GetCurrentThreadID, FocusedThreadID, false) ;
end;

// kleiner test ....

Delphi-Quellcode:
procedure TForm3.Timer1Timer(Sender: TObject);
var
 s:AnsiString;
begin
  s := 'Hallo';
  SendMessage(GetFocusedControlHandle,wm_setText,0,Integer(@s[ 1]));
end;

himitsu 19. Nov 2012 09:21

AW: Autotext-Programm - aber wie?
 
Hattest du diesen GetFocusedControlHandle-Code nicht schonmal irgendwo gepostet?

Nja, dast iimmernoch ein Fehler drin, denn auf was wird Result denn wohl stehen, wenn AttachThreadInput Nee sagt?
(aber das sollte der Compliler dir auch schon mehrfach gesagt haben)

Marco Steinebach 20. Nov 2012 14:36

AW: Autotext-Programm - aber wie?
 
Hallo,
und ein herzliches Dankeschön für die Antwort.
Das Result auf undefiniert steht, ist beseitigt. ;-)
Aaaaber: Die WM_SetText wird offensichtlich nicht wirklich ausgewertet. Im Windows-Editor klappt alles prima, aber weder bei der Delphi-IDE noch bei Word kommt irgendwas an.
Gibt es eine elegantere möglichkeit, als ernsthaft alle einzelnen Tastendrücke des zu schreibenden Strings per Sendmessage mit allen KeyUps und -downs simulieren zu müssen?
Viele Grüße
Marco

Medium 20. Nov 2012 14:48

AW: Autotext-Programm - aber wie?
 
Es wäre wohl das universellste. Fast. Da nicht alle Programme Eingabefelder aus den Reihen der Common Controls nimmt, und somit ein Textfeld in einem z.B. geskinnten Programm auch gerne mal ohne Handle und jegliche Kenntnis von Windows daher kommt, wäre noch besser daher die Verwendung von SendInput(), was (ohne bewusste Handlung seitens des Senders) kein Programm von tatsächlichen Eingabehandlungen unterscheiden kann. Sprich: Alles verhält sich so, als würde das jemand von seiner Tastatur aus direkt eintippern, und zwar bei egal was gerade den Fokus hat (und wie dann programmintern ggf. noch weiter unterverteilt wird).

himitsu 20. Nov 2012 15:12

AW: Autotext-Programm - aber wie?
 
Es kommt auch drauf an, welche Komponente angesteuert wird und worauf die Programme bei Eingaben letzendlich reagieren.

Bei Delphi und Word wird der Text garantiert nicht in einem billigen Edit/Memo/Richedit verwaltet und daher kann es gut sein, daß diese Komponenten nicht auf WM_SETTEXT reagieren.
Das Notepad dagegen nutzt aber MultiLine-Edit (MEMO) und dieses Windows-Control kennt natürlich MSDN-Library durchsuchenWM_SETTEXT und Co.

Bummi 20. Nov 2012 15:58

AW: Autotext-Programm - aber wie?
 
@himitsu

falls ich es schon mal geschickt haben sollte, dann habe ich wohl genauso schnell und schlampig zusammengetippt wie gestern. Ich kann es leider nicht mehr editieren.

Marco Steinebach 21. Nov 2012 04:46

AW: Autotext-Programm - aber wie?
 
Hallo Zusammen,
Ich habe zu SendInput zumindest mal denSendInputHelper gefunden, der wäre auch schön, läuft aber bei mir nicht, weil mein Delphi steinalt ist - nun ja, dann muß es anders gehen. :-(
Kann mir vielleicht jemand ein schlichtes Beispiel posten, wie ich via SendInput ein "H" und anschließend ein "a" senden kann?
Und gleich noch 'ne Frage, ich dachte mittels
Delphi-Quellcode:
    keybd_event (ord('h'), MapVirtualKey (ord('h'), 0), 0, 0);
    keybd_event (ord('h'), MapVirtualKey (ord('h'), 0), keyeventf_keyup, 0);
sollte man eigentlich auch ein "h" senden können - tut aber nicht. Hat jemand 'ne Idee warum?

Vielen Dank schonmal und viele Grüße
Marco

API 21. Nov 2012 05:18

AW: Autotext-Programm - aber wie?
 
Hallo Marco, google nach Bei Google suchenPostKeyEx32

Marco Steinebach 7. Dez 2012 18:59

AW: Autotext-Programm - aber wie?
 
Liste der Anhänge anzeigen (Anzahl: 1)
Hallo zusammen,
Ich brauche bitte doch nochmal eure Hilfe.
Ich hab's jetzt so umgebaut, daß das Wort, welches vor dem Cursor steht, in die Zwischenablage kopiert, ausgewertet, ggf. ersetzt, und dann mittels strg+v wieder eingefügt wird. Daß die Zwischenablage dabei überschrieben werden kann, ist klar, stör micht aber nicht - außer, es gibt eine schönere lösung.
Aber, wenn ich das Programm ausführe, kommt es zu eine exception, und mir ist überhaupt nicht klar, warum.
Hat jemand 'ne Idee, was ich hier falsch gemacht abe?
Delphi-Quellcode:
unit main;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs;

type
  TGlobalHotkey = record // damit's zusammengefaßt ist
    AtomName: string;
    atom: integer;
  end;
  TForm1 = class(TForm)
    procedure FormCreate(Sender: TObject);
    procedure FormClose(Sender: TObject; var Action: TCloseAction);
  private
    { Private-Deklarationen }
    globalHotkey: TGlobalHotkey;
    hNextViewer: DWORD; // Handle des nexten Mitglieds in der Clipboard-Viewer-Kette
    hf: hwnd; // handle des fordergrundfensters, in dem strg+leertaste gedrückt wird.
    procedure CopyTextToClipboard (aWnd: HWND; aText: PChar);
    function GetFocusedControlHandle: HWND;
  public
    { Public-Deklarationen }
    procedure WMHotKey (var Msg: TWMHotKey); message WM_HOTKEY;
    procedure WMChangeCBChain(var Msg: TMessage); message WM_CHANGECBCHAIN;
    procedure WMDrawClipboard(var Msg: TMessage); message WM_DRAWCLIPBOARD;
  end;

var
  Form1: TForm1;

implementation

{$R *.DFM}

procedure TForm1.FormCreate(Sender: TObject);
begin
  with globalHotkey do
  begin
    AtomName := 'Test_Strg_Leertaste';
    atom := GlobalFindAtom (pChar(AtomName));
    if atom = 0 then // Atom gibt's noch nicht ...
      atom := GlobalAddAtom (pChar(AtomName));
      // sonst wird das bereits vorhandene in Atom gespeichert.
    RegisterHotkey (handle, atom, mod_Control, vk_space)
  end;
  // hängen wir uns in die Clipboard-Kette..,.
  hNextViewer := SetClipBoardViewer (Handle);
end;

procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
begin
  // hotkey weg und raus aus der Clipboardkette...
  unregisterHotkey (handle, globalHotkey.Atom);
  ChangeClipBoardChain(Handle, hNextViewer);
  action := caFree;
end;

procedure TForm1.WMChangeCBChain(var Msg: TMessage);
begin
  if Msg.WParam = hNextViewer then
    hNextViewer := Msg.lParam
  else if hNextViewer <> 0 then
    SendMessage(hNextViewer, MSG.Msg, MSG.wParam, MSG.lParam);
end;

procedure TForm1.CopyTextToClipboard(aWnd: HWND; aText: PChar);
var
  Data: THandle;
  DataPtr: Pointer;
  Size: Integer;
  oldWND: HWND;
begin
  Size := Length(aText) + 1;
  OpenClipboard(aWnd); //Open Clipboard
  try
    EmptyClipboard; // Clear Clipboard
    oldWND := SetClipboardViewer(aWnd); // Clipboard für Programm registr.
    Data := GlobalAlloc(GMEM_MOVEABLE or GMEM_DDESHARE, Size); // Get Memory
    try
      DataPtr := GlobalLock(Data);
      try
        Move(aText^, DataPtr^, Size);
        SetClipboardData(CF_TEXT, Data); // Clpbrd-Format as Text & send Text
      finally
        GlobalUnlock(Data);
      end;
    except
      GlobalFree(Data); // Free res. Memory
      raise; // Get global Memory Err
    end;
    ChangeClipboardChain(aWnd, oldWND);
  finally
    CloseClipboard; //Close Clipboard
  end;
end;

procedure TForm1.WMDrawClipboard(var Msg: TMessage);
var
  hClipbrdObj: THandle;
  pClipbrdObj: Pointer;
  s: string;
begin
  OpenClipboard(Handle);
  hClipbrdObj := GetClipboardData(CF_TEXT);
  if hClipbrdObj = 0 then // nix text oder drin...
  begin
    CloseClipBoard;
    exit;
  end;
  pClipbrdObj := GlobalLock(hClipbrdObj);
  s := pChar (pClipbrdObj); // sollte der im Clipboard stehende Text sein
  GlobalUnlock(DWORD(pClipbrdObj));
  GlobalFree(hClipbrdObj);
  CloseClipBoard;
  if uppercase (s) <> 'MFG' then exit; // blos zum testen.
  s := 'Mit freundlichen Grüßen!';
  CopyTextToClipboard (handle, pChar(s));
  // strg+v
  keybd_event (vk_control, 0, 0, 0);
  keybd_event (vkKeyScan('v'), 0, 0, 0);
  keybd_event (vkKeyScan('v'), 0, keyeventf_keyup, 0);
  keybd_event (vk_control, 0, keyeventf_keyup, 0);
end;

function TForm1.GetFocusedControlHandle: HWND;
var
  FG_Window_Handle: HWND;
  FocusedThreadID: DWORD;
begin
  FG_Window_Handle := GetForegroundWindow;

  FocusedThreadID := GetWindowThreadProcessID (FG_Window_Handle, nil);

  if AttachThreadInput (GetCurrentThreadID, FocusedThreadID, true) then
    Result := GetFocus
  else result := 0;
  AttachThreadInput (GetCurrentThreadID, FocusedThreadID, false);
end;

procedure TForm1.WMHotKey(var Msg: TWMHotKey);
begin
  hf := GetFocusedControlHandle;
  if hf = 0 then exit;

  // strg+shift+pfeil links - Wort vor'm Cursor markieren...
  keybd_event (vk_control, 0, 0, 0);
  keybd_event (vk_Shift, 0, 0, 0);
  keybd_event (vk_left, 0, 0, 0);
  keybd_event (vk_left, 0, keyeventf_keyup, 0);
  keybd_event (vk_shift, 0, keyeventf_keyup, 0);
  keybd_event (vk_control, 0, keyeventf_keyup, 0);
  // strg+c - ab in die Zwischenablage
  keybd_event (vk_control, 0, 0, 0);
  keybd_event (vkKeyScan('c'), 0, 0, 0);
  keybd_event (vkKeyScan('c'), 0, keyeventf_keyup, 0);
  keybd_event (vk_control, 0, keyeventf_keyup, 0);
  // jetzt, sollte, eigentlich WMDrawClipboard greifen...
end;

end.
Viele Grüße
Marco
p.s.: bitte entschuldigt, daß ich nicht mehr alle Querverweise parat habe, bei wem ich mir was abgeschaut habe!

Sir Rufo 7. Dez 2012 21:59

AW: Autotext-Programm - aber wie?
 
Es wäre sehr freundlich, wenn du uns die Exception Meldung verraten würdest (Einfach Strg-C) und dann hier einfügen und auch an welcher Stell das passiert (ausführen des Programms im Debugger, dann wird dir die Stelle angezeigt)

Luckie 8. Dez 2012 06:16

AW: Autotext-Programm - aber wie?
 
Und wo bekommst du die Exception?

Marco Steinebach 8. Dez 2012 11:10

AW: Autotext-Programm - aber wie?
 
... genau das ist ja mein Problem, daß ich eben keine Exception der üblichen Form bekomme, sondern
- entweder eine Schutzverletzung "lesen von Adresse bla..."
- oder "Clipboardtest.exe hat einen Fehler verursacht und muß beendet werden"
oder es funktioniert ein, zwei mal, und dann kriege ich eine Schutzverletzung beim Schließen des Programms.
Offensichtlich wühle ich irgendwo im Arbeitsspeicher rum, wo ich nichts zu suchen habe.

Probiere ich das Ganze, während Delphi läuft, lande ich nach druck auf strg+c einfach ohne weitere Meldungen im CPU-Fenster.

Laufe ich mit dem Debugger drüber, läuft die Funktion WMDrawClipBoard scheinbar durch, und erst beim "end" lande ich CPU-Fenster.
Übrigens geht daß auch schief, wenn man einen text nimmt, der kein CopyTextToClipBoard zur folge hat, also biespielsweise im Word "test" schreibt und anschließend strg+c drückt.

Viele Grüße
Marco

Sir Rufo 8. Dez 2012 15:23

AW: Autotext-Programm - aber wie?
 
Wenn du das CPU Fenster siehst, dann wird dir auch der Aufruf Stack gezeigt, und daran kann man nachverfolgen, welcher Aufruf in deinem Quellcode da gerade am Zug ist.

Ich vermute mal, dass du beim Zugriff auf die Zwischenablage diesen Fehler bekommst.
Da dein Programm ja nicht den exklusiven Zugriff auf die Zwischenablage hat (ist ja für alle gedacht), musst du prüfen und ggfs warten, bis du wirklich den Zugriff auf die Zwischenablage hast.

Macht man das nicht, dann knallt es eben

Marco Steinebach 8. Dez 2012 15:51

AW: Autotext-Programm - aber wie?
 
Der Stack zeigt nur, daß WMDrawClipBoard gerade am zug ist...

Gibt's eine extra Methode um zu prüfen, ob ich an die Zwischenablage ran darf, oder einfach eine Schleife um OpenClipBoard herum? ...

Ich brauch nochmal 'n Schups, bitte...

Sir Rufo 8. Dez 2012 16:26

AW: Autotext-Programm - aber wie?
 
Werte doch mal den Resultcode von MSDN-Library durchsuchenOpenClipboard aus

Marco Steinebach 29. Dez 2012 12:50

AW: Autotext-Programm - aber wie?
 
Hallo zusammen,
meine Güte, warum schlage ich mich eigentlich nicht mit was einfacherem Rumm. :-)
Also: ich bin jetzt soweit, daß die Methode, das wort vor dem Cursr zu markieren, in die Zwischenablage zu kopieren, es auszuwerten, ggf. zu ersetzen, und dann mittels strg+v wieder einzufügen läuft, bis auf Outlook. Ich weiß nicht, was mein Outlook mit der Zwischenablage tut, aber irgendwie halten die sich da offensichtlich nicht an ihre eigenen Regeln.
Gibt es irgend eine Möglichkeit, das wort, welches vor dem Cursor steht, auszulesen? Oder wird's doch ernsthaft ein Keyboard-Hook?
Falls ja, würde das nämlich auch die Zwischenablage in ruhe lassen...
Viele Grüße
Marco


Alle Zeitangaben in WEZ +1. Es ist jetzt 08:26 Uhr.

Powered by vBulletin® Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024-2025 by Thomas Breitkreuz