Pass a callback function from Visual Basic to C# - c#

I have a .dll written in C# and I use it in Visual Basic for making a Com object. I call C# functions from my Visual Basic code. I'm quite new to this technology and I encountered with such a problem. In C# code I have a method, that receives a callback function as a parameter, gets data from server then calls that callback function. Now I need to to call that function from my Visual Basic code and pass to it a callback.
I think it should look something like this
// C# dll
public bool GetDataFromServer(int someData1, Action<MyCustomType> callback, int someData2)
{
// request to server, get data, create an object of MyCustomType
// call callback and pass MyCustomType object to it as a parameter
}
// Visual Basic part
Public Sub DisplayData(ByRef resp As My_Dll.MyCustomType)
' do something with resp
End Sub
// call Dll method
My_Dll.GetDataFromServer(1, DisplayData, 2) ' I get a compile error
But I can't get it work, it event does not compile. I get a compilation error saying "Argument not optional". I've also tried to pass callback function with AddressOf, but I get an error again saying - "Invalid use of AddressOf operator".
Now my question is - what am I doing wrong? What is the correct way of passing a callback function from Visual Basic to C# and then get it invoked.
Thank you for your time!

Anything containing generics is not visible to COM, so your GetDataFromServer with Action<MyCustomType> is not COM visible. You must define a method on a non-generic class without generic type arguments of itself and without generic parameter or return types.
The usual way of passing a callback in COM is to pass an interface pointer with a method to be called. Avoid the AddressOf approach, however feasible, it's really bad practice once you want the callback to work out-of-process.
A common trick with IDispatch objects is to define a class with a default method with DispId 0 (DISPID_VALUE), which may be invoked by the COM server through IDispatch::Invoke. In some languages, such as JScript, objects may be callable through this technique.
To see how you can do this in VB6/VBA, search for VB_UserMemId. Essentially, you must edit the class file in its raw format (in VBA, you must export it) and add an attribute, such as:
Sub Call()
Attribute Call.VB_UserMemId = 0
End Sub
In VBA, you'd delete the class and import from the edited file. The effect is that the Call method now has DispId 0. You may call it whatever you want, but remember to edit both the Sub name and the Attribute.
In C#, I believe the only ways to achieve such call is with a dynamic variable with:
obj[0]
or through Type.InvokeMember or similar IDispatch::Invoke approaches:
obj.GetType().InvokeMember("", BindingFlags.InvokeMethod, null, obj, null);
The latter is more reliable and you pass the arguments you want, while the former uses a misleading syntax and forces you to pass an argument.
In general, if you control the whole situation and you don't need callable objects, just go for a plain method.
Regarding the AddressOf error, this operator works on module procedures and functions, so the error you see is probably because DisplayOptionQuotes is a method.

Related

I would like to know the difference between function and method in C#

Example code for my question
MessageBox.Show();// is a method and
and private void Example()
{
} //is a function?
is there any real difference or not, please explain
Let's first get the general definition of what function means in a Computer Science sense:
Functions are "self contained" modules of code that accomplish a
specific task. Functions usually "take in" data, process it, and
"return" a result. Once a function is written, it can be used over and
over and over again. Functions can be "called" from the inside of
other functions.
or another definition:
In programming, a named section of a program that performs a specific
task is called a function. In this sense, a function is a type of
procedure or routine. Some programming languages make a distinction
between a function, which returns a value, and a procedure, which
performs some operation but does not return a value.
By these definitions, just about every named code block that can perform a repeatable task when called is a function, though some languages would have that code return a value.
However, in C# according to the ECMA specifications, the word function is very rarely used (comparatively), and can mean something very specifically (depending on the context) or define a large subset of language features.
Notable mentions from the ECMA C# Specifications
Anonymous functions
An anonymous-method-expression or lambda-expression is classified as
an anonymous function
Function members
Function members are members that contain executable statements.
Function members are always members of types and cannot be members of
namespaces. C# defines the following categories of function members:
Methods
Properties
Events
Indexers
User-defined operators
Instance constructors
Static constructors
Finalizers
Async Functions
A method (§15.6) or anonymous function (§12.16) with the async
modifier is called an async function. In general, the term async is
used to describe any kind of function that has the async modifier. It
is a compile-time error for the formal parameter list of an async
function to specify any ref or out parameters.
Local functions
Starting with C# 7.0, C# supports local functions. Local functions are
private methods of a type that are nested in another member. They can
only be called from their containing member.
In short, trying to determine what a function is and isn't in C# is problematic at best (you should worry about other things). Most of the time the terminology of method and function can be used interchangeably, however it also can be used to describe a very specific named language feature (as seen above).
People could argue about the historical definition forever or quote specs to prove the point either way. However, for what you should be concerned with (at this level) is that most things you would think of as methods or functions are actually both (with few exceptions).
A function in C# (or more precise "local function") is a function defined inside another method.
Both examples in the post show "methods" returning a value. There is no naming difference whether method returns a value or void, or whether value returned by a call to the method is used or not.
The other types of syntactic constructs in C# that you can call "function" (in addition to local functions and methods):
constructors
property and event accessors (also you probably should avoid calling them functions, at very least not automatically generated once)
anonymous methods/functions striclty called anonymous function expressions
lambda expressions
finalizers (also you probably should stick with another wrong name than calling it function - "destructor" if you really don't like proper "finalizer").
Your Example() is a method definition, whereas MessageBox.Show() is a method that is being called. MessageBox.Show() also has a definition but it isn't in your code, it is in the .Net code.

Passing an IDispatch parameter from C#

I'm writing a C# COM dll that will be used by both Managed C# as well as Delphi and C++ programs and javascript. The COM dll includes a monitor part where the application registers a function that is supposed to return a string value to the dll. I have done this before for javascript 'applications' where you would simply pass a function as a parameter to the dll. When the dll needs to query the javascript 'application' the following is run:
Type t = theScript.GetType();
object ret = t.InvokeMember("", BindingFlags.InvokeMethod, null, theScript, new object[] { });
'theScript' is stored as an Object in the C# dll. As far as I understand this is accomplished by IDispatch. My theory now is that I should be able to use the same approach for the other languages. So I made a COM visible method with this signature:
void RegisterQuery(object method);
However I can't figure out how to pass a parameter to this method from C#. I've tried using a delegate but calling the delegate simply returns the ToString() method from the delegate (the one that returns the calss name). Nothing else that I have tried even compiles.
So I have two questions:
How shall I pass a method parameter to this dll from C# so that the dll will be able to call the methods, in the manner specified above, when it needs to?
Is this really IDispatch or is there another reason why it works for javascript?
Thanks in advance
Turns out that this was really simple:
Type t = theScript.GetType();
object ret = t.InvokeMember(theObject, BindingFlags.InvokeMethod, null, theScriptName, new object[] { });

Javascript error calling overloaded methods of a C# class

I'm writing a C# class library, and calling it from some Javascript code (technically Jscript.NET). I recently added some overloaded methods, and Javascript has trouble deciding which one to call, because it doesn't always know the types of its variables. I understand why this is happening in most cases, but I've got one example where I don't understand it.
Here are the overloaded method declarations in the C# class.
public virtual DeviceMessage RequestInUnits(
Command command,
int value,
UnitOfMeasure unit)
public virtual DeviceMessage RequestInUnits(
Command command,
Measurement measurement)
My application has a scripting feature that uses Jscript.NET. Here's some Javascript code that tries to call one of those methods on the C# class.
c.RequestInUnits(Command.MoveAbsolute, 0);
That's not a legal call, because the only method with two parameters expects a Measurement object as the second parameter. However, I would expect a type mismatch error. Instead, here's the compilation error I get.
More than one method or property matches this argument list at line 3 column 1
If I replace the 0 with "", then I get a type mismatch error. Why does Javascript think it can convert a number to an object? Why does it think it can coerce the types to more than one of those methods? Only one method takes two parameters.
This isn't a critical problem, but I don't like it when my library causes confusing error messages in calling code. I'd prefer to avoid that if I can.

Refer to C# method from JavaScript without calling it

I have a WebBrowser control set up with ObjectForScripting such that I can call C# methods from JavaScript hosted in the browser control.
Everything seems to be going fine except that in my unit tests I have some checks on the window.external methods I am using and it turns out that...
window.external.MyMethod
...seems to work just the same as...
window.external.MyMethod()
What if I need a reference to the method? Or in the case of my unit test:
typeof(window.external.MyMethod) === 'function'
But this calls the method (and fails because it returns the type of whatever was returned by the method)
How do I get a reference to the method without calling it?
Looking at what documentation I could find, it looks like referring to any member of window.external initiates a call to the host:
...a reference to "window.external.speech" will call the host to resolve the name "speech."
I'm not specifically familiar with this API, but it seems like you might be able instead to do something like:
window.external.checkType('MyMethod') === 'function'
Where checkType is a function you declare within your C# code that can tell you the type of the property provided.

VB Compiler does not do implicit casts to Object?

I've recently had a strange issue with one of my APIs reported. Essentially for some reason when used with VB code the VB compiler does not do implicit casts to Object when trying to invoke the ToString() method.
The following is a minimal code example firstly in C# and secondly in VB:
Graph g = new Graph();
g.LoadFromEmbeddedResource("VDS.RDF.Configuration.configuration.ttl");
foreach (Triple t in g.Triples)
{
Console.WriteLine(t.Subject.ToString());
}
The above compiles and runs fine while the below does not:
Dim g As Graph = New Graph()
g.LoadFromEmbeddedResource("VDS.RDF.Configuration.configuration.ttl")
For Each t As Triple In g.Triples
Console.WriteLine(t.Subject.ToString())
Next
The second VB example gives the following compiler exception:
Overload resolution failed because no
accessible 'ToString' accepts this
number of arguments.
This appears to be due to the fact that the type of the property t.Subject that I am trying to write to the console has explicitly defined ToString() methods which take parameters. The VB compiler appears to expect one of these to be used and does not seem to implicitly cast to Object and use the standard Object.ToString() method whereas the C# compiler does.
Is there any way around this e.g. a VB compiler option or is it best just to ensure that the type of the property (which is an interface in this example) explicitly defines an unparameterized ToString() method to ensure compatability with VB?
Edit
Here are the additional details requested by Lucian
Graph is an implementation of an interface but that is actually irrelevant since it is the INode interface which is the type that t.Subject returns which is the issue.
INode defines two overloads for ToString() both of which take parameters
Yes it is a compile time error
No I do not use hide-by-name, the API is all written in C# so I couldn't generate that kind of API if I wanted to
Note that I've since added an explicit unparameterized ToString() overload to the interface which has fixed the issue for VB users.
RobV, I'm the VB spec lead, so I should be able to answer your question, but I'll need some clarification please...
What are the overloads defined on "Graph"? It'd help if you could make a self-contained repro. It's hard to explain overloading behavior without knowing the overload candidates :)
You said it failed with a "compiler exception". That doesn't really exist. Do you mean a "compile-time error"? Or a "run-time exception"?
Something to check is whether you're relying on any kind of "hide-by-name" vs "hide-by-sig" behavior. C# compiler only ever emits "hide-by-sig" APIs; VB compiler can emit either depending on whether you use the "Shadows" keyword.
C# overload algorithm is to walk up the inheritance hierarchy level by level until it finds a possible match; VB overload algorithm is to look at all levels of the inheritance hierarchy simultaneously to see which has the best match. This is all a bit theoretical, but with a small self-contained repro of your problem I could explain what it means in practice.
Hans, I don't think your explanation is the right one. Your code gives compile-time error "BC30455: Argument not specified for parameter 'mumble' of ToString". But RobV had experienced "Overload resolution failed because no accessible 'ToString' accepts this number of arguments".
Here's a repro of this behavior. It also shows you the workaround, cast with CObj():
Module Module1
Sub Main()
Dim itf As IFoo = New CFoo()
Console.WriteLine(itf.ToString()) '' Error BC30455
Console.WriteLine(CObj(itf).ToString()) '' Okay
End Sub
End Module
Interface IFoo
Function ToString(ByVal mumble As Integer) As String
End Interface
Class CFoo
Implements IFoo
Function ToString1(ByVal mumble As Integer) As String Implements IFoo.ToString
Return "foo"
End Function
End Class
I think this is annotated in the VB.NET Language Specification, chapter 11.8.1 "Overloaded method resolution":
The justification for this rule is
that if a program is loosely-typed
(that is, most or all variables are
declared as Object), overload
resolution can be difficult because
all conversions from Object are
narrowing. Rather than have the
overload resolution fail in many
situations (requiring strong typing of
the arguments to the method call),
resolution the appropriate overloaded
method to call is deferred until run
time. This allows the loosely-typed
call to succeed without additional
casts.
An unfortunate side-effect of this,
however, is that performing the
late-bound call requires casting the
call target to Object. In the case of
a structure value, this means that the
value must be boxed to a temporary. If
the method eventually called tries to
change a field of the structure, this
change will be lost once the method
returns.
Interfaces are excluded from this
special rule because late binding
always resolves against the members of
the runtime class or structure type,
which may have different names than
the members of the interfaces they
implement.
Not sure. I'd transliterate it as: VB.NET is a loosely typed language where many object references are commonly late bound. This makes method overload resolution perilous.

Categories

Resources