C# MS Word获取可见文本

7
我将尝试使用Microsoft.Office.Interop.Word在C#中获取MS Word窗口中显示的文本内容。请注意,这不是整个文档或页面,而是用户看到的相同内容。
以下代码似乎适用于简单的文档:
Application word = new Application();
word.Visible = true;
object fileName = @"example.docx";
word.Documents.Add(ref fileName, Type.Missing, Type.Missing, true);

Rect rect = AutomationElement.FocusedElement.Current.BoundingRectangle;

Range r1 = word.ActiveWindow.RangeFromPoint((int)rect.Left, (int)rect.Top);
Range r2 = word.ActiveWindow.RangeFromPoint((int)rect.Left + (int)rect.Width, (int)rect.Top + (int)rect.Height);
r1.End = r2.Start;

Console.WriteLine(r1.Text.Replace("\r", "\r\n"));

然而,当文档包含其他结构,如标题时,只有部分文本会被返回。

那么,正确的方法是什么呢?

非常感谢!

更新的代码

Rect rect = AutomationElement.FocusedElement.Current.BoundingRectangle;

foreach (Range r in word.ActiveDocument.StoryRanges) {
    int left = 0, top = 0, width = 0, height = 0;
    try {
        try {
            word.ActiveWindow.GetPoint(out left, out top, out width, out height, r);
        } catch {
            left = (int)rect.Left;
            top = (int)rect.Top;
            width = (int)rect.Width;
            height = (int)rect.Height;
        }
        Rect newRect = new Rect(left, top, width, height);
        Rect inter;
        if ((inter = Rect.Intersect(rect, newRect)) != Rect.Empty) {
            Range r1 = word.ActiveWindow.RangeFromPoint((int)inter.Left, (int)inter.Top);
            Range r2 = word.ActiveWindow.RangeFromPoint((int)inter.Right, (int)inter.Bottom);
            r.SetRange(r1.Start, r2.Start);

            Console.WriteLine(r.Text.Replace("\r", "\r\n"));
        }
    } catch { }
}
4个回答

4
可能会存在一些问题:
  • 它不可靠。你能确保每次都能得到一致的结果吗?例如,在一个简单的“=rand()”文档中,连续运行5次程序而不改变Word的状态。当我这样做时,每次都会在控制台打印出不同的范围。我首先会从这里开始:你获取范围的逻辑似乎有问题。例如,rect.Left每次针对同一份文档都返回不同的数字。
  • 它在处理其他故事时变得棘手。也许RangeFromPoint不能跨越多个故事边界。然而,我们假设它可以。你仍然需要枚举每个故事,例如:

enumerator = r1.StoryRanges.GetEnumerator(); { while (enumerator.MoveNext() { Range current = (Range) enumerator.Current; } }

你是否尝试过查看如何以编程方式提取Office.Interop.Word.Document对象当前查看页面的文本


谢谢你的回答,你关于StoryRanges的建议很有用! - Nacho
不用谢。看起来“更新的代码”是解决方案。好奇,这是什么屏幕阅读器? - JohnZaj
这是一个语音识别应用程序。 - Nacho
一位Word Interop的智者曾经告诉我:了解Word interop只能让你走得更远。大多数情况下,是Word与用户(或程序员)斗争,而不是相反。我希望你已经找到了解决方案。如果没有,我建议研究一下OOXML所提供的内容(以及旧版xml格式的WordML)。例如,获取Range.XML / Range.WordOpenXML。 - JohnZaj

1
上述讨论非常具体,与Office版本有关。我认为我的代码在所有情况下都能正常工作。
        IntPtr h = (IntPtr)Globals.ThisAddIn.Application.ActiveWindow.Hwnd;
        String strText = NativeInvoker.GetWindowText(h);
        if (strText != null && strText.StartsWith(Globals.ThisAddIn.Application.ActiveWindow.Caption))
        {
            h = NativeInvoker.FindWindowEx(h, IntPtr.Zero, "_WwF", "");
            h = NativeInvoker.FindWindowEx(h, IntPtr.Zero, "_WwB", null);
            h = NativeInvoker.FindWindowEx(h, IntPtr.Zero, "_WwG", null);

            Rect t;
            if (NativeInvoker.GetWindowRect(h, out t))
            {
                Range r1 = (Range)Globals.ThisAddIn.Application.ActiveWindow.RangeFromPoint((int)t.Left, (int)t.Top);
                Range r2 = (Range)Globals.ThisAddIn.Application.ActiveWindow.RangeFromPoint((int)t.Right, (int)t.Bottom);
                Range r = Globals.ThisAddIn.Application.ActiveDocument.Range(r1.Start, r2.Start);
                ....

你可以从任何地方参考NativeInvoker类的内容。
我希望我的代码能帮助你的工作。
Phon.

1

您可能正在看到跨页面元素选择的副作用。

在大多数情况下,如果您将光标移动到屏幕左上角,然后向下移动到屏幕右下角,它只会选择主体文本(不包括标题或页脚)。此外,如果文档具有列,并且这些列从屏幕外开始或结束,则当您从第一列选择文本时,直到最后一列的文本都将被选择,即使它超出了屏幕范围。

据我所知,除非您愿意忽略不一致性,或者想要专门处理所有用例(图像、列、表格等),否则没有简单的方法可以实现您的目标。

如果您能告诉我们您想要做什么,那么我们可以提供替代方案,否则请将答案标记为正确。


0

我在我的Word插件中有类似的要求。

尝试下面的代码,这对我有效。

IntPtr h = Process.GetCurrentProcess().MainWindowHandle;

            h = NativeMethodsActiveScreen.FindWindowExW(h, new IntPtr(0), "_WwF", "");
            h = NativeMethodsActiveScreen.FindWindowExW(h, new IntPtr(0), "_WwB", null);
            h = NativeMethodsActiveScreen.FindWindowExW(h, new IntPtr(0), "_WwG", null);

            NativeMethodsActiveScreen.tagRECT t = new NativeMethodsActiveScreen.tagRECT();
            NativeMethodsActiveScreen.GetWindowRect(h, out t);

            var Aw = RibbonHelper.SharedApplicationInstance.ActiveWindow;
            Range fullDocRange = RibbonHelper.SharedApplicationInstance.ActiveDocument.Range();
            Range r1 = RibbonHelper.SharedApplicationInstance.ActiveWindow.RangeFromPoint(t.left, t.top);
            Range r2 = RibbonHelper.SharedApplicationInstance.ActiveWindow.RangeFromPoint(t.right, t.bottom);
            Range r = RibbonHelper.SharedApplicationInstance.ActiveDocument.Range(r1.Start, r2.Start);

如果这有帮助,请标记回答为有用。

谢谢。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接