Domanda


La domanda

La mia domanda è: C # supporta nativly IDispatch con ritardo di associazione?


Fingi Sto cercando di automatizzare Office, pur essendo compatibile con qualsiasi versione installata dal cliente.

Nel mondo .NET se si è sviluppato con Office 2000 installato, tutti gli sviluppatori e tutti i clienti, da ora fino alla fine dei tempi, devono disporre di Office 2000.

Nel mondo prima di .NET, abbiamo usato COM per parlare con le applicazioni di Office.

Ad esempio:

1) Utilizza la versione indipendente ProgID

"Excel.Application"

che si risolve in:

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

e quindi usando COM, chiediamo di istanziare una di queste classi in un oggetto:

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

E ora partiamo per le gare, in grado di utilizzare Excel dall'interno della mia applicazione. Naturalmente, se davvero vuoi usare l'oggetto, devi chiamare per avere dei metodi di chiamata.

Noi potremmo ottenere le varie dichiarazioni interfaccia , tradotte nella nostra lingua. Questa tecnica è buona perché otteniamo

  • associazione anticipata
  • code-insight
  • compilare il controllo della sintassi del tipo

e alcuni codici di esempio potrebbero essere:

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

Ma c'è un aspetto negativo nell'uso delle interfacce: dobbiamo prendere visione delle varie dichiarazioni di interfaccia, tradotte nella nostra lingua. E siamo bloccati usando invocazioni basate sul metodo, dovendo specificare tutti i parametri, ad esempio:

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

Ciò ha dimostrato, nel mondo reale, di avere degli aspetti negativi tali che vorremmo rinunciare volentieri:

  • associazione anticipata
  • code-intuizione
  • verifica della sintassi del tempo di compilazione

e utilizza invece IDispatch late binding:

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

Poiché l'automazione di Excel è stata progettata per VB Script, è possibile omettere molti parametri, anche senza di essi senza sovraccarico.

Nota: non confondere il mio esempio di Excel con un motivo per cui voglio usare IDispatch. Non tutti gli oggetti COM sono Excel. Alcuni oggetti COM non hanno supporto se non tramite IDispatch.

È stato utile?

Soluzione

È possibile, relativamente, utilizzare l'associazione IDispatch con associazione tardiva in C #.

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

Ecco alcuni esempi per l'utilizzo di Excel. In questo modo non è necessario aggiungere una dipendenza inutile alla PIA bloaty di 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 );

Altri suggerimenti

Devi aspettare che esca C # 4.0 per ottenere l'associazione tardiva che stai cercando. Ogni volta che ho bisogno di funzionalità di interoperabilità, torno alla modalità VB.Net in modo da poter sfruttare le funzionalità COM che C # sembra mancare.

Il semplice metodo che uso è la creazione di una classe in VB.Net che fa funzionare IDispatch e quindi espone i metodi che voglio usare come metodi del mio wrapper e quindi posso chiamarli a piacimento dal mio codice C #. Non è la soluzione più elegante, ma negli ultimi mesi mi ha tirato fuori da una marmellata o due.

La parola chiave

dinamica di C # 4 supporta IDispatch e associazione tardiva. Puoi leggere le serie dinamiche di Sam Ng per ulteriori informazioni

Oh, e C # 4 è disponibile solo come CTP oggi. Dovrai aspettare Visual Studio vNext o utilizzare la beta (che gira su un PC virtuale Windows Server 2008) per usarlo.

probabilmente puoi cavartela con codice molto più bello in C # 2.0 / 3.0 se prendi il tempo per scrivere un'interfaccia contenente i metodi e le proprietà che desideri dell'oggetto e aggiungo alcuni attributi (lo scrivo dalla memoria, quindi i dettagli potrebbero non essere corretti, ma giuro che ha funzionato per me ...)

    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
    }

Come accennato, il codice non funzionerà immediatamente, è più un suggerimento su cosa cercare in msdn.

Come altri hanno già detto, usando la "dinamica" di c # 4 parole chiave rock. Ecco un semplice esempio: è molto più succinto dell'uso di " 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"
}

hey amico, al momento ho 2 progetti codeplex per risolvere questo problema.

il primo è LateBindingApi.Excel http://excel.codeplex.com mappato chiamata di richiamo in ritardo al modello a oggetti ben noto. questo era un progetto di prova per il seguente progetto.

il secondo è un generatore di codice http://latebindingapi.codeplex.com lo strumento crea progetti c # dalle librerie di tipi COM. i progetti generati includono oggetti mapper con latebind che accedono al server COM. il clou è che lo strumento converte le librerie di tipo COM in diverse versioni in un singolo progetto (ad esempio Excel 9,10,11) e contrassegna tutte le entità con un attributo SupportByLibrary auto-definito. Ho analizzato tutte le app per ufficio nella versione 9,10,11,12,14 con questo strumento ora e ho generato una soluzione c #, disponibile come beta testata con codice di esempio nella pagina principale.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top