前言

前面已经介绍在C++,C#,VB6,Delphi6语言中嵌入MSScriptControl.ScriptControl并执行脚本代码的实例,现在介绍在C,Python,Perl,GO,Java,Javascript 语言中嵌入ControlMSScriptControl.ScriptControl并执行脚本代码的实例。
MSScriptControl.ScriptControl 是Windows环境中一个用于脚本执行的COM组件。下面这个表格汇总了它支持的主要脚本语言和基本方法:

支持语言 说明
VBScript 微软的Visual Basic脚本语言
JScript 微软的JavaScript实现
其他语言 理论上支持任何实现了ActiveX Scripting接口的引擎(如PERL、REXX),但非微软官方提供,需单独安装相应引擎

🔧 主要方法与属性

MSScriptControl.ScriptControl 提供了一些重要的方法和属性:

  • Language:设置或获取脚本引擎的解释语言,例如 “VBScript” 或 “JScript”。
  • AddCode:向脚本引擎中添加要执行的脚本代码。
  • Eval:对表达式进行求值并返回结果。
  • Run:运行一个指定的过程。
  • ExecuteStatement:解释并执行脚本语句。
  • AddObject:向脚本引擎中添加一个对象,以便在脚本中使用该对象提供的方法等。
  • AllowUI:控制是否允许脚本显示用户界面元素(如消息框)。
  • Timeout:设置或返回控件执行脚本的超时值。
  • Error:发生错误时,此属性返回一个错误对象。

🗂️ 组件使用步骤

使用该组件的一般步骤如下:

  1. 在程序中加入或创建ScriptControl
  2. 定义一个ScriptControl的实例。
  3. 调用AddCode等方法向脚本引擎中加入要执行的脚本。
  4. 调用RunEval等方法执行脚本。

💻 各语言调用示例

由于 MSScriptControl.ScriptControl 是一个COM组件,不同语言有各自的调用方式。这里提供C、Python、Perl、Go、Java和JavaScript的调用示例。

1. C语言 (使用Windows COM API)

C语言调用COM组件相对复杂,需要基本的COM知识。

#include <windows.h>
#include <oleauto.h>

int main() {
    HRESULT hr;
    IDispatch *pScriptControl = NULL;
    VARIANT varResult, varLanguage, varCode, varProcedure;
    BSTR bstrLanguage, bstrCode, bstrProcedure;
    DISPID dispid;
    DISPPARAMS dispparams = {NULL, NULL, 0, 0};
    EXCEPINFO excepInfo;

    // 初始化COM
    CoInitialize(NULL);

    // 创建MSScriptControl.ScriptControl对象
    hr = CoCreateInstance(&CLSID_ScriptControl, NULL, CLSCTX_ALL, &IID_IDispatch, (void**)&pScriptControl);
    if (SUCCEEDED(hr)) {
        // 设置语言属性 (VBScript)
        bstrLanguage = SysAllocString(L"VBScript");
        varLanguage.vt = VT_BSTR;
        varLanguage.bstrVal = bstrLanguage;
        hr = InvokePut(pScriptControl, L"Language", &varLanguage); // 需自行实现InvokePut

        // 准备要执行的VBScript代码
        bstrCode = SysAllocString(L"Function Greet(name)\r\n Greet = \"Hello, \" & name & \"!\"\r\nEnd Function");
        varCode.vt = VT_BSTR;
        varCode.bstrVal = bstrCode;

        // 调用AddCode方法添加代码
        hr = InvokeMethod(pScriptControl, L"AddCode", &varCode); // 需自行实现InvokeMethod

        // 准备运行的过程名和参数
        bstrProcedure = SysAllocString(L"Greet");
        varProcedure.vt = VT_BSTR;
        varProcedure.bstrVal = bstrProcedure;

        VARIANT varParam;
        varParam.vt = VT_BSTR;
        varParam.bstrVal = SysAllocString(L"World");

        dispparams.rgvarg = &varParam;
        dispparams.cArgs = 1;

        // 调用Run方法执行函数
        hr = pScriptControl->lpVtbl->Invoke(pScriptControl, GetIDForName(pScriptControl, L"Run"), &IID_NULL, LOCALE_USER_DEFAULT, DISPATCH_METHOD, &dispparams, &varResult, &excepInfo, NULL);
        if (SUCCEEDED(hr)) {
            // 输出结果
            printf("Result: %ls\n", varResult.bstrVal);
            VariantClear(&varResult);
        }

        // 清理资源
        SysFreeString(bstrLanguage);
        SysFreeString(bstrCode);
        SysFreeString(bstrProcedure);
        SysFreeString(varParam.bstrVal);
        pScriptControl->lpVtbl->Release(pScriptControl);
    }

    CoUninitialize();
    return 0;
}

说明:C中使用COM需要处理大量细节,如获取Dispatch ID、调用方法等。上述示例简化了InvokePutInvokeMethodGetIDForName等辅助函数的实现。实际编码中需查阅COM文档仔细处理。

2. Python

Python可通过pywin32库调用COM组件。

import win32com.client

def python_script_control_example():
    try:
        # 创建MSScriptControl.ScriptControl对象
        sc = win32com.client.Dispatch("MSScriptControl.ScriptControl")
        sc.Language = "VBScript"
        
        # 执行VBScript
        print("=== VBScript ===")
        vbs_code = """
        Function AddNumbers(x, y)
            AddNumbers = x + y
        End Function
        
        Function Greet(name)
            Greet = "Hello, " & name
        End Function
        """
        sc.AddCode(vbs_code)
        result_add = sc.Run("AddNumbers", 5, 3)
        result_greet = sc.Run("Greet", "Python")
        print(f"5 + 3 = {result_add}")
        print(f"Greeting: {result_greet}")
        
        # 执行JScript
        print("\n=== JScript ===")
        sc.Language = "JScript"
        js_code = """
        function multiply(a, b) {
            return a * b;
        }
        function getMessage(platform) {
            return "Running in " + platform;
        }
        """
        sc.AddCode(js_code)
        result_multiply = sc.Run("multiply", 6, 7)
        result_message = sc.Run("getMessage", "Python")
        print(f"6 * 7 = {result_multiply}")
        print(f"Message: {result_message}")
        
        # 使用Eval计算表达式
        print("\n=== Eval Example ===")
        sc.Language = "VBScript"
        expression_result = sc.Eval("3 * 4 + 5")
        print(f"Eval result: {expression_result}")
        
    except Exception as e:
        print(f"An error occurred: {e}")

if __name__ == "__main__":
    python_script_control_example()
3. Perl

Perl可以通过Win32::OLE模块调用COM组件。

use strict;
use warnings;
use Win32::OLE;

sub perl_script_control_example {
    eval {
        # 创建MSScriptControl.ScriptControl对象
        my $sc = Win32::OLE->new('MSScriptControl.ScriptControl');
        $sc->{Language} = 'VBScript';
        
        # 执行VBScript
        print "=== VBScript ===\n";
        my $vbs_code = '
        Function ConcatenateStrings(str1, str2)
            ConcatenateStrings = str1 & " " & str2
        End Function
        
        Sub DisplayMessage(msg)
            MsgBox msg
        End Sub
        ';
        $sc->AddCode($vbs_code);
        my $result_concat = $sc->Run('ConcatenateStrings', 'Hello', 'Perl');
        print "Concatenated string: $result_concat\n";
        
        # 执行JScript
        print "\n=== JScript ===\n";
        $sc->{Language} = 'JScript';
        my $js_code = '
        function calculateHypotenuse(a, b) {
            return Math.sqrt(a*a + b*b);
        }
        
        function getVersion() {
            return "JScript version 1.0";
        }
        ';
        $sc->AddCode($js_code);
        my $result_hypo = $sc->Run('calculateHypotenuse', 3, 4);
        my $result_version = $sc->Run('getVersion');
        print "Hypotenuse: $result_hypo\n";
        print "Version: $result_version\n";
        
        # 使用Eval计算表达式
        print "\n=== Eval Example ===\n";
        $sc->{Language} = 'VBScript';
        my $expression_result = $sc->Eval('Len("Hello World")');
        print "Length of 'Hello World': $expression_result\n";
    };
    
    if ($@) {
        print "An error occurred: $@\n";
    }
}

perl_script_control_example();
4. Go

Go语言可以通过go-ole库调用COM组件。

package main

import (
    "fmt"
    "log"

    "github.com/go-ole/go-ole"
    "github.com/go-ole/go-ole/oleutil"
)

func go_script_control_example() {
    // 初始化COM
    ole.CoInitialize(0)
    defer ole.CoUninitialize()

    // 创建MSScriptControl.ScriptControl对象
    unknown, err := oleutil.CreateObject("MSScriptControl.ScriptControl")
    if err != nil {
        log.Fatal("CreateObject failed:", err)
    }
    defer unknown.Release()

    sc, err := unknown.QueryInterface(ole.IID_IDispatch)
    if err != nil {
        log.Fatal("QueryInterface failed:", err)
    }
    defer sc.Release()

    // 设置语言属性
    _, err = oleutil.PutProperty(sc, "Language", "VBScript")
    if err != nil {
        log.Fatal("PutProperty Language failed:", err)
    }

    // 执行VBScript
    fmt.Println("=== VBScript ===")
    vbsCode := `
    Function ReverseString(s)
        ReverseString = StrReverse(s)
    End Function
    
    Function CalculateArea(radius)
        CalculateArea = 3.14159 * radius * radius
    End Function
    `
    _, err = oleutil.CallMethod(sc, "AddCode", vbsCode)
    if err != nil {
        log.Fatal("AddCode VBScript failed:", err)
    }

    resultReverse, err := oleutil.CallMethod(sc, "Run", "ReverseString", "Hello")
    if err != nil {
        log.Fatal("Run ReverseString failed:", err)
    }
    fmt.Printf("Reverse of 'Hello': %v\n", resultReverse.ToString())

    resultArea, err := oleutil.CallMethod(sc, "Run", "CalculateArea", 5)
    if err != nil {
        log.Fatal("Run CalculateArea failed:", err)
    }
    fmt.Printf("Area of circle with radius 5: %v\n", resultArea.ToString())

    // 执行JScript
    fmt.Println("\n=== JScript ===")
    _, err = oleutil.PutProperty(sc, "Language", "JScript")
    if err != nil {
        log.Fatal("PutProperty Language JScript failed:", err)
    }

    jsCode := `
    function findMax(arr) {
        return Math.max.apply(null, arr);
    }
    
    function repeatString(str, count) {
        return Array(count + 1).join(str);
    }
    `
    _, err = oleutil.CallMethod(sc, "AddCode", jsCode)
    if err != nil {
        log.Fatal("AddCode JScript failed:", err)
    }

    // 注意:传递数组参数可能需要特殊处理,这里简化了
    resultMax, err := oleutil.CallMethod(sc, "Run", "findMax", []int32{10, 5, 25, 3, 15})
    if err != nil {
        log.Fatal("Run findMax failed:", err)
    }
    fmt.Printf("Max value: %v\n", resultMax.ToString())

    resultRepeat, err := oleutil.CallMethod(sc, "Run", "repeatString", "Go!", 3)
    if err != nil {
        log.Fatal("Run repeatString failed:", err)
    }
    fmt.Printf("Repeated string: %v\n", resultRepeat.ToString())
}

func main() {
    go_script_control_example()
}
5. Java

Java可以通过Jacob(Java COM Bridge)或类似的COM桥接库调用COM组件。

import com.jacob.activeX.ActiveXComponent;
import com.jacob.com.Dispatch;
import com.jacob.com.Variant;

public class JavaScriptControlExample {
    public static void main(String[] args) {
        // 创建MSScriptControl.ScriptControl对象
        ActiveXComponent sc = new ActiveXComponent("MSScriptControl.ScriptControl");
        
        try {
            // 设置语言属性
            Dispatch.put(sc, "Language", "VBScript");
            
            // 执行VBScript
            System.out.println("=== VBScript ===");
            String vbsCode = 
                "Function CountVowels(str)\n" +
                "    Dim count, i, char\n" +
                "    count = 0\n" +
                "    For i = 1 To Len(str)\n" +
                "        char = LCase(Mid(str, i, 1))\n" +
                "        If InStr(\"aeiou\", char) > 0 Then\n" +
                "            count = count + 1\n" +
                "        End If\n" +
                "    Next\n" +
                "    CountVowels = count\n" +
                "End Function\n" +
                "\n" +
                "Function GetTypeName(value)\n" +
                "    GetTypeName = TypeName(value)\n" +
                "End Function";
            
            Dispatch.call(sc, "AddCode", vbsCode);
            
            Variant resultVowels = Dispatch.call(sc, "Run", "CountVowels", "Hello World");
            Variant resultType = Dispatch.call(sc, "Run", "GetTypeName", 42);
            
            System.out.println("Vowels in 'Hello World': " + resultVowels);
            System.out.println("Type of 42 in VBScript: " + resultType);
            
            // 执行JScript
            System.out.println("\n=== JScript ===");
            Dispatch.put(sc, "Language", "JScript");
            
            String jsCode = 
                "function formatCurrency(amount, symbol) {\n" +
                "    return symbol + amount.toFixed(2);\n" +
                "}\n" +
                "\n" +
                "function createArray() {\n" +
                "    return ['Java', 'COM', 'Scripting'];\n" +
                "}";
            
            Dispatch.call(sc, "AddCode", jsCode);
            
            Variant resultCurrency = Dispatch.call(sc, "Run", "formatCurrency", 19.99, "$");
            Variant resultArray = Dispatch.call(sc, "Run", "createArray");
            
            System.out.println("Formatted currency: " + resultCurrency);
            System.out.println("Created array: " + resultArray);
            
            // 使用Eval计算表达式
            System.out.println("\n=== Eval Example ===");
            Dispatch.put(sc, "Language", "VBScript");
            Variant evalResult = Dispatch.call(sc, "Eval", "2^8 + 3*5");
            System.out.println("Eval result: " + evalResult);
            
        } finally {
            sc.safeRelease();
        }
    }
}
6. JavaScript (Node.js)

Node.js可以通过node-win32-ole或类似模块调用COM组件。

const ole = require('win32-ole');

function javascriptScriptControlExample() {
    try {
        // 创建MSScriptControl.ScriptControl对象
        const sc = new ole.ActiveXObject('MSScriptControl.ScriptControl');
        sc.Language = 'VBScript';
        
        // 执行VBScript
        console.log('=== VBScript ===');
        const vbsCode = `
        Function IsPalindrome(str)
            Dim cleanStr, reversedStr
            cleanStr = LCase(Replace(str, " ", ""))
            reversedStr = StrReverse(cleanStr)
            IsPalindrome = (cleanStr = reversedStr)
        End Function
        
        Function GetSystemInfo
            GetSystemInfo = "Running on Windows with VBScript"
        End Function
        `;
        sc.AddCode(vbsCode);
        
        const resultPalindrome = sc.Run('IsPalindrome', 'A man a plan a canal Panama');
        const resultSystemInfo = sc.Run('GetSystemInfo');
        
        console.log(`Is palindrome: ${resultPalindrome}`);
        console.log(`System info: ${resultSystemInfo}`);
        
        // 执行JScript
        console.log('\n=== JScript ===');
        sc.Language = 'JScript';
        
        const jsCode = `
        function calculateBMI(weight, height) {
            return (weight / (height * height)).toFixed(2);
        }
        
        function getRandomColor() {
            var colors = ['red', 'green', 'blue', 'yellow', 'purple'];
            return colors[Math.floor(Math.random() * colors.length)];
        }
        `;
        sc.AddCode(jsCode);
        
        const resultBMI = sc.Run('calculateBMI', 70, 1.75);
        const resultColor = sc.Run('getRandomColor');
        
        console.log(`BMI: ${resultBMI}`);
        console.log(`Random color: ${resultColor}`);
        
        // 使用Eval计算表达式
        console.log('\n=== Eval Example ===');
        sc.Language = 'VBScript';
        const evalResult = sc.Eval('Date()');
        console.log(`Current date: ${evalResult}`);
        
    } catch (error) {
        console.error(`An error occurred: ${error.message}`);
    }
}

javascriptScriptControlExample();

⚠️ 重要说明与局限

  1. 环境依赖MSScriptControl.ScriptControl 是Windows平台的COM组件,主要适用于Windows环境。
  2. 语言支持:其核心支持VBScript和JScript。虽然通过实现ActiveX Scripting接口可支持其他语言(如PerlScript),但这通常需要额外安装相应的脚本引擎,并非微软官方标配。
  3. 现代替代:对于新的项目,可以考虑使用更现代的脚本引擎,例如:
    • V8 JavaScript引擎 (用于JavaScript)
    • Lua 等轻量级脚本语言
    • 各语言内置的表达式求值库
  4. 安全考虑:在生产环境中执行动态脚本时,务必注意代码安全,防止注入攻击。
Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐