¿C#.NET es compatible con IDispatch vinculante tarde?
late-binding (6)
Debes esperar a que C # 4.0 salga para obtener el enlace final que estás buscando. Cada vez que necesito capacidades de interoperabilidad, vuelvo al modo VB.Net para poder aprovechar las capacidades COM que parecen faltar en C #.
El método simple que uso es crear una clase en VB.Net que haga el trabajo IDispatch y luego exponer los métodos que quiero usar como métodos de mi contenedor y luego puedo llamarlos a voluntad desde mi código C #. No es la más elegante de las soluciones, pero me ha sacado de un aprieto o dos en los últimos meses.
La pregunta
Mi pregunta es: ¿C # nativamente admite IDispatch de enlace tardío?
Imagino que estoy tratando de automatizar Office, al tiempo que soy compatible con cualquier versión que el cliente haya instalado.
En el mundo de .NET si desarrolló con Office 2000 instalado, cada desarrollador y cada cliente, desde ahora hasta el final de los tiempos, debe tener Office 2000.
En el mundo anterior a .NET, usamos COM para hablar con aplicaciones de Office.
Por ejemplo:
1) Use la versión independiente ProgID
"Excel.Application"
que resuelve a:
clsid = {00024500-0000-0000-C000-000000000046}
y luego usando COM, pedimos que una de estas clases sea instanciada en un objeto:
IUnknown unk;
CoCreateInstance(
clsid,
null,
CLSCTX_INPROC_SERVER | CLSCTX_LOCAL_SERVER,
IUnknown,
out unk);
Y ahora nos vamos a las carreras, podemos usar Excel desde mi aplicación. Por supuesto, si realmente quieres usar el objeto, tienes que llamar para tener alguna forma de llamar a los métodos.
Podríamos obtener las diversas declaraciones de interfaz traducidas a nuestro idioma. Esta técnica es buena porque obtenemos
- vinculación temprana
- código-visión
- compilar la verificación de sintaxis
y algún código de ejemplo podría ser:
Application xl = (IExcelApplication)unk;
ExcelWorkbook workbook = xl.Workbooks.Add(template, lcid);
Worksheet worksheet = workbook.ActiveSheet;
Pero hay una desventaja de usar interfaces: tenemos que obtener las diversas declaraciones de interfaz, transadas en nuestro idioma. Y estamos atrapados utilizando invocaciones basadas en métodos, teniendo que especificar todos los parámetros, por ejemplo:
ExcelWorkbook workbook = xl.Workbooks.Add(template, lcid);
xl.Worksheets.Add(before, after, count, type, lcid);
Esto ha demostrado, en el mundo real, tener tales desventajas que voluntariamente nos daríamos por vencidos:
- vinculación temprana
- código-visión
- tiempo de compilación de comprobación de sintaxis
y en su lugar use IDispatch binding tardío:
Variant xl = (IDispatch)unk;
Variant newWorksheet = xl.Worksheets.Add();
Debido a que la automatización de Excel se diseñó para VB Script, se pueden omitir muchos parámetros, incluso cuando no hay sobrecarga sin ellos.
Nota: No confunda mi ejemplo de Excel con una razón por la cual quiero usar IDispatch. No todos los objetos COM son Excel. Algunos objetos COM no tienen soporte más que a través de IDispatch.
La palabra clave dynamic
C # 4 admite IDispatch y vinculación tardía. Puede leer la serie dinámica de Sam Ng para más información
Ah, y C # 4 solo está disponible como CTP hoy. Tendrá que esperar a Visual Studio vNext o usar la versión beta (que se ejecuta en una PC virtual con Windows Server 2008) para usar eso.
Puede, relativly, utilizar enlace vinculante IDispatch en C #.
http://support.microsoft.com/kb/302902
Aquí hay algunos ejemplos para usar Excel. De esta forma, no es necesario que agregue una dependencia innecesaria a la PIA abatida 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 );
probablemente pueda salirse con la suya con un código mucho mejor en C # 2.0 / 3.0 si se toma el tiempo de escribir una interfaz que contenga los métodos y propiedades que desea del objeto y agregue algunos atributos (lo escribo de memoria, por lo que los detalles pueden no ser correcto, pero juro que funcionó para mí ...)
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
}
Como se mencionó, el código no funcionará de la caja, es más una pista para buscar en msdn.
Hola, Dude, actualmente tengo 2 proyectos Codeplex para resolver este problema.
el primero es LateBindingApi.Excel http://excel.codeplex.com mapeado llamada de invocación con retraso al modelo de objeto bien conocido. este fue un proyecto de prueba para el siguiente proyecto.
el segundo es un CodeGenerator http://latebindingapi.codeplex.com la herramienta crea proyectos c # de bibliotecas de tipos COM. los proyectos generados incluyen objetos mapeador con acceso tardío al servidor COM. Lo más destacado es que la herramienta convierte las bibliotecas de tipo COM en diferentes versiones en un solo proyecto (por ejemplo, excel 9,10,11) y marcó todas las entidades con un atributo definido por SupportByLibrary Attribut. He analizado todas las aplicaciones de Office en la versión 9,10,11,12,14 con esta herramienta ahora y genero una solución de CA, está disponible como versión beta probada con código de muestra en la página principal.
Como otros han dicho, usando las rocas clave "dinámicas" de c # 4. Aquí hay un ejemplo simple: es mucho más sucinto que usar "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"
}