Title: How to get all documents for all frames from a TWebbrowser
Question: If youre stuck when it comes to frames in a TWebBrowser and dont know how to get a grip to the documents inside the frames, this article is for you.
Answer:
!! UPDATED ON 24.12.2007------
(Added IHTMLFrameBase for questioning the framename and changed logic because of wrong exit of procedures.)
THIS IS THE NEW ARTICLE.
OLD ONE (ArtID:1989 "Loop through frames of a TWebBrowser".) IS VOID NOW :-)
This is my second article on the subject.
As i wrote the first one, in early 2001, i was satisfied with what i achieved, as it was sufficient for the task at hand.
This year i had another project to do that handled with the same subject: Automatically filling forms with the help of a webbrowser-component.
But as the project growd i had to face the fact that some websites denied access to certain frames, or at least it seemed so.
As i studied the html-code for the websites that did not work i realised that their code was build on javascript. In fact, all the FRAMESET and FRAME code was depending on javascript-functions like in this example:
document.write("");
document.write("");
If you try to get the documents behind the frames with your normal Webbrowser.frames[0] functions youre lost....well at first.
I discovered a function which will do the hard work for you and get any desired frame-document you like of the entire collection.
For the function to work you need at least these units:
uses Sysutils, MSHTML, ActiveX, SHDocVw;
I used the EmbeddedWB component with updated MSHTML and ShDocVw_EWB to accomplish the task. So if something does not work for you consider
upgrading or installing the EmbeddedWB component from this link:
http://www.bsalsa.com
To use this function just Navigate to an URL with frames and
pass your webbrowser-component and a frame-name to search for.
And now comes the function
---------------------------------------------------------------------
function GetFrameByName(ABrowser:TWebBrowser;aFrameName: string): IHTMLDocument2;
var
searchdoc : IHTMLDocument2; // temporarily holds the resulting page
FoundIt : boolean // Flag if we found the right Frame
// This is the inner recursive portion of the function
function Enum(AHTMLDocument:IHTMLDocument2;aFrameName:string):boolean;
var
OleContainer: IOleContainer;
EnumUnknown: IEnumUnknown;
Unknown: IUnknown;
Fetched: LongInt;
WebBrowser: IWebBrowser;
FBase : IHTMLFrameBase;
begin
Result:=True;
if not Assigned(AHtmlDocument) then Exit;
if not Supports(AHtmlDocument, IOleContainer, OleContainer) then Exit;
if Failed(OleContainer.EnumObjects(OLECONTF_EMBEDDINGS, EnumUnknown)) then
Exit;
while (EnumUnknown.Next(1, Unknown, @Fetched)=S_OK) do
begin
if Supports(Unknown, IHTMLFrameBase,FBase) then
begin
// Here we ask if we have the right Frame
if LowerCase(FBase.name) = LowerCase(aFrameName) then
begin
if Supports(Unknown, IWebBrowser, WebBrowser) then
begin
searchdoc := (WebBrowser.Document as IHTMLDocument2);
FoundIt:=true;
exit;
end;
end;
end;
if NOT (FoundIt) then
begin
if Supports(Unknown, IWebBrowser, WebBrowser) then
begin
Result:=Enum((WebBrowser.Document as IHtmlDocument2),aFrameName);
if not Result then Exit;
end;
end else exit;
end;
end;
begin
FoundIt := false;
result := nil; // just in case
Enum((ABrowser.Document as IHTMLDocument2),aFrameName);
if FoundIt then
result := searchdoc
else
result := nil;
end;
---------------------------------------------------------------------
If you need to get a frame-document by id (integer) you could use the function like this:
---------------------------------------------------------------------
function GetFrameByID(ABrowser:TWebBrowser;aFrameID: integer): IHTMLDocument2;
var
searchdoc : IHTMLDocument2; // temporarily holds the resulting page
i : integer; // IDcount
// This is the inner recursive portion of the function
function Enum(AHTMLDocument:IHTMLDocument2;aFrameID:integer):boolean;
var
OleContainer: IOleContainer;
EnumUnknown: IEnumUnknown;
Unknown: IUnknown;
Fetched: LongInt;
WebBrowser: IWebBrowser;
i : integer;
begin
inc(i);
Result:=True;
if not Assigned(AHtmlDocument) then Exit;
if not Supports(AHtmlDocument, IOleContainer, OleContainer) then Exit;
if Failed(OleContainer.EnumObjects(OLECONTF_EMBEDDINGS, EnumUnknown)) then
Exit;
while (EnumUnknown.Next(1, Unknown, @Fetched)=S_OK) do
begin
if Supports(Unknown, IHMTLFrameBase, FBase) then
begin
// Here we ask if we have the right Frame
if i = aFrameID then
begin
if Supports(Unknown, IWebBrowser, WebBrowser) then
begin
searchdoc := (WebBrowser.Document as IHTMLDocument2);
FoundIt := true;
exit;
end;
end;
end;
if NOT (FoundIt) then
begin
if Supports(Unknown, IWebBrowser, WebBrowser) then
begin
Result:=Enum((WebBrowser.Document as IHtmlDocument2),aFrameName);
if not Result then Exit;
end;
end else exit;
end;
end;
begin
i := -1;
result := nil; // just in case
Enum((ABrowser.Document as IHTMLDocument2),aFrameID);
if FoundIt then
result := searchdoc
else
result := nil;
end;
---------------------------------------------------------------------
This is the result of another 3 days of my live spend coding.
I hope you can use this for your purposes.
Happy coding.