2012-06-24 12 views
9

Vorrei creare un oggetto COM in C# e usarlo tramite IDispatch da JScript. Quella parte è piuttosto semplice.Crea COM/ActiveXObject in C#, utilizza da JScript, con evento semplice

Vorrei anche implementare semplici callback sull'oggetto COM, simile all'evento esposto dall'oggetto XmlHttpRequest che è utilizzabile in un browser. Questo modello consente Javascript per collegare i gestori di eventi come questo:

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

voglio che il mio codice JScript sul lato client a guardare come questo:

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

Che cosa significa il codice C# assomigliare? Mi piacerebbe il caso generale - mi piacerebbe essere in grado di passare argomenti alla fn di Javascript.


Ho visto How can I make an ActiveX control written with C# raise events in JavaScript when clicked?, ma le risposte ci sembrano davvero complicato da implementare e complicato da usare.


Da this article, sembra che gli eventi XMLHttpRequest non sono eventi COM. Il onreadystatechange è una proprietà di tipo IDispatch. Quando i client script impostano tale proprietà su una funzione, JScript esegue il marshalling come un oggetto IDispatch.

L'unico problema che rimane è quello di richiamare l'IDispatch da C#.

risposta

14

Poiché è COM, iniziare definendo un'interfaccia. Facciamo in modo semplice.

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

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

Poi, l'attuazione:

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

Il trucco principale è il metodo FireEvent. Questo ha funzionato per me.

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

     return "ok"; 
    } 

compilazione che tutti insieme, registrarlo con regasm:

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

... e poi usarlo da JScript in questo modo:

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

Funziona.

Si può anche chiamare da 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) 

per passare parametri di ritorno da C# a JScript con questo approccio, credo che gli oggetti devono essere IDispatch, ma naturalmente si può mandare indietro semplice valori sottoposti a marshalling come stringa, int e così via su cui è stato eseguito il marshalling come previsto.

Ad esempio, modificare il codice C# per inviare un riferimento a se stesso e il numero 42.

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

Quindi, è possibile ottenere che in JScript in questo modo:

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

O in VBScript in questo modo:

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

NB: E 'possibile definire la funzione di gestore di eventi jscript ad accettare meno argomenti di quelli inviati dall'oggetto C# quando si richiama l'evento. Nella mia esperienza è necessario progettare il gestore di eventi in VBScript per accettare esplicitamente il numero corretto di argomenti.

+0

Questa è una chiamata in ritardo, non un evento. Utilizzare l'attributo [ComSourceInterfaces] per esporre eventi .NET. –

+3

Esattamente. Non è affatto un evento COM, ma è sicuramente più semplice da costruire e da utilizzare rispetto al cablaggio degli eventi COM per questo scenario limitato: un oggetto e un utente di tale oggetto. E infatti, anche se non è un evento COM, i programmatori lo capiscono correttamente per soddisfare le loro esigenze di "un evento" per un oggetto con script. Questo è il motivo per cui XMLHttpRequest utilizza l'approccio, suppongo, per 'onreadystatechange'. Anche questo è "non ** un evento **", ma certamente è considerato un evento da milioni di sviluppatori. La definizione specifica della COM del termine "evento" è irrilevante per alcuni scopi. – Cheeso

+0

Quando si crea ActiveXObject in JScript, il "" Cheeso.Greet "' deriva da 'ProjId', il nome della DLL,' Namespace.ClassName' o da qualche altra parte? A prescindere da ciò che cerco, sembra sempre che "il server di automazione non sia in grado di creare oggetti". –

2

Wow, wow! Potrebbe essere facile?

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

quindi utilizzare questi comando build

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 

fonte: msdn

Enjoy!