tablacus / TablacusScriptControl

Script Control for 64-bit platforms
https://tablacus.github.io/scriptcontrol_en.html
MIT License
62 stars 17 forks source link

Access Violations after Acccessing ScriptControl.Error object #27

Open CharlieHeaps opened 2 years ago

CharlieHeaps commented 2 years ago

Calling TSC64.dll (v1.2.4) from Delphi. with a TScriptControl object. I'm having problems with handling errors in scripts. When a script has an error in it ScriptControl.ExecuteStatement does not generate an Exception (MSScript control does). TSC64.dll does report back the errors in the form of ScriptControl.Error.Line, ScriptControl.Error.Column, ScriptControl.Error.Description etc. but then generates access violations in Delphi in the _IntfClear routine. I understand that these errors are related to using an interface on a class which does not implement reference counting (I'm not expert enough to dig deeper on that).

Note: If I comment out code referring to the ScriptControl.Error object my code runs fine (although I cannot report errors), but with those references included I get access violations even if the script itself has no errors.

Can you help? Thanks in advance!

tablacus commented 2 years ago

Let me know the issue script occurring access violations.

Regards,

tablacus commented 2 years ago

Please give it try 1.2.4.1. https://github.com/tablacus/TablacusScriptControl/releases/tag/1.2.4.1

Regards,

CharlieHeaps commented 2 years ago

Hi again Gaku.

First of all - huge thanks for looking into this for me. You are a hero! :)

I'm afraid I get the same access violation (AV) with 1.2.4.1. However, I've made a really simple example in which I was able to temporarily avoid the Av by adding in an extra reference to an IScriptError interface. Here is my code, where I have noted my hack. I'm afraid it's written in Delphi/Pascal but hopefully that is OK for you to get a good sense of this.

`
unit MainUnit;

interface

uses Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs, MSScriptControl_TLB, Vcl.StdCtrls, Vcl.Buttons;

type TMainForm = class(TForm) GoBtn: TBitBtn; procedure GoBtnClick(Sender: TObject); private { Private declarations } public { Public declarations } end;

var MainForm: TMainForm;

implementation

{$R *.dfm}

procedure TMainForm.GoBtnClick(Sender: TObject);

var TestScript: string; ScriptControl: TScriptControl; E: IScriptError;

begin ScriptControl := TScriptControl.Create(nil); E := ScriptControl.Error; E._AddRef; //My hack to stop the access violation - take this out and you get an access violation - I'm guessing TSC64 frees up a ref to Script Error but Delphi is unaware try try ScriptControl.Language := 'VBScript'; TestScript := 'MsgBox("Hello World)'; ScriptControl.ExecuteStatement(TestScript); // The original msscript (32-bit) would throw an exception here if the script had an error - TSC64.dll does not if E.Line <> 0 then showmessage(Format('Error at line %d/column %d, Error: "%s", Error Line: "%s", Error Type: "%s"', [E.Line, E.Column, E.Description, E.Text, E.Source])); Except On E: Exception do showmessage(E.Message); end; finally ScriptControl.free; end; end;

end.

`

tablacus commented 2 years ago

Thank you for your help. I have adjusted it in 1.2.5.0 https://github.com/tablacus/TablacusScriptControl/releases/tag/1.2.5.0 Please give it try. Best regards,

CharlieHeaps commented 2 years ago

Hi again Gaku.

12.5.0 definitely seems to have fixed the problem with ScriptControl.ScriptError. My simple test application now works fine. Many thanks!

Unfortunately, I am still noticing a similar access violation in my larger application. In that application, I'm adding an object to the script corresponding to the application object of my program's API (my application is named "LEAP"). The line that triggers the problem is this:

ScriptControl.AddObject('LEAP', Mainfrm.LEAPAuto, true); //Mainfrm.LEAPAuto is my program's main automation object.

This make me think that this is a similar problem about reference counting of the API object. My code runs fine in 32-bit (MSScript) and If I remove the above line the routine also runs OK in 64-bit model (but then, obviously, I cannot make references to the API objects in scripts).

Could you help? Very sorry to bother you with this again!

P.S. I would love to make a financial contribution to your valuable work. Could you email me at charlie.heaps@sei.org if that would be OK?

CharlieHeaps commented 2 years ago

I'm sorry - I think I wrote too soon just now. There also still seems to be an issue with the ScriptControl.ScriptError. It seems the control sometimes now reports the wrong error (usually the one before last). So I think it may be keeping around a stack of script errors. I did not notice this at first because I was freeing up the whole ScriptControl each time I used it. But if I call the same ScriptControl instance each time (e.g. if I create the control once when a form is created, then I run into the above problem).

Two other observations:

  1. ScriptControl.Reset, does not seem to reset the ScriptControl.ScriptError values. The version in msscript (32-bit) does do that.
  2. An error during ScriptControl.ExecuteStatement still does not generate an Exception (unlike 32-bit).

Thanks again and sorry for so many messages!

tablacus commented 2 years ago

I couldn't have fixed it without you. https://github.com/tablacus/TablacusScriptControl/releases/tag/1.2.5.1 Please give it try. Best regards,

CharlieHeaps commented 2 years ago

Notes on testing with 1.2.5.1... The ScriptControl.ScriptError and the ScriptControl.reset now seem to be working perfectly. Thanks! :)

The only remaining issue I can see is the reference counting on objects added with the ScriptControl.AddObject command. I'm still getting access violations when adding a COM object (see earlier post). For now, I managed to stop those errors by using Script.Reset every time I run the script and then use Object._AddRef after adding the object. Here is my code..

If you have time to fix that it would be great, but if not I can certainly work around it.

Thanks again for your great work!

Charlie

` procedure TMainForm.GoBtnClick(Sender: TObject);

//ScriptControl: TScriptControl; set globally //LEAPObj: ILEAPApplication;

begin ScriptControl.Reset; //resets ScriptError line/col etc. and (I think) removes any added objects if LEAPObj <> nil then ScriptControl.AddObject('LEAP', LEAPObj, true); LEAPObj._AddRef; //HACK to avoid a reference count Access Violation try ScriptControl.ExecuteStatement(Memo.Lines.Text); // Msscript and TSC64 both throw exceptions here if script has error. :) Except On E: Exception do if (ScriptControl.Error <> nil) and (ScriptControl.Error.Line <> 0) then showmessage( Format('Error at line %d/column %d, Error: "%s"', [ScriptControl.Error.Line, ScriptControl.Error.Column, ScriptControl.Error.Description]) +crlf+Format('Error type: "%s"',[ScriptControl.Error.Source])) else showmessage(E.Message); end; end;

`

tablacus commented 2 years ago

Hi! https://github.com/tablacus/TablacusScriptControl/releases/tag/1.2.5.2 Please give it try. Regards,

CharlieHeaps commented 2 years ago

1.2.5.2 now fully working! Thank you so much!

JvanderStad commented 2 years ago

v1.2.5.2

I'm also having issues with exceptions. There is a distinction between x86 and x64.

I have included a C# project that is causing issues for me when compiling against x64.

image vs image

ConsoleApp1.csproj

<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
  <PropertyGroup>
    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
    <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
    <ProjectGuid>{AC55B5B0-60D4-4538-B94A-0413AD7898E4}</ProjectGuid>
    <OutputType>Exe</OutputType>
    <RootNamespace>ConsoleApp1</RootNamespace>
    <AssemblyName>ConsoleApp1</AssemblyName>
    <TargetFrameworkVersion>v4.7.2</TargetFrameworkVersion>
    <FileAlignment>512</FileAlignment>
    <AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
    <Deterministic>true</Deterministic>
  </PropertyGroup>
  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
    <PlatformTarget>x64</PlatformTarget>
    <DebugSymbols>true</DebugSymbols>
    <DebugType>full</DebugType>
    <Optimize>false</Optimize>
    <OutputPath>bin\Debug\</OutputPath>
    <DefineConstants>DEBUG;TRACE</DefineConstants>
    <ErrorReport>prompt</ErrorReport>
    <WarningLevel>4</WarningLevel>
    <Prefer32Bit>false</Prefer32Bit>
  </PropertyGroup>
  <ItemGroup>
    <Compile Include="Program.cs" />
  </ItemGroup>
  <ItemGroup>
    <COMReference Include="MSScriptControl">
      <Guid>{0E59F1D2-1FBE-11D0-8FF2-00A0D10038BC}</Guid>
      <VersionMajor>1</VersionMajor>
      <VersionMinor>0</VersionMinor>
      <Lcid>0</Lcid>
      <WrapperTool>tlbimp</WrapperTool>
      <Isolated>False</Isolated>
      <EmbedInteropTypes>True</EmbedInteropTypes>
    </COMReference>
  </ItemGroup>
  <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project>

Program.cs

using MSScriptControl;

namespace ConsoleApp1
{
    internal class Program
    {
        private static void Main(string[] args)
        {
            var scriptControl = new ScriptControl
            {
                AllowUI = false,
                Language = "jscript",
            };

            try
            {
                scriptControl.Eval("thisisverywrong");
            }
            catch (System.Exception ex)
            {
                System.Console.WriteLine(ex.Message);
            }

            System.Console.ReadKey();
        }
    }
}

ConsoleApp1.zip

In some cases the exception is not even raised in x64. Hope this helps to find the problem.

jeholsa commented 2 years ago

I have got similar challenges with 1.2.5.2 version as @JvanderStad on our C++ MFC 64-bit application. I found one easy and another more complex issue: 1) When an error occurs in a script, I am calling IScriptError::get_Description() and I get always the same exception “Member not found”. This happens so in 1.2.3.0 version too. I think this happens because MSScript control has get_Description() as DispID=0xcb where as CTScriptError::Invoke() binds it to 0xcc. When changed this to 0xcb, my get_Description() calls start working. 2) When an error occurs in a script, 1.2.3.0 version did throw an exception, but 1.2.5.2 doesn't. There was the line pass->m_pSC = NULL; added between these two versions in CTScriptControl::ParseScript(), when I just simply comment out it //pass->m_pSC = NULL; I get exceptions thrown again as they should - on my case. Just wondering what will this break.

image

image

Sample app to repeat the challenge: SampleApp.zip

image

tablacus commented 2 years ago

Thank you for your report.

https://github.com/tablacus/TablacusScriptControl/releases/tag/1.2.5.3

Please give it try. Regards,

jeholsa commented 2 years ago

This v1.2.5.3 works fine on my application, I get error messages coming through. Thank you for the fast fix!!