2012-06-24 1 views
9

Je voudrais créer un objet COM en C#, et l'utiliser via IDispatch depuis JScript. Cette partie est assez simple.Créer COM/ActiveXObject en C#, utiliser depuis JScript, avec un événement simple

Je veux également implémenter des rappels simples sur l'objet COM, similaire à l'événement exposé par l'objet XmlHttpRequest qui est utilisable dans un navigateur. Ce modèle permet Javascript pour attacher des gestionnaires d'événements comme celui-ci:

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

Je veux que mon code JScript côté client pour ressembler à ceci:

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

Qu'est-ce que le look de code C# comme? Je voudrais le cas général - je voudrais pouvoir renvoyer des arguments au Javascript fn.


Je l'ai vu How can I make an ActiveX control written with C# raise events in JavaScript when clicked?, mais les réponses LOOK-il vraiment compliqué à mettre en œuvre, et compliqué à utiliser.


De this article, il semble que les événements XMLHttpRequest ne sont pas des événements COM. Le onreadystatechange est une propriété de type IDispatch. Lorsque les clients de script définissent cette propriété sur une fonction, JScript la classe en tant qu'objet IDispatch.

Le seul problème qui reste est alors d'invoquer l'IDispatch de C#.

Répondre

14

Comme c'est COM, commencez par définir une interface. Gardons les choses simples.

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

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

Ensuite, la mise en œuvre:

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

L'astuce principale est la méthode FireEvent. Cela a fonctionné pour moi.

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

     return "ok"; 
    } 

Compile que tous ensemble, inscrivez-vous avec regasm:

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

... Et utilisez-le de JScript comme ceci:

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

Il fonctionne.

Vous pouvez également l'appeler à partir de 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) 

Pour passer des paramètres de retour de C# à JScript avec cette approche, je pense que les objets doivent être IDispatch, mais bien sûr, vous pouvez renvoyer simples les valeurs marshalées en tant que chaîne, int, etc., qui sont marshalées comme vous le souhaitez.

Par exemple, modifiez le code C# pour renvoyer une référence à lui-même et le nombre 42.

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

Ensuite, vous pouvez obtenir que JScript comme ceci:

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

Ou dans VBScript comme ceci:

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

NB: Vous pouvez définir votre fonction de gestionnaire d'événements JScript accepter moins d'arguments que ceux envoyés par l'objet C# lors de l'appel de "l'événement". Dans mon expérience, vous devez concevoir le gestionnaire d'événements dans VBScript pour accepter explicitement le nombre correct d'arguments.

+0

Il s'agit d'un appel lié tardivement, pas d'un événement. Utilisez l'attribut [ComSourceInterfaces] pour exposer les événements .NET. –

+3

Exactement. Ce n'est pas du tout un événement COM, mais il est plus simple à construire et à utiliser que de câbler des événements COM pour ce scénario restreint - un objet et un utilisateur de cet objet. Et en fait, bien que ce ne soit pas un événement COM, les programmeurs le comprennent correctement pour satisfaire leurs besoins en "un événement" pour un objet scriptable. C'est pourquoi XMLHttpRequest utilise l'approche, je suppose, pour 'onreadystatechange'. Cela aussi, n'est "pas ** un événement **" mais il est certainement compris comme un événement par des millions de développeurs. La définition spécifique de COM du terme «événement» n'est pas pertinente à certaines fins. – Cheeso

+0

Lors de la création de l'objet ActiveXObject dans JScript, le '' Cheeso.Greet "' provient-il de 'ProjId', le nom de la DLL,' Namespace.ClassName' ou ailleurs? Peu importe ce que j'essaie, il me semble toujours obtenir "Le serveur d'automatisation ne peut pas créer d'objet". –

2

Wow, ouah! Peut être 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); } 
} 

utilisent alors ces commandes de construction

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

Sur JScript (WSH):

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

source: msdn

Enjoy!

Questions connexes