在C#中创建COM/ActiveXObject,使用JScript,使用简单事件

Che*_*eso 9 c# com wsh activexobject jscript

我想在C#中创建一个COM对象,并通过JScript的IDispatch使用它.那部分非常简单.

我还想在COM对象上实现简单的回调,类似于在浏览器中可用的XmlHttpRequest对象公开的事件.该模型允许Javascript附加如下事件处理程序:

var xmlhttp = new ActiveXObject("MSXML.XMLHTTP"); 
xmlhttp.onReadyStateChange = function() {
  ...
};
Run Code Online (Sandbox Code Playgroud)

我希望我的客户端JScript代码看起来像这样:

var myObject = new ActiveXObject("MyObject.ProgId");
myObject.onMyCustomEvent = function(..args here..) { 
   ...
};
Run Code Online (Sandbox Code Playgroud)

C#代码是什么样的?我想要一般情况 - 我希望能够将参数传递回Javascript fn.


我已经看过如何在点击时用JavaScript编写用C#引发事件的ActiveX控件?,但那里的答案看起来实际上很复杂,而且使用起来很复杂.


这篇文章中,似乎XMLHttpRequest事件不是COM事件.这onreadystatechange是一种类型的财产IDispatch.当脚本客户端将该属性设置为函数时,JScript将其封送为IDispatch对象.

剩下的唯一问题是从C#调用IDispatch.

Che*_*eso 14

因为它是COM,所以首先定义一个接口.让我们保持简单.

[Guid("a5ee0756-0cbb-4cf1-9a9c-509407d5eed6")]
[InterfaceType(ComInterfaceType.InterfaceIsIDispatch)]
public interface IGreet
{
    [DispId(1)]
    string Hello(string name);

    [DispId(2)]
    Object onHello { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

然后,执行:

[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;
    }
}
Run Code Online (Sandbox Code Playgroud)

主要技巧是FireEvent方法.这对我有用.

    private string FireEvent()
    {
        if (onHello == null) return " (N/A)";
        onHello
            .GetType()
            .InvokeMember
            ("",
             BindingFlags.InvokeMethod,
             null,
             onHello,
             new object [] {});

        return "ok";
    }
Run Code Online (Sandbox Code Playgroud)

将所有内容编译在一起,用regasm注册:

%NET64%\regasm.exe Cheeso.Greet.dll /register /codebase
Run Code Online (Sandbox Code Playgroud)

...然后从JScript中使用它,如下所示:

var greet = new ActiveXObject("Cheeso.Greet"), response;
greet.onHello = function() {
    WScript.Echo("onHello (Javascript) invoked.");
};
response = greet.Hello("Fred");
WScript.Echo("response: " + response);
Run Code Online (Sandbox Code Playgroud)

有用.

你也可以从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)
Run Code Online (Sandbox Code Playgroud)

要使用这种方法将参数从C#传递回JScript,我认为对象需要是IDispatch,但是当然你可以发送回作为字符串,整数等编组的简单值,这些值按照您的预期进行编组.

例如,修改C#代码以发回对自身的引用,以及数字42.

        onHello
            .GetType()
            .InvokeMember
            ("",
             BindingFlags.InvokeMethod,
             null,
             onHello,
             new object [] { this, 42 });
Run Code Online (Sandbox Code Playgroud)

然后,你可以在jscript中得到它,如下所示:

greet.onHello = function(arg, num) {
    WScript.Echo("onHello (Javascript) invoked.");
    WScript.Echo("  num = " + num + "  stat=" + arg.status);
};
Run Code Online (Sandbox Code Playgroud)

或者像VBScript那样:

Sub onHello (obj, num)
    WScript.Echo("onHello (VBScript) invoked. status=" & obj.status )
    WScript.Echo("  num= " & num)
End Sub
Run Code Online (Sandbox Code Playgroud)

注意:您可以定义您的jscript事件处理函数,以接受比调用"事件"时由C#对象发送的更少的args.根据我的经验,您需要在VBScript中设计事件处理程序以显式接受正确数量的参数.

  • 究竟.它根本不是一个COM事件,但它确实比为这种受限场景连接COM事件更容易构建和使用 - 一个对象和该对象的一个​​用户.事实上,虽然它不是COM事件,程序员正确地理解它以满足他们对可编写脚本的对象的"事件"的需求.这就是为什么XMLHttpRequest使用这种方法,我想,'onreadystatechange`.这也是"不是**事件**",但它肯定被数百万开发者理解为事件.COM对"事件"一词的具体定义与某些目的无关. (3认同)