Как я могу сравнить объекты Word Interop на «равенство ссылок» И определить коллекцию или родительский объект, к которому, скажем, принадлежит абзац? - PullRequest
7 голосов
/ 18 декабря 2011

Я хотел бы иметь возможность:

  1. сравнивать прокси-серверы Word Interop на основе "равенства ссылок";и
  2. отображать конкретный объект (скажем, абзац) в коллекцию, из которой он получен, ИЛИ как минимум
  3. определить, принадлежат ли два абзаца к одному и тому же разделу, а какой - относительно предыдущегоодин

Почему я хочу это сделать?Я пытаюсь создать надстройку Word, которая действует аналогично проверке орфографии в том смысле, что она работает в фоновом режиме (под фоном я имею в виду регулярное кражу времени из основного потока Word с помощью SendMessage) и сканирует документ на определеннуютекст "жетоны".Я хочу иметь возможность хранить коллекцию токенов и обновлять их по мере изменения документа.Конкретный пример этого - если пользователь редактирует данный абзац, я хочу повторно отсканировать абзац и обновить структуру данных, которая указывает на этот абзац.Если нет способа отобразить абзац, в котором пользователь отредактировал (т. Е. Абзац, где находится начало диапазона выделения), и абзац, который я «сохранил» в структуре данных, я не могу этого сделать.


Пример кода для элемента № 1, выше

Если я напишу следующий код VBA:

Dim Para1 As Paragraph
Dim Para2a As Paragraph
Dim Para2b As Paragraph
Set Para1 = ActiveDocument.Paragraphs(1)
Set Para2a = Para1.Next
Set Para2b = Para1.Next.Next.Previous
If Para2a Is Para2b Then
    Debug.Print ("Para2a Is Para2b")
Else
    Debug.Print ("Para2a Is Not Para2b")
End If

Тогда я получаю вывод:

"Para2a Is Not Para2b"

Возможно, это физически верно (разные COM-прокси), но не логически верно.Мне нужно иметь возможность сравнить эти абзацы и определить, являются ли они логически одним и тем же основным абзацем.

(я планирую написать надстройку на C #, но приведенный выше код VBA демонстрирует вид проблемы, которую янужно преодолеть, прежде чем делать слишком много кодирования).

Для пунктов 2 и 3 выше, надеюсь, они будут понятны.Скажем, у меня есть ссылка на абзац (interop proxy).Я хочу выяснить, «где» это в документе.Это относится к разделу 1?Это в нижнем колонтитуле?Без этой способности все, что я могу разумно сделать, чтобы получить представление о том, откуда что-то происходит, - повторно сканировать весь документ при каждом его изменении, что, конечно, абсурдно неэффективно и не будет достаточно своевременным для пользователя приложения.

Любые мысли с благодарностью!Я рад опубликовать дополнительную информацию по мере необходимости.

1 Ответ

2 голосов
/ 29 января 2012

Навигация по деталям ссылочного равенства в контексте COM Interop - это всегда интересное упражнение.

Я бы не был знаком с деталями реализации методов Paragraph.Next() и Paragraph.Previous(), однако поведение, которое они демонстрируют, очень похоже на поведение коллекций на основе COM в целом в отношении создания Runtime Callable Wrapper.

Как правило, если это возможно, среда избегает создания новых экземпляров RCW в ответ на дополнительные ссылки на COM-объекты, которые уже имеют инициализированный и назначенный RCW. Если RCW уже существует для определенного указателя на IUnknown, внутренний счетчик ссылок, поддерживаемый этим RCW, увеличивается, а затем возвращается RCW. Это позволяет платформе избежать увеличения фактического количества ссылок на COM-объекты (AddRef).

Коллекции на основе COM, которые являются COM-объектами с управляемыми представлениями, реализующими IEnumerable, по-видимому, генерируют новое RCW при каждом доступе к элементу, даже если к этому элементу уже был получен доступ во время сеанса.

Например:

Word.Document document = Application.ActiveDocument;
Paragraphs paragraphs = document.Paragraphs;

Paragraph first = paragraphs[1];
Paragraph second = paragraphs[1];

bool thisIsFalse = (first == second);

Если вы хотите выполнить какую-либо проверку «ссылочного равенства», вам нужно выйти из коллекции на основе COM, в частности, в вашем случае: объект Paragraphs. Вы можете сделать это, просто взяв детей и сохранив их в своей собственной, полностью управляемой и предсказуемой коллекции, например:

List<Paragraph> niceParagraphs = paragraphs.Cast<Paragraph>().ToList();

Хотя использование LINQ с COM Interop может выглядеть немного страшно (если это не так ... это действительно так!) Я вполне уверен, что приведенный выше код безопасен и не оставит никаких висящих ссылок, или что-нибудь еще противное. Однако я не проверил приведенный выше код исчерпывающе.

Не забудьте правильно освободить эти ресурсы, когда вы закончите с ними, по крайней мере, если ваши требования требуют такого уровня осторожности.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...