我正在使用 COM shim 向导开发 Word 共享加载项。 http://blogs.msdn.com/b/mshneer/archive/2010/03/19/com-shim-wizards-for-vs-2010.aspx
一切正常,直到我尝试使用一些线程。它在某些机器上失败,主线程中创建的 COM 对象的 TYPE_E_LIBNOTREGISTERED 异常。
我有一个来自某个 Word 事件的 Word 文档对象 (_doc),并尝试从 STA 线程中使用,如下所示:
Word.Document _doc;
void Start()
{
_mainLoopThread = new Thread(MainLoop);
_mainLoopThread.SetApartmentState(ApartmentState.STA);
_mainLoopThread.IsBackground = true;
_mainLoopThread.Start();
}
void MainLoop()
{
// some code...
Word.Range r = _doc.StoryRanges[Word.WdStoryType.wdMainTextStory];
// some code...
}
在我的开发机器、测试机器和大多数用户上一切正常。但是,对于某些用户,它会失败并出现异常:无法将类型为“Microsoft.Office.Interop.Word.DocumentClass”的 COM 对象转换为接口类型“Microsoft.Office.Interop.Word._Document”。此操作失败,因为 IID 为“{0002096B-0000-0000-C000-000000000046}”的接口的 COM 组件上的 QueryInterface 调用因以下错误而失败:库未注册。(来自 HRESULT 的异常:0x8002801D (TYPE_E_LIBNOTREGISTERED))。
我们能够重现它的唯一方法是安装 Office 2013,然后将其删除并安装 Office 2007。我检查了注册表,但没有发现此 guid 的任何注册问题。我尝试了 MS 的工具,它应该可以清除 Office 2013 的残留物, http://support.microsoft.com/kb/2739501 , 但它没有帮助。
这不是注册问题,因为我可以在主线程中使用相同的 _doc 对象而不会出现任何错误。在 COM-shim 的 rgs 文件中,我使用 STA 模型 InprocServer32 = s '%MODULE%' { val ThreadingModel = s 'Apartment' } 我还在 Properties-Linker-Advanced 中将 CLR 模式设置为 STA,但这没有帮助。
我尝试使用 CoMarshalInterThreadInterfaceInStream,如此处所建议的, 无法从其他 STA 线程调用从 STAThread 创建的 COM 对象, 但即使在我的开发机器上也无法使其工作。这是代码: [DllImport("ole32.dll")] static extern int CoMarshalInterThreadInterfaceInStream([In] ref Guid riid, [MarshalAs(UnmanagedType.IUnknown)] object pUnk, out IStream ppStm);
IStream _docForThreadStream = null;
Guid _docInterfaceGuid;
void InitDocForThread()
{
object[] attr = _doc.GetType().GetCustomAttributes(typeof(GuidAttribute), false);
GuidAttribute g = (GuidAttribute)attr[0];
_docInterfaceGuid = new Guid(g.Value);
CoMarshalInterThreadInterfaceInStream(ref _docInterfaceGuid, Marshal.GetIUnknownForObject(_doc), out _docForThreadStream);
}
[DllImport("ole32.dll")]
static extern int CoGetInterfaceAndReleaseStream(IStream pStm, [In] ref Guid riid, out object ppv);
Word.Document _docForThread = null;
void MainLoop()
{
object pDoc;
int hr = CoGetInterfaceAndReleaseStream(_docForThreadStream, ref _docInterfaceGuid, out pDoc);
// _docForThreadStream != null, hr == E_NOINTERFACE
}
当我尝试从 IStream 获取对象时,CoGetInterfaceAndReleaseStream 给了我 E_NOINTERFACE。任何人都可以向我发送 C# 工作示例的链接吗?我用谷歌搜索,但只找到了一些 C++ 的例子。
似乎 Office COM 注册被破坏,以至于它适用于同一个线程(加载项与 office 在同一个线程中运行),但未能编组到另一个 STA 线程......