Maak COM/ActiveXObject aan in C #, gebruik van JScript, met eenvoudige gebeurtenis

Ik wil graag een COM-object maken in C# en het gebruiken via IDispatch van JScript. Dat deel is vrij eenvoudig.

Ik wil ook eenvoudige callbacks implementeren op het COM-object, vergelijkbaar met de gebeurtenis die wordt weergegeven door het XmlHttpRequest-object dat in een browser kan worden gebruikt. Met dat model kan Javascript event handlers als volgt koppelen:

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

Ik wil dat mijn JScript-code aan de clientzijde er als volgt uitziet:

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

Hoe ziet de C# -code eruit? Ik zou de algemene case graag willen - ik zou graag argumenten terug kunnen geven aan de Javascript fn.


Ik heb Hoe kan ik een ActiveX-besturingselement geschreven maken met C# verhogen gebeurtenissen in JavaScript wanneer erop wordt geklikt? , maar de antwoorden daar zien er erg ingewikkeld uit om te implementeren en zijn ingewikkeld om te gebruiken.


Van dit artikel lijkt het erop dat XMLHttpRequest-evenementen geen COM-gebeurtenissen zijn. De onreadystatechange is een eigenschap van het type IDispatch . Wanneer scriptclients die eigenschap instellen op een functie, ordent JScript het als een IDispatch-object.

Het enige probleem dat overblijft is dan om de IDispatch vanuit C# op te roepen.

9

2 antwoord

Aangezien het COM is, begin met het definiëren van een interface. Laten we het simpel houden.

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

    [DispId(2)]
    Object onHello { get; set; }
}

Vervolgens de implementatie:

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

De belangrijkste truc is de methode FireEvent . Dit werkte voor mij.

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

        return "ok";
    }

Compileer dat allemaal samen, registreer het met regasm:

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

... En gebruik het dan als volgt uit JScript:

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

Het werkt.

Je kunt het ook vanuit VBScript noemen:

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)

Om parameters met deze benadering van C# naar JScript door te geven, denk ik dat objecten IDispatch moeten zijn, maar natuurlijk kun je eenvoudige waarden terugsturen die als string, int enzovoort worden gemarcheerd en die worden gemargreerd zoals je zou verwachten.

Pas bijvoorbeeld de C# -code aan om een ​​referentie naar zichzelf en het nummer 42 terug te sturen.

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

Dan kun je dat in jscript krijgen zoals:

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

Of in VBScript als volgt:

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

NB: U kunt uw jscript-gebeurtenisafhandelingsfunctie definiëren om minder args te accepteren dan worden verzonden door het C# -object wanneer u de "event" aanroept. In mijn ervaring moet je de gebeurtenishandler in VBScript ontwerpen om expliciet het juiste aantal argumenten te accepteren.

14
toegevoegd
Precies. Het is helemaal geen COM-gebeurtenis, maar het is zeker eenvoudiger om COM-gebeurtenissen te bouwen en te gebruiken voor dit beperkte scenario: één object en één gebruiker van dat object. En in feite, hoewel het geen COM-gebeurtenis is, begrijpen programmeurs het correct om te voldoen aan hun behoeften aan "een gebeurtenis" voor een scriptbaar object. Dit is de reden waarom XMLHttpRequest de benadering gebruikt, denk ik, voor onreadystatechange . Ook dat is "niet een evenement " maar het wordt zeker begrepen als een evenement door miljoenen ontwikkelaars. De specifieke definitie van de term "gebeurtenis" door de Commissie is voor sommige doeleinden niet relevant.
toegevoegd de auteur Cheeso, de bron
@SandyGifford - ProgId. Foutopsporing COM-activeringen is een grote pijn. In mijn ervaring is het tegenwoordig meestal een beveiligingsprobleem.
toegevoegd de auteur Cheeso, de bron
Wanneer u het ActiveXObject in JScript maakt, komt de "Cheeso.Greet" van de ProjId , de DLL-naam, Namespace.ClassName of ergens anders allemaal samen? Het maakt niet uit wat ik probeer, ik schijn altijd te krijgen "Automation-server kan geen object maken".
toegevoegd de auteur Sandy Gifford, de bron
Ik ben het er volledig mee eens, maar we hebben de vereiste om Excel te gebruiken op een manier die gewoon niet mogelijk is met het JScript.NET dat we moeten beginnen (je kunt geen huidig ​​exemplaar van de applicatie krijgen, je kunt alleen een nieuwe maken ). Maar toch bedankt. Dit was precies wat ik moest weten.
toegevoegd de auteur Sandy Gifford, de bron
Dit is een late bound call, geen evenement. Gebruik het kenmerk [ComSourceInterfaces] om .NET-gebeurtenissen bloot te leggen.
toegevoegd de auteur Hans Passant, de bron

Wow Wow! Kan het gemakkelijk zijn?

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

gebruik dan deze build-opdracht

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

Over jscript (wsh):

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

source: msdn

Genieten!

2
toegevoegd