从另一个线程更新 excel 电子表格

Posted

技术标签:

【中文标题】从另一个线程更新 excel 电子表格【英文标题】:Update excel spreadsheet from another thread 【发布时间】:2013-11-04 22:15:06 【问题描述】:

我使用 C# 创建了 COM 服务器,我的客户可以在其中接收实时更新。 更新通常从不同的线程触发。 但我注意到当回调方法更新电子表格时 Excel 会崩溃。 有什么方法可以在 UI 线程中调用更新?

附:我知道 Excel 的 RTD 功能。但它不适合我的需要,因为我需要在一次更新中使用多个参数。

【问题讨论】:

【参考方案1】:

你要找的是 ISyncronizedInvoke 的 Invoke / BeginInvoke 方法

在您的 UI 线程上,进行任意控制并保留该引用...

从要触发更新的线程,调用该控件上的 Invoke 或 BeginInvoke(控件实现 ISyncronizedInvoke),并使用您要在 UI 线程上执行的委托...从该委托您可以调用您的 COM 服务器

//编辑:示例代码

using System;
using System.Windows.Forms;
using System.Threading;
using Microsoft.Office.Interop.Excel;
using System.Runtime.InteropServices;

namespace WindowsFormsApplication1

    public partial class Form1 : Form
    
        Thread someWorkerThread;

        Microsoft.Office.Interop.Excel.Application ExApp;
        Worksheet wrkSheet;

        public Form1()
        
            InitializeComponent();

            ExApp = new Microsoft.Office.Interop.Excel.Application();

            ExApp.Visible = true; // or else we won't see the window

            var books = ExApp.Workbooks;
            var wrkBook = books.Add();
            var sheets = wrkBook.Worksheets;
            wrkSheet = sheets.get_Item(1);

            Marshal.ReleaseComObject(sheets);
            Marshal.ReleaseComObject(wrkBook);
            Marshal.ReleaseComObject(books);

            someWorkerThread = new Thread(new ParameterizedThreadStart(threadHandler));
            someWorkerThread.Start(this);
        

        private void threadHandler(object obj)
        // this will be executed on a seperate worker thread
            Control mainFrm = obj as Control;
            if (mainFrm == null)
                throw new ArgumentException("Need to have a Control as parameter");
            for (int i = 1; i < 50;i++ )
            
                Thread.Sleep(2500);
                mainFrm.Invoke(new Action<int>(doStuff), i); // this will invoke the main UI thread
            
        

        private void doStuff(int i)
        // this will be executed on the main UI thread
            var range = wrkSheet.Range[string.Format("A0", i)];
            range.Value = "Hello World!";
            Marshal.ReleaseComObject(range);
        


        #region designer stuff
        /// <summary>
        /// Required designer variable.
        /// </summary>
        private System.ComponentModel.IContainer components = null;

        /// <summary>
        /// Clean up any resources being used.
        /// </summary>
        /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
        protected override void Dispose(bool disposing)
        
            if (disposing && (components != null))
            
                components.Dispose();
            
            base.Dispose(disposing);
        

        #region Windows Form Designer generated code

        /// <summary>
        /// Required method for Designer support - do not modify
        /// the contents of this method with the code editor.
        /// </summary>
        private void InitializeComponent()
        
            this.label1 = new System.Windows.Forms.Label();
            this.SuspendLayout();
            // 
            // label1
            // 
            this.label1.AutoSize = true;
            this.label1.Location = new System.Drawing.Point(76, 84);
            this.label1.Name = "label1";
            this.label1.Size = new System.Drawing.Size(149, 13);
            this.label1.TabIndex = 0;
            this.label1.Text = "I am an ordinary windows form";
            // 
            // Form1
            // 
            this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
            this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
            this.ClientSize = new System.Drawing.Size(284, 262);
            this.Controls.Add(this.label1);
            this.Name = "Form1";
            this.Text = "Form1";
            this.ResumeLayout(false);
            this.PerformLayout();

        

        #endregion

        private System.Windows.Forms.Label label1;

        #endregion
    

请注意:此示例不会清理 wrkSheet 引用和 ExApp 引用...您必须在退出应用程序之前释放它们

【讨论】:

抱歉没有得到我怎样才能到达 ISyncronizedInvoke 的实例? 你的意思是创建隐藏表单吗? 不......你有你的程序......我猜你的程序有一个用户可见的表单......你的调用线程将需要对该表单的引用并使用它来调用 UI 线程....需要一个例子吗? 啊...这是 MS Excel - 我不确定是否实施了 ISyncronizedInvoke 查看示例代码...请注意:此示例不会清理 wrkSheet 引用和 ExApp 引用...您必须在退出应用程序之前释放它们

以上是关于从另一个线程更新 excel 电子表格的主要内容,如果未能解决你的问题,请参考以下文章

Pentaho/PDI/Kettle:如何用 Excel 电子表格中的值填充“插入/更新”?

Excel 2016 Power Query无法加载到电子表格

如何从 Rails 中的 Excel 电子表格填充表格?

ADO 错误信息

自动化 Excel 工作 - 将平面文件转换为 Excel 电子表格

Excel 在团队中使用电子表格打开、修改、保存