Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Programmieren allgemein (https://www.delphipraxis.net/40-programmieren-allgemein/)
-   -   Webbrowser.OleObject mittels Vorfahren erreichen (https://www.delphipraxis.net/156761-webbrowser-oleobject-mittels-vorfahren-erreichen.html)

oki 14. Dez 2010 08:56

Webbrowser.OleObject mittels Vorfahren erreichen
 
Hallo Leute,

ich hoffe, ich kann meine Frage verständlich formulieren. Ich möchte auf ein Textfeld in einem html-Document zugreifen. Das geht ja so:

Delphi-Quellcode:
wb.OleObject.Document.All.Item('ItemName').Value := 'Test';

Nun habe ich das Problem, dass ich nicht direkt über das BrowserControl zugreife sondern den Container Components nach dem Element durchsuche.
Und jetzt wirds kurz kompliziert. Warum nicht direkt. Ich benutze die TEmbeddedWb-Komponente. Binde ich EmbeddedWB in die uses ein, so bekomme ich eine Fehlermeldung des Compilers bei der Benutzung von Laufzeitbibliotheken. Das war so ne Sache mit zu vielen Pfaden, Zweigen? in der BPL-Nutzung. Ist schon ne' Zeit her, als ich mich darum gekümmert habe. Punkt ist, ich kann in der Unit EmbeddedWB nicht einbinden.

Jetzt dachte ich, egal, OleObject sollte durch einen Vorfahren implementiert werden und hab in einer kleinen Testanwendung mal mit Code-Folding geschaut wo das denn ist. Der Aufruf geht direkt zu TOleControl.OleObjekt. Na, dachte mir nichts leichter als das, da haben wir ja die Vererbung incl. Typ-Konvertierung.


Delphi-Quellcode:
TOleControl(Components[i]).OleObject.Document.All.Item('ItemName').Value := 'Test';

sollte die Lösung sein.

Nichts ist. Bis Item werden gültige Adressen angezeigt, dann ist Schluss. Exception mit Zugriff auf 0000.
Ich denke mal, die Typkonvertierung ist hier falsch. Vielleicht wird OleObject auch durch ein Interface eingebunden. Bin bei der ganzen Sache aber leider nicht fitt. Kann mir einer einen Weg zeigen wie ich das hinbekomme?
Das soll es aber nicht sein:


Delphi-Quellcode:
TEmbeddedWB(Components[i]).OleObject.Document.All.Item('ItemName').Value := 'Test';


Ich dank schon mal im Voraus,

Gruß oki

shmia 14. Dez 2010 10:46

AW: Webbrowser.OleObject mittels Vorfahren erreichen
 
Zitat:

Zitat von oki (Beitrag 1068364)
Ich möchte auf ein Textfeld in einem html-Document zugreifen.

Was verstehst du unter einem Textfeld?
Ist es ein Inputfield eines Webforms oder einfach nur ein HTML-Element mit einem gesetzen ID-Attribut.

Zitat:

Zitat von oki (Beitrag 1068364)
Bis Item werden gültige Adressen angezeigt, dann ist Schluss.

Klarer Fall; es gibt das Item mit dem Namen "ItemName" in dem geladenen HTML nicht.

Thom 14. Dez 2010 14:10

AW: Webbrowser.OleObject mittels Vorfahren erreichen
 
Da ich nur einmal kurz mit der TEmbeddedWb-Komponente gearbeitet hatte, bin ich dafür sicher kein Experte.

Ich habe allerdings das Beispiel aus Interesse einmal getestet: Und bei mir war schon nach Document Schluß. Also liefert OleObject schon keinen Zeiger auf Document. Da OleObject eine Variante als Ergebnis liefert, wird anschießend versucht, über _DispInvoke in der Unit Variants einen Zeiger auf Document zu erhalten. Das geschieht durch den Aufruf von VarDispInvoke in der Unit ComObj. Innerhalb dieser Prozedur wird mit GetIDsOfNames die DispatchID von Document innerhalb des Objektes OleObject ermittelt (203). Das funktioniert auch - deshalb wird im Anschluß DispatchInvoke aufgerufen. Hier treten die Probleme auf: Mein Delphi-Compiler (Delphi2010) interpretiert meinen Untersuchungen zufolge den Aufruf von Document aus den RTTI als Methode (DISPATCH_METHOD). Dabei hatte ich mein Formular mit dem Schalter {$METHODINFO ON} compiliert. Document ist aber eine Eigenschaft des übergeordneten Objektes (genauer gesagt: Es ist selbst wieder ein COM-Objekt). Und hier scheint der Fehler aufzutreten: Document wird aufgerufen, statt einen Zeiger auf dieses Objekt zu ermitteln. Dieser wäre nämlich notwendig, um sich zum nächsten Objekt (All) "durchzufragen".

Lange Rede, kurzer Sinn: So scheint das leider nicht zu funktionieren.

Da ich bei meinem aktuellen Projekt ebenfalls auf einige Bugs innerhalb der Ole-Umsetzung gestoßen bin, habe ich das anders gelöst:
Delphi-Quellcode:
procedure SetProperty(WebBrowser: TWebBrowser; NodeName, PropertyName: String; Value: OleVariant);
var
  All, Node: OleVariant;
begin
  //"All"-Objekt des Dokumentes ermitteln:
  All:=GetProp(WebBrowser.Document,'all');
  //HTML-Element aus dem "All"-Objekt ermitteln:
  Node:=GetProp(All,NodeName);
  //Eigenschaft des HTML-Elementes setzen:
  SetProp(Node,PropertyName,Value);
end;
Habe ich gerade getestet - funktioniert. :thumb:
Die notwendigen Funktionen GetProp und SetProp habe ich aus der Unit BrowserTools im Downloadbereich der aktuellen Toolbox-Ausgabe. Dort die Source zum Beitrag Delphi und Google Maps.

oki 15. Dez 2010 07:24

AW: Webbrowser.OleObject mittels Vorfahren erreichen
 
Hallo und guten Morgen.

Sorry, dass ich gestern nicht mehr antworten konnte, ist der Stress ausgebrochen.
Shmia: Ich möchter auf ein Text-Eingabefeld zugreifen (input type Text). Grundsätzlich ist das für das aktuelle Problem wurscht, aber für alternative Wege vielleicht nicht. Zugriff auf ein Element Type button ist auch noch von nöten.

by Thom: Erst mal Dank für deine Mühe. Vorweg, in einem kleinen Testprojekt klappt der Zugriff:

Delphi-Quellcode:
wb.OleObject.Document.All.Item('ItemName').Value := 'Test';

ohne Probleme. WB ist dabei natürlich TEmbeddedWB. Das Problem steckt da, dass ich über einen Vorfahren/anderen Weg zugreifen will/muss. Also ohne Einbindung des Packages/Unit EmbeddedWB. In deiner Funktion übergibst du einen Parameter vom Typ TWebBrowser. Ich denke mal, du gehst davon aus, dass ich dann sicher TEmbeddedWB benutzen muss und die beiden nicht "kompatibel" sind. Damit bin ich dann wieder da wo ich vorher war, ich muss die entsprechenden Units einbinden. Konkret in Bezug auf deine Funktion suche ich jetzt für die Übergabe einen Typ, der einem Vorfahren entspricht. Dabei gehe ich davon aus, dass sowohl TWebBrowser wie auch TEmbeddedWB nicht die Klassen sind, die OleObjekt und somit Dokument einbinden. Das erfolgt doch sicher im Rahmen der Vererbung viel früher. Da ich an keinen anderen Eigenschaften interessiert bin will ich einfach auf diesen Vorfahren casten und somit auf die Eigenschaft zugreigen. Mal ein einfaches Beispiel, ich habe ein Panel und will den Namen haben. In meiner Funktion will ich den Parameter in dem ich das Panel übergebe aber auch für andere Komponenten benutzen. So definiere ich den Parameter als TComponent. Stopfe ich da das Panel rein (TComponent(MyPanel)), so kann ich innerhalb der Funktion mittels AComponent.Name den Namen des übergebenen Panels erreichen. Ja, und das nun mit dem Browser und zugriff auf OleObject. Mein Versuch mit TOleControl ging in die Hose. Welcher Klassentyp ist der richtige?


Gruß oki

Thom 15. Dez 2010 14:26

AW: Webbrowser.OleObject mittels Vorfahren erreichen
 
Liste der Anhänge anzeigen (Anzahl: 3)
Hallo oki,
da hast Du natürlich Recht:
Zitat:

Zitat von oki (Beitrag 1068592)
In deiner Funktion übergibst du einen Parameter vom Typ TWebBrowser. Ich denke mal, du gehst davon aus, dass ich dann sicher TEmbeddedWB benutzen muss und die beiden nicht "kompatibel" sind.

Da diese Prozedur aber nur das Interface von Document benötigt, könnte das einfach abgewandelt werden:
Delphi-Quellcode:
procedure SetProperty(Document: IDispatch; NodeName, PropertyName: String; Value: OleVariant);
var
  All, Node: OleVariant;
begin
  //"All"-Objekt des Dokumentes ermitteln:
  All:=GetProp(Document,'all');
  //HTML-Element aus dem "All"-Objekt ermitteln:
  Node:=GetProp(All,NodeName);
  //Eigenschaft des HTML-Elementes setzen:
  SetProp(Node,PropertyName,Value);
end;
Damit wäre es dann egal, ob das Dokument aus TWebBrowser.Document oder TEmbeddedWB.Document (bzw. TEmbeddedWB.OleObject.Document) stammt.

Ist aber unwichtig: Ich habe das Ganze heute noch einmal - im Gegensatz zu gestern - in einem neuen Projekt ausprobiert. Da hatte ich das einfach in mein aktuelles Projekt "integriert". Und siehe da: Es funktioniert! Das ist schon recht interessant, wie da der Compiler im Hintergrund "herumzaubert", so dass es - abhängig von den Randbedingungen - mal funktioniert und mal nicht...

Zu Deiner Frage: Du brauchst zum Zugriff auf das HTML-Element also nur - wie schon erwähnt - das Interface des Document-Objektes. Dabei ist es egal, ob es als IDispatch oder "verpackt" als (Ole-)Variant vorliegt. Damit der Compiler aber solchen Quelltext wie
Delphi-Quellcode:
Document.Irgendwas.NochWasAnderes
akzeptiert, ist die Verwendung einer Variante besser. Schließlich generiert der Compiler hier den Code nur auf "gut Glück", da er noch nicht wissen kann, ob die Zeiger auch wirklich existieren. Erst zur Laufzeit "fragt" sich das Programm dann recht aufwändig (siehe mein erster Beitrag) von Objekt zu Objekt durch.

Praktisch bedeutet das: Du mußt von der Unit, die die Browser-Komponente beherbergt, das Document-Interface an die Unit, von der aus Du auf die Browser-Komponente zugreifen möchtes, weiterreichen:
Delphi-Quellcode:
procedure TForm1.FormCreate(Sender: TObject);
begin
  EmbeddedWB1.LoadFromFile('Test.html');
end;

procedure TForm1.FormShow(Sender: TObject);
begin
  Form2.Document:=EmbeddedWB1.Document;
end;
Von dort aus kannst Du dann ohne dem Wissen, aus welcher Browser-Komponente das Dokument stammt, darauf zugreifen:
Delphi-Quellcode:
procedure TForm2.Button1Click(Sender: TObject);
var
  Element: Variant;
begin
  if VarIsType(Document,varDispatch) then
  begin
    Element:=Document.All.Item('Test');
    if not VarIsClear(Element)
      then Element.InnerText:=Edit1.Text
      else ShowMessage('Element nicht gefunden');
  end;
end;
Ich habe das Ganze mal als Zip-Datei angehängt.

shmia 15. Dez 2010 16:37

AW: Webbrowser.OleObject mittels Vorfahren erreichen
 
Zitat:

Zitat von oki (Beitrag 1068592)
...Ich möchter auf ein Text-Eingabefeld zugreifen (input type Text)

Dann gäbe es aber einen alternativen Weg durch das Objektmodell des IE (den ich für besser halte).
Ein Document kann ein oder mehrere "WebForms" enthalten.
Ein Form enthält dann ein (oder mehrere) Input Items.
Man kann dann z.B. mit einer Schleife über die Items und die Werte verändern.
Hier ein Beispiel:
Delphi-Quellcode:
procedure FillWebForm(document:OleVariant);
var
  FormItem, Element : OleVariant;
  j : integer;
  itemtype, itemname : string;
begin
  if document.forms.Length = then
    Exit; // keine WebForms vorhanden
  FormItem := document.forms.Item(0); // 1. Formular verwenden
 
  For j:= 0 to FormItem.Length-1 do
  begin
     Element := FormItem.Item(j);
     itemname := Element.Name;
     itemtype := UpperCase(Element.Type);
     if itemtype = 'TEXT' then
     begin
       maxlen := Element.MaxLength;
       Element.Value:= Copy('Hallo das ist ein Test', 1, maxlen);
     end
     else if itemtype = 'CHECKBOX' then
     begin
        // Checkbox zufällig setzen
        if system.Random > 0.5 then
           Element.Checked := True;
     end;

     // weitere Itemtypes sind BUTTON, CHECKBOX, SELECT-ONE, RADIO
 
  end;
  FormItem.Submit; // Webform absenden
end;

oki 17. Jan 2011 14:55

AW: Webbrowser.OleObject mittels Vorfahren erreichen
 
Hallo Leute,

es ist schon ein paar Tage her, aber ich bin an diesem Thema weiter gekommen. Es lag nicht an der Benutzung im speziellen sondern konkret an einer Stelle.

folgender Code arbeitet ohne Probleme:

Delphi-Quellcode:
  wb.OleObject.Document.All.Item('ItemName').Value := 'Test';
  wb.OleObject.Document.All.Item('ItemName').Value := Edit1.Text;

folgender nicht:

Delphi-Quellcode:
  NewText : String;
  ...
  NewText := 'Test';
  wb.OleObject.Document.All.Item('ItemName').Value := NewText;
aber so:

Delphi-Quellcode:
  NewText : String;
  ...
  NewText := 'Test';
  wb.OleObject.Document.All.Item('ItemName').Value := Variant(NewText);
Das schien das ganze Problem gewesen zu sein. Ich denke mal, dass mein Problem erst darurch aufgetaucht ist, dass ich in meiner kleinen Testanwendung Edit1.Text zugewiesen habe und in meinem eigentlichen Code einen String. Für mich war das das gleiche und so habe ich den Unterschied in der Zuweisung gar nicht erst beachtet. Nach dem ich mich durch die Hilfe mittels IXMLDocument2 ... Item usw. gehangelt hatte viel mir die variante Parameterverwendung auf. Naja, nun klappts mit der Typkonvertierung ohne Probleme. Zusätzlich hat mich natürlich noch der Compiler aus der Bahn geworfen, da er dann auch immer schon für die Prüfung auf Document Adresse 0000 ausgeworfen hatte. So ist das halt im Leben.

Wollte auf jeden Fall noch die Lösung posten, damit der Nächste nicht leer da steht.

Zusätzlich noch mal herzlichen Dank für die Code-Snippes zu testen.

Gruß oki

[edit] kurzer Nachtrag; das unter D2007; Gruß oki [/edit]


Alle Zeitangaben in WEZ +1. Es ist jetzt 21:30 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