C# VSTO |在 Office365 中启用 ModernComments 时,尝试访问任何形状的属性时,Word 会崩溃

问题描述 投票:0回答:1

我有一个用于 Word 的 VSTO 插件,其功能之一是设置 CustomDocumentProperties,然后刷新文档内的所有字段。

文档内的字段可以位于文档的主要文本中,但也可以位于形状内部。使用 Document.Fields 属性仅提供文档主文本中的字段集合,而不是形状内的字段。因此,为了确保我们获得所有字段,我们检查每个形状以查看其中是否有任何字段。

这在所有版本的 Word 中都可以正常工作,除非启用了 ModernComments。当启用 ModernComments 并且代码尝试访问形状时,Word 完全崩溃(并退出),并且在文档中工作的用户将丢失所有未保存的更改。我发现其他人遇到类似行为的两个 StackOverflow 帖子:

  1. 访问 Word Shape.Type 会抛出 COMException(HRESULT:0x80004005 E_FAIL)

  2. C# COMException 读取 MSWord Shape 对象 Microsoft.Office.Interop.Word 的属性

在第二篇文章中,建议在访问 Shape 的任何属性之前首先执行 Shape.Select() 调用。这确实可以防止 Word 崩溃,但它会产生浏览文档并选择形状的副作用。

我尝试在迭代所有形状(并对所有形状执行 Shape.Select())然后返回书签之前创建一个书签,但是,当用户在(现代)注释中工作时,如果我导航到书签,视图切换到不同的视图。

这是(尝试访问形状时启用 ModernComments 时 Word 崩溃)是已知问题吗?

考虑到上述问题,可靠的解决方案是什么(用于检索所有字段)?

我还在 Microsoft 论坛上针对此问题创建了一个帖子,此处: https://answers.microsoft.com/en-us/msoffice/forum/all/c-vsto-word-crashes-when-attempting-to-access-the/88a33099-6859-43c4-b506-8c9d0e6afcf0

c# .net ms-word vsto office-addins
1个回答
0
投票

我解决了这个问题。 要向后导航,请使用 Range.Select(),但如果您处于(现代)评论中,请使用 Comment.Edit()。

请参阅下面的代码:

private void GetAllFieldsFromShapes(Word.Shapes shapes, ref List<Word.Field> fields)
    {
        if (fields == null) { fields = new List<Word.Field>(); }

        //We need to keep track of if we are inside of a modern comment.
        //The reason being that, performing a selection of a Shape, does indeed select the Shape in the graphical interface (Word/Excel, etc.)
        //which could introduce all kinds of problems and it just looks weird for the User...
        //We should make sure that the previously selected Shape does not stay selected. The way to do this is different depending on if we're in a (modern) comment or not.
        //If we're in a (modern) comment, then we need to enter the edit view of the comment.
        //If we're not in a (modern) comment, then we need to select the area which we had selected before selecting the Shape.
        var wordApp = Library.OfficeApplication as Word.Application;
        var selection = wordApp.Selection;
        var isInComment = selection.StoryType == Word.WdStoryType.wdCommentsStory;
        var range = selection.Range;

        try
        {
            foreach (var item in shapes)
            {
                if (item is Word.Shape)
                {
                    Word.Shape shape = null;

                    try
                    {
                        shape = item as Word.Shape;

                        //====
                        //I don't know why, but for some shapes, if you do not call 'Shape.Select()' then some of the underlying Properties are released and cannot be accessed.
                        //More info here: https://stackoverflow.com/questions/36043690/c-sharp-comexception-reading-property-of-msword-shape-object-microsoft-office-in
                        //and
                        //here: https://stackoverflow.com/questions/76226042/accessing-word-shape-type-throws-comexception-hresult-0x80004005-e-fail
                        shape.Select();
                        //
                        //However, it is important to note that, by performing this call, the shape is selected and we do actually need to perform an unselect action later on!
                        //
                        //====

                        var fieldsCount = 0;
                        //Get the Fields in the current Shape
                        try { fieldsCount = shape.TextFrame?.TextRange?.Fields?.Count ?? 0; }
                        catch { }

                        if (fieldsCount > 0)
                        {
                            foreach (Word.Field field in shape.TextFrame.TextRange.Fields)
                            {
                                fields.Add(field);
                            }
                        }
                    }
                    finally
                    {
                        if (shape != null) { Marshal.ReleaseComObject(shape); }
                    }
                }
            }
        }
        finally
        {
            //"Unselect" the selected shape

            if (range != null)
            {
                if (isInComment == false)
                {
                    //We can unselect the selected shape by selecting the area the cursor was before we selected the shape
                    try { range.Select(); }
                    catch { }
                }
                else
                {
                    //If we try to select the area where the cursor was when we were in a (modern) comment, then a special window will popup which looks weird.                    
                    //So, to navigate to the place we were before, we need to open the edit window for the (modern) comment

                    if (range.Comments != null)
                    {
                        var comment = range.Comments[1];
                        if (comment != null)
                        {
                            //Open the edit window (which is actually still open, but will cause the window to gain focus again and
                            //which will navigate us back to the selected comment)
                            try { comment.Edit(); }
                            catch { }
                        }
                    }
                }
            }

            if (range != null) { Marshal.ReleaseComObject(range); }
            if (selection != null) { Marshal.ReleaseComObject(selection); }
        }
    }
© www.soinside.com 2019 - 2024. All rights reserved.