【问题标题】:Create COM/ActiveXObject in C#, use from JScript, with simple event在 C# 中创建 COM/ActiveXObject,从 JScript 中使用,带有简单的事件
【发布时间】:2012-06-25 20:54:07
【问题描述】:

我想在 C# 中创建一个 COM 对象,并通过 JScript 中的 IDispatch 使用它。这部分很简单。

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

var xmlhttp = new ActiveXObject("MSXML.XMLHTTP"); 
xmlhttp.onReadyStateChange = function() {
  ...
};

我希望我的客户端 JScript 代码如下所示:

var myObject = new ActiveXObject("MyObject.ProgId");
myObject.onMyCustomEvent = function(..args here..) { 
   ...
};

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


我见过 How can I make an ActiveX control written with C# raise events in JavaScript when clicked? ,但那里的答案看起来真的很难实现,而且使用起来也很复杂。


this article看来,XMLHttpRequest 事件不是 COM 事件。 onreadystatechangeIDispatch 类型的属性。当脚本客户端将该属性设置为函数时,JScript 将其编组为 IDispatch 对象。

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

【问题讨论】:

    标签: c# com wsh jscript activexobject


    【解决方案1】:

    既然是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; }
    }
    

    然后,实现:

    [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;
        }
    }
    

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

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

    一起编译,用 regasm 注册:

    %NET64%\regasm.exe Cheeso.Greet.dll /register /codebase
    

    ...然后像这样从 JScript 中使用它:

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

    它有效。

    您也可以从 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)
    

    要使用这种方法将参数从 C# 传递回 JScript,我认为对象需要是 IDispatch,但当然您可以将简单的值作为字符串、int 等进行封送处理,如您所期望的那样进行封送处理。

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

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

    然后,你可以像这样在 jscript 中得到它:

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

    或者在 VBScript 中像这样:

    Sub onHello (obj, num)
        WScript.Echo("onHello (VBScript) invoked. status=" & obj.status )
        WScript.Echo("  num= " & num)
    End Sub
    

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

    【讨论】:

    • 这是一个后期绑定呼叫,而不是一个事件。使用 [ComSourceInterfaces] 属性公开 .NET 事件。
    • 没错。它根本不是一个 COM 事件,但它确实比为这种受限场景(一个对象和该对象的一个​​用户)连接 COM 事件更容易构建和使用。事实上,虽然它不是 COM 事件,但程序员正确理解它以满足他们对可编写脚本对象的“事件”的需求。这就是为什么 XMLHttpRequest 对onreadystatechange 使用这种方法的原因。这也是“不是事件”,但它肯定被数百万开发者理解为事件。 COM 对术语“事件”的具体定义与某些目的无关。
    • 在 JScript 中创建 ActiveXObject 时,"Cheeso.Greet" 是来自 ProjId、DLL 名称、Namespace.ClassName 还是完全来自其他地方?无论我尝试什么,我似乎总是得到“自动化服务器无法创建对象”。
    • @SandyGifford - ProgId。调试 COM 激活是一件很痛苦的事情。根据我的经验,这些天来,这通常是一个安全问题。
    • 我完全同意,但我们需要以一种方式与 Excel 交互,而这在我们需要开始的 JScript.NET 中是不可能的(您无法获取应用程序的当前实例,您只能创建一个新的)。不过,谢谢。这正是我需要知道的。
    【解决方案2】:

    哇,哇!可能容易吗?

    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); }
    }
    

    然后使用这些构建命令

     sn -k Calculator.snk           
     csc /t:library Calculator.cs
     regsvcs Calculator.dll
    

    在 jscript (wsh) 上:

    c = new ActiveXObject("Calculator");
    WScript.Echo(typeof(c));  // output: object
    WScript.Echo(c.Add(4,1)); // output: 5
    

    来源:msdn

    享受吧!

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2014-06-19
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-01-17
      • 1970-01-01
      • 2012-09-20
      • 1970-01-01
      相关资源
      最近更新 更多