Autor Beitrag
Chryzler
ontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic star
Beiträge: 1097
Erhaltene Danke: 2



BeitragVerfasst: Mi 27.12.06 11:35 
Hi!

Ich muss mithilfe einer Prozedur jede beliebige andere Prozedur mit jedem beliebigen Parameter aufrufen können. Ich dachte mir, ich übergebe der Prozedur die Addresse der Funktion, die ich aufrufen will, und ein dynamisches Array mit Pointern, für alle Argumente.
ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
procedure CallFunc(FuncPtr: Pointer; Args: array of TArgument; ReturnValue: Pointer);
begin
  // FuncPtr = Funktionsaddresse
  // Args = Parameter
  // ReturnValue = evtl. Rückgabewert der Funktion
end;

Das erste Problem, das ich hab, ist, dass die Argumente ja nicht alle die gleichen Typen sein müssen. Das heißt, ich müsste theoretisch TArgument so definieren, oder?
ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
type
  TArgument = record
    Ptr: Pointer;
    Size: Integer;
  end;

Das nächste Problem ist, dass ich nicht weiß, wie ich eine Funktion allein durch deren Addresse und den Addressen der Paramter aufrufen kann. Ich dachte mir da einen Umweg durch Assembler aus:
ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
procedure CallFunc(FuncPtr: Pointer; Args: array of TArgument; ReturnValue: Pointer);
begin
  for i := High(Args) downto 0 do // alle Argumente rückwärts pushen
    asm
      PUSH Args[i].Ptr ???        // was muss hier hin? wie sage ich PUSH, wieviel er pushen soll (Args[i].Length)
    end;
  asm
    CALL FuncPtr;                 // funktioniert perfekt
  end;    
end;

Die Funktion kann ich ja ganz leicht durch CALL FuncPtr aufrufen, aber wie kann ich die Argumente vorher übergeben? Meines Wissens nach, muss man doch alle Argumente rückwärts per PUSH auf den Stack legen, oder?
Nächstes Problem: Wie bekomme ich den Rückgabewert der Funktion (wenn sie denn eine ist), und wie schreibe ich den dann an die Stelle, wo ReturnValue hinzeigt? Ich meine, wo wird der Rückgabe wert gespeichert? In einem Register, wie ESI, oder so? Dann könnte ich doch theoretisch so schreiben:
ausblenden Delphi-Quelltext
1:
2:
3:
asm
  MOV ReturnValue, ESI;
end;


Alles recht kompliziert :(
Ist das bisher von der Idee so richtig, oder geht das ganze viel einfacher?

Chryzler :D
Assembler war noch nie meine Spezialität
alzaimar
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 2889
Erhaltene Danke: 13

W2000, XP
D6E, BDS2006A, DevExpress
BeitragVerfasst: Mi 27.12.06 12:52 
Wozu benötigst Du das? Vielleicht gibt es eine andere, wesentlich sicherere Möglichkeit?
Ich habe das so gelöst:

Alle Prozeduren haben die gleiche Parameterliste:
ausblenden Delphi-Quelltext
1:
Prozedure SampleProc (Const inParam : TParamList; Out outParam : TParamList);					


Wie TParamlist implementiert ist, bleibt Dir überlassen. So bist Du auf der sicheren Seite und zerschießt Dir deine Applikation nicht.

Der Aufruf ist dann denkbar einfach.

_________________
Na denn, dann. Bis dann, denn.
Chryzler Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic star
Beiträge: 1097
Erhaltene Danke: 2



BeitragVerfasst: Mi 27.12.06 13:07 
Wenn ich sehr viele Funktionen aus einer (später sogar aus jeder beliebigen alle Funktionen) DLL in meinem Programm aufrufen möchte, müsste ich entweder alle statisch deklarieren, oder dynamisch. Statisch auf keinen Fall, da ja jede beliebige DLL unterstützt werden soll. Also muss ich sie dynamisch per LoadLibrary einbinden. Das Programm soll immer die Funktion aufrufen, deren Name zum Beispiel in Edit1 steht. Ich kann doch nicht 200 Prozeduren allein von kernel32.dll dynamisch in mein Prog nacheinander laden. Deshalb dachte ich mir das so:
ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
var
  libKernel32, libUser32, libNetApi32, .... : HINSTANCE;

implementation

procedure TForm1.FormCreate(Sender: TObject);
begin
  libKernel32 := LoadLibrary('kernel32.dll');
  libUser32 := LoadLibrary('user32.dll');
  libNetApi32 := LoadLibrary('netapi32.dll');
  ...
end;

procedure TForm1.Button1Click(Sender: TObject);
var
  Addr: Pointer;
begin
  Addr := GetProcAddress(libKernel32, Edit1.Text);
  CallFunc(Addr, ...);  
end;

Das mit der TParamList hab ich jetzt nicht wirklich verstanden.
alzaimar
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 2889
Erhaltene Danke: 13

W2000, XP
D6E, BDS2006A, DevExpress
BeitragVerfasst: Mi 27.12.06 22:23 
Ah... Wie und wo hinterlegst Du die Parameter? Type? Reihenfolge? Aufrufkonvention? (rethorische Frage). Wo Du das machst, kannst du auch einen Wrapper für die von mir vorgeschlagene Variante implementieren. Aber ich gebe zu: Für beliebige DLL ist das wirklich nicht optimal.

Aber ich frage nochmal: Was willst du eigentlich erreichen? Warum willst du beliebige Routinen von beliebigen DLL aufrufen? Würde mich interessieren ...

_________________
Na denn, dann. Bis dann, denn.
Chryzler Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic star
Beiträge: 1097
Erhaltene Danke: 2



BeitragVerfasst: Do 28.12.06 12:28 
Als ich vor ein paar Tagen das MSDN durchstöbert habe und so einige interessante API-Funktionen gefunden habe, wollte ich die mal kurz zum Testen ausprobieren. Da dachte ich mir, Schade eigentlich, dass der Command-Prompt von Windows keine solchen Befehle versteht, wie zum Beispiel:
ausblenden Quelltext
1:
NetMessageBufferSend("127.0.0.1", ...)					

Dann kam ich auf die Idee eine "API Console" zu basteln, mit integriertem kleinen Parser für die Befehle. Es sollte halt ne Liste im Programm geben, wo standardmäßig schon alle Windows-DLLs drin stehen, wo man aber auch seine eigenen DLLs hinzufügen kann. Die API Console soll dann in der Liste alle DLLs durchgehen, automatisch herausfinden, in welcher DLL die Funktion, die aufgerufen werden soll, steht, und diese dann mit den angegebenen Parametern starten.
Mit so nem Programm könnte man dann jede Funktion schnell testen, also praktisch ein erweitertes RunDLL. Dann kam ich noch auf die Idee, so ne kleine Deklarationshilfe für die wichtigsten Funktionen einzubauen:
ausblenden Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
>NetMessageBufferSend

[netapi32.dll]
function NetMessageBufferSend(servername: PChar; msgname: PChar; fromname: PChar; buf: PByte; buflen: DWORD): DWORD;

>FlashWindow

[user32.dll]
function FlashWindow(hWnd: HWND): DWORD;

>

So in der Art hatte ich mir das gedacht. Braucht man öfter als man denkt (ich jedenfalls).
alzaimar
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 2889
Erhaltene Danke: 13

W2000, XP
D6E, BDS2006A, DevExpress
BeitragVerfasst: Do 28.12.06 12:34 
Ah, ok, dann ist meine Idee vielleicht nicht so gut.

Schon mal an eine Skript-Engine gedacht? ... Die übernimmt das Parsen und mit ein wenig Glück auch das dynamische Laden der DLL sowie Aufrufen der Funktion.

Ich meine, wenn Du eine gute Engine findest, sollte Sie die Deklaration verstehen. RemObjects (PasScript?) Oder DWS (sourceforge) wären eine gute Basis.

Kann das nicht sowieso der Windows scripting host?

_________________
Na denn, dann. Bis dann, denn.
jaenicke
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 19326
Erhaltene Danke: 1749

W11 x64 (Chrome, Edge)
Delphi 12 Pro, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
BeitragVerfasst: Do 28.12.06 12:52 
Und WSH kann man ja auch in Delphi nutzen. Dann kann man damit auch die eigene Anwendung steuern.
www.ekassw.com/wshctrl/
alias5000
ontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic star
Beiträge: 2145

WinXP Prof SP2, Ubuntu 9.04
C/C++(Code::Blocks, VS.NET),A51(Keil),Object Pascal(D2005PE, Turbo Delphi Explorer) C# (VS 2008 Express)
BeitragVerfasst: Do 28.12.06 14:17 
Oder einfach TJvInterpreter :?:
Ein Tutorial dazu findest du hier: wiki.delphigl.com/in...php/Tutorial#Skripte

Gruß alias5000

_________________
Programmers never die, they just GOSUB without RETURN
Chryzler Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic star
Beiträge: 1097
Erhaltene Danke: 2



BeitragVerfasst: Do 28.12.06 14:22 
Ähm, ich muss doch eigentlich eine bestimmte Funktion mit Parametern aufrufen, was nützen mir da Skripte in meiner Anwendung? Kapier ich jetzt irgendwie ned :(
alias5000
ontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic star
Beiträge: 2145

WinXP Prof SP2, Ubuntu 9.04
C/C++(Code::Blocks, VS.NET),A51(Keil),Object Pascal(D2005PE, Turbo Delphi Explorer) C# (VS 2008 Express)
BeitragVerfasst: Do 28.12.06 14:24 
Du könntest dann für eine bestimmte DLL auch die Skripte mitgeben, die die DLL aufrufen

_________________
Programmers never die, they just GOSUB without RETURN
Chryzler Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic star
Beiträge: 1097
Erhaltene Danke: 2



BeitragVerfasst: Do 28.12.06 14:27 
Ich muss dann also für jede Prozedur in jeder DLL ein eigenes Skript schreiben, das die Prozedur aufruft? Und wie soll ich dann da Parameter übergeben? Kenn mich mit Skripten null aus. Ich glaube, ich such mir da ne andere Lösung.
alias5000
ontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic star
Beiträge: 2145

WinXP Prof SP2, Ubuntu 9.04
C/C++(Code::Blocks, VS.NET),A51(Keil),Object Pascal(D2005PE, Turbo Delphi Explorer) C# (VS 2008 Express)
BeitragVerfasst: Do 28.12.06 14:33 
user profile iconChryzler hat folgendes geschrieben:
Kenn mich mit Skripten null aus.


Für diesen Fall hab ich dir ja das Tutorial verlinkt. Ist gut und leicht verständlich geschrieben.

Gruß alias5000

_________________
Programmers never die, they just GOSUB without RETURN
alzaimar
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 2889
Erhaltene Danke: 13

W2000, XP
D6E, BDS2006A, DevExpress
BeitragVerfasst: Do 28.12.06 14:40 
Chrysler: Das musst du doch sowieso:

user profile iconChryzler hat folgendes geschrieben:
...Dann kam ich noch auf die Idee, so ne kleine Deklarationshilfe für die wichtigsten Funktionen einzubauen:
ausblenden Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
>NetMessageBufferSend

[netapi32.dll]
function NetMessageBufferSend(servername: PChar; msgname: PChar; fromname: PChar; buf: PByte; buflen: DWORD): DWORD;

>FlashWindow

[user32.dll]
function FlashWindow(hWnd: HWND): DWORD;

>




Hmm.. ich würde mir so eine Deklarationshilfe etwas strukturierter aufbauen, dann sparst du dir das parsen, so z.B.:
ausblenden Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
[NetMessagebufferSend]
DLL=NetApi32.dll
Returns = DWORD
Param01 = PChar
Param02 = PChar
Param03 = PChar
Param04 = PByte
Param05 = DWORD
...

Deine Kommandozeile formst Du anhand der Parametertypangaben so um, das die PChar schön in Quotes eingeschlossen sind etc.
Desweiteren erzeugst Du dir aus den selben Parametertypangaben ein Miniskript, das eventuelle VAR-Parameter sowie Rückgabewerte berücksichtigst.
Dann beppst Du hinten an das Miniskript deinen gebastelten Aufruf rein, ruftst den Skriptinterpreter mit dem Miniskript auf (der ruft seinerseits die DLL, OCX, COM etc. auf). Anschließend wertest Du etwaige Rückgabe- und VAR-Parameter aus.

Fertig.

Ich denke, einen Nachmittag und Du bist durch (ohne die 150.000 API-Definitionen). Aber dafür könntest Du dir auch einen Parser schreiben, der Die ganzen .h-Dateien übersetzt...

Oder Du verwendest gleich einen C-Interpeter ;-)

_________________
Na denn, dann. Bis dann, denn.