C# run VBScript with MSScriptControl AddObject with string failed

3.7k views Asked by At

This is my C# program:

class Program
{
    static void Main(string[] args)
    {
        CallVbsFunction(1); //Work
        CallVbsFunction(1.2); //Work
        CallVbsFunction('a'); //Work
        CallVbsFunction("a"); //!!Exception see bellow
    }

    private static void CallVbsFunction(object p)
    {
        var sc = new MSScriptControl.ScriptControl();
        sc.Language = "VBScript";
        sc.AllowUI = true;

        try
        {         
            sc.AddCode(System.IO.File.ReadAllText("script.vbs"));
            sc.AddObject("myguid", p, false);
            var parameters = new object[] { "a" };
            sc.Run("test", ref parameters);
        }
        catch (Exception e)
        {
            Console.Out.WriteLine(e.ToString());
        }
    }
}

My VBScript file contents:

Function Test(a)
    MsgBox myguid
End Function

And Finally this is my exception when I use AddObject() with string object:

System.Runtime.InteropServices.COMException (0x800A0005): Invalid procedure call or argument at MSScriptControl.IScriptControl.Run(String ProcedureName, Object[]& Parameters) at Srcipting.Program.CallVbsFunction(Object p) in Program.cs

1

There are 1 answers

2
NineBerry On BEST ANSWER

You need to use a wrapper object that is ComVisible:

[ComVisible(true)]
public class StringWrapper
{
    private string wrappedString;

    public StringWrapper(string value)
    {
        wrappedString = value;
    }

    public override string ToString()
    {
        return wrappedString;
    }
}

CallVbsFunction(new StringWrapper("a"));

The problem is that the .net String object looks like a native vb string to the MSScriptControl on the first look but not on the second look.


You only need to use this wrapper when you register a string directly or register a function that returns a string. There is no problem when registering an object that has properties of type string. There is also no problem for the parameters you pass to Run() because these will be correctly marshaled to native vb strings by the .net runtime.


So the maybe best option is to not provide individual strings to your script but an object that encapsulates all the different values you want it to use.

Define this class

[ComVisible(true)]
public class HostOptions
{
    public string OptionA { get; set; }
    public string OptionB { get; set; }
}

Then construct the object and set all the properties and register it with the script control

var hostOptions = new HostOptions();
hostOptions.OptionA = "AAA";
hostOptions.OptionB = "BBB";

sc.AddObject("HostOptions", hostOptions, false);

You can then use it in your script like this:

Function Test(a)
    MsgBox HostOptions.OptionA
    MsgBox HostOptions.OptionB
End Function