Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Object-Pascal / Delphi-Language (https://www.delphipraxis.net/32-object-pascal-delphi-language/)
-   -   Delphi Wie baut man sowas wie writeln/sprintf selbst ? (https://www.delphipraxis.net/126471-wie-baut-man-sowas-wie-writeln-sprintf-selbst.html)

flash2009 25. Dez 2008 04:25


Wie baut man sowas wie writeln/sprintf selbst ?
 
Hey
ich wollte sprintf hooken
das ist vom aussehen ähnlich wie printf oder writln
es wird eine umbestimmte anzahl von parametern übergeben, die man nur ermitteln kann wenn man im string oder pchar der übergeben wird die platzhaltern (%d ..) zählt.

Im Intenet und überall find ich nur so Standart function oder procedure mit ner festen Anzahl an Parametern , wie deklariert man sowas dynamisches.
function meine (var p1,p2... pn):boolean; << sowas

Ansonsten könnt ich da noch mit Inline asm den krempel vom stack nehmen aber das wäre ja murks.

gruß und frohe Weihnachten flash

WS1976 25. Dez 2008 08:02

Re: Wie baut man sowas wie writeln/sprintf selbst ?
 
Hallo,

aus deinem Beitrag ist zwar nicht genau herauszufinden was du willst aber ich interpretiers mal so:
Du suchst eine Möglichkeit die Parameterübergabe in Delphi dynamisch zu machen.
Delphi-Quellcode:
 Procedure Test(a,b:integer;c:boolean=true);
a und b sind hier feste Parameter c kannst du gegebenenfalls weglassen wird dann automatisch auf true gesetzt.

Frohe Weihnacht und einen guten Rutsch wünsche ich dir und allen DP'lern
Rainer

[edit=Sharky]Dalphi-Tag korrigiert. Mfg, Sharky[/edit]

PMM 25. Dez 2008 08:49

Re: Wie baut man sowas wie writeln/sprintf selbst ?
 
Schau die mal die Function "format" an. Die macht so was ähnliches.
Evtl ist ja:
Delphi-Quellcode:
 
writeln(format(frmstr,args));
schon das was du haben willst.
Frohes Fest
PMM

alzaimar 25. Dez 2008 09:33

Re: Wie baut man sowas wie writeln/sprintf selbst ?
 
In Delphi kann man keine beliebige Anzahl von Parametern deklarieren. In C(++) und C# geht das, weil hier die Aufrufkonventionen anders sind und die Sprache zudem spezielle syntaktische Elemente bereit hält.

WS1976 hat schon angedeutet, wie man ein sprintf in Delphi theoretisch deklarieren könnte. Leider benötigst Du hier allgemeingültige Typen (Variants) und damit funktioniert das nicht. Die Lösung liegt im etwas merkwürdigen Konstrukt 'Array Of Const', mit dem man heterogene Daten in Form eines dynamischen Arrays übergeben kann. Allerdings ist der Zugriff auf einzelne Elemente dieses Arrays nicht gerade trivial.

Die Lösung von PMM ist also die Richtige:
Delphi-Quellcode:
Procedure PrintF (Const FmtStr : String; Args : Array Of Const);
Begin
  Writeln (Format(FmtStr, Args))
End;
Und der Aufruf:
Delphi-Quellcode:
PrintF('Hallo %s, Frohe Weihnacht und einen guten Rutsch ins Jahr %d',['flash2009',2009]);

Cyf 25. Dez 2008 13:32

Re: Wie baut man sowas wie writeln/sprintf selbst ?
 
Zitat:

Zitat von alzaimar
Die Lösung liegt im etwas merkwürdigen Konstrukt 'Array Of Const', mit dem man heterogene Daten in Form eines dynamischen Arrays übergeben kann. Allerdings ist der Zugriff auf einzelne Elemente dieses Arrays nicht gerade trivial.

Kannst du das mit dem Array of Const nochmal genauer ausformulieren oder einen Link posten, der es erklärt? Hilft sowas eine Funktion die immer das selbe macht nicht für alle möglichen unsigned/signed int Typen überladen zu müssen und so unnötig viel Code zu linken? Die Offsets im Arrays ggf. selbst zu berechnen wäre, wenn man an die Typengröße ran kommt auch kein Problem.



[OFFTOPIC]Hier und heute stellen sich die Fragen, und ich glaube, Sie stimmen mit mir überein[...] :lol:

mkinzler 25. Dez 2008 13:35

Re: Wie baut man sowas wie writeln/sprintf selbst ?
 
Man muss den Array im Aufruf der Funktion dynamisch erstellen (z.B. VarArrayOf())

Cyf 25. Dez 2008 13:56

Re: Wie baut man sowas wie writeln/sprintf selbst ?
 
Vielleicht ganz nützlicher Link: http://rvelthuis.de/articles/articles-openarr.html

alzaimar 25. Dez 2008 15:20

Re: Wie baut man sowas wie writeln/sprintf selbst ?
 
Zitat:

Zitat von mkinzler
Man muss den Array im Aufruf der Funktion dynamisch erstellen (z.B. VarArrayOf())

Äh. Nein? Eine Routine mit einem 'Array Of Const'-Parameter kann man so aufrufen, wie bei der Format-Funktion. Nur eben der Zugriff ist nicht trivial. Warte mal, ich kram mal in meinem Archiv... *kram* *such* :hello:
Delphi-Quellcode:
Function ElementOfConst(aIndex: Integer; aArray: Array Of Const): Variant;
Begin
  With aArray[aIndex] Do
    Case VType Of
      vtInteger:
        Result := VInteger;
      vtBoolean:
        Result := vBoolean;
      vtChar:
        Result := VChar;
      vtExtended:
        Result := vExtended^;
      vtString:
        Result := VString^;
      vtPChar:
        Result := StrPas(VPChar);
      vtObject:
        Result := VObject.ClassName;
      vtClass:
        Result := VClass.ClassName;
      vtAnsiString:
        Result := String(VAnsiString);
      vtCurrency:
        Result := VCurrency^;
      vtVariant:
        Result := VVariant^;
      vtInt64:
        Result := VInt64^;
    End;
End;
So kann ich auf die einzelnen Elemente eines 'Array Of Const' zugreifen, was eigentlich ein Array Of TVarRec ist (ähnlich aber nicht identisch mit TVarData, also einem Variant).

Ich kann dann also sowas spektakuläres bauen, wie z.B.:
Delphi-Quellcode:
Procedure ListItems (aArgs : Array Of Const);
Var
  i : Integer;

Begin
  For i:=0 to High (aArgs) do
    Writeln (ElementOfConst(i,aArgs));
End;
...
ListItems (['Foo','Bar',1.2, 3.456, 7]);
Ob man das jemals benötigt, steht auf einem anderen Blatt.

Cyf 25. Dez 2008 15:53

Re: Wie baut man sowas wie writeln/sprintf selbst ?
 
Liste der Anhänge anzeigen (Anzahl: 1)
Also ich hab auch mal ein bischen rumprobiert und ein (sinnloses) Beispiel gebastelt. Allerdings bietet die Methode in meinen Augen grade für mich mehr Nachteile als überladen:

1. Man kann keine normalen Arrays mehr übergeben, mag sein dass man das hinbiegen kann, indem man erst irgendwie nach array of TVarRec umwandelt.

2. Der Compiler genieriert beim Aufruf intern erstmal ein array of TVarRec, das verusacht wahrscheinlich grade bei großen Arrays erstmal relativ viel Aufwand, außerdem wird für jedes Element der Typ mitgespeichert (was wenn mans kombinieren möchte natürlich nötig ist), so dass die Größe jedes Elements damit auf 8 Byte anwächst (da das ganze außerdem intern ne Union ist).

3. Wenn ich den Link, den ich gepostet hab, richtig versteh, ist der Zugriff über die Pointer in dem TVarRec zur Modifikation der Orginaldaten (also by reference) problematisch und man muss die Daten erst kopieren, weil die Daten, auf die man zugreift, nach dem Funktionsaufruf nicht mehr existieren, da sie nur temporär erzeugt und auf den Stack gelegt werden. Eine Möglichkeit scheint hier zu sein in der Funktion Heap-Speicher zu reservieren und einen Zeiger darauf zurückzugeben, das verursacht aber auch wieder Aufwand.

Sieht also, da ichs für verschiedene Sortierfunktionen (die natürlich schnell sein sollen) brauch, für mich spontan nicht so geeignet aus, ich werd also entweder auf
Delphi-Quellcode:
procedure SortierWas(Data: Pointer; Count: Integer; ElementSize: Integer)
umsteigen müssen, was aber den Zugriff auf einzelne Elemente recht schwer macht (man müsste die Size abfragen und je nachdem den entsprechenden Pointer casten und dereferenzieren, um die Werte vergleichen zu können, damit verliert man aber Zeit, wenn es um große Arrays geht und braucht immer noch getrennte Funktionen für signed und unsigned), oder ich bleib einfach dabei alles zu überladen, was ich wohl mach. :wink: (Sorry, das war ein wenig Offtopic, ich weiß)

inherited 25. Dez 2008 18:41

Re: Wie baut man sowas wie writeln/sprintf selbst ?
 
Ich hab mal mit solchen Cost-Arrays eine Klasse für MySQL-Zugriffe geschrieben. Damit konnte man dann recht bequem Inserten und sowas

flash2009 25. Dez 2008 19:24

Re: Wie baut man sowas wie writeln/sprintf selbst ?
 
Hey cool was ihr mir alles dazu schreibt,
boah wie blöd ich mir immer vorkomme wenn ich sowas lese ^^ .
Ich glaub ich mach das mit nen DWord Array dann hab ich die meisten Dinge abgedeckt und an PChar Parameter komm ich dann wenn ich das DWord als Pointer übergebe, aber am im grunde will ich die Parameter nur weiterschleifen und den format teil (dieser PChar mit den Platzhaltern (%d) ) auslesen.


gruß flash

alzaimar 25. Dez 2008 20:02

Re: Wie baut man sowas wie writeln/sprintf selbst ?
 
Was willst Du überhaupt anstellen?

flash2009 25. Dez 2008 20:46

Re: Wie baut man sowas wie writeln/sprintf selbst ?
 
ich will die original funktion sprinft, austauschen nur müssen halt die paremeter wieder vom stack
ich seh grad das es wohl in cdecl gemacht ist und nicht stdcall muss ich mal bischen nachforschen

edit: ok ich seh grad das ding ist cdecl das heißt ich muss den stack garnicht fixen :)

flash2009 26. Dez 2008 16:49

Re: Wie baut man sowas wie writeln/sprintf selbst ?
 
ok hier mal eine grafik da ich das nicht so schön beschreiben kann

http://img518.imageshack.us/img518/2712/hukiw1.png

der rechte rote pfeil geht auch über die dll zurück

Delphi-Quellcode:
function test(Buffer:Pointer;Format1:PChar;const arg:Array of Const):Integer;cdecl;
begin
  Result := SprintfOrg(Buffer,Format1,arg);
end;
geht :hello:

Delphi-Quellcode:
function test(Buffer:Pointer;Format1:PChar;const arg:Array of DWord):Integer;cdecl;
begin
  Result := SprintfOrg(Buffer,Format1,arg);
end;
geht :hello:


Funktionsaufruf:
PUSH EAX ; /<%s> = " [17:29:26]"
PUSH EBX ; |<%s> = "abc"
PUSH 004488D8 ; |format = "%s%s"
PUSH ECX ; |s = 0017F388
CALL EBP ; \sprintf

Stack wenn sprintf gecallt ist:
0041F27E /CALL to sprintf from 0041F27C
0017F388 |s = 0017F388
004488D8 |format = "%s%s"
00447648 |<%s> = "abc"
08F12030 \<%s> = " [17:29:26]"

done thx @all
greetz flash

flash2009 27. Dez 2008 05:47

Re: Wie baut man sowas wie writeln/sprintf selbst ?
 
ok schade zu früh gefreut

vieleicht hat noch mal jemand lust mir nen tipp zu geben
irgendwo haperts am zusammenspiel zwischen array und stack pointer

ok const array of ..
dann wird für den array eine adresse vom stack kopiert(move) und der array zeigt auf die adresse
problem ist das der array quasi auf das erste element zeigt.
bei nem Pchar wäre das zb halloallesisttoll
einen index weiter fehlen dann die ersten 4 bytes oallesisttoll wäre noch übrig usw

nächsten versuch das const wegzu lassen , funktioniert (theretisch) praktisch sieht das aber so aus das der compiler das parameter wo die rücksprung adresse steht,den register edx geschoben wird und dann runtergezählt wird bis 0 , das programm is irgendwo bei 40000 oder so und das überschreibt ja dann quasi den gesamten programcode :( .
http://img224.imageshack.us/img224/8113/huk2vq1.png :wall:

in asm würd ich mir vom ersten parameter adresse nehmen 0027FEF8 und dann immer 4 weitergehen , aber wie kann ich sowas schön ohne inline asm realisieren :( .

edit1:
-----------------------
als zwischenlösung bin ich jetzt bei dem kram angekommen
Delphi-Quellcode:
function translate(Buffer: Pointer; Format1: PChar;dwFirst:DWord):Integer;cdecl;
var
  i : Integer;
  c : Integer;
  ptr : Pointer;
begin
  c:= 0;
  i := 0;
  for i := 0 to (length(Format1)-1) do
  if Format1[i] = '%' then
   begin
    inc(c);
    if Format1[i+1] = 's' then
    begin
      ptr := @dwFirst;
      LogToFileStr(PChar(Pointer(Cardinal(ptr)+4*(c-1) )^))
    end;
    if Format1[i+1] = '%' then
      dec(c,2);
   end;
  //Result := SprintfOrg(Buffer,Format1,arg);
end;
ich werd über diesen counter c dann versuchen die parameter wieder auf den stack zu schaufen bevor ich die echte sprintf callen werde, aber ist das noch schön was ich da mache oder geht sowas nicht anders?

Sharky 27. Dez 2008 06:19

Re: Wie baut man sowas wie writeln/sprintf selbst ?
 
Hai flash2009,

sei doch bitte so lieb und hänge die Grafiken als Anhang an deine Postings. Dadurch sind sie:

a) auch noch vorhanden wenn sie auf "deinem" Server nicht mehr bereit stehen
b) muss nur der User der sich dafür interessiert die Daten runterladen.


Danke.

Cyf 27. Dez 2008 13:57

Re: Wie baut man sowas wie writeln/sprintf selbst ?
 
Ich muss grad zugeben, dass ich nicht so ganz folgen kann, was du überhaupt erreichen willst, aber bedenke dass (im Bezug auf array of const):

a) ein PChar sich nicht mitten im Array befindet, sondern nur ein Zeiger auf den Anfang des Strings und dieser dann irgendwo im Speicher ist, dieser ist selbst nur insgesamt 4 Byte groß
b) in einem array of const jedes Element ein 8 Byte großes TVarRec ist
c) die im TVarRec übergeben Zeiger offenbar nur im Scope der Funktion gültig sind (siehe obiger Link)

Kannst du nochmal kurz und knapp erklären, warum du den Stackpointer manuell manipulieren musst und was es mit deiner DLL auf sich hat?

Oreaden 27. Dez 2008 14:23

Re: Wie baut man sowas wie writeln/sprintf selbst ?
 
Zitat:

Zitat von alzaimar
In Delphi kann man keine beliebige Anzahl von Parametern deklarieren. In C(++) und C# geht das, weil hier die Aufrufkonventionen anders sind und die Sprache zudem spezielle syntaktische Elemente bereit hält.

Fast, auch in Delphi kann man Funktionen und Proceduren als CDECL deklarieren, dann kann man eine unbestimmte Anzahl an Parametern übergeben. Der Rest sollte in der OH mit einem Beispiel beschrieben sein, wenn mich meine grauen Zellen nicht ganz verlassen haben.

Also, es ist möglich, der erste Schritt sind wie von Alzaimer erkannt, die Aufrufkonventionen zu korrigieren.

Schöne Grüße
OREADEN

himitsu 27. Dez 2008 15:41

Re: Wie baut man sowas wie writeln/sprintf selbst ?
 
CDECL:

OK, da könnte man dann via Assembler auslesen wieviele Parameter übergeben würden, aber es ist praktisch nicht möglich rauszufinden von welchem Typ diese Parameter sind.

Außerdem ist es in Delphi/Pascal schwer diese Parameter dann anzugeben, da dort via Funktions/Prozedur-Definition geprüft wird welche Parameter angegeben werden müssen und ohne Definition kann man da keine Parameter angeben/eintragen.

flash2009 27. Dez 2008 18:52

Re: Wie baut man sowas wie writeln/sprintf selbst ?
 
Liste der Anhänge anzeigen (Anzahl: 2)
Zitat:

Zitat von Cyf
Ich muss grad zugeben, dass ich nicht so ganz folgen kann, was du überhaupt erreichen willst, aber bedenke dass (im Bezug auf array of const):

a) ein PChar sich nicht mitten im Array befindet, sondern nur ein Zeiger auf den Anfang des Strings und dieser dann irgendwo im Speicher ist, dieser ist selbst nur insgesamt 4 Byte groß
b) in einem array of const jedes Element ein 8 Byte großes TVarRec ist
c) die im TVarRec übergeben Zeiger offenbar nur im Scope der Funktion gültig sind (siehe obiger Link)

Kannst du nochmal kurz und knapp erklären, warum du den Stackpointer manuell manipulieren musst und was es mit deiner DLL auf sich hat?


Ok ich hab alle Posts gelesen und werd mal hier wieder ansetzen.
-------------
Zu erst, ich hab nen Hook entwickelt der die Original Api(function) mit meiner function austauscht und die Original function trozdem noch verfügbar hält um Parameter oder rückgabe Werte zu ändern bevor der Original Code weiter läuft. (der hook ist fertig und eigentlich perfekt, einzige Einschränkung er kann nur functions hooken die in Visual Studio gemacht wurden aber das ist jetzt ne Nebensache.)
-------------
So jetzt zu sprinft die ich hooken will, eine function aus der c++ library
int sprintf ( char * str, const char * format, ... );

Rückgabewert (Integer), Parameter(Pointer auf einen Buffer,ein PChar, eine beliebige Anzahl an Parametern), das ganze ist Standard c++ also cdecl (Parameter von rechts nach links pushen und der Stack wir vom Code der die function called wieder bereinigt(add esp, .. ) )
-------------
So jetzt das Problem ist jetzt dieses ... hierbei handelt es sich um sowas wie nen Array aber das Problem ist das ein const array of.. , genau wie call by reference(var ...) nen Pointer auf ne Array struktur übergibt und das geht da leider nicht. Ohne const ist Delphi wiederum nicht in der Lage zu erkennen wie lang der Array ist.

Mein Ansatz war nun das erste Parameter zu nehmen zb nen DWord der hat 4 Byte und über ein @ and die Adresse zu kommen , somit hab ich die Adresse vom ersten Parameter alle weiteren sind immer 4 Bytes weiter.Das ist jetzt nicht schön aber funktioniert schonmal.

gruß flash

alzaimar 27. Dez 2008 18:58

Re: Wie baut man sowas wie writeln/sprintf selbst ?
 
Zitat:

Zitat von Oreaden
auch in Delphi kann man Funktionen und Proceduren als CDECL deklarieren, dann kann man eine unbestimmte Anzahl an Parametern übergeben.

...aber nicht deklarieren, oder kennst Du einen Trick, um den Compiler zu foppen? In die Richtung ging meine Bemerkung. In C# / C++ gibt es diese '...' Konstrukte in der Prozedurdeklaration (so irgendwie jedenfalls). Ich glaub, dem C-Compiler ist es eh egal, was Du auf den Stack schiebst.

@flash: Such doch mal nach dem Quelltext von sprintf.c. Sollte doch leicht zu finden sein. Die machen doch auch nur Stackpointerarithmetik. C kennt doch eh nur 'Call by Value', sodaß die Offsets durch den Format-String gegeben sind: %c = 1 byte, %d und %s = 4 byte... oder nicht?

flash2009 27. Dez 2008 19:57

Re: Wie baut man sowas wie writeln/sprintf selbst ?
 
ok so sieht das aus und es funktioniert, ist nich ganz schön aber läuft =)

Delphi-Quellcode:
type
  TSprintf = function(Buffer:Pointer;Format1:PChar;dwArray:DWord):Integer;cdecl;

implementation

var
 SprintfOrg : TSprintf;

function translate(Buffer: Pointer; Format1: PChar;dwFirst:DWord):Integer;cdecl;
var
  i : Integer;
  c : Integer;
  ptr1 : Pointer;
begin
  c:= 0;
  i := 0;
  for i := 0 to (length(Format1)-1) do
  if Format1[i] = '%' then
  begin
    inc(c);
    if Format1[i+1] = 's' then       // das ist nicht wichtig für die funktion
    begin                            //von hier
      ptr1 := @dwFirst;
      LogToFileStr(PChar(Pointer(Cardinal(ptr1)+4*(c-1) )^))
    end;                             //bis hier hin
    if Format1[i+1] = '%' then
      dec(c,2);
  end;
  asm
    push eax
    push ebx
    mov eax , c

  @putonstack:
    test eax , eax
    je @stackok
    lea ebx , dwFirst
    shl eax , 2
    add ebx , eax
    shr eax , 2
    sub ebx , 4       //first index = 0 not 1 !
    sub eax , 1
    push DWord ptr [ebx]
    jmp @putonstack

  @stackok:
    push Format1
    push Buffer
    call SprintfOrg    //call the orignal function
    mov Result , eax
    mov eax , c        //Paremeter Counter
    add eax , 2         //+ Buffer and Format
    shl eax , 2           //Size each 4bytes
    add esp , eax      //fix stack because of cdecl
    pop ebx
    pop eax
  end;
end;
greetz flash


Alle Zeitangaben in WEZ +1. Es ist jetzt 22:01 Uhr.

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