Office PIA:绕过Bug Q320369:Old format or invalid type library" error when automating Excel

来源:互联网 发布:网络图书馆图片素材 编辑:程序博客网 时间:2024/06/07 00:33

测试一定要充分!接上回书,话说我提交了Office PIA的代码以后,测试的同学就展开了充分的测试。在此之前我可是信心满满的,自以为已经做了充分的测试了,应该测不出什么bug来了。可是,很快,测试的同学就报了一个Exception issue给我:

System.Runtime.InteropServices.COMException (0x80028018): Old format or invalid type library. (Exception from HRESULT: 0x80028018 (TYPE_E_INVDATAREAD))   at ******.Excel.ExcelApp.CreateWorkbook(String fileName) in C:\svnroot\******\Trunk\Development\Software\******\Excel\ExcelApp.cs:line 213

(以上******是故意打的马赛克,你懂的。微笑


这个问题在我本地从不重现,在测试的机器上也不总是可重现。经比对,发现重现此issue的测试机,其语言和区域设置(Clock, Language, and Region)中的Format设置的是Chinese (Simplified, PRC)。简单地说,就是涉及到中文环境时能复现这个问题。在此要说明一下,本人就职于某大型外企,大家平时的的计算机都是英文的系统和英文的语言、区域、格式设置。

总算发现一定能复现这个exception issue的条件了,但是乍一看这个问题真是匪夷所思,我编程当中没有用到什么涉及到不同的Culture的地方,也不曾hard code地传入什么字符串内容,怎么会单单在非英语环境中出错呢?但是不管怎么说,我还是要检讨一下,说明我之前做的测试很不充分,没有做国际性/本地化测试。


好,问题的背景介绍到此。既然有了线索,那就好办了。借助万能的Google和MSDN,很快就找到了root cause。MSDN中已经有文章说明此事,微软承认这是Microsoft Office PIA的Bug:


BUG: "Old format or invalid type library" error when automating Excel

Link: http://support.microsoft.com/kb/320369

If you automate Microsoft Excel with Microsoft Visual Basic .NET, Microsoft Visual C# .NET, or Microsoft Visual C++, you may receive the following error when calling certain methods:

Error: 0x80028018 (-2147647512)
Description: Old Format or Invalid Type Library


......
......


此文中说到,当前线程的Culture不是英语(“en-US”)的时候,就会遭遇此问题。


怎么解决这个问题呢?其实这个问题我肯定没有办法解决,我只能说绕开这个问题(Workaround)。


首先我做的尝试是,在出问题的地方(Excel.Worksheet.SaveAs),前后分别设置当前Culture为“en-US”和恢复原有Culture属性。但是程序执行到其它的地方又报出同样的问题。可见,这个Office PIA Bug影响的绝不仅仅是SaveAs()这个方法,还有很多其他的方法。具体是哪些方法我就不去较这个劲了,我直接在类的初始化中设置当前线程的Culture为“en-US”,然后在类的Dispose方法中恢复原有Culture属性:


    using System;    using System.Collections.Generic;    using System.Globalization;    using System.IO;    using System.Threading;    using Excel = Microsoft.Office.Interop.Excel;    /// <summary>    /// Excel application interop class.    /// </summary>    public class ExcelApp : IDisposable    {        ......        ......        #region Private variables        /// <summary>Original culture info</summary>        private CultureInfo originalCultureInfo = null;        #endregion        ......        ......        #region Initializations and Finalizations        /// <summary>        /// Initializes a new instance of the ExcelApp class.        /// </summary>        public ExcelApp()        {            this.originalCultureInfo = Thread.CurrentThread.CurrentCulture;            this.SetCurrentCultureAsENU();        }        /// <summary>        /// Finalizes an instance of the ExcelApp class.        /// </summary>        ~ExcelApp()        {            this.Dispose(false);        }        #endregion        ......        ......        /// <summary>        /// Implement Dispose method in IDisposable        /// </summary>        public void Dispose()        {            this.Dispose(true);            GC.SuppressFinalize(this);        }        #endregion        ......        ......        #region Private methods for IDisposable        /// <summary>        /// Dispose method        /// </summary>        /// <param name="disposing">Disposing status</param>        protected void Dispose(bool disposing)        {            if (this.disposed)            {                return;            }            if (disposing)            {                this.Close();            }            this.RestoreCurrentCulture();            this.disposed = true;        }        #endregion        ......        ......        #region Private methods for Office PIA bug fixing        /// <summary>        /// Set current culture as en-US to resolve the bug described in Notice.        /// Notice: this method is to avoid Microsoft Office PIA bug Q320369:        /// BUG: "Old format or invalid type library" error when automating Excel        /// Link: http://support.microsoft.com/default.aspx?scid=kb;en-us;320369        /// </summary>        private void SetCurrentCultureAsENU()        {            Thread.CurrentThread.CurrentCulture = new CultureInfo("en-US");        }        /// <summary>        /// Restore original culture.        /// </summary>        private void RestoreCurrentCulture()        {            Thread.CurrentThread.CurrentCulture = this.originalCultureInfo;        }        #endregion    }

这样,就成功地绕过了这个Office PIA Bug。