Closed Rison-Hub closed 1 year ago
Hello @Rison-Hub,
When a script invokes managed code, ClearScript uses the C# compiler to select the correct method – or construct a suitable generic method – based on the call signature. Unlike Visual Basic and VBScript, C# forces the caller to specify the passing mechanism for each argument.
Because script languages usually give you no way to do that, ClearScript provides its own solution. To pass something by reference to a managed method, you must use a host variable. Here's an example based on your code above:
using (var engine = new VBScriptEngine()) {
engine.AddHostObject("zp", new VBClass());
engine.AddHostObject("host", new HostFunctions());
engine.Execute(@"
Option Explicit
Sub Main
dim reftest
reftest = host.newVar(clng(0))
zp.testRef 5, reftest.ref
zp.print reftest
End Sub
Main
");
}
Good luck!
Executing the code snippet you provided will result in an exception: "The most suitable overload method for 'VBLibrary.VBClass.testRef(int, ref object)' has some invalid arguments."
Executing the code snippet you provided will result in an exception: "The most suitable overload method for 'VBLibrary.VBClass.testRef(int, ref object)' has some invalid arguments."
Oops, sorry. We had modified testRef
slightly. The following should work with your original definition:
using (var engine = new VBScriptEngine()) {
engine.AddHostObject("zp", new VBClass());
engine.AddHostObject("host", new HostFunctions());
engine.AddHostType(typeof(object));
engine.Execute(@"
Option Explicit
Sub Main
dim reftest
reftest = host.newVar(object, clng(0))
zp.testRef 5, reftest.ref
zp.print reftest
End Sub
Main
");
}
Thank you for your suggestion, but there is another question. My second parameter in the VB.NET method is an optional parameter. If I want to pass only one argument, how can I achieve a ByRef parameter that is not specified and request the method? I made a small adjustment to the VB.NET method
Public Sub testRef(ByVal OutStr As Integer, Optional ByRef iShow As Integer = 0) iShow = iShow + OutStr Console.WriteLine("testRef: " & iShow & "!") End Sub
C# code
using (var engine = new VBScriptEngine()) { engine.AddHostObject("zp", new VBClass()); engine.AddHostObject("host", new HostFunctions()); engine.Execute(@" Option Explicit Sub Main dim reftest zp.testRef 5 End Sub Main "); }
Error message: Microsoft.ClearScript.ScriptEngineException: "The method 'testRef' does not have an overload that takes '1' parameter."
Hi again,
Error message: Microsoft.ClearScript.ScriptEngineException: "The method 'testRef' does not have an overload that takes '1' parameter."
Yes, unfortunately, C# doesn't support optional by-reference parameters, and, as we discussed above, ClearScript uses C#'s invocation semantics.
One workaround is to add an overload without the optional parameter:
Public Sub testRef(ByVal OutStr As Integer)
testRef(OutStr, 0)
End Sub
Public Sub testRef(ByVal OutStr As Integer, Optional ByRef iShow As Integer = 0)
iShow = iShow + OutStr
Console.WriteLine("testRef: " & iShow & "!")
End Sub
Cheers!
Thank you very much for your guidance
Please reopen this issue if you have additional questions about this topic. Thank you!
Please reopen this issue if you have additional questions about this topic. Thank you!
@ClearScriptLib I have encountered another issue. I want to pass a two-dimensional array and process it in VB.NET. How can I call and output the processed result values in VBS?
VBS code
Option Explicit
dim timeGap(1,1)
timeGap(0,0)=37
timeGap(0,1)=1
timeGap(1,0)=39
timeGap(1,1)=2
Sub Main
dim ByReftest
ByReftest = host.newVar(object,timeGap)
zp.testArr 5, ByReftest.ref
zp.print ByReftest(0,0)
End Sub
Main
VB.NET Code
Public Sub Print(ByVal strInfo As Object)
Console.WriteLine("strInfo : " & strInfo & "!")
End Sub
Public Sub testArr(ByVal Ipar As Integer, ByRef arr As Object)
arr(0, 0)=arr(0, 0)+Ipar
Console.WriteLine("testRef: " & arr(0, 0) & "!")
End Sub
C# Code
using (var engine = new VBScriptEngine())
{
engine.AddHostObject("host", new HostFunctions());
engine.AddHostObject("zp", VBscript);
engine.AddHostType(typeof(object));
var scriptCode = File.ReadAllText("D:/Script/abc.vbs");
engine.Execute(scriptCode);
}
Executing zp.print ByReftest(0,0) throws an error: Microsoft.ClearScript.ScriptEngineException: "The object does not support the requested invocation operation."
Hi @Rison-Hub,
Executing zp.print ByReftest(0,0) throws an error: Microsoft.ClearScript.ScriptEngineException: "The object does not support the requested invocation operation."
Hmm. ByRefTest
is a host variable holding a .NET array (converted from a VBScript array). So, it's essentially a .NET array. Sadly, ClearScript doesn't support multidimensional indexing for .NET arrays via native VBScript syntax. It's a scenario that hasn't come up before, probably because most ClearScript users work with JavaScript, which has no native support for multidimensional arrays. We'll take a closer look at this for a future release. Thanks!
In the meantime, in this particular case, there's no need to use a host variable at all. Here's a working sample:
Public Sub Print(strInfo As Object)
Console.WriteLine("strInfo : " & strInfo & "!")
End Sub
Public Sub testArr(Ipar As Integer, arr As Object)
arr(0, 0) = arr(0, 0) + Ipar
Console.WriteLine("testRef: " & arr(0, 0) & "!")
End Sub
C# code:
engine.AddHostObject("zp", new VBClass());
engine.Execute(@"
Option Explicit
dim timeGap(1,1)
timeGap(0,0)=37
timeGap(0,1)=1
timeGap(1,0)=39
timeGap(1,1)=2
Sub Main
zp.testArr 5, timeGap
zp.print timeGap(0,0)
End Sub
Main
");
Please let us know if this solution works for you.
Thanks again!
Hi @Rison-Hub,
Executing zp.print ByReftest(0,0) throws an error: Microsoft.ClearScript.ScriptEngineException: "The object does not support the requested invocation operation."
Hmm.
ByRefTest
is a host variable holding a .NET array (converted from a VBScript array). So, it's essentially a .NET array. Sadly, ClearScript doesn't support multidimensional indexing for .NET arrays via native VBScript syntax. It's a scenario that hasn't come up before, probably because most ClearScript users work with JavaScript, which has no native support for multidimensional arrays. We'll take a closer look at this for a future release. Thanks!In the meantime, in this particular case, there's no need to use a host variable at all. Here's a working sample:
Public Sub Print(strInfo As Object) Console.WriteLine("strInfo : " & strInfo & "!") End Sub Public Sub testArr(Ipar As Integer, arr As Object) arr(0, 0) = arr(0, 0) + Ipar Console.WriteLine("testRef: " & arr(0, 0) & "!") End Sub
C# code:
engine.AddHostObject("zp", new VBClass()); engine.Execute(@" Option Explicit dim timeGap(1,1) timeGap(0,0)=37 timeGap(0,1)=1 timeGap(1,0)=39 timeGap(1,1)=2 Sub Main zp.testArr 5, timeGap zp.print timeGap(0,0) End Sub Main ");
Please let us know if this solution works for you.
Thanks again!
Thank you again for your guidance.
@ClearScriptLib I made a slight modification to the format and noticed another issue VB Code
Public Sub Print(strInfo As Object)
Console.WriteLine("strInfo : " & strInfo & "!")
End Sub
Public Sub testByVal(ByVal OutStr As Integer, ByVal iShow As Object)
iShow = iShow + 5
Console.WriteLine("testByVal: " & iShow & "!")
End Sub
Public Sub testArr(Ipar As Integer, arr As Object)
arr(0, 0) = arr(0, 0) + Ipar
Console.WriteLine("testRef: " & arr(0, 0) & "!")
End Sub
C# Code
engine.AddHostObject("host", new HostFunctions());
engine.AddHostObject("zp", VBscript);
engine.AddHostType(typeof(object));
engine.Execute(@"
Option Explicit
dim ByValtest
ByValtest=3
Sub Main
zp.testByVal 5, ByValtest
zp.print ByValtest
End Sub
Main
");
output : 8 3 but I would like it output 8 8
Hi @Rison-Hub,
Because iShow
is a by-value parameter, testByVal
can't change the external variable that provides iShow
's argument. For that, you must use a by-reference parameter:
Public Sub testByRef(ByVal OutStr As Integer, ByRef iShow As Object)
iShow = iShow + 5
Console.WriteLine("testByRef: " & iShow & "!")
End Sub
And then:
engine.Execute(@"
Option Explicit
dim ByReftest
ByReftest = host.newVar(object, 3)
Sub Main
zp.testByRef 5, ByReftest.ref
zp.print ByReftest
End Sub
Main
");
Output:
testByRef: 8!
strInfo : 8!
Note that the situation above with the array is different because .NET arrays are reference types, meaning that you can modify the contents of a .NET array without modifying the variable that holds a reference to it.
In this case, even though the Object
type forces the integer argument to be boxed and passed by reference, boxed values are immutable, so iShow = iShow + 5
creates a new boxed value instead of modifying the one to which ByValtest
refers.
Good luck!
engine.AddHostObject("host", new HostFunctions());
@ClearScriptLib I need to integrate with HALCON, where HObject is a reference type. VB Code
Public Function EmptyHObj() As Object Dim image As HObject = Nothing HOperatorSet.GenEmptyObj(image) Return image End Function
'Read Image Public Sub New_ReadImage(ByVal Template_path As String, ByVal image As Object) Dim ho_Image As HObject = Nothing Dim hv_Template_path As HTuple = Nothing hv_Template_path = New HTuple(Template_path) HOperatorSet.ReadImage(image , hv_Template_path) End Sub
'Display Image Public Sub display_open(ByVal Image As Object) Dim HWindow As HWindowControl = frm_open.HWindowControl1 Dim Window As HTuple = HWindow.HalconWindow HWindow.HalconWindow.ClearWindow() Dim hv_Width As HTuple = Nothing Dim hv_Height As HTuple = Nothing If IsNothing(ho_Image) = False AndAlso ho_Image.IsInitialized Then HOperatorSet.GetImageSize(ho_Image, hv_Width, hv_Height) HOperatorSet.SetPart(Window, 0, 0, hv_Height, hv_Width) HOperatorSet.DispObj(ho_Image, Window) End If hv_Width = Nothing hv_Height = Nothing End Sub
C# Code
engine.AddHostObject("host", new HostFunctions()); engine.AddHostObject("zp", VBscript); engine.AddHostType(typeof(object)); engine.Execute(@" Option Explicit Sub Main dim Image_Path,Image Image_Path="D:\AUTO Test System\Write\Image_Correction\MB2701-A1\Image\0.bmp" Image=zp.EmptyHObj() call zp.New_ReadImage(Image_Path,Image) call zp.display_open(Image) End Sub Main ");
The value passed to display_open is the return value of EmptyHObj, not the value read from New_ReadImage.
However, if you use the following code, the value passed is correct.
VB Code
Public Function EmptyHObj() As Object Dim image As HObject = Nothing HOperatorSet.GenEmptyObj(image) Return image End Function
'Read Image Public Sub New_ReadImage(ByVal Template_path As String, ByRef image As Object) Dim ho_Image As HObject = Nothing Dim hv_Template_path As HTuple = Nothing hv_Template_path = New HTuple(Template_path) HOperatorSet.ReadImage(image , hv_Template_path) End Sub
'Display Image Public Sub display_open(ByVal Image As Object) Dim HWindow As HWindowControl = frm_open.HWindowControl1 Dim Window As HTuple = HWindow.HalconWindow HWindow.HalconWindow.ClearWindow() Dim hv_Width As HTuple = Nothing Dim hv_Height As HTuple = Nothing If IsNothing(ho_Image) = False AndAlso ho_Image.IsInitialized Then HOperatorSet.GetImageSize(ho_Image, hv_Width, hv_Height) HOperatorSet.SetPart(Window, 0, 0, hv_Height, hv_Width) HOperatorSet.DispObj(ho_Image, Window) End If hv_Width = Nothing hv_Height = Nothing End Sub
C# Code
engine.AddHostObject("host", new HostFunctions()); engine.AddHostObject("zp", VBscript); engine.AddHostType(typeof(object)); engine.Execute(@" Option Explicit Sub Main dim Image_Path,Image Image_Path="D:\AUTO Test System\Write\Image_Correction\MB2701-A1\Image\0.bmp" Image=host.newVar(object, zp.EmptyHObj()) call zp.New_ReadImage(Image_Path,Image.ref) call zp.display_open(Image) End Sub Main ");
Hi @ClearScriptLib,
The behavior you're seeing is correct.
According to the HALCON documentation, the ReadImage
method looks like this (in C#):
static void ReadImage(out HObject image, HTuple fileName)
Note that image
is an out
– or output – parameter. At the .NET level, that's just a by-reference parameter. However, in C#, out
– unlike ref
– additionally specifies that the method ignores any passed-in value and replaces it on successful return.
In this case, ReadImage
creates a new object and stores a reference to it in image
. If some other object is passed in, the method ignores it. It does not modify it or operate on it in any way.
Therefore, you don't have to call GenEmptyObj
. However, because Main
needs the new object, New_ReadImage
must either store it in a by-reference parameter or simply return it.
Cheers!
Hi @ClearScriptLib,
The behavior you're seeing is correct.
According to the HALCON documentation, the
ReadImage
method looks like this (in C#):static void ReadImage(out HObject image, HTuple fileName)
Note that
image
is anout
– or output – parameter. At the .NET level, that's just a by-reference parameter. However, in C#,out
– unlikeref
– additionally specifies that the method ignores any passed-in value and replaces it on successful return.In this case,
ReadImage
creates a new object and stores a reference to it inimage
. If some other object is passed in, the method ignores it. It does not modify it or operate on it in any way.Therefore, you don't have to call
GenEmptyObj
. However, becauseMain
needs the new object,New_ReadImage
must either store it in a by-reference parameter or simply return it.Cheers!
Thank you again for your guidance.
VBS CODE: Option Explicit Sub Main dim reftest reftest=0 zp.testRef 5,reftest zp.print reftest
End Sub Main VB.NET CODE: Public Class VBClass Public Sub Print(ByVal OutStr As Object, ByVal Optional iShow As Integer = 1) Console.WriteLine("OutStr: " & OutStr & "!") End Sub
End Class
C# Code: private void button10_Click(object sender, EventArgs e) { var VBscript = new VBClass(); using (var engine = new VBScriptEngine()) { engine.AddHostObject("zp", VBscript); var scriptCode = File.ReadAllText("D:/Script/abc.vbs"); engine.Execute(scriptCode);