I\'d like to create a COM object in C#, and use it via IDispatch from JScript. That part is pretty simple.
I also want to implement simple callbacks on the COM objec
Wow, wow! May be easy?
using System;
using System.EnterpriseServices;
[assembly: ApplicationName("Calculator")]
[assembly: ApplicationActivation(ActivationOption.Library)]
public class Calculator : ServicedComponent
{
public int Add(int x, int y){ return (x + y); }
}
then use these build command
sn -k Calculator.snk
csc /t:library Calculator.cs
regsvcs Calculator.dll
On jscript (wsh):
c = new ActiveXObject("Calculator");
WScript.Echo(typeof(c)); // output: object
WScript.Echo(c.Add(4,1)); // output: 5
source: msdn
Enjoy!
Since it's COM, start by defining an interface. Let's keep it simple.
[Guid("a5ee0756-0cbb-4cf1-9a9c-509407d5eed6")]
[InterfaceType(ComInterfaceType.InterfaceIsIDispatch)]
public interface IGreet
{
[DispId(1)]
string Hello(string name);
[DispId(2)]
Object onHello { get; set; }
}
Then, the implementation:
[ProgId("Cheeso.Greet")]
[ComVisible(true)]
[Guid("bebcfaff-d2f4-4447-ac9f-91bf63b770d8")]
[ClassInterface(ClassInterfaceType.None)]
public partial class Greet : IGreet
{
public Object onHello { get; set; }
public String Hello(string name)
{
var r = FireEvent();
return "Why, Hello, " + name + "!!!" + r;
}
}
The main trick is the FireEvent
method. This worked for me.
private string FireEvent()
{
if (onHello == null) return " (N/A)";
onHello
.GetType()
.InvokeMember
("",
BindingFlags.InvokeMethod,
null,
onHello,
new object [] {});
return "ok";
}
Compile that all together, register it with regasm:
%NET64%\regasm.exe Cheeso.Greet.dll /register /codebase
...And then use it from JScript like this:
var greet = new ActiveXObject("Cheeso.Greet"), response;
greet.onHello = function() {
WScript.Echo("onHello (Javascript) invoked.");
};
response = greet.Hello("Fred");
WScript.Echo("response: " + response);
It works.
You can also call it from VBScript:
Sub onHello ()
WScript.Echo("onHello (VBScript) invoked.")
End Sub
Dim greet
Set greet = WScript.CreateObject("Cheeso.Greet")
greet.onHello = GetRef("onHello")
Dim response
response = greet.Hello("Louise")
WScript.Echo("response: " & response)
To pass parameters back from C# to JScript with this approach, I think objects need to be IDispatch, but of course you can send back simple values marshaled as string, int, and so on which are marshaled as you would expect.
For example, modify the C# code to send back a reference to itself, and the number 42.
onHello
.GetType()
.InvokeMember
("",
BindingFlags.InvokeMethod,
null,
onHello,
new object [] { this, 42 });
Then, you can get that in jscript like so:
greet.onHello = function(arg, num) {
WScript.Echo("onHello (Javascript) invoked.");
WScript.Echo(" num = " + num + " stat=" + arg.status);
};
Or in VBScript like so:
Sub onHello (obj, num)
WScript.Echo("onHello (VBScript) invoked. status=" & obj.status )
WScript.Echo(" num= " & num)
End Sub
NB: You can define your jscript event handler function to accept fewer args than are sent by the C# object when invoking the "event". In my experience you need to design the event handler in VBScript to explicitly accept the correct number of arguments.