delphi webbrowser copy text from website - delphi

In my application I want to copy all the text from a website into a string variable. Because of some issues with Indy, I want to use the webbrowser component.
The following code works perfectly for me:
procedure TForm1.Button1Click(Sender: TObject);
begin
WebBrowser1.Navigate('www.tribalwars.nl');
while WebBrowser1.Busy do
Application.ProcessMessages;
Memo1.Lines.Add((WebBrowser1.Document as IHTMLDocument2).body.innerText);
end;
However, in the example above I use a WebBrowser that has been manually created on my Form1.
Now I want to create it during runtime. I tried the following code:
procedure TForm1.Button2Click(Sender: TObject);
var Web: TWebBrowser;
begin
Web := TWebBrowser.Create(nil);
Web.Navigate('www.tribalwars.nl');
while Web.Busy do
Application.ProcessMessages;
Memo1.Lines.Add((Web.Document as IHTMLDocument2).body.innerText); //This line raises the error mentioned below
Web.Free;
end;
Unfortunately it keeps raising the following error:
Project Project1.exe raised exception class $C0000005 with message 'access violation at 0x005d9b4f: read of address 0x00000000'.
I guess I'm trying to use something that hasn't been created yet, or somewhere in that direction.
I hope someone can help me get this to work!
EDIT: whosrdaddy mentioned that I should make this component visible. How can I do that? I tried this, but it doesn't work:
procedure TForm1.Button2Click(Sender: TObject);
var Web: TWebBrowser;
begin
Web := TWebBrowser.Create(nil);
Web.Left := 50;
Web.Top := 50;
Web.Width := 50;
Web.Height := 50;
Web.Visible := True;
Application.ProcessMessages;
Web.Navigate('www.tribalwars.nl');
while Web.Busy do
Application.ProcessMessages;
Memo1.Lines.Add((Web.Document as IHTMLDocument2).body.innerText);
Web.Free;
end;

The problem is that when you create TWebBrowser dynamically and pass NIL as the owner, unfortunately the parent is also NIL. a non -NIL parent is needed to display anything.
Normally you would do this:
var
pnlBrowser : TPanel;
Web : TWebBrowser;
Web := TWebBrowser.Create(nil);
Web.Parent := pnlBrowser;
BUT, unfortunately, you cannot (directly) do this either (you get an error message "read-only property" if you try).
But luckily, there IS a way to circumvent the problem:
TWinControl(Web).Parent := pnlBrowser; // this works OK!
I have no idea WHY the parent property of the TWebBrowser class is read-only.
Reading the Delphi documentation, also
TControl(Web).Parent := pnlBrowser; // this should also work
as a side note:
If you have TmsMediaPlayer component (the ActiveX version of Microsoft Windows Media Player), setting parent using the Delphi's Parent property will stop any video playing, but setting it directly through a windows API call does not.
IF you want to use your TWebBrowser to play videos, changing the Parent property on the fly may also stop any video playing. If so it is worth trying to change the parent using windows API call directly instead to avoid stopping a video playing in the web browser.

1) try to change Your TWebBrowser component to TEmbeddedWB
- the parameters/events are the same + lots of extras You can use...
2) I think the problem is with the readystate of your created browser after navigation - its not loaded completely (+maybe it has not assigned parent)
try use the following code (replace Your TWebBrowser component name):
Web.Navigate('www.tribalwars.nl')
repeat application.processmessages; until web.readystate=4;
Memo1.Lines.Add((Web.Document as IHTMLDocument2).body.innerText);

Related

How to capture the active control in Firemonkey?

I am migrating a VCL application to FMX. I need to know the class of a control that has focus. The application works with various dynamically created frames with numerous input controls.
In VCL I use the VCL.Forms.TScreen.OnActiveControlChange as this is the one place to consistently capture the active control. This event is not available in FMX.Forms.TScreen. What would be an alternative approach in FMX?
The most similar approach in FMX would be to listen to the TForm.OnFocusChanged event. From within the event handler you could then look up theTForm.Focused property.
Sadly in Delphi 10.3, the approach outlined by #iamjoosy fails spectacularly with an 'Access Violation' under certain circumstances (especially when TabControls / TabItems are used as containers for other controls).
The code I am using:
procedure TForm1.FormFocusChanged(Sender: TObject);
var
Control : iControl;
MyControl : TFMXObject;
begin
Control := form1.focused;
try
MyControl := TFmxObject(Control.GetObject);
form1.Caption := MyControl.Name + ' of type ' + MyControl.ClassName;
finally
MyControl := nil;
Control := nil;
end;
end;
Yet to add some intrigue, the above approach reverts to working fine if:
1) There's no TabControl/TabItem objects
2) If I add the following event handler to each child button (e.g. setting focus back to its parent TabItem):
procedure TForm1.Button2Click(Sender: TObject);
begin
TabItem1.SetFocus;
end;
Hoping someone can offer advice as to whether I'm doing something stupid, or whether I've run into an FMX bug.

invalid class typecast in intraweb

I'm porting an old application from delphi7 to delphi xe8
and from intraweb 8 to intraweb XIV
My app was subdivided in a main program and a number of child packages
and it worked perfectly with the old components.
With theese new components, I now get an exception trying to generate and return a page to the server controller, creating such a page from a child package.
If instead I generate the page from the main app, it works.
In the procedure TIWServerController.IWServerControllerBaseGetMainForm
I call a procedure of a my component (packman) that tries to obtain a main window from a child package.
this is the servercontroller function
procedure TIWServerController.IWServerControllerBaseGetMainForm(var vMainForm : TIWBaseForm);
begin
VMainForm := PackMan.MainLoginForm(webApplication);
end;
and this is the packman function:
function tPackMan.MainLoginForm (aOwner:tComponent) : tIwAppForm;
var Proc : tGetMainFormProc;
begin
#Proc := GetProcAddress (LoginPkg,'MainForm');
Result := Proc(aOwner);
end;
this is the definition of the procedural type:
tGetMainFormProc = function (aOwner:tComponent): tIwAppForm;
and this is the MainForm procedure, in the child package (packlogin).
Initially I tried to create the original form, full of components,
after that I've removed all components from original form, without success,
and finally I tried to construct an empty form, as shown in this sample:
function MainForm (aOwner:tComponent): tIWAppForm;
begin
Result := tIWAppForm.Create(aOwner);
end;
exports MainForm;
I've traced the program behaviour using several Outputdebugstring messages (here not shown) and I've come to the following conclusion:
1) the Mainform procedure, in the package, seems to return a valid tIwAppform
2) this Object is correctly returned to the IWServerControllerBaseGetMainForm procedure
and the variable VMainForm is correctly assigned.
3) if I inspect the classname property of this variable, I see it has the value "tIWAppform".
The exception seems to be generated at the procedure return.
I've interceped it in the procedure IWServerControllerBaseException
with the following code :
procedure TIWServerController.IWServerControllerBaseException(
AApplication: TIWApplication; AException: Exception;
var Handled: Boolean);
begin
Dump ('UNEXPECTED EXCEPTION ' + AException.message);
Handled := true;
end;
What am I missing ?
Any suggestion ?
Regards.
Maurizio.

How to write and show something on Delphi IDE status bar

I want to know how can I write a module to show something like clock or other thing on Borland Delphi 7 IDE status bar, because I know it's possible but I couldn't find how!
To insert a text in a StatusBar, you have to insert a panel first.
Just select your statusbar, find the property "Panels" (or perform double click over the statusbar) and click in "Add new".
After that, you can write what you want inside the panel in the property "Text" (you can insert one or more panels).
To do it programmatically, you can do something like this:
procedure TForm1.Timer1Timer(Sender: TObject);
begin
StatusBar1.Panels[0].Text := 'Today is: ' + FormatDateTime('dd/mm/yyyy hh:nn:ss', Now);
end;
Since OP didn't replied with more details, I'm going to post a little demonstration how to reach a status bar of Delphi's edit window. I had no success with adding new distinct status panel w/o disturbing layout, so I'm just changing the text of INS/OVR indicator panel.
Disclaimer: I still do not have access to the machine with Delphi 7 installed, so I've done that in BDS ("Galileo") IDE. However, differences should be minor. I believe what main difference lies in the way how we locate edit window.
Key strings are: 'TEditWindow' for edit window class name and 'StatusBar' for TStatusBar control name owned by edit window. These strings are consistent across versions.
{ helper func, see below }
function FindForm(const ClassName: string): TForm;
var
I: Integer;
begin
Result := nil;
for I := 0 to Screen.FormCount - 1 do
begin
if Screen.Forms[I].ClassName = ClassName then
begin
Result := Screen.Forms[I];
Break;
end;
end;
end;
procedure Init;
var
EditWindow: TForm;
StatusBar: TStatusBar;
StatusPanel: TStatusPanel;
begin
EditWindow := FindForm('TEditWindow');
Assert(Assigned(EditWindow), 'no edit window');
StatusBar := EditWindow.FindComponent('StatusBar') as TStatusBar;
(BorlandIDEServices as IOTAMessageServices).AddTitleMessage(Format('StatusBar.Panels.Count = %d', [StatusBar.Panels.Count]));
//StatusPanel := StatusBar.Panels.Add;
StatusPanel := StatusBar.Panels[2];
StatusPanel.Text := 'HAI!';
end;
initialization
Init;
finalization
// nothing to clean up yet
Another note: As you see, I use Open Tools API to output debug messages only, to interact with IDE I do use Native VCL classes. Therefore, this code must be in package.
The code above is a relevant part of the unit which should be contained in package. Do not forget to add ToolsAPI to uses clause as well as other appropriate referenced units (up to you).
Package should require rtl, vcl and designide (important!).
Since I run the testcase directly from initialization section, installing the package is enough for testcase to run and produce some result.

Delphi TChromium CEF3 run-time created browser loads pages incorrectly

I'm using TChromium for authorisation.
I have variable Chromium1: TChromium;. I need to destroy chromium on form hide and show on form show. (Because chromium some times raises exception "EExternal Exception in module libcef.dll" and crashes my app, I think that destruction of Chromium when it no uses will help).
I have:
procedure TAuthForm.FormShow(Sender: TObject);
begin
Chromium1 := TChromium.Create(self);
Chromium1.Align := alClient;
Chromium1.OnJsdialog := Chromium1Jsdialog;
Chromium1.OnBeforePopup := Chromium1BeforePopup;
Chromium1.DefaultUrl := 'about:blank';
Chromium1.Parent := Self;
Chromium1.Load(AUTH_URL);
end;
procedure TAuthForm.FormHide(Sender: TObject);
begin
Chromium1.Free;
end;
The problem is that when TAuthForm.FormShow called first, page loads correctly, but when I hide form and show it again browser displays page incorrectly, don't now how to explain here is an example for http://google.com/:
Google(function(){ window.google={kEI:"oKRKUZ2iHafP4QTXwYCwDw",getEI:function(a) {for(var b;a&&(!a.getAttribute||!(b=a.getAttribute("eid")));)a=a.parentNode;return b||google.kEI},https:function(){return"https:"==window.location.protocol},kEXPI:"17259,39523,4000116,4001076,4001568,4001948,4001959,4001975,4002562,4002734,4002855,4003178,4003387,4003495,4003917,4004181,4004213,4004257,4004334,4004340,4004479,4004652,4004653,4004697,4004730,4004755,4004759,4004766,4004898,4004905,4004949,4004953,4005154",kCSI:{e:"17259,39523,4000116,4001076,4001568,4001948,4001959,4001975,4002562,4002734,4002855,4003178,4003387,4003495,4003917,4004181,4004213,4004257,4004334,4004340,4004479,4004652,4004653,4004697,4004730,4004755,4004759,4004766,4004898,4004905,4004949,4004953,4005154",ei:"oKRKUZ2iHafP4QTXwYCwDw"},authuser:0,ml:function(){},pageState:"#",kHL:"ru",time:function(){return(new Date).getTime()},log:function(a, b,c,h){var d=new Image,f=google.lc,e=google.li,g="";d.onerror=d.onload=d.onabort=function(){delete f[e]};f[e]=d;!c&&-1==b.search("&ei=")&&(g="&ei="+google.getEI(h));c=c||"/gen_204?........
it shows the content of all blocks including <title> and <script> as you can see here...
I've tried to find the solution, but found only the same error(I mean libcef.dll exception without solution), here: https://stackoverflow.com/questions/13784792/cef3-application-crash-fault-module-kernelbase-dll in comments...
Chromium1.Free; is the pbm
it destroys your chrom instance , son you can't re see it again
just do :
Chromium1.visible := false; // if you wish to hide it

Problem with running WebService in separate thread in Delphi

I have never asked questions in any community as I always solved problems by myself or could find them online. But with this one I came to dead end and need Help!
To make it very clear – I converted a simple app, found elsewhere to make it use a Tthread object.
The idea is simple – the app checks online using webservice, through THTTPRIO component, weather and put the results in Memo1 lines.
Clicking on Button1 we get it done in standard way – using THTTPRIO put on the Form1 (it's called here htt as in original app) and using main and only thread.
procedure TForm1.Button1Click(Sender: TObject);
var
wf:WeatherForecasts;
res:ArrayOfWeatherData;
i:integer;
begin
wf:=(htt as WeatherForecastSoap).GetWeatherByPlaceName(edit1.Text);
if wf.PlaceName<> '' then
res:=wf.Details;
memo1.Lines.Add('The min and max temps in Fahrenheit is:');
memo1.Lines.Add(' ');
for i:= 0 to high(res) do
begin
memo1.Lines.Add(res[i].Day+' - '+ ' Max Temp. Fahr: '+res[i].MaxTemperatureF+' - '+'Min Temp Fahr: '+res[i].MinTemperatureF);
end
end;
Clicking on Button2 – we use class TThread
procedure TForm1.Button2Click(Sender: TObject);
var WFThread:WeatherThread;
begin
WFThread := WeatherThread.Create (True);
WFThread.FreeOnTerminate := True;
WFThread.Place := Edit1.Text;
WFThread.Resume;
end;
In Execute procedure in WeatherThread1 unit I put this code:
procedure WeatherThread.Execute;
begin
{ Place thread code here }
GetForecast;
Synchronize (ShowWeather);
end;
...and the GetForecast code:
procedure WeatherThread.GetForecast;
var
HTTPRIO: THTTPRIO;
wf:WeatherForecasts;
res:ArrayOfWeatherData;
i:integer;
begin
HTTPRIO := THTTPRIO.Create(nil);
HTTPRIO.URL := 'http://www.webservicex.net/WeatherForecast.asmx';
HTTPRIO.WSDLLocation := 'http://www.webservicex.net/WeatherForecast.asmx?WSDL';
HTTPRIO.Service := 'WeatherForecast';
HTTPRIO.Port := 'WeatherForecastSoap';
wf:=(HTTPRIO as WeatherForecastSoap).GetWeatherByPlaceName(Place);
if Lines=nil then Lines:=TStringList.Create;
if wf.PlaceName<> '' then
res:=wf.Details;
Lines.Clear;
for i:= 0 to high(res) do
begin
Lines.Add(res[i].Day+' - '+ ' Max Temp. Fahr: '+res[i].MaxTemperatureF+' - '+'Min Temp Fahr: '+res[i].MinTemperatureF);
end;
end;
Procedure ShowWeather shows results in Form1.Memo1.
And now there is a problem: In main thread, clicking Button1, everything works fine. But of course when HTTPRIO component communicates – it freezes the form.
With Button2 I put the code in separate thread but it does NOT WANT TO WORK! Something strange happens. When I start application – and click Button2, there is an error when using HTTPRIO component. But it works for a while when I click FIRST Button1 and AFTER THAT Button2 (but it works for a while, 5-7 clicks only).
I suppose I do something wrong but cannot figure out where the problem is and how to solve it. It looks like the code in threaded unit is not thread-safe, but it should be. Please help how to make HTTPRIO work in a thread!!!
You can find zipped full code here.
When I run your code in Delphi 2007, madExcept shows an exception CoInitialize has not been called.
After adding the call to CoInitialize in the execute method, the webservice gets called without problems.
Possible fix
procedure TWeatherThread.Execute;
begin
CoInitialize(nil);
try
...
finally
CoUninitialize;
end;
end;
A long shot, but I'm missing calls to Synchronize here:
You should never update your GUI directly from your thread code.
You should embed those calls inside a method, and call that method using the TThread.Synchronize method for this.
Delphi about has a nice demo on this.
Since Delphi 4, it includes a demo called sortthds.pas in the ...\demos\threads subdirectory that shows the same.
--jeroen
You may be clouding the issue by doing the dynamic RIO creation (RIO objects have a strange lifetime) and threading together, and comparing that outcome to the straightforward Button1. I'd make another button that calls GetForecast without threads. See if that works. If it bombs, then your problem isn't threading.

Resources