I call a function in VBA, from which I want to retrieve an array of strings. For that I declared a function with ByRef parameter of type Variant:
Public Function TestErrorsInArray(ByVal path As String, ByRef errors As Variant) As String
errors = Array("string1", "string2", "string3")
End Function
I tested it in VBA with following sub:
Public Sub TestTestErrorsInArray()
Dim arr As Variant
arr = Array("foo", "bar")
TestErrorsInArray "", arr
End Sub
And it works OK - arr values are changed to those from errors list.
In C# I call it like this:
object errors = null;
var test = WordTools.GetWordApplication().Run("TestErrorsInArray", "", errors);
Unfortunatelly, it seems that errors variable doesn't change at all... It stays as null. If I initiate it as empty array, it stays as empty array etc.
Any ideas what am I doing wrong?
Related
I'm having one method like the following, where Days is an Enum having values from Sunday to Saturday, Which I'm calling using reflection. I'm having lots of similar methods with different signatures And the input parameters are also dynamic which I'm getting as a JSON.
Public Sub ReflectionMethod(ByVal stringParam As String, ByVal enumParam As Days)
Console.WriteLine($"Executed:ReflectionMethod | stringParam : {stringParam}; enumParam : {enumParam}")
End Sub
The following code is used to parse the JSON and call the method using reflection.
Dim inputJson As String = "['Sample Input',2]"
Dim lstObjects As List(Of Object) = JsonConvert.DeserializeObject(Of List(Of Object))(inputJson)
Dim method As MethodInfo = consoleObject.GetType.GetMethod("ReflectionMethod")
method.Invoke(consoleObject, lstObjects.ToArray)
Throwing the following error while executing the above code, During debugging noticed that method.Invoke() method is getting failed nothing wrong with the deserialization.
Exception: Object of type 'System.Int64' cannot be converted to type 'VBConsole.Days'.
Please note: This is not the actual code that I'm using, just created a console application for easy debugging, and an answer in C# is also appreciated, That's why tagged it to c#
In order to pass your array of JSON values into a method to be invoked by reflection, you must deserialize each JSON value to the required argument type. This can be done as follows:
Public Sub InvokeMethodWithJsonArguments(ByVal consoleObject As Object, ByVal methodName As String, ByVal inputJson As String)
Dim lstObjects As List(Of JToken) = JsonConvert.DeserializeObject(Of List(Of JToken))(inputJson) ' Deserialize to an intermediate list of JToken values
Dim method As MethodInfo = consoleObject.GetType.GetMethod(methodName)
Dim parameterTypes = method.GetParameters().Select(Function(p) p.ParameterType) ' Get the required type of each argument.
Dim args = lstObjects.Zip(parameterTypes, Function(first, second) first.ToObject(second)).ToArray() ' Deserialize each JToken to the corresponding required type
method.Invoke(consoleObject, args) ' Invoke with the deserialized arguments.
End Sub
Notes:
I am assuming you need a solution that does not hardcode the argument types.
MethodInfo.GetParameters() returns an array of parameter information matching the signature of the method to be invoked.
JToken.ToObject(Type) deserializes a JSON token to a type specified in runtime.
Demo fiddle here.
I have a class in VB.NET that runs an Excel App. The class has a property Excel.Application that returns the instance of the Excel Workbook/App.
Private m_ExcelProgramHandle As Microsoft.Office.Interop.Excel.Application
' Sets the Excel.App Instance
m_ExcelProgramHandle = somefunc()
Public ReadOnly Property ExcelProgramHandle() as Microsoft.Office.Interop.Excel.Application
Get
ExcelProgramHandle = m_ExcelProgramHandle
End Get
End Property
Now, I want to call a macro within the Excel Application from the VB.NET App to pass and update some variables.
Here is the VBA Macro:
Public Function pass_string_byref(byref str as String) as Integer
str = "abcde"
pass_string_byref = 1
End Function
Public Function pass_arr_byref(byref arr() as String) as Integer
ReDim arr(3)
arr(1) = "a"
arr(2) = "b"
arr(3) = "c"
pass_arr_byref = 1
End Function
Here is how VB.NET calls the macro:
strArr = New String() {""}
retShort = sampleObj.ExcelProgramHandle.Run("pass_string_byref", str)
retShort = ssampleObj.ExcelProgramHandle.Run("pass_arr_byref", strArr)
But the variables doesn't get updated. However, if I change the data type of the property from Excel.Application to Object then it works.
Private m_ExcelProgramHandleAsObj As Object
' Somewhere in the Constructor
' This is how its being set
m_ExcelProgramHandleAsObj = m_ExcelProgramHandle
Public ReadOnly Property ExcelProgramHandle() as Object
Get
ExcelProgramHandle = m_ExcelProgramHandleAsObj
End Get
End Property
Can someone help explain why this happens?
Is there anyway I could use Microsoft.Interop.Excel.Application instead of Object? Other members of the class are using the ExcelProgramHandle property so changing it to Object might break the class. Thus, I think its better to use Excel.Application.
Thanks!
(I am a ASP.NET programmer that uses mostly VB but can also program in C#)
I like to have a list(t) full with keywords and function names.
For example:
Dim MyList as new List(of MyFunctions)
MyList.add(new Myfunction("descr","DBGetDescription()"))
MyList.add(new Myfunction("Name","DBGetName()"))
MyList.add(new Myfunction("create","DBGetCreateTS()"))
Public Class Myfunctions
Public code as String
Public function as String
Public Sub new(in_code as String, in_function as String)
code = in_code
function = in_function
End Sub
End Class
'what do i want to search for:
Dim ToDo as String = "descr"
'search for it
Dim ThisFunction as MyFunction = MyList.find(function(x) x.code = ToDo)
'answer in 'thisfunction' gives me the name of the function i need to use
Dim FunctionIWantToUse = ThisFunction.function
(the DBGetDescription, DBGetName and DBGetCreateTS are existing routines)
I know the name of the function(routine) I want to start but it is inside a variable.
Is it possible in VB (or in C#) to start the function?
Is there a work around?
M.
You can define a delegate which is basically a reference to a function. This allows you to map "names" to actual functions, to make it possible to call a function from a string name.
Delegate Function MyDelegateType() as string
Sub Main
' Create a dictionary mapping string names to the corresponding function.'
' Ive used a dictionary in my sample code, as its more straightforward, '
' but it would be trivial to wrap the name/function tuple up in a class and use a list'
Dim MyList as new Dictionary(of string, MyDelegateType)
MyList.Add("descr", addressof DBGetDescription)
MyList.add("Name", addressof DBGetName)
MyList.add("create", addressof DBGetCreateTS)
' Get the function for a key, and call it.'
Console.WriteLine(MyList.Item("descr")())
End Sub
Function DBGetDescription() as string
return "MyDescription"
End Function
Function DBGetName() as string
return "MyName"
End Function
Function DBGetCreateTS() as string
return "MyCreateTS"
End Function
http://msdn.microsoft.com/en-us/library/bb763133.aspx
Module Module1
Sub Main()
Dim array1 As Func(Of Integer)() = New Func(Of Integer)(4) {}
For i As Integer = 0 To 4
array1(i) = Function() i
Next
For Each funcElement In array1
System.Console.WriteLine(funcElement())
Next
End Sub
End Module
It says the result will always be 5 namely the final value of i. How come?
They don't put the iteration variable in the "closure"?
The problem occurs because lambda expressions do not execute when they are constructed but rather when they are invoked.
See the link below:
http://blogs.msdn.com/b/vbteam/archive/2007/07/26/closures-in-vb-part-5-looping.aspx
Hope it helps.
In C#, I can declare an array variable like this
object[] Parameters;
and initialize it like this:
Parameters = new object[20];
In Visual Basic, declaring and initializing an array is easy:
Dim Parameters(19) As Object
Dim Parameters As Object(19) ' Alternative syntax
How would I initialize an array variable that has already been declared in VB.NET?
Parameters = New Object(19) doesn't work.
For example, how would I translate the following to VB.NET?
int value = 20;
object[] Parameters;
if (value > 10)
{
Parameters = new Object[20];
}
Basically the same Visual Basic code as the others, but I'd use the opportunity to add a bit of style:
Dim value = 20 ' Type inference, like "var" in C#
Dim parameters() As Object ' Could also be "parameters As Object()"
If value > 10 Then
parameters = New Object(19) {} ' That's right, Visual Basic uses the maximum index
End If ' instead of the number of elements.
Local variables (parameters) should start with a lowercase letter. It's an established convention and it helps to get correct syntax highlighting in Stack Overflow (this also applies to the original C# code).
So, why are the braces {} required in Visual Basic? In Visual Basic, both method calls and array access use parenthesis (...). Thus, New X(4) could mean:
Create a new object of type X and pass 4 to the constructor,
Create a 5-element [sic] array of X.
To distinguish the two cases, you use the array initializer syntax in the second case. Usually, the braces contain actual values:
myArray = New Integer() {1, 2, 3}
Dim value As Integer = 20
Dim Parameters() as object
If value > 10 then
Parameters = new Object(value - 1){}
end if
If you are lazy like me you could use an online converter which yields:
Dim value As Integer = 20
Dim Parameters As Object()
If value > 10 Then
Parameters = New [Object](19) {}
End If
And if you are not like me and want to learn VB.NET head over to the documentation of the VB.NET syntax and start reading.