Zugriff auf ein TImage einer externen Anwendung
Hallo,
ist es möglich das TImage einer anderen Applikation zu kontrollieren, bzw. andere Bilder hinein zu schreiben? Wenn ja, wie? Ich weiß sicher das es sich um ein TImage handelt und den Namen habe ich auch, jetzt stellt sich mir die Frage wie ich darauf zugreifen kann. Ich habe so mit Handles und WindowsAPI keine Erfahrungen. Vielen Dank! Gruß KoS |
Re: Zugriff auf ein TImage einer externen Anwendung
Das TImage selber kannst du dir nicht holen. Aber du kannst dir den Device Context des Fensters holen und da drauf rumkritzeln, bzw. du kapselst den DC in ein eigenes TCanvas.
Erklär mal genauer, wo du rummalen willst! |
Re: Zugriff auf ein TImage einer externen Anwendung
Hm wie erklär ich das jetzt am besten ...
Ich habe hier ein Programm welches ein Formular mit mehreren TImages hat, und ich möchte nun den Inhalt der TImages ändern, d.h. also ein neues Bild rein setzten. |
Re: Zugriff auf ein TImage einer externen Anwendung
Das Problem ist, dass TImage ein TGraphicControl ist und somit kein eigenes Window-Handle besitzt. Du kannst eigentlich nur auf dass dem TImage zugrundeliegende Window zeichnen. Ansonsten müsste man sich sehr weit in die Datenstruktur von Delphi einarbeiten.
|
Re: Zugriff auf ein TImage einer externen Anwendung
Also ich habe mal ne "Idee" das was ich hier schreibe kann totaler Schwachsinn sein aaaaber...
TImage.Picture.BITMAP und TImage.CANVAS und TImage.Picture.Bitmap.CANVAS haben alle Handles... Vielleicht wäre da ja was zu machen... Ich wüsste im ersten Moment auch nicht wie aaaaber ich gucke mal (*juhu endlich was zu tun bei meiner langeweile :)*) :coder: Gruß Neutral General |
Re: Zugriff auf ein TImage einer externen Anwendung
Hallo,
@sirius: Im grunde wäre das ja relativ egal, wenn man dann die Positionen Top und Left bekommen könnte um dann darüber ein Bild zu zeichnen, da die TImages immer einen gewissen Abstand zueinander haben. @Neutral General: Wäre super wenn du ein weg finden würdest die TImages anzuprechen. |
Re: Zugriff auf ein TImage einer externen Anwendung
@General
Das sind Device Context Handles und keine Window Handles. |
Re: Zugriff auf ein TImage einer externen Anwendung
Zwischenfrage, ist das sicher das es sich bei den Bildern um ein TImage handelt ?
Wenn das Bild (ist und bleibt ein Bitmap wenn es Windows zeichnet) direkt gezeichnet wird in einer sagen wir mal WM_PAINT - Message ist es nicht möglich zu "covern". |
Re: Zugriff auf ein TImage einer externen Anwendung
Ja, es ist sicher das es sich um ein TImage handelt, weil ich das Form schonmal mittels ResHack unter die Lupe genommen habe.
Ob das TImage ein *.bmp oder ein *.jpg darstellt, wenn es denn überhaupt was darstellt ist ja egal? Edit: ähm ... mal ein anderer Ansatz, es dürfte aber möglich sein direkt etwas auf das Form ein Bild zu malen wenn man entsprechende Positionsangaben machen kann? Da die TImages ja immer auf der selben Position sitzen und lediglich die Bilder ändern. |
Re: Zugriff auf ein TImage einer externen Anwendung
Auf einem Window rumzeichnen geht so:
Delphi-Quellcode:
var dc:hdc; //Device Context Handle
Canvas:Tcanvas; myhandle:THandle; begin //Devicecontext eines Window Handles besorgen //myhandle:=form1.Handle; //so //myhandle:=button2.Handle; //oder so //myhandle:=getforegroundwindow; //oder so myhandle:=getdesktopwindow; //oder so oder mit findwindow ... dc:=getwindowdc(myhandle); //TCanvas auf den Device Context setzen canvas:=TCanvas.Create; canvas.Handle:=dc; //Hier kann man ganz nach belieben malen canvas.Brush.Color:=clbtnface; canvas.Font.Color:=clred; canvas.Font.Size:=15; canvas.TextOut(0,0,'Hallo'); //Speicher wieder freigeben canvas.Free; releasedc(myhandle,dc); end; Ergänzung: Mit folgendem Code kann man ein TImage finden, wenn man nur das WindowHandle, des übergeordneten Fensters hat: (mein Testcode)
Delphi-Quellcode:
Ich hab jetzt keine weiteren Kommentare, da ich nur ein wenig rumgespielt habe. Letztenendes macht es nix andere, als:
var pi,p,pm:ppointer;
s:string; c:pchar; i,a:integer; begin memo1.clear; myhandle:=form1.handle; //myhandle ist das einzige, was ich benötige p:=pointer(getwindowlong(myhandle,gwl_wndproc)+35); pm:=p; p:=p^; p:=pointer(integer(p^)-44); p:=p^; c:=pchar(p); s:=''; inc(c); for i:=1 to pbyte(p)^ do begin s:=s+c^; inc(c); end; memo1.lines.Add(s); //classname des Windows pm:=pointer(integer(pm^)+16); for a:=0 to pinteger(integer(pm^)+8)^-1 do begin //von 0 bis componentcount p:=pointer(integer(pm^)+4); p:=pointer(integer(p^)+4*a); pi:=p; //pi^ ist Zeiger auf ein Objekt p:=p^; p:=pointer(integer(p^)-44); p:=p^; c:=pchar(p); s:=''; inc(c); for i:=1 to pbyte(p)^ do begin s:=s+c^; inc(c); end; memo1.lines.Add(s); //classname einer Komponente if s='TImage' then begin memo1.lines.Add(inttostr(pinteger(integer(pi^)+$40)^)); //left memo1.lines.Add(inttostr(pinteger(integer(pi^)+$44)^)); //top memo1.lines.Add(inttostr(pinteger(integer(pi^)+$48)^)); //widht memo1.lines.Add(inttostr(pinteger(integer(pi^)+$4C)^)); //height end; end;
Delphi-Quellcode:
Allerdings passiert dies ohne Zuhilfenahme der VCL, bezieht sich allerdings strikt darauf.
for i:=0 to Form1.componentcount-1 do begin
memo1.lines.add(Form1.component[i].classname); //Und wenn classname TImage ist, dann noch die Abmaße aufschreiben end; Das funktioniert also für alle Programme, die mit Hilfe der VCL programmiert wurden. Allerdings wird das ganze noch etwas umständlicher, wenn man sich in einem fremden Prozess befindet (siehe virtueller Addressraum). PS: Diese ganze Type-casting zwischen integer und pointer könnte man sich natürlich mit ASM sparen. |
Re: Zugriff auf ein TImage einer externen Anwendung
Hallo,
Zitat:
Delphi-Quellcode:
Was mir jetzt mal sagt das eigentlich nur die "Hauptklassen" angezeigt nicht jedoch die Komponenten auf dem Form.
TApplication
THintWindow TForm1 Wenn ich jedoch direkt folgendes mache:
Delphi-Quellcode:
Erhalte ich nur die Komponenten von dem Fenster:
for i:=0 to Form1.componentcount-1 do begin
memo1.lines.add(Form1.component[i].classname); //Und wenn classname TImage ist, dann noch die Abmaße aufschreiben end;
Delphi-Quellcode:
Hab ich irgendwas vergessen oder warum funktioniert das nicht so wie es soll?
TImage
TImage TMemo TButton |
Re: Zugriff auf ein TImage einer externen Anwendung
Hmm, rätselhaft. vorhin ging es so bei mir jetzt hatte ich dasselbe Ergebnis und muss die eine Zeile ändern:
Delphi-Quellcode:
Aus der 35 ist eine 9 geworden. das ist halt meine Referenz. Ich hab leider den Code von heut mittag verworfen. Da kann ich leider nicht überprüfen warum das vorhin eine 35 war. Der Schlüssel liegt in der Funktion MakeObjectInstance der Unit Classes, aber da hab ich jetzt grad keine Lust mich weiter reinzuarbeiten. Vielleicht klappt die 9 ja ab jetzt :| .
p:=pointer(getwindowlong(myhandle,gwl_wndproc)+9);
Man könnte natürlich auch Tform1 nehmen und dann weiter suchen. aber das ist nicht gerade sinnvoll. |
Re: Zugriff auf ein TImage einer externen Anwendung
Ahhhja ... mit der 9 sieht das ganze doch gleich viel besser aus! Vielen Dank!
Jetzt hab ich aber doch noch 2 Fragen, zum einen - wird es doch auch bestimmt möglich sein den Namen es TImages neben dem Handle ausfindig zu machen? Damit ich dann besser die TImages unterscheiden kann und wie du beim "s='TImage'" eine if-Abfrage machen kann. Zum anderen, wenn ich den ganzen Code mit
Delphi-Quellcode:
mache, bekomm ich eine Zugriffsverletzung, sobald ein anderes Programm im Vordergrund ist, also eben jenes was ich "auslesen" möchte.
myhandle:=getforegroundwindow;
Realisiert mit einem Timer der alle 5 Sec. die Button Prozedur ausführt. Funktioniert aber auf dem eigenen Form. Dazu noch eine Idee? Die ganze WinAPI geschichte was du hier bisher aufgebracht hast ist für mich noch ein buch mit 7 Siegeln. ;-) |
Re: Zugriff auf ein TImage einer externen Anwendung
Ich hab diesen code, von dem größtenteils Bahnhof verstehe :lol: , mal etwas umgeschrieben, sodass er den namen der komponente anzeigt. Allerdings funktioniert das nur bei formularen in der eigenen anwendung, bei formularen aus anderen delphi anwendungen streikt er, und zwar bei
Delphi-Quellcode:
Ich habe mal gehört, dass die VCL von zwei Anwendungen nicht miteinander kompatibel ist. Liegt das daran?
p:=p^;
Delphi-Quellcode:
procedure TForm1.Button1Click(Sender: TObject);
var pi,p,pm:ppointer; s:string; c:pchar; i,a:integer; obj: tPersistent; myhandle: hwnd; begin memo1.clear; myhandle:=...; //myhandle ist das einzige, was ich benötige p:=pointer(getwindowlong(myhandle,gwl_wndproc)+9); pm:=p; p:=p^; p:=pointer(integer(p^)-44); p:=p^; c:=pchar(p); s:=''; inc(c); for i:=1 to pbyte(p)^ do begin s:=s+c^; inc(c); end; memo1.lines.Add(s); //classname des Windows pm:=pointer(integer(pm^)+16); for a:=0 to pinteger(integer(pm^)+8)^-1 do begin //von 0 bis componentcount p:=pointer(integer(pm^)+4); p:=pointer(integer(p^)+4*a); pi:=p; //pi^ ist Zeiger auf ein Objekt obj := TPersistent(pi^); memo1.lines.add(obj.GetNamePath); end; end; |
Re: Zugriff auf ein TImage einer externen Anwendung
@namenlozer
In dem Fall kannst du noch viel früher ansetzen. Bei der Adresse der WndProc-funktion (+9 Bytes dahinter) steht die Adresse eines TWinControls. Nämlich genau das, welches die Messages bekommt. Demnach könnte man ab hier mit VCL-Komponenten arbeiten:
Delphi-Quellcode:
Aber das war nicht sinn der Sache. Ich wollte ja komplett ohne VCL und nur mit Adressen/Pointern auskommen, weil (und jetzt kommt der Knackpunkt) man damit (hoffentlich :mrgreen: ) auch in fremden Prozessen recht einfach lesen kann. Ihr habt ja beide schon bemerkt, dass der Code nur im eigenen Programm/Prozess funktioniert. Das ist auch gut so. Denn schließlich ist die CPU im Protected Mode und Windows ein sicheres Betriebsystem :party: , wo man nicht einfach in fremden Adressbereichen lesen und schreiben kann/darf. Das wäre ja zu schön, da könnte ja jeder Trojaner einfach so eueren Programmspeicher (inkl. Passwörter) auslesen. :dancer2:
p:=pointer(getwindowlong(myhandle,gwl_wndproc)+9);
obj:=Twincontrol(p^); memo1.Lines.add(obj.Name); //.. und dann weiter über obj.componentcount bzw. obj.component[0..obj.componentcount] Ok, soviel Witz am Rande, kommen wir zurück zur Realität (ihr wisst anscheinend noch nicht, was virtueller Adressraum bedeutet -->unbedingt mal bei Wiki oder so reinlesen). Und die Realität kennt Windows und Windows kenn: -openprocess -readprocessmemory -writeprocessmemory So, und damit können wir in einem fremden Prozess auch mitlesen,... irgendwie. Ich schau mal. Ansonsten, was ich da oben bisher gemacht habe, sieht nur kompliziert aus, ist aber nur ein wenig Adressrechnung. Ich springe immer von einer Adresse zur nächsten und rechne zwischendruch konstante Werte dazu (entsprechend wie die VCL aufgebaut ist). Als Startadresse nehme ich mir die WndProc-Funktion, die ich ja recht einfach aus dem Window-Handle bekomme (getwindowlong). |
Re: Zugriff auf ein TImage einer externen Anwendung
Ok, bei mir getestet und es funktioniert:
Delphi-Quellcode:
Man kann auch nach ClassName suchen (dann ist mem.count notwendig, da es ja mehrere TImages geben kann):
unit U_getImage;
interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls, ExtCtrls; const mymsg=WM_User+1; type TSearchtype=(sClassName,sName); type PMemory=^TMemory; TMemory=packed record Thread:array[0..1023] of char; Postmessage:function(wnd:hwnd;msg,wparam,lparam:cardinal):bool;stdcall; exitthread:procedure(exitcode:integer);stdcall; getwindowlong:function(wnd:hwnd;index:integer):cardinal;stdcall; watchwnd:hwnd; backwnd:hwnd; backmsg:integer; count:integer; SearchType:TSearchtype; vgl:array[0..31] of char; //kann noch verlängert werden vgllength:integer; end; type TForm1 = class(TForm) Memo1: TMemo; Button1: TButton; Button2: TButton; procedure Button2Click(Sender: TObject); procedure Button1Click(Sender: TObject); procedure GetMyMsg(var msg:TMessage);message mymsg; private { Private-Deklarationen } public { Public-Deklarationen } end; var Form1: TForm1; implementation {$R *.dfm} function injectThread(memory:Pmemory):integer; stdcall; {Diese Funktion landet nachher in einem fremden Process. Deswegen dürfen hier keine Funktionen von Delphi benutzt werden (inkl. Stringoperationen, VCL, etc.).} var pi,p,pm:ppointer; i,a:integer; c:pchar; left,top,width,height:smallint; same:boolean; wparam,lparam:cardinal; begin lefT:=0; top:=0; width:=0; height:=0; p:=pointer(memory^.getwindowlong(memory^.watchwnd,gwl_wndproc)+9); //Ab hier wie gehabt, nur die Stringoperationen verändert //und die Variante sName eingefügt pm:=pointer(integer(p^)+16); for a:=0 to pinteger(integer(pm^)+8)^-1 do begin //von 0 bis componentcount p:=pointer(integer(pm^)+4); p:=pointer(integer(p^)+4*a); pi:=p; //pi^ ist Zeiger auf ein Objekt if memory^.SearchType=sClassname then begin p:=p^; p:=pointer(integer(p^)-44); end else p:=pointer(integer(p^)+8); p:=p^; c:=pchar(p); if (pbyte(p)^=memory^.vgllength)or(memory^.SearchType=sName) then begin if memory^.SearchType=sClassName then inc(c); same:=false; for i:=1 to memory^.vgllength do begin if memory^.vgl[i-1]<>c^ then break; same:=i=memory^.vgllength; inc(c); end; if same then begin dec(memory^.count); if (memory^.count=0)or(memory^.SearchType=sName) then begin left:=(pinteger(integer(pi^)+$40)^); top:=(pinteger(integer(pi^)+$44)^); width:=pinteger(integer(pi^)+$48)^; height:=pinteger(integer(pi^)+$4C)^; break; end; end; end; end; //4 Zahlen in 2*32Bit kopieren wparam:=left*65536+top; lparam:=width*65536+height; //Ergebnis Nach Hause senden memory^.Postmessage(memory^.backwnd,memory^.backmsg,wparam,lparam); result:=0; //Thread beenden memory^.exitthread(0); end; procedure endpoint; //ohne Funktion nur zum finden des Address-endes von injcetThread asm nop end; procedure TForm1.Button2Click(Sender: TObject); begin close; end; procedure TForm1.Button1Click(Sender: TObject); var myhandle:hwnd; 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 //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:=1; //welches TImage (=mem.vgl)? (nur für Searchtype=sClassName) mem.vgl:='Image3'; //der Vergleichsstring mem.vgllength:=6; //Länge des Vergleichsstrings mem.SearchType:=sName; //vgl vergleichen mit Classname oder Name //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.GetMyMsg(var msg:TMessage); 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; end.
Delphi-Quellcode:
All das funktioniert wie gesagt nur mit Programmen, die mit der VCL programmiert wurden, genauer gesagt, mit der VCL die in Delphi 7 ist (Ich weis ja nicht ob sich an diesen grundlegenden Strukturen bis D2006 etwas geändert hat).
mem.count:=2; //welches TImage (=mem.vgl)? (nur für Searchtype=sClassName)
mem.vgl:='TImage'; //der Vergleichsstring mem.vgllength:=6; //Länge des Vergleichsstrings Und nun noch zum Beispielprogramm: Er sucht jetzt quasi ein Window, dessen Titel PImage ist, das sieht so aus (zweites Programm):
Delphi-Quellcode:
unit U_Image;
interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls, ExtCtrls; type TForm1 = class(TForm) Image1: TImage; Image2: TImage; Image3: TImage; Button1: TButton; procedure Button1Click(Sender: TObject); procedure FormCreate(Sender: TObject); private { Private-Deklarationen } public { Public-Deklarationen } end; var Form1: TForm1; implementation {$R *.dfm} procedure TForm1.Button1Click(Sender: TObject); begin close; end; procedure TForm1.FormCreate(Sender: TObject); begin self.caption:='PImage'; end; end. PS: Eigentlich sollte man noch Sicherheiten einbauen. An der Stelle wo der Thread-Record in den anderen Process kopiert und gestartet wird, kann man an den Ergebnissen der funktionen sehen, ob es überhaupt geklappt hat. Aber das überlasse ich dem geneigten Leser :mrgreen: . Edit: "break" eingefügt. Man muss ja nicht weiter suchen, wenn man schon gefunden hat. |
Re: Zugriff auf ein TImage einer externen Anwendung
Das finden der TImages nach Namen funktioniert soweit richtig super!
Auch das Zeichnen eines Textes mit Canvas in das Form klappt ganz gut. Es gibt aber nach wie vor noch ein anliegen was ich durchs rum probieren noch nicht so richtig hinbekommen habe. Ich habs mit folgendem Versucht:
Delphi-Quellcode:
Bei dem Nutzen des SetSize erhalte ich folgenden Fehler: "Cannot change the size of a JPEG image".
procedure TForm1.Button3Click(Sender: TObject);
var dc:hdc; //Device Context Handle Canvas:Tcanvas; begin dc:=getwindowdc(myhandle); //TCanvas auf den Device Context setzen canvas:=TCanvas.Create; canvas.Handle := dc; Image1.Picture.LoadFromFile('mein_bild.jpg'); Image1.Picture.Graphic.SetSize(ImgWidth,ImgHeight); canvas.Draw(ImgLeft,ImgTop,Image1.Picture.Graphic); canvas.Free; releasedc(myhandle,dc); end; Wenn ich das SetSize vom Image1 raus lasse zeichnet er mir zwar das Bild auf das Ziel-Form, aber in voller Größe. Ich möchte es aber nur entweder in der Größe des eigenen Image1 oder aus den ImgWidth,ImgHeigth Variablen welche dem des ziel TImage entsprechen. Zudem noch eine Frage, wie bekomm ich jetzt noch den Caption-Wert wenn es sich nicht um ein TImage sonder um ein TLable handelt? Vielen Dank! |
Re: Zugriff auf ein TImage einer externen Anwendung
Also bei D7 gibt es kein TGraphic.SetSize. Das muss irgendwie neu sein. Da kann ich dir auch nicht weiterhelfen (außer wenn ich zu Hause mal mei TurboDelphi anschmeiße, aber da hab ich wahrscheinlich keine Zeit). Wahrscheinlich musst du irgendwie das jpg in ein Bitmap umwandeln. Dazu gibts TJPEGImage und die Methode DIBneeded. Musst du mal irgendwie damit probieren.
Zitat:
|
Re: Zugriff auf ein TImage einer externen Anwendung
Zitat:
Soweit ich das sehe ist das SetSize nur ne Funktion die die beiden Zeilen in 1 zusammenfasst. Zitat:
Edit: Mit hilfe deines TJPEGImage.DIBNeeded bin ich auf ein Thread gestoßen der folgendes Resultat hat:
Delphi-Quellcode:
var rect : TRect;
rect.Left := ImgLeft; rect.Top := ImgTop; rect.Right := ImgLeft + ImgWidth; rect.Bottom := ImgTop + ImgHeight; canvas.StretchDraw(rec,Image1.Picture.Graphic); |
Re: Zugriff auf ein TImage einer externen Anwendung
Zitat:
Übrigens: das Canvas wird aber genau wie in deiner eignen Anwendung übermalt, wenn z.B. ein anderes Fenster drüber geschoben wird. Zu dem Label: Ich habe es gefunden. Aber hier bin ich mir am wenigsten sicher, dass der Code generell so eingesetzt werden kann. Bei mir klappts erstmal:
Delphi-Quellcode:
Ich hab die injectThread angepasst um die Caption zu finden. Und es ist die GetmyCaption dazugekommen um die neue Message zu verarbeiten. In die Erste konnte ich es nicht mehr reinpacken.
unit U_getImage;
interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls, ExtCtrls; const mymsg=WM_User+1; type TSearchtype=(sClassName,sName,sCaption); type PMemory=^TMemory; TMemory=packed record Thread:array[0..1023] of char; Postmessage:function(wnd:hwnd;msg,wparam,lparam:cardinal):bool;stdcall; exitthread:procedure(exitcode:integer);stdcall; getwindowlong:function(wnd:hwnd;index:integer):cardinal;stdcall; watchwnd:hwnd; backwnd:hwnd; backmsg:integer; count:integer; SearchType:TSearchtype; vgl:array[0..31] of char; vgllength:integer; end; type TForm1 = class(TForm) Image1: TImage; Memo1: TMemo; Button1: TButton; Button2: TButton; Button3: TButton; Label1: TLabel; procedure Button2Click(Sender: TObject); procedure Button1Click(Sender: TObject); procedure GetMyMsg(var msg:TMessage);message mymsg; procedure GetMyCaption(var msg:TMessage);message mymsg+1; procedure Button3Click(Sender: TObject); private { Private-Deklarationen } myhandle:hwnd; public { Public-Deklarationen } end; var Form1: TForm1; implementation {$R *.dfm} function injectThread(memory:Pmemory):integer; stdcall; {Diese Funktion landet nachher in einem fremden Process. Deswegen dürfen hier keine Funktionen von Delphi benutzt werden (inkl. Stringoperationen, VCL, etc.).} var pi,p,pm:ppointer; i,a:integer; c:pchar; left,top,width,height:smallint; same:boolean; wparam,lparam:cardinal; begin wparam:=0; lparam:=0; p:=pointer(memory^.getwindowlong(memory^.watchwnd,gwl_wndproc)+9); pm:=pointer(integer(p^)+16); for a:=0 to pinteger(integer(pm^)+8)^-1 do begin //von 0 bis componentcount p:=pointer(integer(pm^)+4); p:=pointer(integer(p^)+4*a); pi:=p; //pi^ ist Zeiger auf ein Objekt if memory^.SearchType=sClassname then begin p:=p^; p:=pointer(integer(p^)-44); end else p:=pointer(integer(p^)+8); p:=p^; c:=pchar(p); if (pbyte(p)^=memory^.vgllength)or(memory^.SearchType in[sName,sCaption]) then begin if memory^.SearchType=sClassName then inc(c); same:=false; for i:=1 to memory^.vgllength do begin if memory^.vgl[i-1]<>c^ then break; same:=i=memory^.vgllength; inc(c); end; if same then begin dec(memory^.count); if (memory^.count=0)or(memory^.SearchType in [sName,sCaption]) then begin if memory^.SearchType=sCaption then begin p:=pointer(integer(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)^; //4 Zahlen in 2*32Bit kopieren wparam:=left*65536+top; lparam:=width*65536+height; end; break; end; end; end; end; //Ergebnis Nach Hause senden memory^.Postmessage(memory^.backwnd,memory^.backmsg,wparam,lparam); result:=0; //Thread beenden memory^.exitthread(0); end; procedure endpoint; //ohne Funktion nur zum finden des Address-endes von injcetThread asm nop end; procedure TForm1.Button2Click(Sender: TObject); begin close; end; procedure TForm1.Button1Click(Sender: TObject); 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) mem.vgl:='Label2'; //der Vergleichsstring mem.vgllength:=6; //Länge des Vergleichsstrings mem.SearchType:=sCaption; //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.GetMyMsg(var msg:TMessage); 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); 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; end. |
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. |
Re: Zugriff auf ein TImage einer externen Anwendung
Also ich muss nochmal korrigieren ...
mit dem "neuen" Code von dir, killt sich das Programm nicht mehr selbst, sondern bringt lediglich eine Zugriffsverletzung und das Form schließt sich, das Programm selbst bleibt offen. Zudem hab ich mal folgendes Versucht, was nicht funktioniert:
Delphi-Quellcode:
Ohne die delay Funktion würde sich das eigene Programm aufhängen, aber es kommt kein ergebnis zurück und das Ziel-Form hängt sich dann auch nicht auf, als ob irgendwie die Adressen nicht angesprochen werden.
procedure TForm1.Button1Click(Sender: TObject);
begin memo1.clear; myhandle:=findwindow(nil,'PImage'); if myhandle=0 then exit; memo1.Lines.add('window found!'); delay(1000); startobservation; //eine Besipielanfrage an die newwndProc communicate('Image2',sImageSize); //hier oder in einer anderen Funktion können noch mehr solche Anfragen gestartet werden delay(1000); stopobservation; end; D.h. also wenn das myhandle und startobservation im FormCreate ist funzt es, so nicht. Was deine Frage betrifft was für eine Software das ist, kann ich nur sagen das es sich um eine Präsentationssoftware handelt. Das die Software in Delphi programmiert ist, ist zu 100% sicher, zum einen durch die Formulare die ich in ResHack ermittelt hab und zum anderen wenn ich die Software im HexEditor anschaue steht auch Delphi drin, und das schreibt meines wissens nur Delphi rein. ;-) Welche Version von Delphi, kann ich leider nicht sagen, du kannst schon recht haben das da ne ältere Version hinter steckt. Da ich so langsam die Vermutung hab das die Namen die ich ermittelt hatte, zur Laufzeit vielleicht anders lauten, kann ich irgendwie eine Liste aller Elemente, also die Namen und ggf. die Klasse dazu die auf dem Form existieren auslesen? Mit den Klassen hatten wir das ja schonmal, wobei er mit der grund Version ich glaub auf Seite 1 des Threads, aufhängt bzw. Zugriffsverletzung bekommt (also eigenes Programm). Ich glaub langsam wird kompliziert, oder? :) |
Re: Zugriff auf ein TImage einer externen Anwendung
Zitat:
Zitat:
Zitat:
Zitat:
Für ein Delphi 4 wüsste ich noch, wo eine Lizenz ungenutzt herumliegt. Aber das jetzt zu installieren um auf Verdacht weiterzusuchen....nicht unbedingt. Es war ein Versuch wert. Schade dass er nicht geklappt hat. War aber mal ne schöne Übung. Es ist halt recht schwer auf die Ferne jetzt noch etwas am Code zu drehen. Du müsstest wahrscheinlich selber noch ein bisschen probieren. Als erstes würde ich ständig Messages senden, wenn er wieder eine Codezeile geschafft hat. Dadurch weisst du wo er hängen bleibt. Ich kann dir das mit dem Classname in die letzte Variante noch mal reinprogrammieren. Dürfte aber nix bringen. Die Namen der Komponenten kann man zur Laufzeit meines Wissens nicht mehr ändern. |
Re: Zugriff auf ein TImage einer externen Anwendung
Ok, in deinem TestProgramm ist ein Laufzeitproblem.
[Nebenbei: Seit wann gibts denn die delay-Funktion wieder. Die kenne ich noch aus Turbo Pascal, aber mein D7 hat die nicht.] Du musst eigentlich nicht zwischen findwindo und startobservation warten, sondern zwischen startobservation und communicate. Das Problem ist wie in einer MultiThreadanwendung. die haben wir eigentlich auch. Nur das unser Nebenthread auch gelich in einem anderen Process ist. Der letzte Befehl in Startobservation ist "CreateRemoteThread" Da wird der neue Thread gestartet. Das dauert aber meistens eine Weile. In der Zeit ist unse rHauptthread schon längst in communicate bei sendmessage angekommen. Schickt als eine Message und wartet auf Antwort. Da aber unser Nebenthread die wndProc-Funktion noch nicht umgelenkt hat, kriegen wir keine Antwort und warten ewig. Und deswegen wird in dem Nebenthread (also eigentlich in der neuen wndProc) auch nicht die Info-Prozedur aufgerufen. Und weil die Zugriffsverletzungen nur dort passieren, passiert dann eben nix. Edit: Die Idee ist ja auch, nicht alles hintereinander zu machen, sondern erst einmalig startobservation. Und dann bei Gelegenheit (von mir aus) hundertmal communicate. Soviel du halt an Infos brauchst. In der ganzen Zeit seit startobservation bekommst du auch die WM_Paint Messages mit. Und ganz am Ende des Programms rufst du einmal stopobservation. |
Re: Zugriff auf ein TImage einer externen Anwendung
So, ich hab die Sache mit Classname wieder reingebracht, dürfte aber wie gesagt nicht viel bringen.
folgende Änderungen vom letzten Mal:
Delphi-Quellcode:
type TSearchtype=(sImageSize,sCaption,sImageSizefromClassname);
Delphi-Quellcode:
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:integer; a:cardinal; c:pchar; left,top,width,height:smallint; same:boolean; SearchType:TSearchType; vgl:array[0..31] of char; vgllength:integer; index:word; begin Searchtype:=TSearchType(lparam mod 65536); index:=lparam div 65536; //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 if searchtype=simagesizefromclassname then begin p:=p^; p:=pointer(cardinal(p^)-44); end else p:=pointer(cardinal(p^)+8); p:=p^; c:=pchar(p); if (pbyte(p)^=vgllength)or(SearchType<>sImagesizefromClassname) then begin if SearchType=sImagesizefromClassName then inc(c); 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 dec(index); if (index=0)or(SearchType<>sImagesizefromClassname) 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; end; end; //Ergebnis Nach Hause senden memory^.Postmessage(memory^.backwnd,memory^.backmsg,wparam,lparam); end;
Delphi-Quellcode:
Und die Testroutine:
procedure TForm1.communicate(vgl:string;Searchtype:TsearchType;index:word=0);
//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 if searchtype=sImageSizefromclassname then lparam:=cardinal(Searchtype)+index*65536 else lparam:=cardinal(Searchtype); sendmessage(myhandle,mymsg,wparam,lparam); //Message für unsere newwndproc; und warten bis sie dort verarbeitet wurde globaldeleteatom(wparam); //Atom wieder löschen end;
Delphi-Quellcode:
procedure TForm1.Button1Click(Sender: TObject);
begin //Besipielanfragen an die newwndProc communicate('TImage',sImageSizefromclassname,2); communicate('Image2',sImageSize); communicate('Label1',sCaption); //hier oder in einer anderen Funktion können noch mehr solche Anfragen gestartet werden end; |
Re: Zugriff auf ein TImage einer externen Anwendung
Das Delay ist eine Funktion die ich hier irgendwo aus dem Board gezogen hab.
Das Problem warum ich das mit dem start/stopobservation in ein buttonclick gesetzt hab, ist jenes weil das spezielle Form was die TImages enthält nicht permanent geöffnet ist, was ich dann auch noch irgendwie z.B. mit Timer überprüfen muss. |
Re: Zugriff auf ein TImage einer externen Anwendung
Aslo entweder du baust dann die Verzögerung ein nach startobservation (ein einfaches Sleep(100) dürfte aber ausreichen) oder ich sende aus dem Remotethread eine Message, dass alles vorbereitet ist:
Delphi-Quellcode:
//Deklaration in TForm1
procedure GetReadyMsg(var msg:TMessage);message mymsg+3; . . . 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); memory^.Postmessage(memory^.backwnd,memory^.backmsg+3,0,0); //Message, dass alles eingerichtet/vorbereitet ist while memory^.running do memory^.sleep(200); memory^.setwindowlong(memory^.watchwnd,gwl_wndproc,memory^.oldwindowproc); result:=0; memory^.exitthread(0); end;
Delphi-Quellcode:
Aber, wenn du gleich stopobservation aufrufst, dann bekommst du ja nicht mehr mit, wenn das Window eine WM_Paint Message zum neuzeichnen bekommt.
procedure Tform1.GetReadyMsg(var msg:TMessage);
begin //wird automatisch vom Remotethread "gestartet", wenn er soweit ist communicate('TImage',sImageSizefromclassname,2); communicate('Image2',sImageSize); communicate('Label1',sCaption); stopobservation; //hier oder später end; procedure TForm1.Button1Click(Sender: TObject); begin memo1.clear; myhandle:=findwindow(nil,'PImage'); if myhandle=0 then exit; startobservation; end; Du kannst übrigens auch das Erstellen und das Entfernen des Formulars ereignisgesteuert überwachen. Aber solange wir das mit dem Communicate, also die Info-funktion nicht hinbekommen, brauchen wir den Rest nicht mehr zu probieren. Da bräuchten wir wirklich die entspr. Delphiversion. |
Re: Zugriff auf ein TImage einer externen Anwendung
Zitat:
Hier mal neben bei die Delay-Prozedur:
Delphi-Quellcode:
Die benutze ich in Verbindung mit Windows-Diensten, damit der Prozess nicht mit 100% Auslastung fährt.
procedure Delay(Milliseconds: Integer);
var Tick: DWord; Event: THandle; begin Event := CreateEvent(nil, False, False, nil); try Tick := GetTickCount + DWord(Milliseconds); while (Milliseconds > 0) and (MsgWaitForMultipleObjects(1, Event, False, Milliseconds, QS_ALLINPUT) <> WAIT_TIMEOUT) do begin Application.ProcessMessages; if Application.Terminated then Exit; Milliseconds := Tick - GetTickcount; end; finally CloseHandle(Event); end; end; |
Re: Zugriff auf ein TImage einer externen Anwendung
Hmm hab grad nochmal rum getestet... das Ergebnis ist leider sehr enttäuschend. Kurz um: Zugriffsverletzung im Ziel-Programm.
Gut dann müsste man mal eine andere Taktik angehen. Lassen wir mal das mit dem finden der TImage. Passt zwar jetzt nicht zum Topic, aber es gibt noch noch eine andere Möglichkeit die TLabel zu finden als über die Adress-Rechnung oder? Ich hatte früher mal so sachen gehabt, aber die zu finden, dürften schwierig sein. Die Position und die größe der TImage lässt sich zur Not auch noch manuell und fix programmieren, aber ich brauch auf alle Fälle den Text der Labels. |
Re: Zugriff auf ein TImage einer externen Anwendung
Bei TLabel besteht genau dasselbe Problem, wie bei TImage. Es hat kein Window-Handle. Ansonsten könnet man mit einer WM_GetText Message o.ä. daran kommen. Also es bedarf zum Beispiel nur weniger Schritte um die Playlist aus dem Win-Media Player herauszubekommen, auch im Explorer kann man recht einfach von außen mitlesen. Aber dort benutzt Microsoft halt die grundlegenden Window-Typen aus ihrer eigenen Win-API. TLabel ist aber nur ein Objekt in Delphi, was halt immer auf das formular gezeichnet wird. es hat sogar eine eigene WndProc-Funktion. die wird aber nur über das Parent-window angesteuert. wenn wir die Finden könnten wie sogar den Text bekommen. Aber wie?
Da muss ich erstmal mindestens eine Nacht drüber schlafen. Ich hatte vorhin vesucht noch eine Exception-Behandlung mit einzubauen (in die Info). Aber die funktioniert nicht. Da geht das Programm im Fehlerfall dann einfach aus, ohne sich irgendwie abzumelden und ohne Fehlermeldung. Im eigenen Programm funktioniert die Exceptionbehandlung wunderbar. Musste sie ja in ASM schreiben, weil das try..except ja gleich wieder Klassen instanziert. |
Re: Zugriff auf ein TImage einer externen Anwendung
Also kurz zum Verständnis, ein RichEdit (sei es nun von Delphi oder generell) besitzt ein eigenes Handle, ein Memo auch? nur ein Label wird in dem sinne sozusagen einfach nur draufgezeichnet, d.h. der Text existiert schon irgendwo im Hauptprogramm?
Früher hatte ich ja mal ein Programm mit dem ich RichEdit und ich glaub auch Memos auslesen konnte, aber das eben ohne Adress-Rechnung. Das ganze war einfacher gestrickt. Hab aber wie gesagt den Code nicht mehr zumindest müsste ich wenn doch in tiefen Archiven graben. |
Alle Zeitangaben in WEZ +1. Es ist jetzt 19:15 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