Comment créer un add-in d'automatisation Excel en temps réel dans C# en utilisant RtdServer?

on m'a demandé d'écrire un add-in D'automatisation Excel en temps réel dans C# en utilisant RtdServer pour le travail. J'ai beaucoup compté sur la connaissance que j'ai rencontré dans le débordement de la pile. J'ai décider d'exprimer mes remerciements par la rédaction d'un document qui rassemble tout ce que j'ai appris. Kenny Kerr!--1-->Excel Serveurs RTD: peu de C# de la mise en Œuvre article m'a aidé à obtenir commencé. J'ai trouvé des commentaires par Mike Rosenblum et Govert surtout utile.

24
demandé sur Community 2011-03-22 23:44:19

3 réponses

Excel-DNA. Excel-DNA vous permet de construire un serveur RTD sans inscription. L'enregistrement COM nécessite des privilèges administratifs qui peuvent mener à des maux de tête d'installation. Cela dit, le code ci-dessous fonctionne très bien.)

Pour créer en temps réel une automatisation d'Excel add-in en C# à l'aide de RtdServer:

1) Créer un projet de bibliothèque C# class dans Visual Studio et entrer dans le suivantes:

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) clic Droit sur le projet et Ajouter > Nouvel Élément... > Classe Du Programme D'Installation. Passez en mode code et entrez le code suivant:

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) Cliquez avec le bouton droit de la souris sur les propriétés du projet et cochez ce qui suit: Application > Information sur L'assemblage... > Faire de l'assemblée COM-Visible et Construire > Inscrivez-vous pour COM Interop

3.1) clic droit sur le Projet Ajouter une référence... > .net tab > Microsoft.Bureau.Interop.Excel

4) Construire Solution (F6)

5) Exécutez Excel. Allez à Options Excel > Add-Ins > Gérer Excel Add-Ins > Automation et sélectionnez "StackOverflow.RtdServer"

6) entrer " = RTD ("StackOverflow.RtdServer.ProgId",, 200) " dans une cellule.

7) croisez les doigts et espérez que ça marche!

31
répondu Frank 2014-01-29 00:01:18

appeler UpdateNotify à partir du thread de minuterie provoquera éventuellement des erreurs étranges ou des déconnexions avec Excel.

la méthode UpdateNotify () ne doit être appelée que depuis le même thread que ServerStart (). Ce n'est pas documenté dans L'aide RTDServer, mais c'est une restriction de COM.

La solution est simple. Utilisez la Synchronisationcontext de dispatchers pour capturer le thread qui appelle ServerStart et l'utiliser pour envoyer des appels vers 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

}

6
répondu dlaudams 2014-06-26 04:31:48

suite aux deux réponses précédentes pour le serveur RTD a fonctionné pour moi. Cependant, j'ai rencontré un problème sur une machine x64 tournant Excel x64. Dans mon cas, Jusqu'à ce que je commute la "plate-forme cible" du projet à x64, Excel a toujours montré #N/A.

0
répondu AnonymousUser 2016-04-02 16:31:48