Question

La question

Ma question est la suivante: C # prend-il nativly en charge la liaison tardive IDispatch?

Imaginez que j'essaie d'automatiser Office, tout en étant compatible avec la version installée par le client.

Dans le monde .NET, si vous avez développé avec Office 2000 installé, chaque développeur et chaque client, à partir de maintenant et jusqu'à la fin des temps, doit posséder Office 2000.

Dans le monde qui précédait .NET, nous utilisions COM pour dialoguer avec les applications Office.

Par exemple:

1) Utilisez le ProgID indépendant de la version

"Excel.Application"

qui se résout en:

clsid = {00024500-0000-0000-C000-000000000046}

puis en utilisant COM, nous demandons qu'une de ces classes soit instanciée dans un objet:

IUnknown unk;
CoCreateInstance(
    clsid, 
    null,
    CLSCTX_INPROC_SERVER | CLSCTX_LOCAL_SERVER,
    IUnknown, 
    out unk);

Et maintenant nous allons aux courses - capable d’utiliser Excel à partir de mon application. Bien sûr, si vraiment vous voulez utiliser l'objet, vous devez appeler une méthode d'appel.

Nous pourrions avoir accès aux diverses déclarations d'interface , traduites dans notre langue. Cette technique est bonne parce que nous obtenons

  • liaison anticipée
  • aperçu du code
  • vérification de la syntaxe du type de compilation

et un exemple de code pourrait être:

Application xl = (IExcelApplication)unk;
ExcelWorkbook workbook = xl.Workbooks.Add(template, lcid);
Worksheet worksheet = workbook.ActiveSheet;

Mais il y a un inconvénient à utiliser des interfaces: nous devons avoir accès aux différentes déclarations d’interface, traduites dans notre langue. Et nous sommes bloqués en utilisant des invocations basées sur des méthodes, devant spécifier tous les paramètres, par exemple:

ExcelWorkbook workbook = xl.Workbooks.Add(template, lcid);
xl.Worksheets.Add(before, after, count, type, lcid);

Cela s'est avéré, dans le monde réel, avoir de tels inconvénients que nous renoncerions volontiers:

  • liaison anticipée
  • aperçu du code
  • vérification de la syntaxe de temps de compilation

et utilisez plutôt IDispatch : liaison tardive:

Variant xl = (IDispatch)unk;
Variant newWorksheet = xl.Worksheets.Add();

Etant donné que l’automatisation Excel a été conçue pour VB Script, de nombreux paramètres peuvent être omis, même en l’absence de surcharge.

Remarque: Ne confondez pas mon exemple d'Excel avec la raison pour laquelle je souhaite utiliser IDispatch. Tous les objets COM ne sont pas Excel. Certains objets COM n’ont d’autre prise en charge que via IDispatch.

Était-ce utile?

La solution

Vous pouvez, relativement, utiliser une liaison IDispatch à liaison tardive en C #.

http://support.microsoft.com/kb/302902

Voici quelques exemples d'utilisation d'Excel. De cette façon, vous n’aurez pas besoin d’ajouter une dépendance inutile à l’impressionnant PIA de Microsoft:

//Create XL
Object xl = Activator.CreateInstance(Type.GetTypeFromProgID("Excel.Application"));

//Get the workbooks collection.
//   books = xl.Workbooks;
Object books = xl.GetType().InvokeMember( "Workbooks", 
      BindingFlags.GetProperty, null, xl, null);

//Add a new workbook.
//   book = books.Add();
Objet book = books.GetType().InvokeMember( "Add", 
      BindingFlags.InvokeMethod, null, books, null );

//Get the worksheets collection.
//   sheets = book.Worksheets;
Object sheets = book.GetType().InvokeMember( "Worksheets",
      BindingFlags.GetProperty, null, book, null );

Object[] parameters;

//Get the first worksheet.
//   sheet = sheets.Item[1]
parameters = new Object[1];
parameters[0] = 1;
Object sheet = sheets.GetType().InvokeMember( "Item", 
      BindingFlags.GetProperty, null, sheets, parameters );

//Get a range object that contains cell A1.
//   range = sheet.Range["A1];
parameters = new Object[2];
parameters[0] = "A1";
parameters[1] = Missing.Value;
Object range = sheet.GetType().InvokeMember( "Range",
      BindingFlags.GetProperty, null, sheet, parameters );

//Write "Hello, World!" in cell A1.
//   range.Value = "Hello, World!";
parameters = new Object[1];
parameters[0] = "Hello, World!";
objRange_Late.GetType().InvokeMember( "Value", BindingFlags.SetProperty, 
      null, range, parameters );

//Return control of Excel to the user.
//   xl.Visible = true;
//   xl.UserControl = true;
parameters = new Object[1];
parameters[0] = true;
xl.GetType().InvokeMember( "Visible", BindingFlags.SetProperty,
      null, xl, Parameters );
xl.GetType().InvokeMember( "UserControl", BindingFlags.SetProperty,
      null, xl, Parameters );

Autres conseils

Vous devez attendre que C # 4.0 apparaisse pour obtenir la liaison tardive que vous recherchez. Chaque fois que j'ai besoin de fonctionnalités d'interopérabilité, je repasse en mode VB.Net afin de pouvoir tirer parti des fonctionnalités COM qui semblent manquer à C #.

La méthode simple que j'utilise consiste à créer une classe dans VB.Net qui effectue le travail IDispatch, puis à exposer les méthodes que je souhaite utiliser comme méthodes de mon wrapper, puis à les appeler à volonté à partir de mon code C #. Ce n’est pas la solution la plus élégante, mais cela m’a fait sortir du pétrin au cours des derniers mois.

Le mot clé dynamic de C # 4 prend en charge IDispatch et les liaisons tardives. Vous pouvez consulter la série dynamique de Sam Ng pour plus d'informations

Oh, et C # 4 n’est disponible qu’aujourd’hui en CTP. Vous devrez soit attendre Visual Studio vNext, soit utiliser la version bêta (qui s'exécute sur un ordinateur virtuel Windows Server 2008) pour l'utiliser.

vous pouvez probablement vous en sortir avec un code beaucoup plus agréable en C # 2.0 / 3.0 si vous prenez le temps d'écrire une interface contenant les méthodes et propriétés que vous voulez de l'objet et ajoutez quelques attributs (je l'écris de la mémoire, donc les détails peuvent ne pas être corrects, mais je jure que ça a fonctionné pour moi ...)

    using System.Runtime.Interopservices;

    [Guid("00024500-0000-0000-C000-000000000046")]
    [InterfaceType(ComInterfaceType.InterfaceIsIDispatch)]
    interface IExcel
    {
      // sample property
      string Name{get;}
      // more properties
    }

    // and somewhere else
    void main()
    {
      Object xl = Activator.CreateInstance(Type.GetTypeFromProgID("Excel.Application"));
      IExcel excel = (IExcel)xl;
      string name = xl.name
    }

Comme mentionné précédemment, le code ne fonctionnera pas immédiatement, il s'agit plutôt d'un indice sur ce qu'il faut rechercher dans msdn.

Comme d’autres l’ont déjà dit, utilisez "dynamique" de c # 4. mot clé roches. Voici un exemple simple: c’est tellement plus succint que d’utiliser "InvokeMethod"

dynamic xl = Activator.CreateInstance(Type.GetTypeFromProgID("Excel.Application"));
dynamic books = xl.Workbooks;
dynamic book = books.Add();

Console.WriteLine(books.Count);     //Writes 1

foreach (dynamic b in books)        
{
Console.WriteLine(b.Name);      //Writes "Book1"
}

hé mec, J'ai actuellement 2 projets codeplex pour résoudre ce problème.

le premier est LateBindingApi.Excel http://excel.codeplex.com mapped latebinded appel de l'appel au modèle objet bien connu. c'était un projet test pour le projet suivant.

le second est un générateur de code http://latebindingapi.codeplex.com l'outil crée des projets c # à partir de bibliothèques de types COM. les projets générés incluent des objets de mappeur avec latebind accédant au serveur COM. Le point culminant est que l'outil convertit les bibliothèques de type COM de différentes versions en un seul projet (par exemple, excel 9,10,11) et a marqué toutes les entités avec un attribut SupportByLibrary défini par eux-mêmes. J'ai analysé toutes les applications bureautiques de la version 9,10,11,12,14 avec cet outil et génère une solution c #, disponible sous la forme d'une version bêta testée avec un exemple de code sur la page principale.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top