microsoft / ClearScript

A library for adding scripting to .NET applications. Supports V8 (Windows, Linux, macOS) and JScript/VBScript (Windows).
https://microsoft.github.io/ClearScript/
MIT License
1.77k stars 148 forks source link

The VBS script passes hexadecimal 0 to the backend and prompts "Invalid parameter," while the JS script does not encounter this issue. #545

Closed Rison-Hub closed 1 year ago

Rison-Hub commented 1 year ago

JavaScrpt class

public bool DataCheck_CRC16(string Param, UInt16 Uint = 0x0, UInt16 UXor = 0x0, UInt16 Polynomial = 0xA001, bool @bool = false, StringFormat Format = StringFormat.HEX)
        {
            return false;
        }

VBS Code

Option Explicit
Sub Main
    call zp.DataCheck_CRC16("sdfkls",&HFFFF&,&H0&)
End Sub
Main

C# Code

 var VBscript = new JavaScrpt();
            using (var engine = new VBScriptEngine())
            {
                engine.AddHostObject("host", new HostFunctions());
                engine.AddHostObject("zp", VBscript);
                engine.AddHostType(typeof(object));
                var scriptCode = File.ReadAllText("D:/Script/TestVBS.vb");
                engine.Execute(scriptCode);

            }

Error Message: Microsoft.ClearScript.ScriptEngineException: "The most suitable overload method for 'WinHalcon.JavaScrpt.DataCheck_CRC16(string, ushort, ushort, ushort, bool, WinHalcon.JavaScrpt.StringFormat)' has some invalid arguments."

However, when the VBS code is modified to: call zp.DataCheck_CRC16("sdfkls", &HFFFF&) or call zp.DataCheck_CRC16("sdfkls", &HFFFF&, &HA001&, &HA001&) both can be executed successfully.

JS Code

function Main() {
    zpv.DataCheck_CRC16("sgdf45645",0x1A,0x0)
}
Main();

C# Code

JavaScrpt javaScrpt = new JavaScrpt();
            using (var engine = new V8ScriptEngine())
            {
                engine.AddHostObject("zpv", javaScrpt);
                engine.AddHostType("Console", typeof(Console));
                var scriptCode = File.ReadAllText(@"D:\\Script\\JSTest\\Test.js");
                engine.Execute(scriptCode);
            }

Successfully executed, no issues.

ClearScriptLib commented 1 year ago

Hi @Rison-Hub,

Unfortunately, the behavior you're observing is correct – or, at least, as good as it's likely to get.

Numeric argument binding is a major point of friction between script engines and .NET. We've tried to tune ClearScript to provide predictability and convenience for the most common cases, but there's no perfect solution.

In this case, VBScript passes &H0& as Integer (.NET Int16) despite the & suffix. Int16 is a signed type and cannot be implicitly converted to UInt16 (the declared type of the corresponding parameter). That explains the exception.

On the other hand, VBScript passes &HFFFF and &HA001 as Long (.NET Int32). Now, Int32 is also not implicitly convertible to an unsigned type, but C# supports a special case whereby an Int32 constant can be bound to a narrower or unsigned parameter if it can be converted without data loss.

To work around this issue, you could use CLng to force VBScript to pass your arguments as Int32:

call zp.DataCheck_CRC16("sdfkls",&HFFFF&,CLng(&H0&))

For zero and other small values, you could also use CByte, as .NET's Byte type is implicitly convertible to UInt16.

Another possibility is to use ClearScript's toUInt16:

call zp.DataCheck_CRC16("sdfkls",&HFFFF&,host.toUInt16(&H0&))

Or, if you'd rather not modify your script code, you could add DataCheck_CRC16 overloads or extension methods that are more friendly to VBScript:

public bool DataCheck_CRC16(string Param, int Uint = 0x0, int UXor = 0x0) {
    return DataCheck_CRC16(Param, Convert.ToUInt16(Uint), Convert.ToUInt16(UXor));
}

Good luck!

Rison-Hub commented 1 year ago

@ClearScriptLib Thank you for your guidance. I have another question I'd like to inquire about. I have three JavaScript files: Global.js, Test.js, and Test1.js. I want to make the global variables in Global.js accessible in both Test.js and Test1.js, but the global variables in Test.js and Test1.js should only be accessible within their respective files.

Global.js Code

var GlobalPara=100
function Main() {   
}
Main(); 

Test.js Code

var TestPara=10
function Main() {  
    TestPara=GlobalPara+TestPara;
    console.log(TestPara); //output:110
}
Main(); 

Test1.js Code

var Test1Para=20

function Main() {  
    Test1Para=GlobalPara+Test1Para;
    console.log(Test1Para); //output:120

    Test1Para=GlobalPara+TestPara;//The error parameter TestPara is undefined
    console.log(Test1Para);
}
Main(); 

C# Code


 JavaScrpt javaScrpt = new JavaScrpt();
            using (var engine = new V8ScriptEngine())
            {
                engine.AddHostObject("zpv", javaScrpt);
                engine.AddHostType("Console", typeof(Console));
                var GlobalCode = File.ReadAllText(@"D:\\Script\\JSTest\\Global.js");
                var TestCode= File.ReadAllText(@"D:\\Script\\JSTest\\Test.js");
                var Test1Code= File.ReadAllText(@"D:\\Script\\JSTest\\Test1.js");
                engine.Execute(GlobalCode );
                engine.Execute(TestCode);
                engine.Execute(Test1Code);
            }

I want to achieve that global variables in a specific JavaScript file have a global scope, such as 'GlobalPara' in 'Global.js', while global variables in other files should only have scope within their respective files.

ClearScriptLib commented 1 year ago

Hi @Rison-Hub,

I want to achieve that global variables in a specific JavaScript file have a global scope, such as 'GlobalPara' in 'Global.js', while global variables in other files should only have scope within their respective files.

ClearScript has no control over the behavior of the script language as implemented by the script engine. However, V8 sticks closely to the JavaScript standard, which has a pair of features that affect how scripts exchange data and interact with the global objects: Strict mode and JavaScript modules.

Cheers!