"Invalid procedure call or argument" VBScript calling C# COM method - c#

I have a VBScript app calling COM-registered C# objects. I am able to pass in a COM object to a COM call, as well as receive either a primitive or a COM object back, but I can't do both at the same time! If I try retrieving any value back from the call while also passing in a COM object, I get the exception "Invalid procedure call or argument"
Dim foo
Set foo = Server.CreateObject("Foo")
foo.SetProperty(1)
Dim bar
Set bar = Server.CreateObject("Bar")
Dim return
Set return = bar.Do(foo)
If that last line is simply bar.Do(foo) it works fine.
Also, whether it is
Set return = bar.Do(foo)
or
return = bar.Do(foo)
causes the same error in this case.
My COM classes are classes with only methods exposed, and implementing an interface. I'm getting this error by dealing with only ints, longs, and Strings.

I'm a bit rusty in this area but if your method is returning an int or string shouldn't your code then read:
return = bar.Do(foo)
instead of
Set return = bar.Do(foo)

"return = bar.Do(foo)" should work, as long as Bar.Do is actually returning something. How is Bar.Do defined?

Related

Return value of method is modifying an object passed as a method parameter

I'm in a situation where I am writing a method where the return value directly modifies a class which was used to supply paramaters to the method.
Computer.MatchingPassword = PasswordFinder.Find(Computer.Name, PasswordList)
Does assigning to a property of a passed parameter like this have any hazards?
Technically, your code sample doesn't assign to the parameter, since you passed a property of the Computer object.
In other words, you only gave the function "name", so assigning to a different property of the same object would have no effect.
Now, in the more general case, you are still safe. Even passing the whole object, by the time you assign the return value the method has already completed. Any changes you make won't do anything.
In other words, the following code is functionally equivalent:
string password = PasswordFinder.Find(Computer.Name, PasswordList);
Computer.MatchingPassword = password;
Clearly no problems there. This is true even if it was passed by ref, as again, the method is totally done with the object, so any changes to it won't matter.

dynamic or object in C#

I'm using a javascript dll in c# that evaluate a javascript scope.
For now, I always return the evaluated result by the javascript dll in a string.
However, I'm planning to change the way it's done. I created an enum DataType:
public enum DataType
{
Integer,
Decimal,
Boolean,
Text
}
So depending on the given datatype in a dropdownlist, I'm going to cast the result in an integer, decimal, boolean or text.
When I'm doing the cast, should I return a dynamic or an object of int, decimal, bool or string?
My understanding of the dynamic type is not that clear.
If what you want is to return a value whose type is determined at run time, and not at compile time, you should return dynamic. This will allow you to use the object in all appropriate types without C# giving you a compile time error, and possibly saving yourself from a few casting runtime errors caused by improper casts of object into a specific type.
public dynamic GetAnything(DataType dataType)
{
switch(dataType)
{
case Integer:
return 1;
case Decimal:
return 1.0;
case Boolean:
return true;
case Text:
return "1";
}
}
This will allow you to do:
var result = GetAnything(DataType.Text) + 1;
Without having to perform an explicit cast.
This is no more dangerous than returning an object, as you would do:
var result = (int)GetAnythingAsObject(DataType.Text) + 1;
Which would cause a runtime cast exception since object is in fact a string. Where as with dynamic, this will work for all types that support the + operator.
You can read more about the difference between object and dynamic in this msdn article: http://blogs.msdn.com/b/csharpfaq/archive/2010/01/25/what-is-the-difference-between-dynamic-and-object-keywords.aspx
Note: dynamic requires .Net 4 and over
No, dynamic types are for different usages. Use either an object or add generic calls for each enum type.
You don't have to return an object of a dynamic type.
You should use an object according to the selected data type.
The dynamic type is a type that will be checked at runtime, not during the compilation time. Typically, it's used for scenarios like dynamic API, COM API, Office API (Interoperability libraries). I don't see a reason for using it in your scenario.

C# COM vaiable does not exist in current context

I have a COM object in C#:
Class myClass = new Class;
IPClass myPointer = null;
myPointer = (IPClass) myClass.GetMyPointer();
bool myBool = myPointer.DoSomething();
myClass.GetMyPointer() in C# is returning a dynamic type. In the original code where the COM object is defined, myClass.GetMyPointer returns a pointer to an interface IPClass in C++.
When I test my C# dll, I could not get myBool. VS always says that myBool does not exist in the current context. But DoSomething was always performed. I just could not get the returning value back. I have tried to temporarily stop garbage collection from C# and it did not make any difference.
Anyone has any suggestions?
Try this and see what it returns:
System.Diagnostics.Debug.WriteLine("calling DoSomething()");
System.Diagnostics.Debug.WriteLine((myPointer.DoSomething()).GetType().Name);
System.Diagnostics.Debug.WriteLine("finished call to DoSomething()");

Receiving values back after calling a sub in Access through .NET

I am creating an C# application that must call subs in an Access mdb database file.
I've created a test sub in the mdb file and I can call it from C# and also pass it parameters. That all works fine, but I want to pass a result back to C#. I know I can't do this with a function, so is it possible to pass the variables by reference, then vba can change the variables and I get my result back? I tried the following, but it doesn't work. Anybody know if this is possible?
Thanks
VBA test sub:
Sub test(Byref p1 As String)
p1="bar"
MsgBox p1
End Sub
Call it from C#:
Access.Application oAccess = new Access.Application();
oAccess.Visible = true;
oAccess.OpenCurrentDatabase("d:\\tmp\\test1.mdb", false, "");
string t;
t = "foo"
oAccess.Run("test", t);
//t still equals "foo" at this point, it should be equal to "bar"
For a scalar (a simple number, string, or Boolean) the system makes a
copy of the current value and passes this copy to the called
procedure. Upon return from the called procedure the system discards
the copy. So, even if the called procedure changes the value of a
ByVal argument, there is no way for that change to propagate back to
the caller.
Take a look at this for explanation on passing scalar values. As explained on the page, callee can modify the property of the object, thereby caller can see the modified value (in case of using an object).
Another alternative, is to use a return value from the function.
EDIT:
Function test(p1 As String) as String
test = "Bar"
End Function
c#
t = "foo"
t = oAccess.Run("test", t);

Double parentheses in VB6 Array syntax (passed from .Net com-interop)

I have a C# dll exposed to vb6 via com-interop. This is all working, but I am noticing something strange when I pass an array of a custom objects from .Net into VB6.
Accessing the array from VB6 is what baffles me. If I access the array directly I have to do it like this:
Dim manager as New ObjectManager
'Access with two sets of parentheses:
msgbox manager.ReturnArrayOfObjects()(0).Name
However, if I copy the array first I can access it how I would normally expect to:
Dim manager as New ObjectManager
Dim objectArray() As CustomObject
'copy the array
objectArray = manager.ReturnArrayOfObjects
'access normally:
msgbox objectArray(0).Name
In the first case I had to use two sets of parentheses: manager.ReturnArrayOfObjects()(0).Name In the second case I could just use one set of parentheses: objectArray(0).Name
Does anyone know why this is the case? Am I doing something wrong here with the interop maybe?
Here is a quick stub/sample of the C# interop code.
[ComVisible(true)]
[InterfaceType(ComInterfaceType.InterfaceIsDual)]
[Guid("[Guid here...]")]
public interface IObjectManager
{
[DispId(1)]
CustomObject[] ReturnArrayOfObjects();
}
[ComVisible(true)]
[ClassInterface(ClassInterfaceType.None)]
[Guid("[guid here...]")]
public class ObjectManager: IObjectManager
{
public CustomObject[] ReturnArrayOfObjects()
{
return new CustomObject[] { new CustomObject(), new CustomObject() };
}
}
The class CustomObject() is also exposed to com-interop and working just fine. Please let me know if you need me to post anymore code, but I think these little snippets represent the problem well enough to begin with.
Thanks in advance for your help.
ReturnArrayOfObjects() in the C# code is a method. Your VB6 code is invoking the method, which returns the array, and then accessing the first element. The difference between this
msgbox manager.ReturnArrayOfObjects()(0).Name
and this
objectArray = manager.ReturnArrayOfObjects
msgbox objectArray(0).Name
Is that in the second, you invoke the method by itself without accessing the first element, and VB is allowing you to leave off the parentheses from the method call. Conversely, the language is not allowing you to leave off the parentheses when you directly access the first element. It's simply a language feature, it's not a "double parentheses array syntax" issue.
ReturnArrayOfObjects is a method, that must be called. In VB6, if you're calling a method and supplying no parameters, and it's the entire statement, then you can omit the parenthesis.
However, in your first example, you're calling the method, and then indexing into the array returned by that method. You need the first set of parenthesis to indicate that you're passing no parameters to the method, and then the second set of parenthesis are being used for array indexing.

Categories