プログラミング

ScriptControlの利用

2008年7月4日

先日とりあげた、wscript.exe用のマニフェストを書く方法に加えて、ScriptControlを利用する方法も試してみた。とりあえず、VB(VB5)を用いて簡単に作成したプログラムのソースコードは次のとおり。

Option Explicit

Sub Main()
    Dim cmdLine As String
    cmdLine = LCase(Command)
    If Len(cmdLine) = 0 Then End
    If Right(cmdLine, 1) = """" Then cmdLine = Left(cmdLine, Len(cmdLine) - 1)
    
    Dim isVBScript As Boolean, isEncoded As Boolean
    isEncoded = (Right(cmdLine, 1) = "e")
    isVBScript = (Left(Right(cmdLine, 3), 2) = "vb")
    
    Dim Lang As String
    If isVBScript Then
        Lang = "vbscript"
    Else
        Lang = "jscript"
    End If
    If isEncoded Then Lang = Lang & ".encode"
    
    Dim Code As String, buff As String
    Code = ""
    Open Command For Input As #1
    Do Until EOF(1)
        Line Input #1, buff
        Code = Code + buff + vbCrLf
    Loop
    
    Dim sc
    Set sc = CreateObject("ScriptControl")
    sc.language = Lang
    sc.executestatement Code
End Sub

oyagame.exeという名でexeファイルを作成し、oyagame.exe.manifestを先の記事と同様に作成すると、このexeを解してJScriptのコードが動くことを確認。ただし、JScriptをwscript.exeで実行したときに使えるWScriptオブジェクトは使えないらしい。これがVBScript/JScriptでプログラムを書くときにどれくらい支障をきたすかは、良く分からない。ちょっと考えた感じだと、WScript.Quit()が使えないのは痛いかも。

ところで、JScript.NETも試してみたけれど、今のままではoyagameでは使えない(使いにくい)ことが分かった。その理由は、eval(SDL.ConstJS); で定義する定数が、JScript.NETでは使えないこと。もし需要があるのなら、JScript.NETで使えるようにすること、考えてみたい。

コメント

Kat (2008年7月6日 16:40:12)

上記コードはVBを用いて書かれているけれど、C++を用いたスタンドアローンなEXEの製作もほぼめどがついた。

あとは、WScriptオブジェクトをいかにサポートするかだ。たぶん、次のメソッド・プロパティのサポートでよい。

Arguments
Path
Name
FullName
ScriptName
ScriptFullName
CreateObject
Echo
Quit
Sleep

Kat (2008年7月8日 12:57:07)

MicrosoftのScriptControlのダウンロードサイトによると、Win2000以降はデフォルトでScriptControlが含まれているようだ。なら、manifestを利用したoyagameの実行にScriptControlを使うのは問題ないことになる(XP以降が必要なので)。

http://www.microsoft.com/downloads/details.aspx?FamilyID=151ef63d-23e9-4367-bb25-37310c6222fe&displaylang=ja

Kat (2008年7月9日 18:06:36)

WScript.Quit()の実装は、この辺が参考になりそう。

http://www.microsoft.com/japan/technet/scriptcenter/resources/qanda/sept04/hey0927.mspx

スクリプト中で自身のプロセスIDを取得するのはトリッキーのよう(新たなプロセスを開始して、その親プロセス(自身)のIDをWMIで取得)なので、自身のプロセスIDはEXE中で取得しておいて、スクリプトを動的に書き出せばよいだろう。

Kat (2008年7月9日 18:54:29)

WScript.Sleep()の実装は、この辺。

http://sakura.qp.land.to/?Macro%2F%C5%EA%B9%C6%2F188

t=timer
mSec=100
strComputer = "."
Set objWMIService = GetObject("winmgmts:\\" & strComputer & "\root\cimv2")
set temp=objWMIService.ExecNotificationQuery _
  ("select * from __instancecreationevent within 1" _
      & " where targetinstance isa 'Win32_Process'" _
      & " and targetinstance.ProcessID=0")
On Error Resume Next
temp.NextEvent mSec
msgbox timer-t & vbcrlf & err.description

Kat (2008年7月9日 19:53:59)

ScriptControlを使えば、manifestを用いなくてもCOMオブジェクトが使えそうな方法を思いついたので、忘れないうちにメモ。Win2000/NTでもOKのはず。

1)EXEを起動
2)EXEで、oyagame.dllをロード
3)EXEでは、ロードしたoyagameからオブジェクトを取得するための簡単なIDispatchクラスを記述しておく
4)ScriptControlオブジェクトを作成
5)ScriptControlに、関数を一つ登録(AddCode)。この関数は、上記IDIspatchクラスインスタンスをグローバル変数に割り当てるためのもの。
6)ScriptContorlに、関数を一つ登録。この関数は、上記IDIspatchクラスインスタンス(グローバル変数に割り当てられている)から、oyagameオブジェクトを取得するためのもの。
7)最後に、コードを、ExecuteStatementする。6)で登録した関数から、oyagameオブジェクトを取得可能。

Kat (2008年7月12日 00:12:19)

上記コメントの5)は、以下の例のように実装可。

set sc=WScript.CreateObject("ScriptControl")
sc.language="VBScript"
sc.addcode "dim sc2"
sc.addcode "sub test(obj) set sc2=obj end sub"
sc.run "test",sc
sc.executestatement "msgbox sc2.language"

ScriptControlオブジェクト中のグローバル変数『sc2』に、『sc』というオブジェクトへの参照を渡している。同じことは、C++で書いたEXE中でも可能のはず。

コメント送信