测试一定要充分!接上回书,话说我提交了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。