Ms word 从VSTO加载项检测Word 2016中的文本更改

Ms word 从VSTO加载项检测Word 2016中的文本更改,ms-word,vsto,visual-studio-2015,office-addins,Ms Word,Vsto,Visual Studio 2015,Office Addins,这个问题与Windows 10中运行的Word 2016在Visual Studio(Professional)2015中的开发密切相关(事实上包括该问题答案中的示例代码) 我试图检测Word文档中的文本何时从VSTO加载项发生更改。我从 (2011年11月14日) (2012年10月21日) (2012年10月24日) (2012年11月5日) 没有事件驱动的方法来实现这一点。文本更改时Word不会发送事件 我已经讨论了两种变通方法: 使用事件。不幸的是,当通过按箭头键、使用鼠标、执行撤消

这个问题与Windows 10中运行的Word 2016在Visual Studio(Professional)2015中的开发密切相关(事实上包括该问题答案中的示例代码)

我试图检测Word文档中的文本何时从VSTO加载项发生更改。我从

  • (2011年11月14日)
  • (2012年10月21日)
  • (2012年10月24日)
  • (2012年11月5日)
没有事件驱动的方法来实现这一点。文本更改时Word不会发送事件

我已经讨论了两种变通方法:

  • 使用事件。不幸的是,当通过按箭头键、使用鼠标、执行撤消或重做以及可能的其他操作更改选择时,此事件似乎会发送,但在键入或删除时则不会发送
  • 使用低级keydown事件挂钩。这已经在几个StackOverflow问题中进行了讨论,在2014年2月也被称为“广泛传播的技术”
  • 我试图在回答中使用代码,它似乎观察到了除发送到Word 2016的事件外的所有按键事件

    以下是我正在使用的代码,以便于参考

    使用系统;
    使用系统诊断;
    使用System.Runtime.InteropServices;
    使用系统线程;
    使用System.Windows.Forms;
    名称空间关键字DownWordAddin
    {
    公共部分类ThisAddIn
    {
    专用常量int WH_键盘LL=13;
    私有常量int WM_KEYDOWN=0x0100;
    私有静态IntPtr hookId=IntPtr.Zero;
    私有委托IntPtr HookProcedure(intncode、IntPtr wParam、IntPtr lParam);
    私有静态HookProcedure=HookCallback;
    [DllImport(“kernel32.dll”,CharSet=CharSet.Auto,SetLastError=true)]
    私有静态外部IntPtr GetModuleHandle(字符串lpModuleName);
    [DllImport(“user32.dll”,SetLastError=true)]
    私有静态外部bool unhookwindowshookx(IntPtr hhk);
    [DllImport(“user32.dll”,CharSet=CharSet.Auto,SetLastError=true)]
    私有静态外部IntPtr SetWindowsHookEx(intidhook、HookProcedure lpfn、IntPtr hMod、uint dwThreadId);
    [DllImport(“user32.dll”,CharSet=CharSet.Auto,SetLastError=true)]
    私有静态外部IntPtr CallNextHookEx(IntPtr hhk、intncode、IntPtr wParam、IntPtr lParam);
    私有静态IntPtr SetHook(HookProcedure)
    {
    使用(Process=Process.GetCurrentProcess())
    使用(ProcessModule=process.MainModule)
    返回SetWindowsHookEx(WH_KEYBOARD,procedure,GetModuleHandle(module.ModuleName),0);
    }
    专用静态IntPtr钩子回调(int nCode、IntPtr wParam、IntPtr lParam)
    {
    如果(nCode>=0&&wParam==(IntPtr)WM\u KEYDOWN)
    {
    int pointerCode=Marshal.ReadInt32(LPRAM);
    字符串按键=((键)指针代码).ToString();
    //对按键进行某种处理。
    变量线程=新线程(()=>{
    Debug.WriteLine(按键);
    });
    thread.Start();
    }
    返回CallNextHookEx(hookId、nCode、wParam、lParam);
    }
    私有void ThisAddIn_启动(对象发送方,事件参数e)
    {
    hookId=SetHook(程序);
    }
    私有void ThisAddIn\u关闭(对象发送方,事件参数e)
    {
    UnhookWindowsHookEx(hookId);
    }
    #区域VSTO生成的代码
    /// 
    ///设计器支持所需的方法。
    /// 
    私有void InternalStartup()
    {
    this.Startup+=new System.EventHandler(ThisAddIn\u启动);
    this.Shutdown+=new System.EventHandler(ThisAddIn\u Shutdown);
    }
    #端区
    }
    }
    
    当我使用此加载项运行Word 2016时,我会看到键控事件发送到边缘浏览器甚至Visual Studio,但不会发送到Word本身


    Word 2016中是否以某种方式阻止了键控挂钩,或者我做错了什么?

    我在Word 2013中也遇到过同样的问题,必须想出一个有点“创造性”的解决方案。它用于监视活动文档文本中的更改,并在更改时触发事件。这并不理想,但我们做了我们必须做的事情来让事情顺利进行

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    using System.Xml.Linq;
    using Word = Microsoft.Office.Interop.Word;
    using Office = Microsoft.Office.Core;
    using Microsoft.Office.Tools.Word;
    using System.ComponentModel;
    
    namespace WordUtils {
        public class TextChangeDetector {
    
            public Word.Application Application;
            private BackgroundWorker bg;
    
            public delegate void TextChangeHandler(object sender, TextChangedEventArgs e);
            public event TextChangeHandler OnTextChanged;
    
            public TextChangeDetector(Word.Application app) {
                this.Application = app;
            }
    
            public void Start() {
                bg = new BackgroundWorker();
                bg.WorkerReportsProgress = true;
                bg.WorkerSupportsCancellation = true;
                bg.ProgressChanged += bg_ProgressChanged;
                bg.DoWork += bg_DoWork;
                bg.RunWorkerAsync(this.Application);
            }
    
            private void bg_ProgressChanged(object sender, ProgressChangedEventArgs e) {
                switch (e.ProgressPercentage) {
                    case 50: //change
                        if (OnTextChanged != null) {
                            OnTextChanged(this, new TextChangedEventArgs((char)e.UserState));
                        }
                        break;
                }
            }
    
            private void bg_DoWork(object sender, DoWorkEventArgs e) {
    
                Word.Application wordApp = e.Argument as Word.Application;
                BackgroundWorker bg = sender as BackgroundWorker;
                string lastPage = string.Empty;
    
                while (true) {
                    try {
                        if (Application.Documents.Count > 0) {
                            if (Application.ActiveDocument.Words.Count > 0) {
                                var currentPage = Application.ActiveDocument.Bookmarks["\\Page"].Range.Text;                         
    
                                if (currentPage != null && currentPage != lastPage) {
                                    var differ = new DiffPlex.Differ();
                                    var builder = new DiffPlex.DiffBuilder.InlineDiffBuilder(differ);                               
                                    var difference = builder.BuildDiffModel(lastPage, currentPage);
                                    var change = from d in difference.Lines where d.Type != DiffPlex.DiffBuilder.Model.ChangeType.Unchanged select d;
                                    if (change.Any()) {                                    
                                        bg.ReportProgress(50, change.Last().Text.Last());
                                    }
    
                                    lastPage = currentPage;
                                }
    
    
                            }
                        }
                    } catch (Exception) {
    
                    }
    
                    if (bg.CancellationPending) {
                        break;
                    }
                    System.Threading.Thread.Sleep(100);
                }
            }
    
            public void Stop() {
                if (bg != null && !bg.CancellationPending) {
                    bg.CancelAsync();
                }
            }
        }
    
        public class TextChangedEventArgs : EventArgs {
            public char Letter;
            public TextChangedEventArgs(char letter) {
                this.Letter = letter;
            }
        }
    }
    
    用法:

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Xml.Linq;
    using Word = Microsoft.Office.Interop.Word;
    using Office = Microsoft.Office.Core;
    using Microsoft.Office.Tools.Word;
    using WordUtils;
    
    namespace WordAddIn1 {
        public partial class ThisAddIn {
            TextChangeDetector detector;
    
            private void ThisAddIn_Startup(object sender, System.EventArgs e) {
                detector = new TextChangeDetector(Application);
                detector.OnTextChanged += detector_OnTextChanged;
                detector.Start();
            }
    
            void detector_OnTextChanged(object sender, TextChangedEventArgs e) {
                Console.WriteLine(e.Letter);
            }
    
            private void ThisAddIn_Shutdown(object sender, System.EventArgs e) {
                detector.Stop();
            }
    
            #region VSTO generated code
    
            /// <summary>
            /// Required method for Designer support - do not modify
            /// the contents of this method with the code editor.
            /// </summary>
            private void InternalStartup() {
                this.Startup += new System.EventHandler(ThisAddIn_Startup);
                this.Shutdown += new System.EventHandler(ThisAddIn_Shutdown);
            }
    
            #endregion
        }
    }
    
    使用系统;
    使用System.Collections.Generic;
    使用System.Linq;
    使用系统文本;
    使用System.Xml.Linq;
    使用Word=Microsoft.Office.Interop.Word;
    使用Office=Microsoft.Office.Core;
    使用Microsoft.Office.Tools.Word;
    使用WordUtils;
    名称空间WordAddIn1{
    公共部分类ThisAddIn{
    文本转换检测器;
    私有void ThisAddIn_启动(对象发送方,System.EventArgs e){
    检测器=新的TextChangeDetector(应用);
    detector.OnTextChanged+=detector\u OnTextChanged;
    检测器。启动();
    }
    无效检测器\u OnTextChanged(对象发送器,textchangedventargs e){
    控制台写线(e.Letter);
    }
    私有void ThisAddIn_关闭(对象发送方,System.EventArgs e){
    检测器。停止();
    }
    #区域VSTO生成的代码
    /// 
    ///设计器支持所需的方法-不修改
    ///此方法的内容与代码编辑器一起使用。
    /// 
    私有void InternalStartup(){
    this.Startup+=new System.EventHandler(ThisAddIn\u启动);
    this.Shutdown+=new System.EventHandler(ThisAddIn\u Shutdown);
    }
    #端区
    }
    }
    
    如果不在VSTO加载项中使用低级钩子,则所有操作都应该正常工作

    [DllImport(“kernel32”,CharSet=CharSet.Auto,SetLastError=true)]
    public static extern int GetCurrentThreadId();
    const int WH_键盘=2;
    私有静态IntPtr SetHook(HookProc