Thursday, February 27, 2014

[VBScript] Boosting myself to HIGH process priority

The script gets its own name, then looks through the Win32_Processes for a match with CSCRIPT.EXE and a CommandLine containing that name. Any matches (should be only one, but could be more) are boosted to HIGH process priority.

This demonstrates WMI calls from both VBScript and JScript. Note the need to explicitly define an Enumerator in the JScript version.
Option Explicit

Dim sName
Dim sComputer
Dim oWMI
Dim cProcesses
Dim oProcess

Const HIGH = 256
sComputer = "."
Set oWMI = GetObject("winmgmts:\\" & sComputer & "\root\cimv2")
sName = WScript.ScriptName

Set cProcesses = oWMI.ExecQuery("Select * from Win32_Process Where Name = 'cscript.exe' And CommandLine LIKE '%" & sName & "%'")
For Each oProcess In cProcesses
  oProcess.SetPriority(HIGH) 
  WScript.Echo "Boosted myself"
Next
And in JScript
var HIGH = 256;
var sComputer = ".";
var sName = WScript.ScriptName;    

var query = GetObject("winmgmts:\\\\" + sComputer + "\\root\\cimv2")
    .ExecQuery("Select * from Win32_Process " + 
    "Where Name = 'cscript.exe' And CommandLine LIKE '%" + sName + "%'")

// Enumerate WMI objects
var cProcesses = new Enumerator(query);

for ( ; !cProcesses.atEnd(); cProcesses.moveNext()) { 
    var oProcess = cProcesses.item()
    oProcess.Priority = HIGH
    WScript.Echo( "Boosted myself")
}
© Copyright Bruce M. Axtens, 2014

Tuesday, February 25, 2014

[VBScript] How can I display 64 bit double number using VBScript on 32 Bit OS?

"How can I display 64 bit double number using VBScript on 32 Bit OS?" That was the question on StackOverflow back at the end of 2012. And nobody had answered it. What a challenge!

I assumed a pure-VBScript solution was needed, and set about finding and implementing a Very Large Integer class with which I could then implement a Hex64 to integer function. A workable candidate function was found on Rosetta Code in the
Liberty BASIC solution to the Long Multiplication task.

The code for the class is as follows. It's pretty much the same as the original.

Option Explicit
Class VeryLongInteger
  'http://rosettacode.org/wiki/Long_Multiplication#Liberty_BASIC
  Public Function MULTIPLY(Str_A, Str_B)
    Dim signA, signB, sResult, Str_Shift, i, d, Str_T
    signA = 1
    If Left(Str_A,1) = "-" Then 
      Str_A = Mid(Str_A,2)
      signA = -1
    End If
    signB = 1
    If Left(Str_B,1) = "-" Then 
      Str_B = Mid(Str_B,2)
      signB = -1
    End If
    sResult = vbNullString
    Str_T = vbNullString
    Str_shift = vbNullString
    For i = Len(Str_A) To 1 Step -1
      d = CInt(Mid(Str_A,i,1))
      Str_T = MULTBYDIGIT(Str_B, d)
      sResult = ADD(sResult, Str_T & Str_shift)
      Str_shift = Str_shift & "0"
      'print d, Str_T, sResult 
    Next
    If signA * signB < 0 Then sResult = "-" + sResult
    'print sResult
    MULTIPLY = sResult
  End Function
  
  Private Function MULTBYDIGIT(Str_A, d)
    Dim sResult, carry, i, a, c
    'multiply Str_A by digit d
    sResult = vbNullString
    carry = 0
    For i = Len(Str_A) To 1 Step -1
      a = CInt(Mid(Str_A,i,1))
      c = a * d + carry
      carry = c \ 10
      c = c Mod 10
      'print a, c
      sResult = CStr(c) & sResult 
    Next
    If carry > 0 Then sResult = CStr(carry) & sResult
    'print sResult
    MULTBYDIGIT = sResult
  End Function
  
  Public Function ADD(Str_A, Str_B)
    Dim L, sResult, carry, i, a, b, c
    'add Str_A + Str_B, for now only positive
    l = MAX(Len(Str_A), Len(Str_B))
    Str_A=PAD(Str_A,l)
    Str_B=PAD(Str_B,l)
    sResult = vbNullString 'result
    carry = 0
    For i = l To 1 Step -1
      a = CInt(Mid(Str_A,i,1))
      b = CInt(Mid(Str_B,i,1))
      c = a + b + carry
      carry = Int(c/10)
      c = c Mod 10
      'print a, b, c
      sResult = CStr(c) & sResult
    Next
    If carry>0 Then sResult = CStr(carry) & sResult
    'print sResult
    ADD = sResult
  End Function
  
  Private Function Max(a,b)
    If a > b Then
      Max = a
    Else
      Max = b
    End If
  End Function
  
  Private Function pad(a,n)  'pad from right with 0 to length n
    Dim sResult
    sResult = a
    While Len(sResult) < n
      sResult = "0" & sResult
    Wend
    pad = sResult
  End Function
End Class
With that defined I have now all I need to implement a Hex64 function. This I did in two forms:

* A memoized version which precomputes all the relevant powers of 16

Function PrecomputedFromHex64(sHex)
  Dim VLI
  Set VLI = New VeryLongInteger
  
  Dim Sixteen(16)
  Sixteen(0) = "1"
  Sixteen(1) = "16"
  Sixteen(2) = VLI.MULTIPLY(Sixteen(1),"16")
  Sixteen(3) = VLI.MULTIPLY(Sixteen(2),"16")
  Sixteen(4) = VLI.MULTIPLY(Sixteen(3),"16")
  Sixteen(5) = VLI.MULTIPLY(Sixteen(4),"16")
  Sixteen(6) = VLI.MULTIPLY(Sixteen(5),"16")
  Sixteen(7) = VLI.MULTIPLY(Sixteen(6),"16")
  Sixteen(8) = VLI.MULTIPLY(Sixteen(7),"16")
  Sixteen(9) = VLI.MULTIPLY(Sixteen(8),"16")
  Sixteen(10) = VLI.MULTIPLY(Sixteen(9),"16")
  Sixteen(11) = VLI.MULTIPLY(Sixteen(10),"16")
  Sixteen(12) = VLI.MULTIPLY(Sixteen(11),"16")
  Sixteen(13) = VLI.MULTIPLY(Sixteen(12),"16")
  Sixteen(14) = VLI.MULTIPLY(Sixteen(13),"16")
  Sixteen(15) = VLI.MULTIPLY(Sixteen(14),"16")
  
  Dim theAnswer, i, theDigit, theMultiplier, thePower, aPower
  theAnswer = "0"
  aPower = 0
  For i = Len(sHex) To 1 Step -1
    theDigit = UCase(Mid(sHex,i,1))
    theMultiplier = InStr("0123456789ABCDEF",theDigit)-1
    thePower = Sixteen(aPower)
    thePower = VLI.MULTIPLY(CStr(theMultiplier),thePower)
    theAnswer = VLI.ADD(theAnswer,thePower )
    aPower = aPower + 1
  Next
  PrecomputedFromHex64 = theAnswer
End Function
* A non-memoized version which computes the relevant power when it is needed.
Function FromHex64(sHex)
  Dim VLI
  Set VLI = New VeryLongInteger       
  Dim theAnswer, i, theDigit, theMultiplier, thePower, aPower
  theAnswer = "0"
  thePower = "1"
  For i = Len(sHex) To 1 Step -1
    theDigit = UCase(Mid(sHex,i,1))
    theMultiplier = InStr("0123456789ABCDEF",theDigit)-1
    theAnswer = VLI.ADD(theAnswer,VLI.MULTIPLY(thePower,theMultiplier))
    thePower = VLI.MULTIPLY(thePower,"16")
  Next
  FromHex64 = theAnswer
End Function
The memoized version is slightly faster than the non-memoized, despite the overhead of the memoization. If the class/function pair were to be used a lot in a script, one might consider making both the VLI instantiation and the Sixteen() array global and precomputing the array at the beginning of the script rather than on each invocation.

A rough test of the code follows:

Const test = "FFFFFFFFFFFFFFFF" '"41417724EBA8953E"
Dim t, I, S
t=Timer
For I = 1 To 100
  S = FromHex64(test)
Next
WScript.Echo "No memoization", Timer-t

t=Timer
For I = 1 To 100
  S = PrecomputedFromHex64(test)
Next
WScript.Echo "Memoized each time",Timer-t

Function GlobalMemoFromHex64(sHex)  
  Dim theAnswer, i, theDigit, theMultiplier, thePower, aPower
  theAnswer = "0"
  aPower = 0
  For i = Len(sHex) To 1 Step -1
    theDigit = UCase(Mid(sHex,i,1))
    theMultiplier = InStr("0123456789ABCDEF",theDigit)-1
    thePower = Sixteen(aPower)
    thePower = VLI.MULTIPLY(CStr(theMultiplier),thePower)
    theAnswer = VLI.ADD(theAnswer,thePower )
    aPower = aPower + 1
  Next
  GlobalMemoFromHex64 = theAnswer
End Function

Dim VLI
Set VLI = New VeryLongInteger

Dim Sixteen(16)
Sixteen(0) = "1"
Sixteen(1) = "16"
Sixteen(2) = VLI.MULTIPLY(Sixteen(1),"16")
Sixteen(3) = VLI.MULTIPLY(Sixteen(2),"16")
Sixteen(4) = VLI.MULTIPLY(Sixteen(3),"16")
Sixteen(5) = VLI.MULTIPLY(Sixteen(4),"16")
Sixteen(6) = VLI.MULTIPLY(Sixteen(5),"16")
Sixteen(7) = VLI.MULTIPLY(Sixteen(6),"16")
Sixteen(8) = VLI.MULTIPLY(Sixteen(7),"16")
Sixteen(9) = VLI.MULTIPLY(Sixteen(8),"16")
Sixteen(10) = VLI.MULTIPLY(Sixteen(9),"16")
Sixteen(11) = VLI.MULTIPLY(Sixteen(10),"16")
Sixteen(12) = VLI.MULTIPLY(Sixteen(11),"16")
Sixteen(13) = VLI.MULTIPLY(Sixteen(12),"16")
Sixteen(14) = VLI.MULTIPLY(Sixteen(13),"16")
Sixteen(15) = VLI.MULTIPLY(Sixteen(14),"16")

t=Timer
For I = 1 To 100
  S = GlobalMemoFromHex64(test)
Next
WScript.Echo "Global memo",Timer-t
Running the above in VBSEdit a few times under varying conditions gave the following results:
No memoization 2.632813
Memoized each time 1.023438
Global memo 0.328125

No memoization 1.667969
Memoized each time 0.8867188
Global memo 0.2695313

No memoization 1.488281
Memoized each time 0.9335938
Global memo 0.3046875
The global precomputation of the memo array is fastest technique, but if you're only using the function once you'll get by with the non-memo version.

Further optimisations are possible. Compilation, of course, would make it even faster.

Enjoy!
© Copyright Bruce M. Axtens, 2014

Wednesday, September 11, 2013

[Open Source] Github and Bitbucket

The blog's been very quiet lately. Too quiet. But I've been busy.

I got started on
Github, but the only thing I have there is a failed attempt at building a JSON library for the Euphoria programming language.

Most of the more recent stuff is happening on Bitbucket where I have various implementations (javascript v1 and javascript v2 (minimal), euphoria, vb6, php, and c#) of Steve Skiena's integer bignums library and a few other bits and bobs, namely:


All projects are in varying stages of completeness, and looking for users and improvers.


© Copyright Bruce M. Axtens, 2013

Friday, June 21, 2013

[JScript.NET --> C#.NET] Goodbye JScript.NET; Hello C#.NET

I'd better make it official: I've given up on JScript.NET (and to a lesser extent, JScript Classic.) There's just not enough support for it in current tools and IDEs.

So for the past couple of months I've been converting all my server-side javascripts (predominantly in JScript Classic) to C#.

I've also had one of my periodic clean-outs. Am trying to limit myself to a smaller set of programming languages for work and for play. That, and try to catch up on some of the things I've promised to others, like how to do web-scripting in Lhogho.


© Bruce M. Axtens, 2013

Friday, September 21, 2012

[JScript] Another Config library


I've been doing a fair bit of JScript of late, and as I find working with Registry for runtime configuration a bit tiresome, I've cooked up a Config object. It's defined using the prototype approach to object definition. Config files are, in this case, ANSI or Unicode (UTF16LE) files, in the form
key=value
Creating an instance of the object involves passing in the name of the config file. I've hit upon the following technique for tying the config file to the script, using the FileSystemObject and the WScript object.
var oFSO = new ActiveXObject("Scripting.FileSystemObject"),
    sHere = oFSO.GetParentFolderName(WScript.ScriptFullName),
    sConfig = oFSO.BuildPath(sHere, 
        oFSO.GetBaseName(WScript.ScriptName) + ".config");
That way one may always be sure that (for example) Melchizedek.js will have a Melchizedek.config next to it.

First, the main function

function Config(sFile) {
    this.kind = "";
    this.oFSO = new ActiveXObject("Scripting.FileSystemObject");
    this.text = "";
    var resp = [];
    this.file = sFile;
    if (this.oFSO.FileExists(sFile)) {
        resp = function(oFSO, sFilename) {
            var forReading = 1;
            var asUnicode = -1;
            var asANSI = 0;
            var resp = [];
            if (oFSO.FileExists(sFilename)) {
                var handle = oFSO.OpenTextFile(sFilename, forReading, false,
                        asANSI);
                var BOM = handle.Read(2);
                handle.Close();
                if (BOM.charCodeAt(0) == 0xFF && BOM.charCodeAt(1) == 0xFE) {
                    handle = oFSO.OpenTextFile(sFilename, forReading, false,
                            asUnicode);
                    resp[0] = "unicode";
                } else {
                    handle = oFSO.OpenTextFile(sFilename, forReading, false,
                            asANSI);
                    resp[0] = "ansi";
                }
                resp[1] = handle.ReadAll();
                handle.Close();
                return resp;
            } else {
                return resp;
            }
        }(this.oFSO, sFile);
        this.kind = resp[0];
        this.text = resp[1];
        this.name = sFile;
        this.filefound = true;
    } else {
        this.filefound = false;
    }
    return this.filefound;
}
Note that the code, in an attempt to deal appropriately with config files being in ANSI or UTF-16LE format, reads the first two bytes of the config file. If the UTF16LE BOM is there, it reads the file as Unicode. Otherwise, it reads as ANSI.

A few other things are set so that the other methods will work, namely for exists, name and kind.

Config.prototype.name = function() {
        return this.name;
};

Config.prototype.exists = function() {
        return this.filefound;
};

Config.prototype.kind = function () {
        return this.kind;
};
Next come the main workhorses of the object: define, retrieve and save.
Config.prototype.retrieve = function(sName) {
        if (arguments.length > 1) {
                var sDefault = arguments[1];
        } else {
                sDefault = null;
        }

        var re = new RegExp("^" + sName + "=(.*?)$", "m");
        var arr = re.exec(this.text);
        if (arr === null) {
                return sDefault;
        } else {
                return RegExp.$1;
        }
};

Config.prototype.define = function(sName, sValue) {
        var sNew = sName + "=" + sValue;
        var re = new RegExp("^" + sName + "=(.*?)$", "m");
        var arr = re.exec(this.text);
        if (arr === null) {
                this.text = this.text + "\n" + sNew;
        } else {
                this.text = this.text.replace(re, sNew);
        }
};

Config.prototype.save = function() {
        var sFile = "";
        var handle = "";
        // allow for save to a different file
        if (arguments.length > 0) {
                sFile = arguments[0];
        } else {
                sFile = this.file;
        }
        if (this.kind === "ansi") {
                handle = this.oFSO.CreateTextFile(sFile, true, false);
        } else {
                handle = this.oFSO.CreateTextFile(sFile, true, true);                
        }
        handle.Write(this.text);
        handle.Close();
};
retrieve accepts two parameters: the key in the config store, and the default (in the event that the key is not found.) Rather than using a Scripting.Dictionary, the config data is stored as a string, and regular expressions are used to extract and update it.

define accepts two parameters: the key in the config file, and the value to be associated with it. If the key is not found in the stored keys and values, it is appended to it.

save accepts one optional parameter: the name of the file into which to write the stored string of keys and values. If no name is given, the filename given when Config() was first called is used.

The code may be used as follows:

var c = new Config("dog.cfg");
'the define() will either add or update the store to 
'    sound=bark
c.define("sound","bark");
'the save() will write the store to dog.cfg
c.save();

'Config reloads dog.cfg
var c = new Config("dog.cfg");
'if "sound" is a key in dog.cfg, the associated value will be displayed
'otherwise the default.
WScript.Echo(c.retrieve("sound","woof"));

'display the kind (ansi or unicode)
WScript.Echo(c.kind);
'... whether the file exists
WScript.Echo(c.exists());
'... and what its name is
WScript.Echo(c.name);
The result of running the above code (from within SciTE):
>cscript /nologo Config.js
 bark
 unicode
 -1
 dog.cfg
 >Exit code: 0
I have recently pushed this code through Google's Closure Compiler and the difference in code size is significant -- the result is about 50% smaller than the original. I expect it runs faster too, though I haven't bothered to check it out thoroughly.
function Config(a) {
  this.kind = "";
  this.oFSO = new ActiveXObject("Scripting.FileSystemObject");
  this.text = "";
  var b = [];
  this.file = a;
  if(this.oFSO.FileExists(a)) {
    var b = this.oFSO, d = [];
    if(b.FileExists(a)) {
      var c = b.OpenTextFile(a, 1, !1, 0), e = c.Read(2);
      c.Close();
      255 == e.charCodeAt(0) && 254 == e.charCodeAt(1) ? (c = b.OpenTextFile(a, 1, !1, -1), d[0] = "unicode") : (c = b.OpenTextFile(a, 1, !1, 0), d[0] = "ansi");
      d[1] = c.ReadAll();
      c.Close()
    }
    b = d;
    this.kind = b[0];
    this.text = b[1];
    this.name = a;
    this.filefound = !0
  }else {
    this.filefound = !1
  }
  return this.filefound
}
Config.prototype.name = function() {
  return this.name
};
Config.prototype.exists = function() {
  return this.filefound
};
Config.prototype.kind = function() {
  return this.kind
};
Config.prototype.retrieve = function(a) {
  var b = 1 < arguments.length ? arguments[1] : null;
  return null === RegExp("^" + a + "=(.*?)$", "m").exec(this.text) ? b : RegExp.$1
};
Config.prototype.define = function(a, b) {
  var d = a + "=" + b, c = RegExp("^" + a + "=(.*?)$", "m");
  this.text = null === c.exec(this.text) ? this.text + "\n" + d : this.text.replace(c, d)
};
Config.prototype.save = function() {
  var a = "", a = "", a = 0 < arguments.length ? arguments[0] : this.file, a = "ansi" === this.kind ? this.oFSO.CreateTextFile(a, !0, !1) : this.oFSO.CreateTextFile(a, !0, !0);
  a.Write(this.text);
  a.Close()
};

Enjoy!
© Copyright Bruce M. Axtens, 2012

Saturday, July 14, 2012

[JScript.NET] Finally weaned off Scripting.FileSystemObject

Finally removed all reference to Scripting.FileSystemObject. Now we're 99% there with respect to the dotNet-ification of the Access Compress And Repair script.

I say 99% percent because I'm still using ActiveXObject() to connect to the Access.Application object. Apparently, I should be using some other technique.

So here's the code of the application thus far.
import System.Environment;
import System.IO;

var sDatabaseName : String = argNamed("name");
var sDataFolder : String = argNamed("data");
var sTempFolder : String = argNamed("temp");
var sBackupFolder : String = argNamed("backup");

if (  sDatabaseName === "" ) {
 usage();
}

if (  sDataFolder === "" ) {
 usage();
}

if (  sTempFolder  === "" ) {
 usage();
}

if (  sBackupFolder === "" ) {
 usage();
}

var sDatabaseFile : String = System.IO.Path.Combine(sDataFolder, sDatabaseName);
var sBackupFile : String = System.IO.Path.Combine(sBackupFolder, sDatabaseName);
var sTempFile : String = System.IO.Path.Combine(sTempFolder, sDatabaseName);

try {
 File.Delete( sTempFile );
} catch ( e ) {
 //~ print( e.message + ': ' + sTempFile );
}

print("CompactRepair ",sDatabaseFile," to ",sTempFile);
var oACC = new ActiveXObject("Access.Application");
oACC.CompactRepair( sDatabaseFile, sTempFile, true );
var acQuitSaveNone = 2;
oACC.Quit(acQuitSaveNone);

// copy source to backup, overwriting
try {
 File.Delete( sBackupFile );
} catch( e ) {
 //~ print( e.message + ': ' + sBackupFile );
}
print("Moving ",sDatabaseFile," to ",sBackupFile);
File.Move( sDatabaseFile, sBackupFile );

// copy temp to source, overwriting
try {
 File.Delete( sDatabaseFile );
} catch( e ) {
 //~ print( e.message + ': ' + sDatabaseFile );
}
print("Moving ",sTempFile," to ",sDatabaseFile);
File.Move( sTempFile, sDatabaseFile );
System.Environment.Exit(4);

function argNamed( sname : String ) {
 var result : String = "";
 var aCmdline = System.Environment.GetCommandLineArgs();
 var i : short = 1;
 for ( ; i < aCmdline.length; i++ ) {
  if (aCmdline[i].toLowerCase().slice(0, sname.length + 2) == ( "/" + sname.toLowerCase() + ":" )) {
   var inner : String = aCmdline[i].slice( sname.length + 2 ) 
   result = (inner.charAt(0) == '"' ? inner.slice(1,inner.length-1) : inner);
  }
 }
 return result;
}

function usage() {
 var aArgs = System.Environment.GetCommandLineArgs(); 
 print( aArgs[0] );
 print( " /name: /data: /temp: /backup:");
 System.Environment.Exit(1);
}
Enjoy!

© Copyright Bruce M. Axtens, 2012

Friday, July 13, 2012

[JScript.NET] Parsing the commandline, working with MS Access

JScript.NET: A bit of a backwater these days. I'm told that Visual Studio 2005 was the last iteration of that IDE to have good support for it. Now one must jump through a couple of hoops to use VS2010 for JScript.NET debugging and otherwise one just has the commandline compiler, jsc. Interestingly, every iteration of the .NET framework has had a jsc compiler, so someone out there must still love it a little. In general, however, it's use is deprecated in favour of other .NET languages like IronPython, CS-Script and IronJS.

Still, I've been wanting to get something working in JScript.NET ...

A while back I wrote a tool in JScript to coordinate the Convert and Repair of Access databases. It works fine, albeit a little slowly being interpreted. It's short and, as it turned out, converted fairly easily once I began to get my head around the shift from Windows Scripting Host (WSH) to .NET.

In the original, I connected to the Scripting.FileSystemObject, using ActiveXObject(). I still do that in the JScript.NET version, simply because I haven't found out what the .NET equivalents are yet.
var oFSO = new ActiveXObject("Scripting.FileSystemObject");
Next came parsing the command line. This is very easy in JScript and VBScript thanks to the WScript object's Arguments object, which even parses the command line into a dictionary, so that /name:<thing> is accessible as WScript.Arguments.Named("name") (as below):
var sDatabaseName = WScript.Arguments.Named("name");
var sDataFolder = WScript.Arguments.Named("data");
var sTempFolder = WScript.Arguments.Named("temp");
var sBackupFolder = WScript.Arguments.Named("backup");
However, .NET's System.Environment.GetCommandLineArgs() is nowhere near as helpful, so I had to write something to parse the commandline that would at least come fairly close to the original in functionality.
function argNamed( sname : String ) {
 var result : String = "";
 var aCmdline = System.Environment.GetCommandLineArgs();
 var i : short = 1;
 for ( ; i < aCmdline.length; i++ ) {
  if (aCmdline[i].toLowerCase().slice(0, sname.length + 2) == ( "/" + sname.toLowerCase() + ":" )) {
   var inner : String = aCmdline[i].slice( sname.length + 2 ) 
   result = (inner.charAt(0) == '"' ? inner.slice(1,inner.length-1) : inner);
  }
 }
 return result;
}

var sDatabaseName : String = argNamed("name");
var sDataFolder : String = argNamed("data");
var sTempFolder : String = argNamed("temp");
var sBackupFolder : String = argNamed("backup");
Note that I'm using type annotations in the code to give the compiler a few hints about how to handle the intent of the code. Next, the handling the command line itself. Not a particularly good way of doing it in the original, but hey, it worked.
var bWorking = true;

if ( undefined === sDatabaseName ) {
        WScript.Echo( "Specify database name with /name:" );
        bWorking = false;
}

if ( undefined === sDataFolder ) {
        WScript.Echo( "Specify data path with /data:" );
        bWorking = false;
}

if ( undefined === sTempFolder ) {
        WScript.Echo( "Specify temp folder with /temp:" );
        bWorking = false;
}

if ( undefined === sBackupFolder ) {
        WScript.Echo( "Specify backup folder with /backup:" );
        bWorking = false;
}

if ( ! bWorking ) {
        WScript.Quit();
}
With WScript.Quit() unavailable, I had to find something that would work in JScript.NET. I tried return and break but the compiler complained. Finally, I stumbled over System.Environment.Exit();.
if (  sDatabaseName == "" ) {
        print( "Please specify database name on commandline with /name:" );
        System.Environment.Exit(1);
}

if ( sDataFolder == "" ) {
        print( "Please specify data path on commandline with /data:" );
        System.Environment.Exit(2);
}

if ( sTempFolder == "" ) {
        print( "Please specify temp folder on commandline with /temp:" );
        System.Environment.Exit(3);
}

if ( sBackupFolder == "" ) {
        print( "Please specify backup folder on commandline with /backup:" );
        System.Environment.Exit(4);
}
(Having a more sensible help text is next on the agenda.) Next I create the filenames that will be used later in the Access Compact and Repair process. These also appear in the JScript.NET version for the usual reasons (that is, I don't know yet how to do a BuildPath equivalent in .NET.)
var sDatabaseFile = oFSO.BuildPath(sDataFolder, sDatabaseName);
var sBackupFile = oFSO.BuildPath(sBackupFolder, sDatabaseName);
var sTempFile = oFSO.BuildPath(sTempFolder, sDatabaseName);
Next, assorted deletes, moves and the setup and execution of the CompactRepair Access.Application's CompactRepair() method.
try {
 oFSO.DeleteFile( sTempFile );
} catch ( e ) {
 //~ WScript.Echo( e.message + ': ' + sTempFile );
}

WScript.Echo("CompactRepair",sDatabaseFile,"to",sTempFile);
var oACC = new ActiveXObject('Access.Application');
oACC.CompactRepair( sDatabaseFile, sTempFile, true );
oACC.Quit();

try {
 oFSO.DeleteFile( sBackupFile );
} catch( e ) {
 //~ WScript.Echo( e.message + ': ' + sBackupFile );
}
WScript.Echo("Moving",sDatabaseFile,"to",sBackupFile);
oFSO.MoveFile( sDatabaseFile, sBackupFile );

// copy temp to source, overwriting
try {
 oFSO.DeleteFile( sDatabaseFile );
} catch( e ) {
 //~ WScript.Echo( e.message + ': ' + sDatabaseFile );
}
WScript.Echo("Moving",sTempFile,"to",sDatabaseFile);
oFSO.MoveFile( sTempFile, sDatabaseFile );

WScript.Quit();
The JScript.NET version is almost exactly the same:
try {
 oFSO.DeleteFile( sTempFile );
} catch ( e ) {
 //~ print( e.message + ': ' + sTempFile );
}

print("CompactRepair ",sDatabaseFile," to ",sTempFile);
var oACC = new ActiveXObject("Access.Application");
oACC.CompactRepair( sDatabaseFile, sTempFile, true );
oACC.Quit();

// copy source to backup, overwriting
try {
 oFSO.DeleteFile( sBackupFile );
} catch( e ) {
 //~ print( e.message + ': ' + sBackupFile );
}
print("Moving ",sDatabaseFile," to ",sBackupFile);
oFSO.MoveFile( sDatabaseFile, sBackupFile );

// copy temp to source, overwriting
try {
 oFSO.DeleteFile( sDatabaseFile );
} catch( e ) {
 //~ print( e.message + ': ' + sDatabaseFile );
}
print("Moving ",sTempFile," to ",sDatabaseFile);
oFSO.MoveFile( sTempFile, sDatabaseFile );
System.Environment.Exit(4);
Notice the change from WScript.Quit() to System.Environment.Exit(). Notice also the change from WScript.Echo() to print(). One weird thing about the latter is that the arguments in a WScript.Echo() are output separated by a space, but are not separated at all in a print(), thus the extra spaces in the quoted strings. Sample invocation:
acarNET.exe /name:database.mdb /data:c:\acartest\data /temp:c:\temp /backup:c:\acartest\backup 
Compiled, the JScript.NET version runs very quickly in comparison to the original JScript version. It almost makes me wish JScript.NET had a future. As it stands, I'm going to have to redo this in something else. Sigh. © Copyright Bruce M. Axtens, 2012

Thursday, July 05, 2012

[CGI] Mobile Browser Detection

A PearlTree with some links to information about detecting mobile phones via the UserAgent string. Also a ColdFusion example.

© Copyright Bruce M. Axtens, 2012

Friday, June 29, 2012

[CMD.EXE] Console weirdness

What do you make of a command line tool that stops working and seems to wait for keyboard input, but the code doesn't have a keyboard input instruction anywhere in it? I just had a batch file (that calls two PHP files and an EXE) sit idle for 2 hours and then start working as soon as I hit a key in its window.

After digging around a bit for a solution, where should I find it but at PerlMonks:

If your cmd.exe windows have "Quick edit" mode enabled, then if you (or somebody) left-clicks in the window (say to bring it to the top of the window stack), then it can select (highlight for copy & paste), one or more characters. A side effect of this is that output to the screen is suspended until the C&P is completed by right-clicking.


And if the program is regularly outputting to the screen, then undisplayed output will accumulate until the internal buffer fills, at which point the next write attempt will block until the select state is ended. Effectively suspending the program. The select state can be ended by right-clicking in the window so completing the copy & paste operation.


It can also be ended by typing any key, which abandons the C&P and allow the blocked write to succeed and the program to continue. Ie. Exactly the symptoms you've described.


The Quick-edit mode is very useful, but it can be confusing if you are not aware of it, or its affects.
        -- Re^3: Perl suddenly pauses for key input
Weird ... can it get any weirder than this?
© Copyright Bruce M. Axtens, 2012

[Batch] Running on Low

Running on LOW priority (or any other priority class) can be done from the command line with the START command, e.g.
START /LOW SomeBatch.cmd
Alternatively, one can embed the START command with the priority class modifier within the batch file itself, viz
@echo off
 setlocal enableextensions
 if %1@==@ (
  start /low %0 running
 goto :eof
 )
 echo This instance is running at LOW priority class
 pause
 exit
One could even make the priority class one of the parameters to the call, e.g.
@echo off
 setlocal enableextensions
 set PC=%1
 if %PC%@==@ (
 echo Please specify priority class as first argument.
 goto :eof
 ) 
 if %2@==@ (
 start /%PC% %0 %1 running
 goto :eof
 )
 echo Supposedly running %PC%
 pause
 exit
The trick is to check, in the first instance, for something in the %1 slot, and in the second, in the %2 slot, with the assumption that 'nothing' means %0 is running for the first time. Note also, that no checking of meaningful priority classes is done. That's left as an exercise for the reader.
© Copyright Bruce M. Axtens, 2012

Thursday, June 28, 2012

[Backup] Genie9 Free Timeline

Gratuitous ad:
The PC equivalent of Apple's TimeMachine: Genie Timeline

[PearlTrees] Programming Languages

I've created a PearlTrees account and have part populated it with some programming language research which you can get to directly here.

Thursday, June 21, 2012

[JScript] The name of the caller


A few postings back I demonstrated how to pull the name of the current function out of arguments.callee. Now to finding out the name of the caller:

function foo() {
 try {
  var myCaller = foo.caller.toString().split(/ /)[1].split(/\(/)[0];
 } catch( e ) {
  myCaller = e.message;
 }
 WScript.Echo(myCaller );

}

function bar() {
 foo();
}

bar();
foo();
The output from all that is
bar
'foo.caller' is null or not an object
So if foo is called from bar then myCaller contains "bar" otherwise an error is raised as the global context doesn't qualify as a "caller". Notice, too, that unlike getting the callee, the caller requires the name of the current function, not arguments.© Copyright Bruce M. Axtens, 2012

[Codeaholic] Well that was intelligent ... NOT!

As you may have noticed, the layout has changed. What you wouldn't have noticed is that my Google Analytics javascript got clobbered in the process. So even if my readership have noticed, I've no record of them having noticed. Smart move, Bruce, real smart move. Oh well, fixed now.
© Copyright Bruce M. Axtens, 2012

Wednesday, June 20, 2012

[Go] Out with JScript, in with Go?

Today I ran a large JScript against a month's worth of hourly REST traffic logs. The logs were parsed and manipulated into a series of tables in an Access database. It took about 12 hours to run. 12 hours ... and on a decently-specced laptop -- a Toshiba Satellite Pro L670.

So I'm looking around for something as flexible as JScript/javascript but which produces native binaries ... and Go seems like a good possibility. Any other suggestions?




I know, I did say 24 hours initially. Frightening the effect of 4 hours sleep on the middle-aged mind.

© Copyright Bruce M. Axtens, 2012

Monday, June 18, 2012

[JScript] Scatter redux

The previous version has a problem: when the recordset has zero records, it falls over. The updated version is below:

function Scatter( o ) {
 var result = {};
 var bEmpty = true;
 var vba = {};
 if ( o.RecordCount > 0 ) {
  bEmpty = false;
  vba = new VBArray( o.GetRows() );
 }
 var fldCount = o.Fields.Count;
 for (var i = 0; i < fldCount; i++ ) {
  result[o.Fields(i).Name] = [];
  if ( ! bEmpty ) {
   for ( var j = vba.lbound(2); j <= vba.ubound(2); j++ ) {
    result[o.Fields(i).Name].push( vba.getItem(i,j) );
   }
  }
 }
 return result;
}


© Copyright Bruce M. Axtens, 2012

Saturday, June 16, 2012

[JScript] Scatter (like the FoxPro function)

On a roll tonight!

Scatter and Gather are a pair of functions in FoxPro and related languages which would take a record and copy it into the memvar space, naming the memvar the same as the field name and storing the field's value. Gather did the opposite.

Now I've got that working in JScript. I hand it a recordset and it creates an object with the field names as property names and the values of each as a list attached to the property. This makes things like this possible:

var X = Scatter( oRS );
 for (var i = 0; i < X.AdvertiserParentFK.length; i++ ) {
  WScript.Echo( i, X.AdvertiserParentFK[i]);
 }
And so here's the function:
function Scatter( o ) {
  var result = {};
  var vba = new VBArray( o.GetRows() );
  var fldCount = o.Fields.Count;
  for (var i = 0; i < fldCount; i++ ) {
   result[o.Fields(i).Name] = [];
   for ( var j = vba.lbound(2); j <= vba.ubound(2); j++ ) {
    result[o.Fields(i).Name].push( vba.getItem(i,j) );
   }
  }
  return result;
 }

More to enjoy! 


© Copyright Bruce M. Axtens, 2012.

[JScript] ADODB GetRows to JScript

StackOverflow to the rescue again. Where would I be without them?

Anyway, I was looking up how to return a GetRows() call on an ADODB recordset to JScript and worked out the following from this posting about returning SafeArrays to JScript


function getRowsToMatrix( o ) {
  var vba = new VBArray( o );
  var dims = vba.dimensions();
  var result = [];
  for ( var i = vba.lbound(2); i <= vba.ubound(2); i++ ) {
   result[i] = [];
   for ( var j = vba.lbound(1); j <= vba.ubound(1); j++ ) {
    result[i].push(vba.getItem(j,i));
   }
  }
  return result;
 }

What I end up with is an array of arrays, which I can address as follows:


var X = getRowsToMatrix( oRS.GetRows() );
var recs = X.length;
var flds = X[0].length;
var lastFldLastRec = X[recs-1][flds-1]; // X is zero-based

Enjoy!



© Copyright Bruce M. Axtens, 2012

[JScript] The name of the current function

This is a plug for StackOverflow, where I spend a fair bit of time.

Today I went looking for Javascript code to give me the name of the currently executing function. I found answers
here and here. And now I have the following in my script:

var fname = arguments.callee.toString().split(/ /)[1].split(/\(/)[0];

Woe betide if callee is ever disabled!


© Copyright Bruce M. Axtens, 2012

Thursday, March 29, 2012

[iPad] Dragon Dictate app

I wrote to Nuance about Dragon Dictate for iPad. We've been having issues getting it to work behind the school's proxy. I had researched what others were saying about it, but thought I'd try and get it from the horse's mouth, so to speak. This is what they said:
Hello,

Please note that the app is not proxy-aware, so if you're using a proxy, you'll need to disable it in order to get traffic through to the app. We do not believe that future versions of the app will change this.

Regards,

Nuance Mobility Support
So if you have a school firewall, and you want to use Dragon Dictate for iPad, start tweaking the proxy, because there's no way Nuance are going to tweak their product.