Delphi-PRAXiS
Seite 1 von 3  1 23      

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Win32/Win64 API (native code) (https://www.delphipraxis.net/17-win32-win64-api-native-code/)
-   -   Delphi Zugriff auf ein TImage einer externen Anwendung (https://www.delphipraxis.net/86561-zugriff-auf-ein-timage-einer-externen-anwendung.html)

KoS 15. Feb 2007 16:57


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

sirius 15. Feb 2007 17:03

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!

KoS 15. Feb 2007 17:10

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.

sirius 18. Feb 2007 19:05

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.

Neutral General 18. Feb 2007 19:08

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

KoS 19. Feb 2007 08:34

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.

sirius 19. Feb 2007 09:27

Re: Zugriff auf ein TImage einer externen Anwendung
 
@General
Das sind Device Context Handles und keine Window Handles.

turboPASCAL 19. Feb 2007 09:33

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".

KoS 19. Feb 2007 11:22

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.

sirius 19. Feb 2007 12:34

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:
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;
Ich hab jetzt keine weiteren Kommentare, da ich nur ein wenig rumgespielt habe. Letztenendes macht es nix andere, als:
Delphi-Quellcode:
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;
Allerdings passiert dies ohne Zuhilfenahme der VCL, bezieht sich allerdings strikt darauf.
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.

KoS 19. Feb 2007 17:30

Re: Zugriff auf ein TImage einer externen Anwendung
 
Hallo,
Zitat:

Zitat von sirius
Ich hab jetzt keine weiteren Kommentare, da ich nur ein wenig rumgespielt habe. Letztenendes macht es nix andere, als:

Also ich hab das mal eben ausprobiert und bekomme Folgendes Resultat:
Delphi-Quellcode:
TApplication
THintWindow
TForm1
Was mir jetzt mal sagt das eigentlich nur die "Hauptklassen" angezeigt nicht jedoch die Komponenten auf dem Form.

Wenn ich jedoch direkt folgendes mache:
Delphi-Quellcode:
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;
Erhalte ich nur die Komponenten von dem Fenster:
Delphi-Quellcode:
TImage
TImage
TMemo
TButton
Hab ich irgendwas vergessen oder warum funktioniert das nicht so wie es soll?

sirius 19. Feb 2007 19:47

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:
p:=pointer(getwindowlong(myhandle,gwl_wndproc)+9);
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 :| .

Man könnte natürlich auch Tform1 nehmen und dann weiter suchen. aber das ist nicht gerade sinnvoll.

KoS 19. Feb 2007 21:17

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:
myhandle:=getforegroundwindow;
mache, bekomm ich eine Zugriffsverletzung, sobald ein anderes Programm im Vordergrund ist, also eben jenes was ich "auslesen" möchte.
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. ;-)

Namenloser 19. Feb 2007 22:10

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:
  p:=p^;
Ich habe mal gehört, dass die VCL von zwei Anwendungen nicht miteinander kompatibel ist. Liegt das daran?

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;

sirius 20. Feb 2007 07:41

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:
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]
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:
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).

sirius 20. Feb 2007 09:42

Re: Zugriff auf ein TImage einer externen Anwendung
 
Ok, bei mir getestet und es funktioniert:
Delphi-Quellcode:
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.
Man kann auch nach ClassName suchen (dann ist mem.count notwendig, da es ja mehrere TImages geben kann):
Delphi-Quellcode:
  mem.count:=2; //welches TImage (=mem.vgl)? (nur für Searchtype=sClassName)
  mem.vgl:='TImage'; //der Vergleichsstring
  mem.vgllength:=6; //Länge des Vergleichsstrings
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).


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.

KoS 20. Feb 2007 12:43

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:
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;
Bei dem Nutzen des SetSize erhalte ich folgenden Fehler: "Cannot change the size of a JPEG image".
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!

sirius 20. Feb 2007 13:06

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:

Zudem noch eine Frage, wie bekomm ich jetzt noch den Caption-Wert wenn es sich nicht um ein TImage sonder um ein TLable handelt?
Du willst aus dem Programm, wo du Images gesucht hast auch den Inhalt von Labels wissen? Erklär mal genauer!

KoS 20. Feb 2007 14:05

Re: Zugriff auf ein TImage einer externen Anwendung
 
Zitat:

Zitat von sirius
Also bei D7 gibt es kein TGraphic.SetSize.

Ich habs auch mit TGraphic.Height, TGraphic.Width versucht, kommt ebenfalls der Fehler.
Soweit ich das sehe ist das SetSize nur ne Funktion die die beiden Zeilen in 1 zusammenfasst.

Zitat:

Zitat von sirius
Zitat:

Zudem noch eine Frage, wie bekomm ich jetzt noch den Caption-Wert wenn es sich nicht um ein TImage sonder um ein TLable handelt?
Du willst aus dem Programm, wo du Images gesucht hast auch den Inhalt von Labels wissen? Erklär mal genauer!

Ja genau so wie du es gesagt hast. In dem Form wo ich die Images suche gibt es auch Labels dessen Text ich wissen möchte. Ebenfalls Namen bekannt.

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);

sirius 20. Feb 2007 14:26

Re: Zugriff auf ein TImage einer externen Anwendung
 
Zitat:

Mit hilfe deines TJPEGImage.DIBNeeded bin ich auf ein Thread gestoßen der folgendes Resultat hat:
Wenn es funktioniert... Mit dem Canvas kannst du machen, was du auch sonnst in deiner eigenen Anwendung machen kannst.
Ü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:
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.
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.

Namenloser 20. Feb 2007 14:51

Re: Zugriff auf ein TImage einer externen Anwendung
 
Zitat:

Zitat von sirius
@namenlozer(ihr wisst anscheinend noch nicht, was virtueller Adressraum bedeutet -->unbedingt mal bei Wiki oder so reinlesen).

Also eigentlich schon, aber da dein code so kompliziert aussah, bin ich davon ausgegangen, dass er das umgeht xD

KoS 20. Feb 2007 15:03

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;

sirius 20. Feb 2007 15:04

Re: Zugriff auf ein TImage einer externen Anwendung
 
Zitat:

Zitat von NamenLozer
Zitat:

Zitat von sirius
@namenlozer(ihr wisst anscheinend noch nicht, was virtueller Adressraum bedeutet -->unbedingt mal bei Wiki oder so reinlesen).

Also eigentlich schon, aber da dein code so kompliziert aussah, bin ich davon ausgegangen, dass er das umgeht xD

Naja, was heist kompliziert? Ein bisschen Adressrechnung, mehr ist es ja nicht. Sieht wahrscheinlich von außen nur ein wenig verstrickt aus.
Aber die Sicherheiten der CPU umgehen, schaffe ich so einfach nicht, dazu brauche ich schon Windows und die WIN-API :mrgreen:

sirius 20. Feb 2007 19:21

Re: Zugriff auf ein TImage einer externen Anwendung
 
Zitat:

Zitat von KoS
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?

Ja, das geht. Irgendjemand hatte ja bereits erwähnt, dass du die WM_Paint Message (an das fremde Formular) abfangen musst.

Zitat:

Zitat von KoS
Es müsste doch eigentlich klappen wenn ich ein eigenes Form für jedes Image erstelle es an die entsprechende Position schiebe,

Ach nee, die jetzige Variante ist schon besser. Man könnte nur noch überlegen, von TCanvas wegzugehen und mit der Win-API direkt auf den DC (Device Context) zu zeichen. Dazu brauchst du allerdings das Original als Bitmap... ist nur so ne Idee. Tcanvas macht wahrscheinlich auch nix anderes als "BitBlt" von Windows.

Zitat:

Zitat von KoS
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.

Das war höchst wahrscheinlich, dass dies nicht klappt. Weil ein TButton ja ein wenig anders aufgebaut ist. Dadurch funktioniert die Adressrechnugn nicht mehr und ich greife auf eine Adresse zu, wo ich keinen Zugriff habe ->EAccessViolation und das Programm, in dem sie auftrat (also der fremde Process) stürzt ab.
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:

Zitat von KoS
Die bisher letzte Frage die ich hätte bezieht sich auf Optimierung.

Da kann man sicherlich noch einiges optimieren. Du kannst ja das TMemory vorbereiten und musst es nicht jedesmal neu füllen, oder nur den Teil den du änderst. Alles ab " getwindowthreadprocessid" würde ich ungern auseinanderreißen, außer man programmiert alles um.
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.

KoS 20. Feb 2007 21:05

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!

sirius 20. Feb 2007 22:00

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:
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.
Prinzipielle Idee:
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.

Luckie 20. Feb 2007 23:06

Re: Zugriff auf ein TImage einer externen Anwendung
 
Jungs, das ist einfach nur krank. :o

sirius 21. Feb 2007 07:55

Re: Zugriff auf ein TImage einer externen Anwendung
 
Zitat:

Zitat von Luckie
Jungs, das ist einfach nur krank. :o

Sind das nicht alle Programmierer :mrgreen: ?

KoS 21. Feb 2007 17:05

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?

sirius 21. Feb 2007 18:32

Re: Zugriff auf ein TImage einer externen Anwendung
 
Zitat:

Zitat von KoS
Ideen?

Derzeit nicht.

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:
type TTest=record
       irgendetwas:integer;
       meinZeiger:pointer;// den will ich errechnen
       ....
end;
..., 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.
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.

KoS 21. Feb 2007 18:51

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:
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;
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.
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? :)

sirius 21. Feb 2007 19:56

Re: Zugriff auf ein TImage einer externen Anwendung
 
Zitat:

Zitat von KoS
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.

Ja. Das ist, weil der Zugriffsfehler (der immer noch derselbe ist) nicht mehr in unserem (ungesicherten) Thread passiert sondern in der WndProc, also in dem Thread des Formulars. Und dort wird Exception-Behandlung betrieben. Deswegen stürzt nicht gleich das ganze Programm ab.

Zitat:

Zitat von KoS
Zudem hab ich mal folgendes Versucht, was nicht funktioniert:
...
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.
D.h. also wenn das myhandle und startobservation im FormCreate ist funzt es, so nicht.

Das ist komisch, muss ich gleich mal überprüfen.

Zitat:

Zitat von KoS
Das die Software in Delphi programmiert ist, ist zu 100% sicher,

Die Gründe sind einleuchtend :-D

Zitat:

Zitat von KoS
Ich glaub langsam wird kompliziert, oder? :)

Ja, der letzte Teil war mir etwas zu kompliziert. Wieso hängt sich jetzt da wieder etwas auf, was eigentlich bisher klappte?

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.

sirius 21. Feb 2007 20:13

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.

sirius 21. Feb 2007 20:34

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:
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;
Und die Testroutine:
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;

KoS 21. Feb 2007 20:55

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.

sirius 21. Feb 2007 21:10

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:
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;
Aber, wenn du gleich stopobservation aufrufst, dann bekommst du ja nicht mehr mit, wenn das Window eine WM_Paint Message zum neuzeichnen bekommt.
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.

KoS 22. Feb 2007 09:14

Re: Zugriff auf ein TImage einer externen Anwendung
 
Zitat:

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.
Da geb ich dir allerdings Recht! Ich probier das heute Nachmittag nochmal aus. Ich hoffe das klappt dann, wobei ich nicht sehr optimistisch bin.

Hier mal neben bei die Delay-Prozedur:
Delphi-Quellcode:
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;
Die benutze ich in Verbindung mit Windows-Diensten, damit der Prozess nicht mit 100% Auslastung fährt.

KoS 22. Feb 2007 18:43

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.

sirius 22. Feb 2007 19:19

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.

KoS 23. Feb 2007 08:43

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.
Seite 1 von 3  1 23      

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