Re: Zugriff auf ein TImage einer externen Anwendung
Zitat:
|
Re: Zugriff auf ein TImage einer externen Anwendung
Das die von Canvas gezeichnete Grafik verschwindet ist mir schon aufgefallen, sollte aber hoffe ich kein Problem darstellen. Oder gibt es da noch ne Möglichkeit das ganze (ohne ein Timer) permanent sichtbar zu machen?
--- Hehe, da is mir grad was witziges eingefallen. Es müsste doch eigentlich klappen wenn ich ein eigenes Form für jedes Image erstelle es an die entsprechende Position schiebe, Border:=None und Always OnTop. Allerdings wäre dann das Orginal-Bild nicht mehr mittels OnClick ausführbar, das müsste man dann auch wieder durchschleifen, Fenster schließen und ein Mouse-Click event auf diesen Punkt ausführen. Ich glaub das wird jetzt aber etwas zu ausführlich ;-) Also bisher läuft alles was du fabriziert hast super! Das einzige was mir aufgefallen ist wenn ich versuche das Caption eines TButton auszulesen das sich dann die fremde Anwendung killt. Aber das war auch nur ein test und wir nicht in der Praxis angewand. Nur ne allgemeine Info. Die bisher letzte Frage die ich hätte bezieht sich auf Optimierung. Ich hab jetzt eine Prozedur geschrieben die das Event aus dem ButtonClick ausführt und ich nur den Komponenten/ClassNamen sowie den SearchType übergeben muss. Wie kann man das ganze noch mehr Optimieren und nur die zeilen ausführen die wirklich für die Datenbeschaffung gebraucht werden? D.h. die Handler Zuweisung kann ich ja z.B. außerhalb der Prozedur machen. Aber ich weiß hier eben nicht was speziell für das beziehen der Informationen nötig ist bzw. was zusammen hängt. Es sollen letztendlich mehrere Abfragen hintereinander durchgeführt werden, die Informationen gesammelt und dann weitere Aktion wie z.B. die Bilder zeichnen durchgeführt werden. Vielen Dank! Hier mal meine leicht modifizierte Prozedur:
Delphi-Quellcode:
procedure TForm1.GetData(ComponentName: string; SearchType : TSearchtype);
var mem:TMemory; lib:THandle; size:integer; process:cardinal; processid:cardinal; procmem:PMemory; tmp:cardinal; threadID:cardinal; thread:THandle; begin memo1.clear; myhandle:=findwindow(nil,'PImage'); //Beispielprogramm finden if myhandle=0 then exit; //mem ist der Record der nachher in den anderen Process kopiert wird mem.backwnd:=self.Handle; //Handle, damit wir Nachrichten zurückschicken können mem.backmsg:=mymsg; //Message-Nr., damit wir unsere Message wiederfinden mem.watchwnd:=myhandle; //Das Handle für getwindowlong mem.count:=6; //welches TControl (=mem.vgl)? (nur für Searchtype=sClassName) StrPCopy(@mem.vgl,ComponentName); mem.vgllength:=Length(ComponentName); //Länge des Vergleichsstrings mem.SearchType:=SearchType; //vgl vergleichen mit Classname oder Name oder Caption zurücksenden //kopieren der funktion injectthread in den Record size:=integer(@endpoint)-integer(@injectThread); move(injectthread,mem.thread,size); //EinsprungAdresse von 3 WinAPI-funktionen, die nacher benötigt werden //Die Adressen sind in jedem Process gleich lib:=getmodulehandle('user32.dll'); mem.Postmessage:=getprocaddress(lib,'PostMessageA'); mem.getwindowlong:=getprocaddress(lib,'GetWindowLongA'); lib:=getmodulehandle('kernel32.dll'); mem.exitthread:=getprocaddress(lib,'ExitThread'); //Thread-Record in anderen Process kopieren und mem.Thread starten getwindowthreadprocessid(myhandle,@processid); process:=openprocess(PROCESS_ALL_ACCESS,false,processid); //Speicher reservieren procmem:=virtualallocex(process,nil,sizeof(Tmemory),MEM_COMMIT,PAGE_EXECUTE_READWRITE); //Kopieren writeprocessmemory(process,procmem,@mem,sizeof(TMemory),tmp); //Starten thread:=createremotethread(process,nil,0,@procmem.thread,procmem,0,threadid); //Warten bis injectthread beendet ist waitforsingleobject(thread,infinite); //Speicher wieder freigeben closehandle(thread); virtualfreeex(process,procmem,0,mem_decommit); closehandle(process); end; procedure TForm1.Button1Click(Sender: TObject); begin GetData(Edit1.Text,sName); end; procedure TForm1.Button4Click(Sender: TObject); begin GetData(Edit2.Text,sCaption); end; |
Re: Zugriff auf ein TImage einer externen Anwendung
Zitat:
Aber die Sicherheiten der CPU umgehen, schaffe ich so einfach nicht, dazu brauche ich schon Windows und die WIN-API :mrgreen: |
Re: Zugriff auf ein TImage einer externen Anwendung
Zitat:
Zitat:
Zitat:
Aber ein TButton stammt von TWinControl ab, da kommt man meist recht einfach an den Text ran. Der Button hat ja schonmal ein "echtes" Window-Handle. Zitat:
Da ich nicht genau weis, in welcher Reihenfolge und zu welcher Zeit du die Infos brauchst, fällt es mir schwer darauf zu optimieren. Wenn ich allerdings die Sache mit WM_Paint mit reinbringe wird die Sache wahrscheinlich gleich ganz kompliziert aussehen, aber dann ist der Rest (mit den Images etc) Nebensache. Ich versuch mal was. |
Re: Zugriff auf ein TImage einer externen Anwendung
Lassen wir mal jetzt von dem ganzen Text alles außen vor und sprechen grad nur mal über die Frage der Optimierung.
Geh einfach mal davon aus das ich eine Prozedur haben werden die mittels der gewünschten Funktion den Text eines Labels (sagen wir mal Label_n von 3) holt, was in der Datenbank dazu sucht und ein Bild auf Image_n (ebenfalls von 3) zeichnet. D.h. die optimierte Prozedur soll dann n mal aufgerufen werden und eben dann auch nicht unbedingt jedes mal den Handle und ich sag mal alle unnötigen sachen neu setzten, sondern sich auf den Kern der Aufgabe konzentrieren und die Daten beschaffen. Wenn du möchtest kannst du gern mal was mit dem WM_Paint basteln, aber wie gesagt war nur eine Idee vorher mit den Forms und ich denke ich werde auch ein fixes Image nicht benötigen. Genaueres allerdings dazu kann ich erst morgen spät nachmittag geben, sobald ich das ganze mal mit der entsprechenden Anwendung testen kann. Nochmals vielen Dank! |
Re: Zugriff auf ein TImage einer externen Anwendung
Ok, jetzt wirds ganz konfus. Ich hab auch etwas länger gebraucht als ich eigentlich dachte. Man muss hier so hintenrum denken, da hatte ich ein paar kleine Fehlerchen drinn. Jetzt sind sie hoffentlich alle raus (einer ist ohne mein zutun verschwunden :gruebel: ).
Ich lass dich heute abend erstmal mit dem Code alleine. Erklärungen gibts später, oder auf direkte Anfrage:
Delphi-Quellcode:
Prinzipielle Idee:
unit U_getImage;
interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls, ExtCtrls; const mymsg=WM_User+1; type TSearchtype=(sImageSize,sCaption); //Ich hab die Bezeichnungen mal angepasst und sClassname gibts nicht mehr type PMemory=^TMemory; TMemory=packed record newwndProc:array[0..511] of char; //hier kommt die neue WndProc-Funktion für das fremde Window rein Thread:array[0..255] of char; //hier kommt die Thread-Funktion rein (die schließlich gestartet wird) getInfo:array[0..1023] of char; //hier kommt die Funktion zur Ermiitlung der Infos über Image und Label rein //API-Funktionen bzw. deren Addressen Postmessage:function(wnd:hwnd;msg,wparam,lparam:cardinal):bool;stdcall; exitthread:procedure(exitcode:integer);stdcall; sleep:procedure(ms:cardinal);stdcall; getwindowlong:function(wnd:hwnd;index:integer):pointer;stdcall; setwindowlong:function(wnd:hwnd;index:integer;newvalue:pointer):integer;stdcall; callwindowProc:function(proc:pointer;wnd:hwnd;msg,wparam,lparam:cardinal):integer;stdcall; globalgetatomname:function(nAtom:cardinal;buf:pointer;size:integer):integer;stdcall; getupdaterect:function(wnd:hwnd;rect:prect;erase:bool):bool;stdcall; getInfoFunc:procedure(memory:PMemory;wparam,lparam:cardinal);stdcall; //Funktionszeiger auf getInfo, also die Info-Funktion oldwindowProc:Pointer;//Zeiger auf die alte WndProc watchwnd:hwnd; backwnd:hwnd; backmsg:integer; updateRect:TRect; running:boolean; //Thread (und newwndProc) läuft bis running=false end; type TForm1 = class(TForm) Memo1: TMemo; Button1: TButton; Button2: TButton; procedure Button2Click(Sender: TObject); procedure GetMyMsg(var msg:TMessage);message mymsg; procedure GetMyCaption(var msg:TMessage);message mymsg+1; procedure GetCommandMsg(var msg:TMessage);message mymsg+2; procedure startObservation; procedure stopObservation; procedure communicate(vgl:string;Searchtype:TSearchtype); procedure Button1Click(Sender: TObject); procedure FormCreate(Sender: TObject); procedure FormClose(Sender: TObject; var Action: TCloseAction); private { Private-Deklarationen } myhandle:hwnd; process:cardinal; procmem:PMemory; thread:THandle; observed:boolean; public { Public-Deklarationen } end; var Form1: TForm1; implementation {$R *.dfm} function MyWndProc(Memory: PMemory; Wnd: hWnd; Msg,wParam,lParam: Integer): Integer; stdcall; forward; procedure WndProcDispatcher; //Funktion zum Finden des Records PMemory asm CALL @@1 @@1: POP EAX SUB EAX,5 POP EDX PUSH EAX PUSH EDX JMP MyWndProc end; function MyWndProc(Memory: PMemory; Wnd: hWnd; Msg,wParam,lParam: Integer): Integer; stdcall; //die neue WindowProc-funktion des fremden Formulars //die "alte" wird mittels CallWindowproc auch noch aufgerufen begin case msg of wm_close:begin memory^.running:=false; result:=memory^.CallWindowProc(memory^.oldwindowProc,wnd,msg,wparam,lparam); memory^.Postmessage(memory^.backwnd,memory^.backmsg+2,msg,0); end; wm_paint:begin memory^.getupdaterect(memory^.watchwnd,@memory^.updaterect,false); result:=memory^.CallWindowProc(memory^.oldwindowProc,wnd,msg,wparam,lparam); memory^.Postmessage(memory^.backwnd,memory^.backmsg+2,msg,cardinal(@memory^.updateRect)); end; mymsg: begin memory^.getinfofunc(memory,wparam,lparam); result:=1; end; mymsg+1: begin memory^.running:=false; result:=1; end; else result:=memory^.CallWindowProc(memory^.oldwindowProc,wnd,msg,wparam,lparam); end; end; function injectThread(memory:Pmemory):integer;stdcall; //Die eigentliche Thread-Funktion //Hauptaufgabe: einklinken und am Ende wieder ausklinken unserer neuen WndProc //dazwischen nur sleep, bis die Message mymsg+1 and die neue WndProc kommt begin memory^.running:=true; memory^.oldwindowProc:=memory^.getwindowlong(memory^.watchwnd,gwl_wndproc); memory^.getInfoFunc:=@memory^.getInfo; memory^.setwindowlong(memory^.watchwnd,gwl_wndproc,memory); while memory^.running do memory^.sleep(200); memory^.setwindowlong(memory^.watchwnd,gwl_wndproc,memory^.oldwindowproc); result:=0; memory^.exitthread(0); end; procedure Info(memory:Pmemory;wparam,lparam:cardinal); stdcall; //Die bisherige Funktion zum ermitteln und senden der Infos bezüglich TImage und TLabel var pi,p,pm:ppointer; i,a:integer; c:pchar; left,top,width,height:smallint; same:boolean; SearchType:TSearchType; vgl:array[0..31] of char; vgllength:integer; begin Searchtype:=TSearchType(lparam); //Da Strings nicht über Messages gesendet werden können, benötigen wir ein Atom vgllength:=memory^.GlobalGetAtomName(wparam,@vgl,32); wparam:=0; lparam:=0; p:=pointer(cardinal(memory^.oldwindowproc)+9); pm:=pointer(integer(p^)+16); for a:=0 to pinteger(integer(pm^)+8)^-1 do begin //von 0 bis componentcount p:=pointer(cardinal(pm^)+4); p:=pointer(cardinal(p^)+4*a); pi:=p; //pi^ ist Zeiger auf ein Objekt p:=pointer(cardinal(p^)+8); p:=p^; c:=pchar(p); same:=false; for i:=1 to vgllength do begin if vgl[i-1]<>c^ then break; same:=i=vgllength; inc(c); end; if same then begin if SearchType=sCaption then begin p:=pointer(cardinal(pi^)+$64); wparam:=cardinal(p^); c:=pchar(p^); while c^<>#0 do begin inc(c); inc(lparam); end; inc(memory^.backmsg); end else begin left:=pinteger(integer(pi^)+$40)^; top:=pinteger(integer(pi^)+$44)^; width:=pinteger(integer(pi^)+$48)^; height:=pinteger(integer(pi^)+$4C)^; wparam:=left*65536+top; lparam:=width*65536+height; end; break; end; end; //Ergebnis Nach Hause senden memory^.Postmessage(memory^.backwnd,memory^.backmsg,wparam,lparam); end; procedure endpoint; //ohne Funktion nur zum finden des Address-endes von Info asm nop end; procedure TForm1.Button2Click(Sender: TObject); begin close; end; procedure TForm1.startObservation; //1. Record wird mit allen Infos gefüllt und in den fremden Prozess geschrieben //2. Thread-Funktion aus dem Record wird gestartet var mem:TMemory; lib:THandle; size:integer; processid:cardinal; tmp:cardinal; threadID:cardinal; begin if observed then exit; //mem ist der Record der nachher in den anderen Process kopiert wird mem.backwnd:=self.Handle; //Handle, damit wir Nachrichten zurückschicken können mem.backmsg:=mymsg; //Message-Nr., damit wir unsere Message wiederfinden mem.watchwnd:=myhandle; //Das Handle für getwindowlong //kopieren der ganzen compilierten Funktionen size:=integer(@endpoint)-integer(@Info); move(Info,mem.getInfo,size); size:=integer(@info)-integer(@injectThread); move(injectthread,mem.Thread,size); size:=integer(@injectThread)-integer(@wndProcDispatcher); move(wndprocdispatcher,mem.newwndproc,size); //EinsprungAdresse von allen WinAPI-funktionen, die nacher benötigt werden //Die Adressen sind in jedem Process gleich lib:=getmodulehandle('user32.dll'); mem.Postmessage:=getprocaddress(lib,'PostMessageA'); mem.getwindowlong:=getprocaddress(lib,'GetWindowLongA'); mem.setwindowlong:=getprocaddress(lib,'SetWindowLongA'); mem.callwindowproc:=getprocaddress(lib,'CallWindowProcA'); mem.getupdaterect:=getprocaddress(lib,'GetUpdateRect'); lib:=getmodulehandle('kernel32.dll'); mem.exitthread:=getprocaddress(lib,'ExitThread'); mem.sleep:=getprocaddress(lib,'Sleep'); mem.globalgetatomname:=getprocaddress(lib,'GlobalGetAtomNameA'); //Thread-Record in anderen Process kopieren und mem.Thread starten getwindowthreadprocessid(myhandle,@processid); process:=openprocess(PROCESS_ALL_ACCESS,false,processid); //Speicher reservieren procmem:=virtualallocex(process,nil,sizeof(Tmemory),MEM_COMMIT,PAGE_EXECUTE_READWRITE); //Kopieren writeprocessmemory(process,procmem,@mem,sizeof(TMemory),tmp); //Starten thread:=createremotethread(process,nil,0,@procmem.thread,procmem,0,threadid); observed:=true; end; procedure tForm1.stopObservation; //Thread im fremden Process beenden (+WndProc zurücksetzen) und Speicher freigeben begin if not observed then exit; Postmessage(myHandle,mymsg+1,0,0); //Message zum beenden des Threads (über Variable "running") waitforsingleobject(thread,infinite); //Warten bis Thread beendet wurde //Handles und Speicher freigeben closehandle(thread); virtualfreeex(process,procmem,0,mem_decommit); //Speicher freigeben closehandle(process); observed:=false; end; procedure TForm1.GetMyMsg(var msg:TMessage); //Message über Image empfangen begin memo1.Lines.add(inttostr(msg.WParamlo)); memo1.Lines.add(inttostr(msg.WParamhi)); memo1.Lines.add(inttostr(msg.lParamlo)); memo1.Lines.add(inttostr(msg.lParamhi)); end; procedure TForm1.GetMyCaption(var msg:TMessage); //Message über TLabel empfangen var process,processID,tmp:cardinal; s:string; begin if myhandle=0 then exit; //in msg.wparam steht der Pointer auf das TLabel.caption im anderen Process //in msg.lparam die Länge des TCaption getwindowthreadprocessid(myhandle,@processid); process:=openprocess(PROCESS_VM_READ,false,processid); setlength(s,msg.LParam); readprocessmemory(process,pointer(msg.wparam),@s[1],msg.lparam,tmp); closehandle(process); memo1.Lines.add(s); end; procedure Tform1.getcommandmsg(var msg:Tmessage); //Message empfangen wenn des fremde Window eine WM_Paint oder WM_close bekommt var process,processID,tmp:cardinal; rec:Trect; begin case msg.WParam of wm_paint:begin memo1.lines.Add('onPaint'); if myhandle=0 then exit; getwindowthreadprocessid(myhandle,@processid); process:=openprocess(PROCESS_VM_READ,false,processid); readprocessmemory(process,pointer(msg.lparam),@rec,sizeof(rec),tmp); closehandle(process); //Der Bereich der neu gezeichnet werden muss memo1.lines.add(inttostr(rec.Left)); memo1.lines.add(inttostr(rec.top)); memo1.lines.add(inttostr(rec.right)); memo1.lines.add(inttostr(rec.bottom)); end; wm_close:begin observed:=false; close; end; end; end; procedure TForm1.communicate(vgl:string;Searchtype:TsearchType); //Eine Anfrage an unsere neue WndProc starten var wparam,lparam:cardinal; begin wparam:=globaladdAtom(pchar(vgl)); //String in ein Atom laden; Nr. des Atoms in wparam lparam:=cardinal(Searchtype); //Searchtype in lparam sendmessage(myhandle,mymsg,wparam,lparam); //Message für unsere newwndproc; und warten bis sie dort verarbeitet wurde globaldeleteatom(wparam); //Atom wieder löschen end; procedure TForm1.Button1Click(Sender: TObject); begin //eine Besipielanfrage an die newwndProc communicate('Image1',sImageSize); //hier oder in einer anderen Funktion können noch mehr solche Anfragen gestartet werden end; procedure TForm1.FormCreate(Sender: TObject); begin //myhandle setzen und dann startobservation memo1.clear; myhandle:=findwindow(nil,'PImage'); if myhandle=0 then exit; startobservation; end; procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction); begin //nicht vergessen! stopobservation; end; end. Ich lasse den Thread (der mit StartObservation gestartet wird) in dem anderen Process einfach bestehen und lösche ihn erst mit StopObservation. Dann leite ich die windowProc des Formulars um und bekomme alle Messages an das Formular mit (meine eigenen und z.B. WM_Paint). Deswegen kann ich jetzt mit Messages mit meinem Thread kommunizieren. die "Communicate" kannst du also so oft wie du willst aufrufen. Du musst halt nur zuerst startobservation aufrufen und davor das myHandle setzen. Vergiss nicht stopobservation am Ende, wenn du auch keine WM_Paint Benachrichtigungen mehr willst. ansonsten bleiben Speicherlöcher in dem fremden Process. PS: WM_close (also wenn das fremde Programm geschlossen wird) schicke ich dir auch noch mit. (Ich hab dann auch gleich mal mein Programm geschlossen.) PPS: Inspiration und Idee dazu von Hagen (negaH) : hier auf Seite 4 Edit: Kommentare ergänzt Edit3: alles zu GetUpdateRect ergänzt. Damit kannst du feststellen, ob du überhaupt neu zeichnen musst. Zum vergleichen gibts die Funktion "IntersectRect" von Delphi. |
Re: Zugriff auf ein TImage einer externen Anwendung
Jungs, das ist einfach nur krank. :o
|
Re: Zugriff auf ein TImage einer externen Anwendung
Zitat:
|
Re: Zugriff auf ein TImage einer externen Anwendung
Also ich hab jetzt mal die mein Prog mit der richtigen Software getestet und jetzt bin ich doch etwas enttäuscht.
Ich hab zwar noch den Code verwendet den du als vorletztes gepostet hast, aber das düfte ja grundlegend eigentlich kein Unterschied machen. Das FindWindow klappt ja noch, aber wenn man dann versucht die Daten zu holen, sei es nun vom TImage oder TLabel, schmiert mir der fremde Prozess ab. Ideen? |
Re: Zugriff auf ein TImage einer externen Anwendung
Zitat:
Die Bedingung war ja, dass dein Programm kompatibel zur VCL in D7 ist. Ansonsten funktioniert die Adressrechnung nicht und es kommt zur EAccessViolation. Die könnten wir zwar noch abfangen, aber ein Ergebnis bekommen wir trotzdem nicht. Ich kann ja noch mal drüber nachdenken, aber außer dass man entweder die VCL (vielleicht ist es ja Delphi2 oder VisualBasic o.ä.) ermittelt und dann über diese Entwicklungsumgebung die Adressen herausfindet fällt mir nur noch Patchen des Programms ein. Aber sowas hab ich das letzte mal unter MS-DOS gemacht. Und es ist alles andere als leichter geworden. Was ist denn das überhaupt für ein Programm und warum willst du da andere Bilder drüberlegen? Nochmal zusammengefasst: Das gleiche Programm funktionert bei einem BeispielProgramm und bei dem Originalprogramm nicht. Dann sehe ich auch kaum Chancen. Du musst es ja so sehen, dass ich z.B. bei einem record (kann auch eine Klasse sein; Was es bei uns eigentlich auch immer ist.):
Delphi-Quellcode:
..., wenn ich den Pointer meinZeiger ermitteln will und ich "nur" den Zeiger auf den Record habe, dann addiere ich einfach alles was vor "meinZeiger" liegt. In dem Fall: 1 x Integer ist 1 x 4 Bytes.
type TTest=record
irgendetwas:integer; meinZeiger:pointer;// den will ich errechnen .... end; Und der Zeiger zeigt dann auf die nächste Klasse, da brauch ich dann auch wieder eine bestimmte Variable bzw. Eigenschaft und addiere wieder eine bestimmte Zahl usw. Und diese Zahlen hab ich natürlich aus meiner VCL von D7 bekommen. Wenn sich an der Reihenfolge oder sonstwie irgendetwas anders ist funktioniert das ganze Konzept nicht mehr. Wenn da eben mal zufällig kein Pointer steht, ist das zwar erstmal egal. Ich lese jede Variable als Pointer, aber dann lande ich irgendwo (ist ja eher eine Zufallsadresse), und das ist zu 99,9% da, wo ich keine Zugriffsrechte hab ->EAccessVirolation. Um ne Hausnummer zu nennen. Der Adressraum beträgt 4GB und ein normales Programm benötigt vielleicht 10 bis 20 kB, der Rest zwischen 20kB und 4GB führt zu EAccessViolation. |
Alle Zeitangaben in WEZ +1. Es ist jetzt 11:22 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