Wednesday, June 27, 2007

MS notices the roadmap...

http://rrelyea.spaces.live.com/blog/cns!167AD7A5AB58D5FE!2169.entry

Rob Relyea on the WPF/XAML team at MS just sent me this link to his blog. It's good to get some attention from those folks about our roadmap and strategy. While MS is certainly a competitor at one level, they are also huge partner at many other levels. Rob was just asking for some clarification on our roadmap, so I sent some of it along to him. Before you bother asking, there are still many aspects of our plans that must continue to remain undisclosed, so I won't go into the substance of my response. Rob is one of our many “friends on the inside” at MS :-).

Thursday, June 14, 2007

Connecting an event to a Active Script function

In this article I described how to connect Active Script to a VCL Win32 application. This post will describe how to extend that demo to show how to directly connect an event (like OnClick) directly up to an ActiveScript function. You can get the new version of the demo from CodeCentral right here. Let's get started.

First off we'll add a button to the form and change the caption to “Hook Event.” We'll use the TEdit that's already there to get the name of the Active Script function to hook up when the Hook Even button is pressed. Here'e what the form looks like now:



Double-click the Hook Event button and then add this code:
procedure TForm3.Button2Click(Sender: TObject);
var
Info: EXCEPINFO;
State: TOleEnum;
Code: WideString;
Disp: IDispatch;
Id: Cardinal;
Name: WideString;
PropInfo: PPropInfo;
begin
Code := Memo1.Text;
OleCheck(FScript.GetScriptState(State));
if State = SCRIPTSTATE_CONNECTED then
OleCheck(FScript.SetScriptState(SCRIPTSTATE_DISCONNECTED));
OleCheck(FParse.ParseScriptText(PWideChar(Code), nil, nil, nil, 0, 0,
SCRIPTITEM_ISVISIBLE or SCRIPTITEM_ISPERSISTENT, nil, Info));
OleCheck(FScript.SetScriptState(SCRIPTSTATE_CONNECTED));
OleCheck(FScript.GetScriptDispatch(nil, Disp));
Name := Edit1.Text;
OleCheck(Disp.GetIDsOfNames(GUID_NULL, @Name, 1, GetThreadLocale, @Id));
if FClickMethod.Code <> nil then
begin
ReleaseMethodPointer(FClickMethod);
FillChar(FClickMethod, SizeOf(FClickMethod), 0);
end;
PropInfo := GetPropInfo(CheckBox1, 'OnClick', [tkMethod]);
FClickMethod := CreateMethodPointer(Disp, Id, PropInfo.PropType^);
SetMethodProp(CheckBox1, 'OnClick', FClickMethod);
end;

So the difference we have from the last method is that we don't want the script state to be disconnected at the end, so we first get the script state. If its connected, then disconnect it. Just like the last installment, we'll parse the script text, and set the state to connected. If there are any statements outside a function block, they'll execute immediately but we want to leave the script in the connected state. So the next thing is to get an IDispatch for the whole script. That is the GetScriptDispatch call. This is where we'll use the TEdit control on the form. The text contains the name of the Active Script function to look up in the GetIDsOfNames call. If the function is found, this function will return its “ID.” Once we have that, the rest of the code is all about creating a special TMethod* “stub” that is then assigned to the OnClick of the CheckBox1 component. Remember I mentioned the CreateMethodPointer() function in ObjComAuto? This is where that little tidbit of magic is used. Once we have a TMethod we can assign that to the OnClick of CheckBox1 using the SetMethodProp function. We have to do it this way to get around the type incompatibility of the TNotifyEvent and TMethod. That's all there is to wiring up the event. Let's see this puppy in action!

Run the application and paste the following code into Memo1:
function CheckBox1Click(sender) {
if (sender.Checked == 1) {
Application.MainForm.Caption = "CheckBox1 is checked"
} else {
Application.MainForm.Caption = "CheckBox1 is NOT checked"
}
}

Then enter “CheckBox1Click” into Edit1 and press the “Hook Event” button. As long as you got no error messages, you should be able to click CheckBox1 on and off and the caption of the form should change to reflect the checked state of the checkbox!

Again, the source code for this demo is in CodeCentral here: http://cc.codegear.com/item/24667
*TMethod - This is a data structure that reflects the actual in-memory representation of a method pointer. It simply consists of the address of the actual method to call and a reference to the specific instance of the class on which the method is defined.

Wednesday, June 13, 2007

Adding Active Scripting to your Delphi Win32 application

I know, I know. This has been done before and there are components out there that add this kind of capability to a Delphi application. What I wanted to do, however, was to show that nearly all the functionality to do this is already in the product you have right now (at least in Delphi 2007). Yes, this is based heavily on COM. Yes, COM is a huge, complicated technology. What is interesting is how well Delphi and the Object Pascal language on which it is based is suited to making your life so much easier when dealing with COM. The complete source to this demo project is available on CodeCentral right here.

Native Object Pascal language support for reference counted interfaces is only the start. There is a whole host of other things that Delphi provides in both the RTL and through a very little known technique of enabling extended meta-data on all public methods and properties of a class. Steve Trefethen has been hinting at a lot of this kind of support in his articles about creating a native compiled Win32 web “mash-up” with Google Maps. In nothing more than a couple of hours, I had a simple demo up and running that was running Java Script from within a Delphi application which would then access and manipulate various controls on a form. Let's get started.

First, we're going to examine the units from the Delphi RTL that are pertinent to making this happen. Looking in sourcewin32rtlcommon, you'll find an interesting unit, ObjAuto.pas. Take a moment to peruse this units. Looking in ObjAuto.pas there is some... how should I say this... very “interesting” code. I won't go into the gory details here since that would be a whole series of articles. Basically what this code does is allow a method of an object to be dynamically invoked through an IDispatch Invoke method call. This code also forms a lot of the basis for the implementation of Win32 SOAP servers and clients. The IDispatch angle is very important since that is how any Active Script code accesses stuff “on the outside.” There is another unit, ObjComAuto.dcu, for which we don't supply source (I'll work on rectifying that oversight), which contains the TObjectDispatch class and a couple of helper global functions.

Between ObjAuto.pas and ObjComAuto.pas nearly 80% of what is needed to make this all work is already there. All we need to do now is to provide a little bit of glue code to wire some of this stuff up. First up is to implement the IActiveScriptSite and IActiveScriptSiteWindow interfaces. You can find the declaration of these interfaces in AscrLib.pas down in sourcewin32websnap. You can read the documentation about them here.

First is IActiveScriptSite:
IActiveScriptSite = interface(IUnknown)
['{DB01A1E3-A42B-11CF-8F20-00805F2CD064}']
function GetLCID(out plcid: LongWord): HResult; stdcall;
function GetItemInfo(pstrName: PWideChar;
dwReturnMask: LongWord;
out ppiunkItem: IUnknown;
out ppti: IUnknown): HResult; stdcall;
function GetDocVersionString(out pbstrVersion: WideString): HResult; stdcall;
function OnScriptTerminate(var pvarResult: OleVariant;
var pexcepinfo: EXCEPINFO): HResult; stdcall;
function OnStateChange(ssScriptState: tagSCRIPTSTATE): HResult; stdcall;
function OnScriptError(const pscripterror: IActiveScriptError): HResult; stdcall;
function OnEnterScript: HResult; stdcall;
function OnLeaveScript: HResult; stdcall;
end;

Then there is IActiveScriptSiteWindow:
IActiveScriptSiteWindow = interface(IUnknown)
['{D10F6761-83E9-11CF-8F20-00805F2CD064}']
function GetWindow(out phwnd: wireHWND): HResult; stdcall;
function EnableModeless(fEnable: Integer): HResult; stdcall;
end;

These are the only two interfaces you need to implement for the running Active Script to communicate back to the hosting application. Let's get started on the actual demostration app. Create a new VCL Win32 application and drop a TMemo control and a TButton. Then drop a few other controls down on the form because these will be the ones manipulated by the Active Script (although you can manipulate all the controls on the form). Here's what mine looked like:



Now switch to the source view and add AscrLib to the uses clause and add IActiveScriptSite and IActiveScriptSiteWindow to the form declaration as interfaces to implement.
TForm3 = class(TForm, IActiveScriptSite, IActiveScriptSiteWindow)

Then “implement” them:
TForm3 = class(TForm, IActiveScriptSite, IActiveScriptSiteWindow)
Memo1: TMemo;
Button1: TButton;
RadioButton1: TRadioButton;
RadioButton2: TRadioButton;
ListBox1: TListBox;
ComboBox1: TComboBox;
Edit1: TEdit;
CheckBox1: TCheckBox;
CheckBox2: TCheckBox;
private
{ Private declarations }
{ IActiveScriptSite }
function GetLCID(out plcid: LongWord): HResult; stdcall;
function GetItemInfo(pstrName: PWideChar;
dwReturnMask: LongWord;
out ppiunkItem: IUnknown;
out ppti: IUnknown): HResult; stdcall;
function GetDocVersionString(out pbstrVersion: WideString): HResult; stdcall;
function OnScriptTerminate(var pvarResult: OleVariant;
var pexcepinfo: EXCEPINFO): HResult; stdcall;
function OnStateChange(ssScriptState: tagSCRIPTSTATE): HResult; stdcall;
function OnScriptError(const pscripterror: IActiveScriptError): HResult; stdcall;
function OnEnterScript: HResult; stdcall;
function OnLeaveScript: HResult; stdcall;
{ IActiveScriptSiteWindow }
function GetWindow(out phwnd: HWND): HResult; stdcall;
function EnableModeless(fEnable: Integer): HResult; stdcall;
public
{ Public declarations }
end;

Most of these functions we don't care at this point, so just put Result := S_OK; in the body of them. The methods GetItemInfo, GetWindow, and OnScriptError are the ones we're going to look at right now. Here's GetItemInfo:
function TForm3.GetItemInfo(pstrName: PWideChar; dwReturnMask: LongWord; out ppiunkItem,
ppti: IInterface): HResult;
begin
Result := S_FALSE;
if SameText('Application', pstrName) then
begin
if dwReturnMask and SCRIPTINFO_IUNKNOWN <> 0 then
begin
ppiunkItem := TAutoObjectDispatch.Create(TApplicationWrapper.Connect(Application)) as IInterface;
Result := S_OK;
Exit;
end;
if dwReturnMask and SCRIPTINFO_ITYPEINFO <> 0 then
begin
Result := TYPE_E_ELEMENTNOTFOUND;
Exit;
end;
end;
end;

This is really all that is needed in order for the Active Script to get access to the application's context. We'll discuss TAutoObjectDispatch and TApplicationWrapper in a moment. So what happens in the script when it refers to “Application“ that is an unknown identifier so the script engine calls out to the script site and asks for this object. In this case this will be the VCL application object, so we have to create a wrapper object and around that a dispatch object that provides the necessary IDispatch based late-binding for the script runtime. From here the real magic starts.

Now lets tell the Active Scripting engine to run a script. To do this we first need to create a CoClass that implements the IActiveScript interface. Through some judicious spelunking with Google, I was able to find the JavaScript and VBScript CLSID GUIDs.
const
CLSID_VBScript: TGUID = '{b54f3741-5b07-11cf-a4b0-00aa004a55e8}';
CLSID_JScript: TGUID = '{f414c260-6ac0-11cf-b6d1-00aa00bbbb58}';

Just put these at the top of the unit. Add two private fields to the form class of type IActiveScript and IActiveScriptParse. Add ComObj to the uses clause. Double click the form to create the OnCreate event and inject this code into it:
procedure TForm3.FormCreate(Sender: TObject);
begin
CoInitializeEx(nil, COINIT_APARTMENTTHREADED);
OleCheck(CoCreateInstance(CLSID_JScript, nil, CLSCTX_INPROC_SERVER, IID_IActiveScript, FScript));
OleCheck(FScript.SetScriptSite(Self as IActiveScriptSite));
OleCheck(FScript.AddNamedItem('Application', SCRIPTITEM_ISVISIBLE or SCRIPTITEM_ISSOURCE));
if Supports(FScript, IActiveScriptParse, FParse) then
OleCheck(FParse.InitNew);
end;

Note the AddNamedItem call. That adds the “Application” object to the namespace of the script. So now we're ready to execute some script. Double click the Button1 (the one next to the Memo1). Add the following code to the method body:
procedure TForm3.Button1Click(Sender: TObject);
var
Info: EXCEPINFO;
Code: WideString;
begin
Code := Memo1.Text;
OleCheck(FParse.ParseScriptText(PWideChar(Code), nil, nil, nil, 0, 0,
SCRIPTITEM_ISVISIBLE or SCRIPTITEM_ISPERSISTENT, nil, Info));
OleCheck(FScript.SetScriptState(SCRIPTSTATE_CONNECTED));
OleCheck(FScript.SetScriptState(SCRIPTSTATE_DISCONNECTED));
end;

Now run the application and enter the following into the Memo:
function main() {
var Component
var MainForm = Application.MainForm
for (var i = 0; i < MainForm.ComponentCount; i++) {
Component = MainForm.GetComponent(i)
MainForm.ListBox1.Items.Add(Component.Name)
}
}

main()

Press Button1. The Listbox should now fill up with all the names of the components on the form. So what the blazes happened!?? Remember the TAutoObjectDispatch class and the TApplicationWrapper from the GetItemInfo function? TAutoObjectDispatch is a descendant of the TObjectDispatch class in the ObjComAuto.dcu unit. Here's the basic declaration of TObjectDispatch:
TObjectDispatch = class(TInterfacedObject, IDispatch)
protected
function GetObjectDispatch(Obj: TObject): TObjectDispatch; virtual;
function GetMethodInfo(const AName: ShortString; var AInstance: TObject): PMethodInfoHeader; virtual;
function GetPropInfo(const AName: string; var AInstance: TObject; var CompIndex: Integer): PPropInfo; virtual;
property Instance: TObject read FInstance;
public
{ IDispatch }
function GetIDsOfNames(const IID: TGUID; Names: Pointer;
NameCount: Integer; LocaleID: Integer; DispIDs: Pointer): HRESULT;
virtual; stdcall;
function GetTypeInfo(Index: Integer; LocaleID: Integer;
out TypeInfo): HRESULT; stdcall;
function GetTypeInfoCount(out Count: Integer): HRESULT; stdcall;
function Invoke(DispID: Integer; const IID: TGUID; LocaleID: Integer;
Flags: Word; var Params; VarResult: Pointer; ExcepInfo: Pointer;
ArgErr: Pointer): HRESULT; virtual; stdcall;
public
constructor Create(Instance: TObject; Owned: Boolean = True);
destructor Destroy; override;
end;

We're going to create a descendant, TAutoObjectDispatch, that overrides GetObjectDispatch, GetMethodInfo, and GetPropInfo. In GetObjectDispatch, we'll look up a registered “wrapper” class for the given object, create that wrapper, and then place that back into another instance of TAutoObjectDispatch. I've already done a lot of this in an Automation.pas unit that is included with this project. There are also a bunch of “wrapper” classes for various VCL objects. Remember where I mentioned extended meta-data on public methods? Well the wrappers are where this is handled. The base wrapper class is declared within the {$METHODINFO ON} directive which will tell the compiler to generate extra method RTTI on all public methods of this class and all descendants of that class. It is this extra information that the dynamic invocation code uses to call the functions.

When the scripting engine calls GetItemInfo asking for an interface to the “Application” item, an IDispatch is returned. Then it calls GetIDsOfNames asking for the “MainForm” property. It then calls Invoke to get another IDispatch representing the MainForm, and the process starts all over again. By using the object wrappers you can control with fine detail everything that you want the script to have access to. Keep in mind that all published properties are always available, however you can still control this access by overriding GetPropInfo on TObjectDispatch.

In the next installment, I'll show how you can wire an event directly up to a script function. In the mean-time, take a close look at CreateMethodPointer in ObjComAuto.int contained within the demo for a clue as to how this will be done.

Again, the CodeCentral demo can be found here: http://cc.codegear.com/item/24664

Friday, June 1, 2007

A look at "array of const" for fun and profit

Back when Delphi was being developed we began to lament the lack of a function that would be able to format a string with format specifiers and some unknown number of parameters. C and C++ folks have long enjoyed the “...” variable number of parameters declaration used so effectively on things like printf, sprintf and others. Without getting too deep into the reasons and mechanics behind why this was not actually possible given the way function parameters were passed in Pascal, but suffice it to say it had to do with the order in which parameters were pushed onto the stack and who was responsible for cleaning said stack. Actually, if you really want the gory details, I'm sure Hallvard would be able to wax poetically on the whole underpinnings and machine-level workings :-).

So here we are wanting to have a nice string format function that allows you to specify any number of parameters both as constants and variables for maximum flexibility. Sure we could have just introduced a direct clone of printf and even followed the same syntax, but that just didn't seem to “fit” the whole idea of maximum type safety. See the problem with the printf function is that if you want to format a string with the text representation of the value of a byte followed by an integer, there was no information passed in that clearly indicated that 'x' param is a byte. It also forces you to specify the same parameter multiple times if you wanted to use it in more than one place. It was essentially a variable length array with elements of varying sizes without any information as to the overall length and size of each element! Gee... I wonder why the world is filled with so many buffer overrun errors? Anyway, I digress :-).

So the requirements were simple. We needed a language construct that would allow us to more-or-less declare a function to take a variable number of parameters. Since a function's parameter list is essentially a compiler generated array pushed onto the stack (or passed in CPU registers), why not just allow an array to be declared in place and passed to the function? We also wanted this array to be self-describing and type-safe. So when you declare a parameter as an “array of const” the compiler actually makes that into an open array parameter as an “array of TVarRec.” The declaration for TVarRec is as follows:
PVarRec = ^TVarRec;
TVarRec = record { do not pack this record; it is compiler-generated }
case Byte of
vtInteger: (VInteger: Integer; VType: Byte);
vtBoolean: (VBoolean: Boolean);
vtChar: (VChar: Char);
vtExtended: (VExtended: PExtended);
vtString: (VString: PShortString);
vtPointer: (VPointer: Pointer);
vtPChar: (VPChar: PChar);
vtObject: (VObject: TObject);
vtClass: (VClass: TClass);
vtWideChar: (VWideChar: WideChar);
vtPWideChar: (VPWideChar: PWideChar);
vtAnsiString: (VAnsiString: Pointer);
vtCurrency: (VCurrency: PCurrency);
vtVariant: (VVariant: PVariant);
vtInterface: (VInterface: Pointer);
vtWideString: (VWideString: Pointer);
vtInt64: (VInt64: PInt64);
end;

It's just a variant record. If you also look closely, all the data fields are the same size. They all max out at pointer size (which has some interesting implications for 64bit, but that is a subject for another day). So if the data being passed in is > 4 bytes it is done as a pointer to this data. An array with elements of this type will be a constant element size array. Also, since “array of const” becomes an “array of TVarRec” it is an open array so the length of the array is also passed in. This satisfies one objection to the C-style “...” construct. The other objection is solved by the fact that the compiler will encode into the VType field a value representing the type of that element. Another side benefit of this is that you can now refer to the position of an element in addition to its value. This allows you to do interesting things like explicitly refer to the element you want to format in the string using the format specifier '%x:y' where x is the ordinal position of the element with 0 being the first element.
So how do you use this special array construct? If you declared your function or method with a parameter of type “array of const” in the body of the method you just treat that parameter as an “array of TVarRec” and use all the standard array indexing and range checking functions, like Low and High. Here's a simple function that just writes to the console the type and value of each element:

procedure PrintArrayOfConst(const Args: array of const);
var
I: Integer;
begin
for I := Low(Args) to High(Args) do
begin
Write('Arg[', I, ']:');
case Args[I].VType of
vtInteger: Writeln('Integer = ', Args[I].VInteger);
vtBoolean: Writeln('Boolean = ', BoolToStr(Args[I].VBoolean, True));
vtChar: Writeln('Char = ''', Args[I].VChar, '''');
vtExtended: Writeln('Extended = ', FloatToStr(Args[I].VExtended^));
vtString: Writeln('ShortString = ''', Args[I].VString^, '''');
vtPChar: Writeln('PChar = ''', Args[I].VPChar, '''');
vtAnsiString: Writeln('AnsiString = ''', string(Args[I].VAnsiString), '''');
vtWideChar: Writeln('WideChar = ''', Args[I].VWideChar, '''');
vtPWideChar: Writeln('PWideChar = ''', Args[I].VPWideChar, '''');
vtWideString: Writeln('WideString = ''', WideString(Args[I].VWideChar), '''');
vtInt64: Writeln('Int64 = ', Args[I].VInt64^);
vtCurrency: Writeln('Currency = ', CurrToStr(Args[I].VCurrency^));
else
Writeln('Unsupported');
end;
end;
end;

Of course it doesn't support all things, but it should give you an idea what you can do with this little known but widely used (you've used Format() right?) language feature. Here's a sample of how to call this function:
    PrintArrayOfConst([1, 'c', 'this is a string', 12.5, True, PChar('This is a pchar'), Int64(123456)]);

I had to do some typecasting in order to get the compiler to recognize some of the literals as specific type because it will automatically pick the most natural type to use. Of course if you pass in variables for the array elements, the type of that variable is preserved. So go have fun, and definately do try this at home.