WebBrowser (MSIE) - Capture JS Errors using Exec o

2020-02-29 04:26发布

问题:

I need to capture script errors in MS IE activex control (dubbed as WebBrowser).

As I see it, this is done by implementing the IOleCommandTarget interface in my application and listening for OLECMDID_SHOWSCRIPTERROR.

I did the above and I know it works since my Exec method is called, but here's the problem; it is only called once with nCmdID being set to $00000037 (OLECMDID_PAGEACTIONBLOCKED), but never $00000028 (OLECMDID_SHOWSCRIPTERROR).

In order to trigger that error, I've been using this code:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
        <title>TEST SCRIPT</title>
    </head><body>
        <script type="text/javascript">
            document.body.style.background='yellow';
            setTimeout(function(){
                document.body.style.background='red';
                causeERROR(); // purposefully undefined function
                document.body.style.background='green';
            },500);
        </script>
    </body>
</html>

The code above shows a yellow page, then after a few milliseconds, a red one and if script execution continues (after the error occurs) it should be green.

Right after turning red, I get MSIE's script error dialog, which is all fine. But my Exec method is not triggered.

Note: The delayed error above is to ensure a runtime error as opposed to a page load error, just in case it does a difference.

My IOleCommandTarget implementation is as follows (Delphi):

type
  TNulWBContainer = class(TWebBrowser, IUnknown, IOleClientSite,
                          IDocHostUIHandler, IDispatch, IOleCommandTarget)
  protected
    { OTHER STUFF }
    {IOleCommandTarget Interface}
    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;

implementation

{ OTHER STUFF }

function TNulWBContainer.QueryStatus(CmdGroup: PGUID; cCmds: Cardinal;
  prgCmds: POleCmd; CmdText: POleCmdText): HResult; stdcall;
begin
  prgCmds.cmdf := OLECMDF_ENABLED;
  Result := S_OK; //inherited QueryStatus(CmdGroup,cCmds,prgCmds,CmdText);
end;

function TNulWBContainer.Exec(CmdGroup: PGUID; nCmdID, nCmdexecopt: DWORD;
  const vaIn: OleVariant; var vaOut: OleVariant): HResult; stdcall;
begin
  ShowMessage('nCmdID=$'+IntToHex(nCmdID,8));
  Result:=OLECMDERR_E_UNKNOWNGROUP;
end;

end.

I'm running this stuff on Windows 7 Ult 64bit. I have MS Script Debugger installed and MSIE's "Disable script debugging for nnn" turned off for both options. MSIE is v9.0.8112.16421.

Note: I didn't tag this under Delphi since all native languages out there can be easily translated to Delphi, even some managed languages such as VB or C#.


Related Links:

  • http://support.microsoft.com/kb/261003
  • http://www.codeproject.com/KB/shell/popupblocker.aspx
  • http://www.tech-archive.net/Archive/VC/microsoft.public.vc.atl/2007-10/msg00158.html - This is kind of cryptic, I didn't understand (2), (4) and (5).

回答1:

This is the silliest bug in ages! No seriously!

MS, you really SUCK on this one ;)

I got it working, my code was actually correct from the beginning (ie, two days ago ;))

As the article said, you need Disable Script Debugging (Internet Explorer) unchecked.

BUT Disable Script Debugging (Other) must be checked (in Internet Options > Advanced).

Weird huh? I got the tip from here: http://www.delphigroups.info/2/9/938468.html (2005-04-29 09:42:48 PM).

Yes, that's 6 years ago. I'm surprised, no, amazed, that such a thing could be possible and not documented anywhere.

Edit: Programmatic fix:

uses Registry;

// ...

  with TRegistry.Create do
    try
      RootKey:=HKEY_CURRENT_USER;
      if OpenKey('\Software\Microsoft\Internet Explorer\Main',False) then begin
        WriteString('DisableScriptDebuggerIE','no');
        WriteString('Disable Script Debugger','yes');
        CloseKey;
      end;
    finally
      Free;
    end;

// ...