![]() |
AW: SendInputHelper - Ein Wrapper, der den Umgang mit SendInput vereinfacht
Zitat:
|
AW: SendInputHelper - Ein Wrapper, der den Umgang mit SendInput vereinfacht
DANKE nochmals für SendInputHelper - einfach nützlich und genial!
|
AW: SendInputHelper - Ein Wrapper, der den Umgang mit SendInput vereinfacht
Zitat:
|
AW: SendInputHelper - Ein Wrapper, der den Umgang mit SendInput vereinfacht
Zitat:
Es sind solche Programmierer wie Waldemar Derr, die mit ihrer ausgezeichneten Arbeit Delphi voranbringen! |
AW: SendInputHelper - Ein Wrapper, der den Umgang mit SendInput vereinfacht
Wenn beim Add oder Flush eine Exception auftreten könnte, dann würde noch der Ressourcenschutzblock fehlen.
Delphi-Quellcode:
with TSendInputHelper.Create do
try AddShortCut([ssWin], 'r'); // [Win] + [R] AddDelay(100); // Verzögerung in ms AddText('cmd', TRUE); // TRUE = AppendReturn Flush; // Erst hier werden die zuvor hinzugefügten Eingaben gebündelt abgesetzt. finally Free; end; Wenn du das auf einen Record umschreibst und z.B. intern nur dynamische Arrays (anstatt objekte/Pointer/Listen) verwendets, OK, ein Interface statt Class ginge auch, aber warum komplizierter. :angle2: dann kann man auf Create und Free verzichten, also wie eine einfach Variable benutzten und dann vergessen. (wie Integer, TPoint oder String+StringHelper) PS, dann ginge auch sowas wie Ducktyping. (überall jeweils Self als Result zurückgegeben)
Delphi-Quellcode:
TSendInputHelper.Create.AddShortCut([ssWin], 'r').AddDelay(100).AddText('cmd', TRUE).Flush;
Ja, Dank Custom-Managed-Records kann man nun auch in Records eigentlich alles benutzen, so lange ein Record nur sein eigenens Zeugs nutzt und man beim Kopieren von Variablen alle Inhalte, anstatt nur die Referenzen kopiert, oder man für Objekte/Pointer sich eine Referenzzählung baut (geht, aber macht kein Spaß, da es keine Record-Vererbung gibt und Generics hier kaum nutzbar sind). Warum eigentlich .Flush und nicht .Send ? |
AW: SendInputHelper - Ein Wrapper, der den Umgang mit SendInput vereinfacht
Zitat:
|
AW: SendInputHelper - Ein Wrapper, der den Umgang mit SendInput vereinfacht
z.B. statt TList<> und TStringList verwende ich zunehmend lieber ein TArray<String>, sowie eben Records anstatt Klassen. (sind teilweise einfacher als die doppelte Deklaration eines Interfaces)
und dennoch kann man so von der automatischen Speicherverwaltung profitieren. (seit den Custom-Managed-Records sind nun auch die letzten Probleme mit "unsicheren" Typen behebbar, also Pointer und Objekte innerhalb des Reocrds, sowie die einfachen dynamischen Arrays, welchen leider seit Jahrzehnten das CopyOnWrite fehlt, entgegen z.B. einem String, der intern auch nur ein aufgemotztes Char-Array ist, aber bei Welchem das CopyOnWrite funktioniert)
Delphi-Quellcode:
type
TMyRecord = record private FList: TArray<Integer>; public class function Create: TMyRecord; static; function Add(a: Integer): TMyRecord: function Send; function Clear; end; class function TMyRecord.Create: TMyRecord; begin Result := Self; end; function TMyRecord.Add(a: Integer): TMyRecord: begin FList := FList + [a]; end; function TMyRecord.Send; begin // mach was mit FList end; function TMyRecord.Clear; begin FList := []; // oder FList := nil; oder SetLength(FList, 0); end; TMyRecord.Create.Add(123).Add(456).Send; var R: TMyRecord; R.Add(123); R.Add(456); R.Send; R.Clear; // damit die 123 und 456 verschwinden (oder beim Send, z.B. via Parameter, nach dem Senden automatisch leeren lassen) R.Add(789); R.Send; R.Add(123).Add(456).Send; R.Clear; R.Add(789).Send;
Delphi-Quellcode:
bzw.
type
TSendInputHelper = record private FData: TArray<TInput>; // oder eben dein TInputArray class function MergeInputs(InputsBatch: array of TInputArray): TInputArray; static; public class function ConvertShiftState(ClassesShiftState: System.Classes.TShiftState): TSIHShiftState; static; ... procedure AddShift(ShiftState: TSIHShiftState; Press, Release: Boolean); overload; procedure AddShift(ShiftState: System.Classes.TShiftState; Press, Release: Boolean); overload; ... end;
Delphi-Quellcode:
---
function AddShift(ShiftState: TSIHShiftState; Press, Release: Boolean): TSendInputHelper; overload;
function AddShift(ShiftState: System.Classes.TShiftState; Press, Release: Boolean): TSendInputHelper; overload; ... end;
Delphi-Quellcode:
gibt es nicht, da es leider keine Recordvererbung gibt. :cry:
protected
Sinnvoll ist somit eh nur
Delphi-Quellcode:
und
private
Delphi-Quellcode:
.
public
Delphi-Quellcode:
können nur müssen
class function
Delphi-Quellcode:
sein, wodurch es dann auch kein Self gibt. (ohne Vererbung gibt es in einer Funktion eh immer nur den einen "Klassen"-Typ)
static
Delphi-Quellcode:
class function ConvertShiftState(ClassesShiftState: System.Classes.TShiftState): TSIHShiftState; static;
Das in TMyRecord.Add, nennt sich "string-like Operator", sowie Insert und Delete gibt es auch seit 'ner Weile. ![]() Ebenso weitere Array-Helper (unit System.Generics.Collections)
Delphi-Quellcode:
BinarySearch geht aber nur für sortierte Arrays.
FList := [456, 123, 789];
TArray.Sort<Integer>(FList); if TArray.BinarySearch<Integer>(FList, 456, idx) then ShowMessage(idx.ToString); // = '1' Für TArray<String> gäbe es auch ![]() ![]() Constructoren für Record sind zu praktisch, aber die müssen leider immer Parameter besitzen (vermutlich eine kranke Kompatibilität zu C++ oder so), aber
Delphi-Quellcode:
Das alte TPoint kennst'e doch noch?
type
TMyRecord = record constructor Create; // [dcc32 Fehler] E2394 Parameterlose Konstruktoren sind für Record-Typen nicht zulässig constructor Create(Value: Integer); constructor Create(Values: array of Integer); overload; // bzw. (Values: TArray<Integer>) end; TMyRecord = record class function Create: TMyRecord; static; // der Bugfix für den E2394 end;
Delphi-Quellcode:
neu sieht es so aus
TPoint = record
X: Integer; Y: Integer; end;
Delphi-Quellcode:
TPoint = record
X: FixedInt; Y: FixedInt; public constructor Create(P : TPoint); overload; constructor Create(const X, Y : Integer); overload; //operator overloads class operator Equal(const Lhs, Rhs : TPoint) : Boolean; class operator NotEqual(const Lhs, Rhs : TPoint): Boolean; class operator Add(const Lhs, Rhs : TPoint): TPoint; class operator Subtract(const Lhs, Rhs : TPoint): TPoint; class operator Implicit(Value: TSmallPoint): TPoint; class operator Explicit(Value: TPoint): TSmallPoint; class function PointInCircle(const Point, Center: TPoint; const Radius: Integer): Boolean; static; inline; /// <summary> Zero point having values of (0, 0). </summary> class function Zero: TPoint; inline; static; function Distance(const P2 : TPoint) : Double; procedure SetLocation(const X, Y : Integer); overload; procedure SetLocation(const P : TPoint); overload; procedure Offset(const DX, DY : Integer); overload; procedure Offset(const Point: TPoint); overload; function Add(const Point: TPoint): TPoint; function Subtract(const Point: TPoint): TPoint; function IsZero : Boolean; function Angle(const APoint: TPoint): Single; end;
Delphi-Quellcode:
Viele Records haben keinen Constructor und Delphi generiert auch (noch) keinen Standard-Constructor. :cry:
var Demo: TArray<TPoint>; // oder : array of TPoint;
Demo := [TPoint.Create(1, 2), TPoint.Create(3, 4)]; Demo := Demo + [TPoint.Create(5, 6)]; Insert(TPoint.Create(7, 8), Demo, 3); TInput leider auch, aber das kann man noch nachrüsten:
Delphi-Quellcode:
type
TInputHelper = record helper for TInput constructor Create(P: TMouseInput); overload; constructor Create(P: TKeybdInput); overload; constructor Create(P: THardwareInput); overload; end; // bzw. TInputHelper = record helper for TInput constructor Create(dx, dy: LongInt; mouseData, dwFlags: DWORD); overload; //constructor Create(dx, dy: LongInt; mouseData, dwFlags, time: DWORD; dwExtraInfo: ULONG_PTR); overload; constructor Create(wVk, wScan: WORD; dwFlags: DWORD); overload; //constructor Create(wVk, wScan: WORD; dwFlags, time: DWORD; dwExtraInfo: ULONG_PTR); overload; //constructor Create(P: THardwareInput); overload; end; PS: Record-Helper für sowas wie Enum und Set geht noch nicht so, aber man könnte sich einen Record mit AutoCasts basteln. Also TShiftStateEx als Parameter für deine Funktionen. Dann brauchst'e das Overload nicht mehr, da sich die beiden Sets größtenteils automatisch von und dahin casten, bei Zuweisung := , sowie bei Übergabe an den Parameter. Zu geil wäre es natürlich, wenn man stattdessen TSIHShiftState so anpassen könnte, dass TShiftState zuweisungskompatibel würde. (aber sowas erwarte ich frühestens im nächsten Jahrtausend von Embarcadero)
Delphi-Quellcode:
TShiftStateEx = record
private FState: TSIHShiftState; function GetShiftState: TShiftState; // Result := TShiftState(FState - ssWin); procedure SetShiftState(Value: TShiftState); // FState := TSIHShiftState(Value); public class operator Implicit(a: TSIHShiftState): TShiftStateEx; // Result.FState := a; -> entspricht TheRecord := SIHShiftState; class operator Implicit(a: TShiftState): TShiftStateEx; // Result.FState := TSIHShiftState(a); -> entspricht TheRecord := ShiftState; class operator Implicit(a: TShiftStateEx): TShiftState; // Result := TShiftState(a.FState - ssWin); -> entspricht ShiftState := TheRecord; class operator Explicit(a: TShiftStateEx): TSIHShiftState; // Result := a.FState; -> entspricht SIHShiftState := TSIHShiftState(TheRecord); // Und noch ein paar kleine Hilfsfunktionen, falls man nicht alles direkt über harte oder implizite Casts machen will: property AsShiftState: TShiftState read GetShiftState write SetShiftState; property AsSIHShiftState: TSIHShiftState read FState write FState; function HasMenu: Boolean; // Result := ssWin in FState; end;
Delphi-Quellcode:
// anstatt
procedure AddShift(ShiftState: TSIHShiftState; Press, Release: Boolean); overload; procedure AddShift(ShiftState: System.Classes.TShiftState; Press, Release: Boolean); overload; procedure AddShortCut(ShiftState: TSIHShiftState; ShortChar: Char); overload; procedure AddShortCut(ShiftState: TSIHShiftState; ShortVK: Word); overload; procedure AddShortCut(ShiftState: System.Classes.TShiftState; ShortChar: Char); overload; procedure AddShortCut(ShiftState: System.Classes.TShiftState; ShortVK: Word); overload; // nur noch procedure AddShift(ShiftState: TShiftStateEx; Press, Release: Boolean); overload; procedure AddShortCut(ShiftState: TShiftStateEx; ShortChar: Char); overload; procedure AddShortCut(ShiftState: TShiftStateEx; ShortVK: Word); overload; |
AW: SendInputHelper - Ein Wrapper, der den Umgang mit SendInput vereinfacht
Hi Himitsu,
vielen Dank für dein ausführliches Feedback. Wenn du magst, kannst du einen PullRequest erstellen. Ich würde dich natürlich in den Credits aufführen. Sonst weiß ich nicht, wann ich dazu komme, dies einzubauen... |
Alle Zeitangaben in WEZ +1. Es ist jetzt 19:31 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