Jump to content
Sign in to follow this  
eimhym

ComObject Proxy, seamless Windows Script Control & AutoItObj

Recommended Posts

eimhym

Hi guys I'm new here, both in forum and using autoit. IMHO this really is awesome tool and great community too, clean documentation and abundant examples really helps. Thanks for your great effort and keep up!

AutoIt doesn't support associative array which a bit let down, but hey, we got COM Extensions! You can get more info about COM here: yet another idea to implement it this thread is all about.

; #FUNCTION# ==================================================================
; Name      : _ComObj_init
; Description  : Creates MS Windows Script control and deploy it as proxy for
;               AutoIt COM object binding.
; Syntax      : _ComObj_init([$VBScriptSupport = false])
; Parameter : $VBScriptSupport
;               By default JScript doesn't have alert() function, it is provided
;               by browser's window object.
;               We can emulate this using VBScript's MsgBox function, which is
;               performance hog because we need another ScriptControl instance.
;               Other advantage is to be able to execute other VBScript's methods
;               within function via vb.Eval() method.
;               This feature is disabled by default.
; =============================================================================
Func _ComObj_init ($VBScriptSupport = false)
  Local $_Script
  $_Script = ObjCreate("ScriptControl")
  $_Script.Language = "JScript"
  $_Script.Timeout = 60000
  $_Script.AddCode("var $=this,arrays=[],vb;function set_vbscript($) {vb=$;vb.Language='VBScript';vb.AllowUI=true;}function alert(s){if(vb)vb.Eval('MsgBox(Unescape(""'+s+'""), 0, ""Autoit"")')}function get_proxy(){return $}function new_object($){$=new Function('return{'+$+'}')();$.set=function(k, v){$[k]=v};$.unset=function(k){delete $[k]};$.set_object=function(k,$){this.set(k,new_object($))};$.set_array=function(){var v=[];for(var i=0;i<arguments.length;i++)v[i]=arguments[i];$.set(v.shift(),new_array.apply(this,v))};$.set_function=function(k,p,$){this.set(k,new_function(p,$))};return $}function new_array(){var v=[];for(var i=0;i<arguments.length;i++)v[i]=arguments[i];return v}function array_get($,k){return $[k]}function array_set($,k,v){return $[k]=v}var new_function=(function(){function F(r) {return Function.apply(this,r)}F.prototype = Function.prototype;return function(p,$){p=p.split(/\s*,\s*/);p.push($);return new F(p)}})()")
  If $VBScriptSupport = true Then $_Script.Run("set_vbscript", ObjCreate("ScriptControl"))
  Return $_Script
EndFunc
; =============================================================================

This UDF only use Script Control methods to initialize the script engine, once loaded creation of object, function and array are done by the script itself. The script passed into AddCode method above is inlined to save space, the exploded version below:

<html>
<script language="javascript">
var $=this,arrays=[],vb;

// VBScript support
function set_vbscript($) {
  vb=$;
  vb.Language='VBScript';
  vb.AllowUI=true;
}

// alert() emulation
function _alert(s){if(vb)vb.Eval('MsgBox(Unescape("'+s+'"), 0, "Autoit")')}

// Core functions
function get_proxy(){return $}
function new_object($){
  $=new Function('return{'+$+'}')();
  $.set=function(k, v){$[k]=v};
  $.unset=function(k){delete $[k]};
  $.set_object=function(k,$){this.set(k,new_object($))};
  $.set_array=function(){
    var v=[];for(var i=0;i<arguments.length;i++)v[i]=arguments[i];
    $.set(v.shift(),new_array.apply(this,v))
  };
  $.set_function=function(k,p,$){this.set(k,new_function(p,$))};
  return $
}
function new_array(){var v=[];for(var i=0;i<arguments.length;i++)v[i]=arguments[i];return v}
function array_get($,k){return $[k]}
function array_set($,k,v){return $[k]=v}
function new_date(){return new Date()}
var new_function=(function(){
function F(r) {return Function.apply(this,r)}
F.prototype = Function.prototype;
return function(p,$){p=p.split(/\s*,\s*/);p.push($);return new F(p)}}
)();



function unit_test(){
var err;
try {
  err = "function new_object test";
  var obj = new_object("num:1234567890");
  if (obj.num*1 !== 1234567890) throw err;
  obj.set_object("obj", "num:1234567890");
  if (obj == obj.obj) throw err; // not the same object
  if (obj.num*1 !== obj.obj.num*1) throw err;
  err = "method object.set test";
  obj.set("str", "a string");
  if (typeof obj.str != "string" || obj.str != "a string") throw err;
  err = "method object.unset test";
  obj.unset("str");
  if (typeof obj.str != "undefined") throw err;
  err = "function new_array test";
  var arr = new_array(1,2,3,4,5,6,7,8,9,0);
  obj.set_array("arr",1,2,3,4,5,6,7,8,9,0);
  for (var i in arr)
   if (arr[i] !== obj.arr[i]) throw err;
  err = "function new_function test";
  var fn = new_function("a, b,c ,d", "(a+b)*c+d");
  obj.set_function("fn", "a, b,c ,d", "(a+b)*c+d");
  if (fn(1,2,3,"MB") !== obj.fn(1,2,3,"MB")) throw err;
  obj.set_function("is_same", "o", "return o==this"); // function's "this" reference test
  if (! obj.is_same(obj) ) throw err;
  err = "JScript alert support test";
  if (window.ActiveXObject) {
   _alert("This should be swallowed");
   set_vbscript( new ActiveXObject('ScriptControl') );
   _alert("Hello from VB world!");
  }
  else throw "please run this test with IE";
}
catch (e) { alert(err + " failed: " + e); return }
return true;
}
if ( unit_test() ) alert ("All tests passed.");
</script>
</html>

As you can see these functions are merely wrapper for Javascript object creation, and COM nature provide AutoIt direct access toward its functions make the integration seamless. Example worth thousands words, so here we go:

; ComObject; create and invoke COM object, properties, function and array:
Local $_ComObj_proxy, $_com
Local $obj, $complex_obj, $fn, $bucket, $array_to_string, $typeof, $i, $v

; Initialize COM Object proxy with VBScript support, to enable alert() feature.
$_ComObj_proxy = _ComObj_init(true)
; $_com is now our gateway, providing methods:
$_com = $_ComObj_proxy.Run("get_proxy")

; - new_object($json)
;     Creates and return new COM object accessible from AutoIt.
;     parameter:
;     - JSON string to initialize new object
;       or empty string to start with a basic one.
;       Every objects created are equiped with set() and unset() method, so
;       either start with a complex object or the basic one, we can always
;       add or remove object's properties and methods.

; basic obj:
$obj = $_com.new_object("")
$obj.set("Version", @AutoItVersion)  ; simple types
$obj.set("randomVal", Random(10, 1000))  ;

; the complex one:
$complex_obj = $_com.new_object( _
    "num:123, str:'a string', arr:[1,2,3], " _
  & "child:{msg:'Hello AutoIt World!'}, hail:function(){alert(this.child.msg + ' (' + this.AutoIt.Version + ')')}" _
  )
$complex_obj.set("AutoIt", $obj)  ; nesting our basic object as $complex_obj.AutoIt
$complex_obj.hail()

; - new_function($parameters, $statements)
;     Creates and return new owner-less function.
;     We can later assign it into object via set() method.
;     parameters:
;     - A string of function parameter names, separated by comma.
;     - A string contains all the function statements

; either this way:
$fn = $_com.new_function("msg", "alert('You command me to say: ' + msg + '!')")
$obj.set("blarp", $fn)
$obj.blarp("AutoIt is Awesome")

; or use object's shortcut, both is the same
$obj.set_function("calc", "size, h ,s", 'alert("For " + size + "MB disk image with " + h + " heads and " + s + " sector per track"+ escape("\r\n") + "You will need " + Math.ceil(size*Math.pow(2, 20)/h/s/512) + " cylinders to make it compatible with GRUB.")')
$obj.calc(128, 16, 63);

; - new_array($value[, $value ...])
;     Creates and return new Variant array,
;     We can later assign it into object via set() method.
;     parameters:
;     - list of comma delimited values. Value can be basic AutoIt variables (string or number)
;       also can hold COM object or other JScript Array.

; NOTE:
;   JScript Array is not compatible with AutoIt array, thus we can't
;   access its items in AutoIt way.
;   In fact JScript Array also not compatible with VBArray nor Variant Array,
;   that because JScript Array is NOT actually an array.
;   The good news is we have the full access to Array methods like push, pop, join, etc.
;   while for Array item access these functions are provided: array_get and array_put (see below)

$bucket = $_com.new_array(1, 2, 3, 4, "blue", "red", $obj)
$obj.set("bucket", $bucket)

; object shortcut:
$obj.set_array("box") ; empty array

; example to play with JScript Array
$array_to_string = $_com.new_function("r", "var s='';for (var i=0;i<r.length;i++) s+=i==0?r[i]:', '+r[i];return s")
MsgBox( 0, "Autoit", "Before: " & $array_to_string($bucket) )
$typeof = $_com.new_function("v", "return typeof v")
For $i = 0 To $bucket.length - 1
  $v = $_com.array_get($bucket, $i) ; array_get example
  $_com.array_set( $bucket, $i, $v & " is " & $typeof($v) ) ; array_put example
  If $typeof($v) = "number" Then
    $obj.box.push($v)
  EndIf
Next
MsgBox( 0, "Autoit", "After: " & $array_to_string($bucket) )
MsgBox( 0, "Autoit", "$obj.box: " & $array_to_string($obj.box) )

And below is example to make AutoIt functions accessible from within JScript. For this purpose AutoItObject come in handy dandy, it cant be more easier. Thanks guys, AutoItObject - you want it ;)

; Exposing AutoIt functions with AutoItObject

; Render scene :P
Run("notepad.exe")
WinWaitActive("[CLASS:Notepad]")

; Prepare AutoItObject to be exposed
_AutoItObject_Startup()
Global $jsExposed = _AutoItObject_Create()
_AutoItObject_AddMethod($jsExposed, "send", "_jsExposed_Send")
_AutoItObject_AddMethod($jsExposed, "sleep", "_jsExposed_Sleep")
Func _jsExposed_Send($oSelf, $keys)
    #forceref $oSelf
    Send($keys)
EndFunc
Func _jsExposed_Sleep($oSelf, $delay)
    #forceref $oSelf
    Sleep($delay)
EndFunc

; Send AutoItObject into JScript world
$obj.set("AutoIt", $jsExposed)
$obj.set_function("play", "", "var key,$=this;" _
  & "function log (s) { $.AutoIt.send(s) }" _
  & "function sleep (c) { $.AutoIt.sleep(c) }" _
  & "log('JScript object\'s member:{ENTER}');" _
  & "log('------------------------{ENTER}');" _
  & "sleep(700);" _
  & "try {" _
  & "for (key in $) {" _
  &   "if (typeof $[key] != 'undefined')" _
  &  "log( key + ' => ' + (typeof $[key]) + '{ENTER}');" _
  &  "log( '>>> ' + $[key] + '{ENTER}');" _
  & "}" _
  & "} catch(e) { alert(e) };" _
  & "sleep(700);" _
  & "log('{ENTER}{ENTER}5 secs to autodestruct ...{ENTER}');" _
  & "sleep(5000);" _
  )

$obj.play() ; Action !

; clean up
Send("!f")
Send("x")
WinWaitActive("Notepad")
Send("n")
WinWaitClose("[CLASS:Notepad]")
$jsExposed = 0

Thats it folks, have fun :)

Tested with:

+ AutoIt: v3.3.8.0 (feedback needed)

+ AutoItObject: 1.2.8.2 (feedback needed)

+ OS: XP SP3 (feedback needed)

ComObject.zip

Share this post


Link to post
Share on other sites
eimhym

First um ... I'm sorry, I don't know how to edit the first post yet ;) anyone can help?

So the update posted here:

You now have Javascript Framework power within AutoIt! For example Array is now have each() method, no longger have to do For ... Next anymore

:)

Note: Only server-side framework is supported. MooTools is tested and work fine, please send your test result for other framewok whenever you can, thanks

Update

  • ComObject is now isolated in UDF, new functions are:

    ComObject_Init

    ComObject_LoadFile

    ComObject_LoadHttp

  • Modifed core functions to support Javascript Framework
  • Framework/Script can be loaded either from local file or web server
  • Test script now named ComObject.test.au3
Tested Framework:

MooToos v1.4.5

ComObject_0001.zip

Share this post


Link to post
Share on other sites
eimhym

Update:

  • Now become JScriptObject, for:

    1.) Now tightly coupled with AutoItObject. Include JScriptObject.au3 will automatically include AutoItObject (http://autoitobject.origo.ethz.ch/)

    2.) More represents its nature. AutoItObject, which also a (customized) COM object specially crafted for AutoIt use. JScriptObject is plain JScript's object not (yet) hampered in anyway. Still, JScriptObject is accessible from AutoIt thanks to COM support.

  • alert() is now emulated using AutoIt's MsgBox(). VBScript support no longer required but still an option if someone need to invoke VBS code.
  • array_set() and array_get() replaced with Array.prototype.set() and get(). unset() introduced to remove an Array element (see example)
  • Introduce _JScriptObject_AddMethod() which wrap _AutoItObject_AddMethod(). You can now easily assign AutoIt function as JScriptObject method .No need to create new AutoItObject (see example)
To Do:
  • Find a way (or ask AutoItObject team) to make AutoItObject can inherit from JScript's object. Any attempt resulting fatal error so far
  • or even better: mock JScript object directly. For now AutoIt function call within JScript object has to take quite long route: JScript wrapper --> AutoItObject --> AutoIt function. This means serious debugging JScript's inner work, which, devotion required :duh:
Not much, but as a proof-of-concept now JScriptObject have a pretty much clearer goal ;)

JScriptObject.zip

Share this post


Link to post
Share on other sites
eimhym

Yesss! edit post button popping out of thin air LOL

Share this post


Link to post
Share on other sites
biase

After played with your code for a while and the result is you can include a JS file.

Maybe it's not interesting much but may be you or the others comes with new ideas.

$fso = ObjCreate('Scripting.FileSystemObject')
$jso = ObjCreate("ScriptControl")
$jso.Language = "JScript"
$jso.AddCode($fso.OpenTextFile("include.js", 1).ReadAll())
$jso.Run("set_vbscript", ObjCreate("ScriptControl"))
$jsn = $jso.Run("get_proxy")

and the include.js

var $=this,arrays=[],vb;function set_vbscript($) {vb=$;vb.Language='VBScript';vb.AllowUI=true;}function alert(s){if(vb)vb.Eval('MsgBox(Unescape(""'+s+'""), 0, ""Autoit"")')}function get_proxy(){return $}function new_object($){$=new Function('return{'+$+'}')();$.set=function(k, v){$[k]=v};$.unset=function(k){delete $[k]};$.set_object=function(k,$){this.set(k,new_object($))};$.set_array=function(){var v=[];for(var i=0;i<arguments.length;i++)v[i]=arguments[i];$.set(v.shift(),new_array.apply(this,v))};$.set_function=function(k,p,$){this.set(k,new_function(p,$))};return $}function new_array(){var v=[];for(var i=0;i<arguments.length;i++)v[i]=arguments[i];return v}function array_get($,k){return $[k]}function array_set($,k,v){return $[k]=v}var new_function=(function(){function F(r) {return Function.apply(this,r)}F.prototype = Function.prototype;return function(p,$){p=p.split(/\s*,\s*/);p.push($);return new F(p)}})()

Not just that, you also can use other JScript codes inside it or you can use in other JS file and include it.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
Sign in to follow this  

×