visual programar programacion paso macro formularios crear comandos c# vsto excel-addins rtd excel-udf

programar - ¿Cómo creo un complemento de automatización de Excel en tiempo real en C#usando RtdServer?



programar en excel 2010 (3)

(Como alternativa al enfoque que se describe a continuación, debería considerar usar Excel-DNA . Excel-DNA le permite construir un servidor RTD sin registro. El registro COM requiere privilegios administrativos que pueden llevar a dolores de instalación. Dicho esto, el código a continuación funciona bien.)

Para crear un complemento de automatización de Excel en tiempo real en C # utilizando RtdServer:

1) Cree un proyecto de biblioteca de clase C # en Visual Studio e ingrese lo siguiente:

using System; using System.Threading; using System.Collections.Generic; using System.Runtime.InteropServices; using Microsoft.Office.Interop.Excel; namespace StackOverflow { public class Countdown { public int CurrentValue { get; set; } } [Guid("EBD9B4A9-3E17-45F0-A1C9-E134043923D3")] [ProgId("StackOverflow.RtdServer.ProgId")] public class RtdServer : IRtdServer { private readonly Dictionary<int, Countdown> _topics = new Dictionary<int, Countdown>(); private Timer _timer; public int ServerStart(IRTDUpdateEvent rtdUpdateEvent) { _timer = new Timer(delegate { rtdUpdateEvent.UpdateNotify(); }, null, TimeSpan.Zero, TimeSpan.FromSeconds(5)); return 1; } public object ConnectData(int topicId, ref Array strings, ref bool getNewValues) { var start = Convert.ToInt32(strings.GetValue(0).ToString()); getNewValues = true; _topics[topicId] = new Countdown { CurrentValue = start }; return start; } public Array RefreshData(ref int topicCount) { var data = new object[2, _topics.Count]; var index = 0; foreach (var entry in _topics) { --entry.Value.CurrentValue; data[0, index] = entry.Key; data[1, index] = entry.Value.CurrentValue; ++index; } topicCount = _topics.Count; return data; } public void DisconnectData(int topicId) { _topics.Remove(topicId); } public int Heartbeat() { return 1; } public void ServerTerminate() { _timer.Dispose(); } [ComRegisterFunctionAttribute] public static void RegisterFunction(Type t) { Microsoft.Win32.Registry.ClassesRoot.CreateSubKey(@"CLSID/{" + t.GUID.ToString().ToUpper() + @"}/Programmable"); var key = Microsoft.Win32.Registry.ClassesRoot.OpenSubKey(@"CLSID/{" + t.GUID.ToString().ToUpper() + @"}/InprocServer32", true); if (key != null) key.SetValue("", System.Environment.SystemDirectory + @"/mscoree.dll", Microsoft.Win32.RegistryValueKind.String); } [ComUnregisterFunctionAttribute] public static void UnregisterFunction(Type t) { Microsoft.Win32.Registry.ClassesRoot.DeleteSubKey(@"CLSID/{" + t.GUID.ToString().ToUpper() + @"}/Programmable"); } } }

2) Haga clic con el botón derecho en el proyecto y agregue> Nuevo elemento ...> Clase de instalador. Cambie a la vista de código e ingrese lo siguiente:

using System.Collections; using System.ComponentModel; using System.Diagnostics; using System.Runtime.InteropServices; namespace StackOverflow { [RunInstaller(true)] public partial class RtdServerInstaller : System.Configuration.Install.Installer { public RtdServerInstaller() { InitializeComponent(); } [System.Security.Permissions.SecurityPermission(System.Security.Permissions.SecurityAction.Demand)] public override void Commit(IDictionary savedState) { base.Commit(savedState); var registrationServices = new RegistrationServices(); if (registrationServices.RegisterAssembly(GetType().Assembly, AssemblyRegistrationFlags.SetCodeBase)) Trace.TraceInformation("Types registered successfully"); else Trace.TraceError("Unable to register types"); } [System.Security.Permissions.SecurityPermission(System.Security.Permissions.SecurityAction.Demand)] public override void Install(IDictionary stateSaver) { base.Install(stateSaver); var registrationServices = new RegistrationServices(); if (registrationServices.RegisterAssembly(GetType().Assembly, AssemblyRegistrationFlags.SetCodeBase)) Trace.TraceInformation("Types registered successfully"); else Trace.TraceError("Unable to register types"); } public override void Uninstall(IDictionary savedState) { var registrationServices = new RegistrationServices(); if (registrationServices.UnregisterAssembly(GetType().Assembly)) Trace.TraceInformation("Types unregistered successfully"); else Trace.TraceError("Unable to unregister types"); base.Uninstall(savedState); } } }

3) Haga clic con el botón derecho en Propiedades del proyecto y marque lo siguiente: Aplicación> Información de ensamblaje ...> Crear ensamblaje COM-Visible y compilar> Registrar para COM Interop

3.1) Haga clic con el botón derecho en el proyecto Agregar referencia ...> pestaña .NET> Microsoft.Office.Interop.Excel

4) Solución de compilación (F6)

5) Ejecutar Excel. Vaya a Opciones de Excel> Complementos> Administrar complementos de Excel> Automatización y seleccione "StackOverflow.RtdServer"

6) Ingrese "= RTD (" StackOverflow.RtdServer.ProgId ",, 200) en una celda.

7) Cruza los dedos y ¡espera que funcione!

Me encargaron escribir un complemento de automatización de Excel en tiempo real en C # utilizando RtdServer para el trabajo. Confié mucho en el conocimiento que encontré en Stack Overflow. He decidido expresar mi agradecimiento escribiendo un documento sobre cómo unir todo lo que he aprendido. Servidores de RTD de Excel de Kenny Kerr : el artículo de Implementación de Minimal C # me ayudó a comenzar. Los comentarios de Mike Rosenblum y Govert me parecieron especialmente útiles.


Llamar a UpdateNotify desde el hilo del temporizador eventualmente causará errores extraños o desconexiones de Excel.

El método UpdateNotify () solo debe invocarse desde el mismo hilo que llama a ServerStart (). No está documentado en la ayuda de RTDServer, pero es una restricción de COM.

La solución es simple. Use DispatcherSynchronizationContext para capturar el hilo que llama a ServerStart y utilícelo para enviar llamadas a UpdateNotify:

public class RtdServer : IRtdServer { private IRTDUpdateEvent _rtdUpdateEvent; private SynchronizationContext synchronizationContext; public int ServerStart( IRTDUpdateEvent rtdUpdateEvent ) { this._rtdUpdateEvent = rtdUpdateEvent; synchronizationContext = new DispatcherSynchronizationContext(); _timer = new Timer(delegate { PostUpdateNotify(); }, null, TimeSpan.Zero, TimeSpan.FromSeconds(5)); return 1; } // Notify Excel of updated results private void PostUpdateNotify() { // Must only call rtdUpdateEvent.UpdateNotify() from the thread that calls ServerStart. // Use synchronizationContext which captures the thread dispatcher. synchronizationContext.Post( delegate(object state) { _rtdUpdateEvent.UpdateNotify(); }, null); } // etc

}


Seguir las dos respuestas anteriores para el servidor RTD funcionó para mí. Sin embargo, me encontré con un problema cuando estaba en una máquina x64 que ejecutaba Excel x64. En mi caso, hasta que cambié la "plataforma de destino" del proyecto a x64, Excel siempre mostraba # N / A.