?
Solved

ActiveX: Dropped on Form/Create via constructor

Posted on 2005-03-30
9
Medium Priority
?
737 Views
Last Modified: 2010-04-16
This is a really strange problem and is possibly worth at least a thousand points. It is about the differences between controls dropped on forms and created dynamically. At least so I think. Perhaps I'm doing something wrong?

Basically I can't get the MS Script Control to work - unless it's dropped on a form.

To test this you'll need to import the MS Script control to get the unit MSScriptControl_TLB.

The form contains, as ScriptControl1, a dropped MS Script control, a memo for holding the code (Javascript text), and several buttons.

If you start the program and press About you'll see which version of the control you have. I have version 1.0 from Win2K pro. Press the load button to load the Javascript from c:\temp\test.js. If you press Compile you'll get the correct error - namely a missing bracket. If you press Create you'll get the same error. But if you do this the other way round the Create button gives the no error message nor position. It seems that pressing Run causes something to get set or unset so that the behavior changes.

What I need is the ability to create the control in a thread and set the OnError call back.

unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  OleCtrls, MSScriptControl_TLB, StdCtrls, OleServer, ActiveX;

type
  TForm1 = class(TForm)
    Button1: TButton;
    ScriptControl1: TScriptControl;
    Button2: TButton;
    Button3: TButton;
    Button4: TButton;
    Button5: TButton;
    Memo1: TMemo;
    edExcept: TEdit;
    Label1: TLabel;
    procedure ScriptControl1Error(Sender: TObject);
    procedure Button1Click(Sender: TObject);
    procedure Button2Click(Sender: TObject);
    procedure Button3Click(Sender: TObject);
    procedure Button4Click(Sender: TObject);
    procedure Button5Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.DFM}

procedure TForm1.ScriptControl1Error(Sender: TObject);
var
   iError : IScriptError;
begin
   iError:=ScriptControl1.Error;
   ShowMessage(IntToStr(iError.Get_Line)+','+IntToStr(iError.Get_Column)+
               ':' + iError.Get_Description);
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
  ScriptControl1._AboutBox;
end;

procedure TForm1.Button2Click(Sender: TObject);
const
   CRLF:WideString = #$0D#$0A;
begin
   Memo1.Lines.LoadFromFile('c:\temp\test.js');
end;

procedure TForm1.Button3Click(Sender: TObject);
begin
   try
      ScriptControl1.AddCode(Memo1.Text);
   except
      edExcept.Text:='exception';
   end;
end;

procedure TForm1.Button4Click(Sender: TObject);
var
  Bounds : array [0..0] of TSafeArrayBound;
  Params : PSafeArray;
begin
  Bounds[0].cElements:=0;
  Bounds[0].lLbound:=0;
  Params:=SafeArrayCreate(varVariant, 1, Bounds);
  ScriptControl1.Run('test',Params);
end;

procedure TForm1.Button5Click(Sender: TObject);
var
   ScriptControl2 : TScriptControl;
begin
  ScriptControl2:=TScriptControl.Create(nil);
//  ScriptControl.ControlData:=ScriptControl1.ControlData;
  try
     ScriptControl2.OnError:=ScriptControl1Error;
     ScriptControl2.Language:='JScript';
     ScriptControl2.AddCode(Memo1.Text);
  except
     edExcept.Text:='exception occured';
  end;
  ScriptControl2.Free;
end;

end.

(* dfm *)
object Form1: TForm1
  Left = 285
  Top = 116
  Width = 696
  Height = 480
  Caption = 'Form1'
  Color = clBtnFace
  Font.Charset = DEFAULT_CHARSET
  Font.Color = clWindowText
  Font.Height = -13
  Font.Name = 'MS Sans Serif'
  Font.Style = []
  OldCreateOrder = False
  PixelsPerInch = 120
  TextHeight = 16
  object Label1: TLabel
    Left = 56
    Top = 336
    Width = 59
    Height = 16
    Caption = 'Exception'
  end
  object Button1: TButton
    Left = 64
    Top = 384
    Width = 75
    Height = 25
    Caption = 'About'
    TabOrder = 0
    OnClick = Button1Click
  end
  object ScriptControl1: TScriptControl
    Left = 16
    Top = 8
    Width = 32
    Height = 32
    OnError = ScriptControl1Error
    ControlData = {
      21433412080000002403000024030000D2F1594E010000002200000010270000
      000007004A00530063007200690070007400}
  end
  object Button2: TButton
    Left = 152
    Top = 384
    Width = 75
    Height = 25
    Caption = 'Load'
    TabOrder = 2
    OnClick = Button2Click
  end
  object Button3: TButton
    Left = 248
    Top = 384
    Width = 75
    Height = 25
    Caption = 'Compile'
    TabOrder = 3
    OnClick = Button3Click
  end
  object Button4: TButton
    Left = 344
    Top = 384
    Width = 75
    Height = 25
    Caption = 'Run'
    TabOrder = 4
    OnClick = Button4Click
  end
  object Button5: TButton
    Left = 448
    Top = 384
    Width = 75
    Height = 25
    Caption = 'Create'
    TabOrder = 5
    OnClick = Button5Click
  end
  object Memo1: TMemo
    Left = 56
    Top = 8
    Width = 577
    Height = 305
    Lines.Strings = (
      'Memo1')
    TabOrder = 6
  end
  object edExcept: TEdit
    Left = 120
    Top = 336
    Width = 497
    Height = 24
    TabOrder = 7
  end
end

(* c:\temp\test.js *)
// test piece of Javascript

function test(para)

var x = para;
   if (Para==0) alert('Parameter is zero!');
}
0
Comment
Question by:BigRat
  • 6
  • 3
9 Comments
 
LVL 13

Accepted Solution

by:
BlackTigerX earned 2000 total points
ID: 13663635
a few problems

the test function has two errors

the missing braket and the "P"ara, instead of "p"ara

then, in your error handler, you have hardcoded the one in your form, not the one created dinamically:

var
   iError : IScriptError;
begin
   iError:=ScriptControl1.Error; //always calls the one on the form

I'll show you the code I have... is working as expected so far...
0
 
LVL 13

Expert Comment

by:BlackTigerX
ID: 13664366
ah... found something else...

you can't call "alert", that's not part of JScript, that's why I was getting a "object expected" error in that specific line
0
 
LVL 13

Expert Comment

by:BlackTigerX
ID: 13664504
here are some comments I gathered from some people that know more about JScript:

"JScript doesn't have anything native (like VBScript's MsgBox).  When WSH is the host, the WScript
object has the echo method.  But the Script Control as a host for the script engines doesn't provide
any built-in objects with properties/methods.

It up to your application (as the host of the script control) to create/add it's own object to the
script control to expose a comparable alert method.

Or you can just use the WshShell object's PopUp method from JScript. "
----------------------
"You *could* use WshShell.Popup from JScript...  Otherwise, you application as the ultimate host
needs to create an object with an "alert-like" method (a simple wrapper around MsgBox) that you then
add with AddObject...  (see the AddObject documentation for optional arguments) "
----------
and this from a Microsoft guy:
"Sure, the "object expected" is coming from the JScript engine when it
attempts to execute the line of code

alert("Hi there");

"alert" is a method of the Internet Explorer document object model.  If
you're not running your script under IE DOM, the script engine has no idea
what "alert" means -- it is expecting that alert will resolve to a function
object, hence "object expected".

FYI, JScript has no built-in message box routine like VBScript's "MsgBox". "
-------------

so basically your code is good, let me put what I have, is almost the same as yours
0
Technology Partners: We Want Your Opinion!

We value your feedback.

Take our survey and automatically be enter to win anyone of the following:
Yeti Cooler, Amazon eGift Card, and Movie eGift Card!

 
LVL 13

Expert Comment

by:BlackTigerX
ID: 13664552
//code:
unit Unit1;

interface

uses
  Variants, Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  OleCtrls, MSScriptControl_TLB, StdCtrls, OleServer, ActiveX;

type
  TForm1 = class(TForm)
    Button1: TButton;
    Button2: TButton;
    Button3: TButton;
    Button4: TButton;
    Button5: TButton;
    Memo1: TMemo;
    edExcept: TEdit;
    Label1: TLabel;
    procedure ScriptControl1Error(Sender: TObject);
    procedure Button1Click(Sender: TObject);
    procedure Button2Click(Sender: TObject);
    procedure Button3Click(Sender: TObject);
    procedure Button4Click(Sender: TObject);
    procedure Button5Click(Sender: TObject);
    procedure FormCreate(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
  private
    { Private declarations }
    FScriptControl:TScriptControl;
  public
    { Public declarations }
    aScriptControl:TScriptControl;
  end;

var
  Form1: TForm1;

implementation

{$R *.DFM}

procedure TForm1.ScriptControl1Error(Sender: TObject);
var
   iError : IScriptError;
begin
   iError:=FScriptControl.Error;
   ShowMessage(Format('Control Error: %d,%d:%s', [iError.Get_Line, iError.Get_Column, iError.Get_Description]));
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
  aScriptControl._AboutBox;
end;

procedure TForm1.Button2Click(Sender: TObject);
const
   CRLF:WideString = #$0D#$0A;
begin
   Memo1.Lines.LoadFromFile('c:\temp\test.js');
end;

procedure TForm1.Button3Click(Sender: TObject);
begin
   try
     aScriptControl.AddCode(Memo1.Text);
   except
     on E:Exception do
       edExcept.Text:='exception:'+E.Message;
   end;
end;

procedure TForm1.Button4Click(Sender: TObject);
var
  V: Variant;
begin
  V:=VarArrayOf([1]); //if you pass a zero, it will fail at run time, because "alert" doesn't exist
  aScriptControl.Run('test', PSafeArray(TVarData(V).VArray));
end;

procedure TForm1.Button5Click(Sender: TObject);
var
   ScriptControl2 : TScriptControl;

  Bounds : array [0..0] of TSafeArrayBound;
  Params : PSafeArray;
begin
  ScriptControl2:=TScriptControl.Create(nil);
//  ScriptControl.ControlData:=ScriptControl1.ControlData;
  try
    FScriptControl:=ScriptControl2;
    ScriptControl2.OnError:=ScriptControl1Error;
    ScriptControl2.Language:='JScript';
    ScriptControl2.AllowUI:=False;
    ScriptControl2.AddCode(Memo1.Text);

    Bounds[0].cElements:=0;
    Bounds[0].lLbound:=0;
    Params:=SafeArrayCreate(varVariant, 1, Bounds);

    ScriptControl2.Run('test', Params)
  except
    on E:Exception do
      edExcept.Text:='exception occured:'+E.Message;
  end;
  ScriptControl2.Free;
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
  aScriptControl:=TScriptControl.Create(nil);
  aScriptControl.Language:='JScript';
  aScriptControl.AllowUI:=True;
  aScriptControl.OnError:=ScriptControl1Error;
  aScriptControl.State:=0;
  aScriptControl.SitehWnd:=Handle;
  FScriptControl:=aScriptControl
end;

procedure TForm1.FormDestroy(Sender: TObject);
begin
  aScriptControl.Free
end;

end.
//***************************
//form
object Form1: TForm1
  Left = 285
  Top = 116
  Width = 532
  Height = 367
  Caption = 'Form1'
  Color = clBtnFace
  Font.Charset = DEFAULT_CHARSET
  Font.Color = clWindowText
  Font.Height = -10
  Font.Name = 'MS Sans Serif'
  Font.Style = []
  OldCreateOrder = False
  OnCreate = FormCreate
  OnDestroy = FormDestroy
  PixelsPerInch = 96
  TextHeight = 13
  object Label1: TLabel
    Left = 46
    Top = 273
    Width = 47
    Height = 13
    Caption = 'Exception'
  end
  object Button1: TButton
    Left = 52
    Top = 312
    Width = 61
    Height = 20
    Caption = 'About'
    TabOrder = 0
    OnClick = Button1Click
  end
  object Button2: TButton
    Left = 124
    Top = 312
    Width = 60
    Height = 20
    Caption = 'Load'
    TabOrder = 1
    OnClick = Button2Click
  end
  object Button3: TButton
    Left = 202
    Top = 312
    Width = 60
    Height = 20
    Caption = 'Compile'
    TabOrder = 2
    OnClick = Button3Click
  end
  object Button4: TButton
    Left = 280
    Top = 312
    Width = 60
    Height = 20
    Caption = 'Run'
    TabOrder = 3
    OnClick = Button4Click
  end
  object Button5: TButton
    Left = 364
    Top = 312
    Width = 61
    Height = 20
    Caption = 'Create'
    TabOrder = 4
    OnClick = Button5Click
  end
  object Memo1: TMemo
    Left = 46
    Top = 7
    Width = 468
    Height = 247
    Lines.Strings = (
      'Memo1')
    TabOrder = 5
  end
  object edExcept: TEdit
    Left = 98
    Top = 273
    Width = 403
    Height = 24
    TabOrder = 6
  end
end
0
 
LVL 13

Expert Comment

by:BlackTigerX
ID: 13665036
also, this is relevant and good to know:

//***This calls a function or sub with parameters:

procedure TForm1.Button2Click(Sender: TObject);
var  V: Variant;
begin
  V := VarArrayOf(['test1', 'test2', 'test2']);
  ScriptControl1.Run('Test', PSafeArray(TVarData(V).VArray)­);
end;

//***This calls a function or sub without params:
procedure TForm1.Button2Click(Sender: TObject);
var
    psa: PSafeArray; (* Used to allocate a safe array *)
    sab: TSafeArrayBound; (* Array bound descriptor *)
begin
    sab.lLbound := 0;
    sab.cElements := 0;
    psa := SafeArrayCreate(VT_VARIANT, 1, sab);
    scriptControl1.run ('Test', psa);
    SafeArrayDestroy (psa);
end;

//***One other way to archive the same if the name is known at design time is:
procedure TForm1.Button2Click(Sender: TObject);
var  V: Variant;
begin
  v := scriptControl1.CodeObject;
  v.Test ('test1, 'test2', 'test3');
end;
0
 
LVL 27

Author Comment

by:BigRat
ID: 13670037
>>the test function has two errors

It is irrelevant how many errors the test function has, the point is getting the control to report the error messages. The real code is part of an IDE, which handles RatScript, Javascript, HTML, CSS and XML/XSLT. The "Compile" function performs RatScript compile, Javascript syntax check, HTML/CSS check with w3c.org's Tidy lib, and well-formedness of XML and XSLT with Delphi components.
0
 
LVL 27

Author Comment

by:BigRat
ID: 13670068
>>then, in your error handler, you have hardcoded the one in your form, not the one created dinamically.

Ooops. That certainly is the error here. I have changed the code to read :-

procedure TForm1.ScriptControl1Error(Sender: TObject);
var
   iError : IScriptError;
begin
   iError:=(Sender as TScriptControl).Error;  //  <==== Changed HERE!!!
   ShowMessage(IntToStr(iError.Get_Line)+','+IntToStr(iError.Get_Column)+
               ':' + iError.Get_Description);
end;

and it works perfectly of course  :(. Now I'll just go back to the IDE and see what is different. Back in an hour or so.....
0
 
LVL 27

Author Comment

by:BigRat
ID: 13670785
Here is no the working JScript compiler code :-

procedure TJScriptCompiler.OnError(Sender : TObject);
var
   ErrorObj : IScriptError;
  begin (* gets called when an error occurs *)
   ErrorObj:=(Sender as TScriptControl).Error;
   if Assigned(OnCompMessage) then
      OnCompMessage(ErrorObj.Get_Description,ErrorObj.Get_Line);
  end;

function TJScriptCompiler.Compile : Boolean;
var
   Reply    : HResult;
   ErrorObj : IScriptError;
  begin
   Reply:=CoInitialize(nil);
   if Reply<>S_OK then
      begin
      if Assigned(OnCompMessage) then
         OnCompMessage('Cannot init COM '+ IntToStr(Reply),1);
      Result:=false;
      end
   else
      begin
      try
         ScriptControl:=TScriptControl.Create(nil);
         ScriptControl.AllowUI:=false;
         ScriptControl.OnError:=OnError;
         ScriptControl.Language:=WideString('JScript');
         ScriptControl.AddCode(Text);
         Result:=true;
      except
         begin
//         ErrorObj:=ScriptControl.Error;
//         if Assigned(OnCompMessage) and Assigned(ErrorObj) then
//            OnCompMessage(ErrorObj.Get_Description,ErrorObj.Get_Line);
//         ShowMessage(ErrorObj.Get_Text + ' ' + IntToStr(ErrorObj.Get_Line));
         Result:=false;
         end;
      end;
      ScriptControl.Free;
      CoUninitialize;
      end;
  end;

I had made the same mistake of using an uninitialized script control variable to get the error message. Furthermore I had turned off the CoInitialize() to test outside the thread and the result was an uninitialized IScriptError object (the methods seem to go via Fintf variable which was nil).
0
 
LVL 13

Expert Comment

by:BlackTigerX
ID: 13671344
you certainly know more about using the script control... looks good =o)
0

Featured Post

Concerto's Cloud Advisory Services

Want to avoid the missteps to gaining all the benefits of the cloud? Learn more about the different assessment options from our Cloud Advisory team.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

Hello everybody This Article will show you how to validate number with TEdit control, What's the TEdit control? TEdit is a standard Windows edit control on a form, it allows to user to write, read and copy/paste single line of text. Usua…
In my programming career I have only very rarely run into situations where operator overloading would be of any use in my work.  Normally those situations involved math with either overly large numbers (hundreds of thousands of digits or accuracy re…
Look below the covers at a subform control , and the form that is inside it. Explore properties and see how easy it is to aggregate, get statistics, and synchronize results for your data. A Microsoft Access subform is used to show relevant calcul…
When cloud platforms entered the scene, users and companies jumped on board to take advantage of the many benefits, like the ability to work and connect with company information from various locations. What many didn't foresee was the increased risk…
Suggested Courses
Course of the Month8 days, 7 hours left to enroll

616 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question