Forms TWebBrowser-它是否仅在显示Delphi父窗体时才起作用?

Forms TWebBrowser-它是否仅在显示Delphi父窗体时才起作用?,forms,delphi,twebbrowser,Forms,Delphi,Twebbrowser,我有一张表格,叫做FrmCheck,上面有一个Twebbrowser。 不需要显示webbrowser,但为了方便起见,我可以使用它(而不是Indy或动态创建TwebbBrowser)。 FrmCheck上唯一的公共函数是函数CheckIP(TheIP:string):boolean导航到几个网页,对IP地址进行一些处理,设置布尔retun值并退出 该功能工作正常 但是,我注意到,当从另一个窗体调用函数CheckIP时,只有当FrmCheck(包含TWebBrowser的窗体)显示时,它才会返回

我有一张表格,叫做FrmCheck,上面有一个Twebbrowser。 不需要显示webbrowser,但为了方便起见,我可以使用它(而不是Indy或动态创建TwebbBrowser)。 FrmCheck上唯一的公共函数是
函数CheckIP(TheIP:string):boolean导航到几个网页,对IP地址进行一些处理,设置布尔retun值并退出

该功能工作正常

但是,我注意到,当从另一个窗体调用函数CheckIP时,只有当FrmCheck(包含TWebBrowser的窗体)显示时,它才会返回

这管用

procedure TForm1.TestMyIPaddress(Sender: TObject);
var 
    myIP : string;
begin
myIP := GetExternalIPAddress;
FrmCheck.Show;

if FrmCheck.CheckIP(myIP) then
   ShowMessage('New IP address ' + myIP +' added to those allowed access')
else
    ShowMessage('IP address already there') ;
end;
但是有了FrmCheck.Show;注释掉该函数不返回

这不管用

procedure TForm1.TestMyIPaddress(Sender: TObject);
var 
    myIP : string;
begin
myIP := GetExternalIPAddress;
 //FrmCheck.Show;

if FrmCheck.CheckIP(myIP) then
   ShowMessage('New IP address ' + myIP +' added to those allowed access')
else
    ShowMessage('IP address already there') ;
end;
作为一项工作,我发现我可以展示表格,但立即让它隐形

也就是说,这是可行的,不会在屏幕上显示所需的行为形式

procedure TForm1.TestMyIPaddress(Sender: TObject);
var 
    myIP : string;
begin
myIP := GetExternalIPAddress;
FrmCheck.Show;
FrmCheck.Visible := False;

if FrmCheck.CheckIP(myIP) then
   ShowMessage('New IP address ' + myIP +' added to those allowed access')
else
    ShowMessage('IP address already there') ;
end;
这是预期的行为吗

TWebBrowser是否只有在显示表单时才能正常工作(即使表单不可见),或者我是否应该在其他地方寻找解释


与MartynA不同的是,这里是表单的代码,使用了真实的函数名,而不是我用来明确问题要点的简化函数名

我仍然只是在问这样一个问题:“TWebBrowser只有在显示表单时才能正常工作吗?”?而不是我的代码有什么问题

unit U_FrmCheckIPaddressIsInAllowedHosts;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, OleCtrls,
  MSHTML,    //to access the ole bits of twebrowser
  StrUtils,  //for 'containstext' function
  IdHTTP,   //for GetExtenalIPAddress function
  SHDocVw,   //to get to the Twebbroswer Class so we can extend it
  ActiveX // For IOleCommandTarget   when adding extensions to Twebbrowser
  ;

type

//override Twebbrowser to add functionality to suppres js errors yet keep running code
//from https://stackoverflow.com/questions/8566659/how-do-i-make-twebbrowser-keep-running-javascript-after-an-error
  TWebBrowser = class(SHDocVw.TWebBrowser, IOleCommandTarget)
  private
    function QueryStatus(CmdGroup: PGUID; cCmds: Cardinal; prgCmds: POleCmd;
      CmdText: POleCmdText): HRESULT; stdcall;

    function Exec(CmdGroup: PGUID; nCmdID, nCmdexecopt: DWORD;
      const vaIn: OleVariant; var vaOut: OleVariant): HRESULT; stdcall;
  end;
  ////////////////////////////////////////////////////

  TFrmCheckIPaddressIsInAllowedHosts = class(TForm)
    WebBrowser1: TWebBrowser;
    procedure WebBrowser1BeforeNavigate2(ASender: TObject;
      const pDisp: IDispatch; var URL, Flags, TargetFrameName, PostData,
      Headers: OleVariant; var Cancel: WordBool);
    procedure WebBrowser1DocumentComplete(ASender: TObject;
      const pDisp: IDispatch; var URL: OleVariant);
    procedure WebBrowser1NavigateComplete2(ASender: TObject;
      const pDisp: IDispatch; var URL: OleVariant);
    procedure FormCreate(Sender: TObject);
    procedure FormDestroy(Sender: TObject);

  private      { Private declarations }
    CurDispatch: IDispatch;  //used to wait until document is loaded
    FDocLoaded: Boolean;     //flag to indicate when document is loaded
    addresses : TStringList;  //to hold the list of IP addresses already in hosts list
    TheIPAddress:string;
    AddressAdded : Boolean; //set to True if added



    procedure LogIntoCpanelAndCheckIPaddress;
    function GetElementById(const Doc: IDispatch; const Id: string): IDispatch;
    function GetTextOfPage(WB:twebbrowser) : string;
    function IPaddressAlreadyPresent(TheIPAddress:string; HostList2:TstringList): boolean ;
    procedure Logout;
    procedure AddNewIPaddress(TheIPaddress: string);
    function GetExternalIPAddress: string;   //works without needing to create a file
  public
    { Public declarations }
     function CheckIPAddressAndAddIfNecessary(IPaddress:string):boolean;     //returns true if address added,false otherwise
  end;

var
  FrmCheckIPaddressIsInAllowedHosts: TFrmCheckIPaddressIsInAllowedHosts;
  CheckForIPaddress : Boolean;
  CanExit : Boolean;   //flag to say we have checked the address and maybe added it

implementation

{$R *.dfm}

{ TForm5 }


{ TWebBrowser extensions}

function TWebBrowser.Exec(CmdGroup: PGUID; nCmdID, nCmdexecopt: DWORD;
  const vaIn: OleVariant; var vaOut: OleVariant): HRESULT; stdcall;
begin
  // presume that all commands can be executed; for list of available commands
  // see SHDocVw.pas unit, using this event you can suppress or create custom
  // events for more than just script error dialogs, there are commands like
  // undo, redo, refresh, open, save, print etc. etc.
  // be careful, because not all command results are meaningful, like the one
  // with script error message boxes, I would expect that if you return S_OK,
  // the error dialog will be displayed, but it's vice-versa
  Result := S_OK;

  // there's a script error in the currently executed script, so
  if nCmdID = OLECMDID_SHOWSCRIPTERROR then
  begin
    // if you return S_FALSE, the script error dialog is shown
    Result := S_FALSE;
    // if you return S_OK, the script error dialog is suppressed
    Result := S_OK;
  end;
end;   { end of TWebBrowser extensions}



function TWebBrowser.QueryStatus(CmdGroup: PGUID; cCmds: Cardinal;
  prgCmds: POleCmd; CmdText: POleCmdText): HRESULT;  stdcall;
begin
    Result := S_OK;
end;


procedure TFrmCheckIPaddressIsInAllowedHosts.AddNewIPaddress(TheIPaddress: string);
var
  Elem: IHTMLElement;

begin
//get hold of the new hosts box and enter the new IP address
  Elem := GetElementById(WebBrowser1.Document, 'host') as IHTMLElement;
  if Assigned(Elem) then
   if Elem.tagName = 'INPUT' then (Elem as IHTMLInputElement).value := TheIPaddress;

   //now click the add hosts button
     Elem := GetElementById(WebBrowser1.Document, 'submit-button') as IHTMLElement;
  if Assigned(Elem) then
    Elem.click;
end;


function TFrmCheckIPaddressIsInAllowedHosts.CheckIPAddressAndAddIfNecessary(IPaddress:string):boolean;
begin
TheIPAddress :=     IPaddress;
AddressAdded := False;
LogIntoCpanelAndCheckIPaddress  ;
Result := AddressAdded;
end;

procedure TFrmCheckIPaddressIsInAllowedHosts.FormCreate(Sender: TObject);
begin
  addresses := TStringList.create;
end;

procedure TFrmCheckIPaddressIsInAllowedHosts.FormDestroy(Sender: TObject);
begin
 addresses.Free;
end;



function TFrmCheckIPaddressIsInAllowedHosts.GetElementById(const Doc: IDispatch;  const Id: string): IDispatch;
 var
  Document: IHTMLDocument2;     // IHTMLDocument2 interface of Doc
  Body: IHTMLElement2;          // document body element
  Tags: IHTMLElementCollection; // all tags in document body
  Tag: IHTMLElement;            // a tag in document body
  I: Integer;                   // loops thru tags in document body
begin
  Result := nil;
  // Check for valid document: require IHTMLDocument2 interface to it
  if not Supports(Doc, IHTMLDocument2, Document) then
    raise Exception.Create('Invalid HTML document');
  // Check for valid body element: require IHTMLElement2 interface to it
  if not Supports(Document.body, IHTMLElement2, Body) then
    raise Exception.Create('Can''t find <body> element');
  // Get all tags in body element ('*' => any tag name)
  Tags := Body.getElementsByTagName('*');
  // Scan through all tags in body
  for I := 0 to Pred(Tags.length) do
      begin
        // Get reference to a tag
        Tag := Tags.item(I, EmptyParam) as IHTMLElement;
        // Check tag's id and return it if id matches
        if AnsiSameText(Tag.id, Id) then
        begin
          Result := Tag;
          Break;
        end;
      end;
end;

function TFrmCheckIPaddressIsInAllowedHosts.GetExternalIPAddress: string;
 //this is a copy of the function that is already in U_GeneralRoutines in mambase
var
i: integer;
PageText : string;
MStream : TMemoryStream;
HttpClient: TIdHTTP;  //need 'uses IdHTTP '

begin
//use http://checkip.dyndns.org to return ip address in a page containing the single line below
// <html><head><title>Current IP Check</title></head><body>Current IP Address: 82.71.38.7</body></html>
 Result := '';
 MStream := TMemoryStream.Create;
 HttpClient := TIdHTTP.Create;
 try
    try
    HttpClient.Get( 'http://checkip.dyndns.org/', MStream );  //download web page to a memory stream (instead of a file)
    HttpClient.Disconnect;  //not strickly necessary but prevents error 10054 Connection reset by peer
    SetString(PageText, PAnsiChar(MStream.Memory), MStream.Size) ; //assign stream contents to a string called PageText
    for i := 1 to Length(PageText) do      //extract just the numeric ip address from the line returned from the web page
        if (PageText[i] in ['0'..'9','.']) then
           Result := Result + PageText[i]  ;
    except
    on E : Exception do
      begin
      showmessage ('Could not download from checkip'  +slinebreak
                  +'Exception class name = '+E.ClassName+ slinebreak
                  +'Exception message = '+E.Message);
      end  //on E
    end;//try except

 finally
    MStream.Free;
    FreeAndNil(HttpClient);   //freenamdnil needs sysutils
 end;
end;


function TFrmCheckIPaddressIsInAllowedHosts.GetTextOfPage(WB: twebbrowser): string;
var
  Document: IHtmlDocument2;
begin
  document := WB.document as IHtmlDocument2;
  result := trim(document.body.innertext);  // to get text
 end;

function TFrmCheckIPaddressIsInAllowedHosts.IPaddressAlreadyPresent(TheIPAddress: string;
  HostList2: TstringList): boolean;
const
      digits = ['0'..'9'];
  var
    i,j,k : integer;
    line : string;
    match : boolean;
begin
result := false;  //assume the IP address is not there

////////////////////////
 for i := 0 to HostList2.Count - 1 do
     begin
     Line := HostList2[i];  // or Memo1.Lines.Strings[i]; //  get one line

     if (line <> '') and (line[1] in digits) then  //first character is a digit so we are on an IP address row  - note if line = '' then line[i] is not (and cannot be), evaluated

   //  if length(line) >= length(TheIPAddress) then  //could possibly match
        begin
        match := true;    //assume they match
        for j := 1 to length(TheIPAddress) do
          begin
          if not ((TheIPAddress[j] = line[j]) or (line[j] = '%')) then   //they don't match
              match := false;
          end;
         //set flag for result of this comparison
        if match then  //every position must have matched
          begin
          result := match;
          Exit;   //quit looping through lin4es as we have found it
          end;
        end; // if length(line) >= length(TheIPAddress)
     end;// for i := 0 to HostList.Lines.Count - 1
end;

procedure TFrmCheckIPaddressIsInAllowedHosts.LogIntoCpanelAndCheckIPaddress;
var
  Elem: IHTMLElement;
  Document: IHtmlDocument2;
 // d: OleVariant;
begin

//set teh global variable to say whether we check the text of the page or not
CheckForIPaddress := True; //as we haven't checked yet. this gets set to false after the first check
CanExit := False;  //don't exit this section until we have checked the address

//navigate to the cpanel IP hosts page - as part of this process we wil have to log on

  WebBrowser1.Navigate('https://thewebsite address.html');  //this goes through the login page
   repeat
     Application.ProcessMessages
   until FDocLoaded;

//while the page is loading, every time WebBrowser1DocumentComplete fires
//we check to see if we are on the hosts page and if so process the ip address

//now the log on page will be showing as part of navigating to the hosts page so
//fill in the user name and passwrord
   Elem := GetElementById(WebBrowser1.Document, 'user') as IHTMLElement;
  if Assigned(Elem) then
   if Elem.tagName = 'INPUT' then (Elem as IHTMLInputElement).value := 'the user';

//now the password
  Elem := GetElementById(WebBrowser1.Document, 'pass') as IHTMLElement;
  if Assigned(Elem) then
   if Elem.tagName = 'INPUT' then (Elem as IHTMLInputElement).value := 'thepassword';

   // now click the logon button
 Elem := GetElementById(WebBrowser1.Document, 'login_submit') as IHTMLElement;
  if Assigned(Elem) then
    Elem.click;

   repeat
     Application.ProcessMessages
   until FDocLoaded;

    //now we are logged on so see what the url is so we know the security token
   //    memo1.Lines.Add(WebBrowser1.LocationURL); //debug, show the url so we can get the security code

   //now wait until we have finished any residual processing of the IP address and then exit
   repeat
     Application.ProcessMessages
   until CanExit;
   Logout;
 end;

procedure TFrmCheckIPaddressIsInAllowedHosts.Logout;
begin
WebBrowser1.Navigate( 'https://thelogouturl' );
   repeat
     Application.ProcessMessages
   until FDocLoaded;
   showmessage('logged out');
end;


procedure TFrmCheckIPaddressIsInAllowedHosts.WebBrowser1BeforeNavigate2(ASender: TObject;
  const pDisp: IDispatch; var URL, Flags, TargetFrameName, PostData,
  Headers: OleVariant; var Cancel: WordBool);
begin
   CurDispatch := nil;
      FDocLoaded := False;
end;

procedure TFrmCheckIPaddressIsInAllowedHosts.WebBrowser1DocumentComplete(ASender: TObject;
  const pDisp: IDispatch; var URL: OleVariant);
  var s : string;
begin
     if (pDisp = CurDispatch) then
      begin
        FDocLoaded := True;
        CurDispatch := nil;
      end;

    //WebBrowser1DocumentComplete is called many times and so FDocLoaded could be true many times
    //to avoid checking the ip address multiple times we use a global variable CheckForIPaddress as a flag
    //to ensure we only check once

    if CheckForIPaddress and FDocLoaded then     //if CheckForIPaddress is false then we have already checked so don't do it again
        begin
        //now check which page we are on. if its the hosts page then we have the text we need
         s := GetTextOfPage(Webbrowser1);
         if ContainsText(s,'Remote Database Access Hosts') then //we are on the hosts page
          begin     //process the ip address with respect to those already recorded
          CheckForIPaddress := false; //reset the flag so that we don't bother checking each time FDocLoaded is true
          addresses.text :=s;       //put the addresses into a list so we can check them
          if IPaddressAlreadyPresent(TheIPAddress, addresses) then
              begin
              AddressAdded := false;
             // showmessage('already there');
             // Logout;
              end
          else
             begin
            // showmessage('not there');
             AddNewIPaddress(TheIPAddress);
             AddressAdded := True;
            // Logout;
             end;
          //either way we can now exit
          CanExit := True; //the procedure  LogIntoCpanelAndGotToHostsPage can exit back to the main program when it finishes
          end;
        end; //if FDocLoaded



end;

procedure TFrmCheckIPaddressIsInAllowedHosts.WebBrowser1NavigateComplete2(ASender: TObject;
  const pDisp: IDispatch; var URL: OleVariant);
begin
       if CurDispatch = nil then
        CurDispatch := pDisp;
end;

end.
单元U_frmcheckipddresssinallowedhosts;
接口
使用
窗口、消息、系统工具、变体、类、图形、控件、窗体、,
对话,鹰嘴,
MSHTML,//访问浏览器的ole位
StrUtils,//用于“containstext”函数
IdHTTP,//用于GetExtenalIPAddress函数
SHDocVw,//进入Twebbroswer类,以便扩展它
ActiveX//用于向Twebbrowser添加扩展时的IOleCommandTarget
;
类型
//重写Twebbrowser以添加功能以支持js错误,同时保持代码运行
//从https://stackoverflow.com/questions/8566659/how-do-i-make-twebbrowser-keep-running-javascript-after-an-error
TWebBrowser=class(SHDocVw.TWebBrowser,IOleCommandTarget)
私有的
函数QueryStatus(CmdGroup:PGUID;cCmds:Cardinal;prgCmds:POleCmd;
CmdText:POleCmdText):HRESULT;stdcall;
函数Exec(CmdGroup:PGUID;nCmdID,nCmdexecopt:DWORD;
常量vaIn:OleVariant;变量vaOut:OleVariant):HRESULT;stdcall;
结束;
////////////////////////////////////////////////////
TFRMCheckIPAddressSinAllowedHosts=类(TForm)
WebBrowser1:TwebbBrowser;
重新授权前的程序WebBrowser1(申请人:TObject;
常量pDisp:IDispatch;变量URL、标志、TargetFrameName、PostData、,
标题:OleVariant;变量取消:WordBool);
程序WebBrowser1文档完成(申请人:ToObject;
常量pDisp:IDispatch;变量URL:OleVariant);
程序WebBrowser1导航完成2(申请人:ToObject;
常量pDisp:IDispatch;变量URL:OleVariant);
过程表单创建(发送方:ToObject);
销毁程序表(发送方:TObject);
私有{私有声明}
CurDispatch:IDispatch//用于等待文档加载
FDocLoaded:布尔值//指示何时加载文档的标志
地址:TStringList//保存主机列表中已存在的IP地址列表
女:弦;
地址:布尔//如果添加,则设置为True
程序LogIntoCpanelAndCheckIPaddress;
函数GetElementById(常量文档:IDispatch;常量Id:string):IDispatch;
函数GetTextOfPage(WB:twebbrowser):字符串;
函数IPaddressAlreadyPresent(TheIPAddress:string;HostList2:TstringList):布尔值;
程序注销;
过程AddNewIPaddress(IPAddress:string);
函数getExternalPaddress:string//无需创建文件即可工作
公众的
{公开声明}
函数CheckIPAddressAndAddIfNecessary(IPaddress:string):布尔值//如果添加了地址,则返回true,否则返回false
结束;
变量
frmcheckipaddresssinallowedhosts:tfrmcheckipaddresssinallowedhosts;
CheckForIPaddress:布尔值;
CanExit:布尔型//标志表示我们已经检查了地址,可能已经添加了地址
实施
{$R*.dfm}
{TForm5}
{TWebBrowser扩展名}
函数TWebBrowser.Exec(CmdGroup:PGUID;nCmdID,nCmdexecopt:DWORD;
常量vaIn:OleVariant;变量vaOut:OleVariant):HRESULT;stdcall;
开始
//假设所有命令都可以执行;有关可用命令的列表,请参见
//请参阅SHDocVw.pas单元,使用此事件可以抑制或创建自定义
//事件不仅仅是脚本错误对话框,还有如下命令
//撤消、重做、刷新、打开、保存、打印等。
//请小心,因为并非所有命令结果都像
//对于脚本错误消息框,我希望如果您返回S_OK,
//将显示错误对话框,但反之亦然
结果:=S_正常;
//当前执行的脚本中存在脚本错误,因此
如果nCmdID=olcemdid\u SHOWSCRIPTERROR,则
开始
//如果返回S_FALSE,将显示脚本错误对话框
结果:=S_假;
//如果返回S_OK,则脚本错误对话框将被抑制
结果:=S_正常;
结束;
结束;{TWebBrowser扩展的末尾}
函数TWebBrowser.QueryStatus(CmdGroup:PGUID;cCmds:Cardinal;
prgCmds:POleCmd;CmdText:POleCmdText):HRESULT;stdcall;
开始
结果:=S_正常;
结束;
过程TfrmCheckIPAddresssAllowedHosts.AddNewIPaddress(IPAddress:string);
变量
要素:IHTMlement;
开始
//抓住“新主机”框并输入新的IP地址
Elem:=GetElementById(WebBrowser1.Document,'host')作为IHTMlement;
如果分配(元素),则
如果Elem.tagName='INPUT',则(Elem作为IHTMLInputElement)。值:=TheIPaddress;
//现在单击添加主机按钮
元素:=GetElementById(WebBrowser1.Doc