none
Hook了Word的键盘消息,屏幕疯狂刷屏 RRS feed

  • 问题

  • 我Hook了Word的键盘消息。

    本意是为在用户按Delete键时做一些自己的处理。

    看代码:
          public static int KeyboardHookProc(int nCode, IntPtr wParam, IntPtr lParam)
            {
                if (nCode < 0)
                {
                    return Win32APIProxy.CallNextHookEx(hHook, nCode, wParam, lParam);
                }
                else
                {                
                    app.ActiveDocument.Content.Text = String.Empty;
                    return Win32APIProxy.CallNextHookEx(hHook, nCode, wParam, lParam);
                }
            }
    
    就是这段代码 app.ActiveDocument.Content.Text = String.Empty; 
    很不稳定,很容易Word就卡在那里了,界面显示是好像Word一直在处理消息呢, 编辑区域疯狂的在刷屏。  不晓的描述的是否准确,请各位帮助给些意见。
    ;) 谢谢。

    因为太笨,所以努力!
    2009年8月3日 9:09

答案

  • 你好,

    我开始考虑是否Add-In和回调函数没有运行在UI线程下,以致会有多线程读写同一块数据的问题,但是现在这些都在一个线程下,问题就和多线程没有关系了。

    其次我不觉得这种方法是好方法,Word如何处理KeyBoard以便修改文档,现在的做法等于是在不通过Word,自己的代码先对文档进行了修改,Word的行为当然不正确了。

    请给出你程序的逻辑,让我们来看看有没有其他的办法。
    Please remember to mark the replies as answers if they help and unmark them if they provide no help.
    Welcome to the All-In-One Code Framework! If you have any feedback, please tell us.
    • 已标记为答案 孙绍棕 2009年8月7日 8:51
    2009年8月6日 5:49
    版主
  • 请教了一位对Win32 API编程极富经验的同学后,转换了一种方式解决了问题。 

    大体思路为:

     1. 在插件加载后启动一个线程 
     2. 在线程里面调用 PeekMessage 函数接受消息,处理具体的逻辑。 
     3. 钩挂函数里只将接受到的消息通过函数 PostThreadMessage函数转发到我的线程。 

    另: 就该问题到目前为止也无一个明确的论断,那位同学的意见是 

    1. 最好不要在钩挂函数里面做具体的处理, Microsoft 这样说的。 
    2. 有可能是消息一直被阻塞。 

     那位同学只是提供了一个思路,问题就解决了。

     觉得自己还是太笨,再究其深层原因是基础不扎实,基础不扎实思考问题的时候就容易忽略细节,而只关注宏观上的事情。结果很多东西都做的不尽人意。 

     不过,比起07年的时候连 for 循环都搞不清楚是怎么回事,为什么这样,好的太多了! 

     用心,开心的学习,做事。 

     谢谢Microsoft 论坛上的朋友的关注,还有 这位斑竹的热心帮助。 ;)
    因为太笨,所以努力!
    • 已标记为答案 孙绍棕 2009年8月7日 8:51
    2009年8月7日 8:48

全部回复

  • 你好,

    给出你的场景的详细描述和完整的可重现这个问题的代码,Hook有local hook和global hook两种类型,你的hook是什么类型的。
    Please remember to mark the replies as answers if they help and unmark them if they provide no help.
    Welcome to the All-In-One Code Framework! If you have any feedback, please tell us.
    2009年8月4日 10:22
    版主
  • 总算有人回帖了,谢谢。

    我Hook 的 类型是 WH_KEYBOARD。

    场景:

    在Word中有若干域,我在按Delete时判断选择点是否有域? 如果有则自动在文档的末尾删除一些文字。  (文档末尾的文字是我自动生成的一些内容。)

    代码示例.  
    首先是实现IDT接口,注册为Word插件。 而后,具体与此相关的代码如下:
        public delegate int HookProc(int nCode, IntPtr wParam, IntPtr lParam);
    
        public class KeyboardHook
        {
            static int hHook = 0;
    
            public const int WH_KEYBOARD = 2;
    
            static HookProc KeyboardHookProcedure;
    
            public static int KeyboardHookProc(int nCode, IntPtr wParam, IntPtr lParam)
            {
                if (nCode < 0)
                {
                    return Win32APIProxy.CallNextHookEx(hHook, nCode, wParam, lParam);
                }
                else
                {
                    /*
                     Microsoft.Office.Interop.Word.Application app = App/*该对象需要引用Word进程*/;
                    app.ActiveDocument.Content.Text = String.Empty;
                     */
                    return Win32APIProxy.CallNextHookEx(hHook, nCode, wParam, lParam);
                }
            }
    
            internal static void InstallHookForKeyboard()
            {
                if (hHook == 0)
                {
                    KeyboardHookProcedure = new HookProc(KeyboardHookProc);
    
                    hHook = Win32APIProxy.SetWindowsHookEx(WH_KEYBOARD,
                        KeyboardHookProcedure,
                        Win32APIProxy.GetModuleHandle(null),
                        AppDomain.GetCurrentThreadId());
    
                    if (hHook == 0)
                    {
    #if DEBUG
                        Console.WriteLine("Hook failed.");
                        Console.WriteLine("GetLastError: {0}", Win32APIProxy.GetLastError());
    #endif
                        return;
                    }
                }
                else
                {
                    bool ret = Win32APIProxy.UnhookWindowsHookEx(hHook);
                    //If UnhookWindowsHookEx fails.
                    if (ret == false)
                    {
                        return;
                    }
                    hHook = 0;
                }
            }
        }
    以上即是完整Hook代码,调用在这里:

    public class WordAddIn : IDTExtensibility2
        {
            #region IDTExtensibility2 成员
    
            public void OnAddInsUpdate(ref Array custom)
            {
    
            }
    
            public void OnBeginShutdown(ref Array custom)
            {
    #if DEBUG
                Console.WriteLine("Begin Shutdown");
    #endif
            }
            public void OnConnection(object Application, ext_ConnectMode ConnectMode, object AddInInst, ref Array custom)
            {
                try
                {
    #if DEBUG
                    //for debug
                    Win32APIProxy.AllocConsole();
                    //System.Diagnostics.Debugger.Launch();
    #endif
                    KeyboardHook.InstallHookForKeyboard();
                }
                catch (Exception ex)
                {
                  
                }
            }
            public void OnDisconnection(ext_DisconnectMode RemoveMode, ref Array custom)
            {
                try
                {
                   KeyboardHook.InstallHookForKeyboard();
                }
                catch (Exception ex)
                {
                  
                }
            }
      
            public void OnStartupComplete(ref Array custom)
            {
               
            }
    
            #endregion
    Hook 参考的了微软的示例代码。


    因为太笨,所以努力!
    2009年8月5日 0:26
  • 你好,

    我看到你在回调过程中处理app.ActiveDocument.Content.Text,通常这不是一个好的方法,看下是不是这里有多线程的问题,请检查一下AppDomain.GetCurrentThreadId在OnDisconnection和InstallHookForKeyboard过程中,看一下这两个值是否相同。
    Please remember to mark the replies as answers if they help and unmark them if they provide no help.
    Welcome to the All-In-One Code Framework! If you have any feedback, please tell us.
    2009年8月5日 8:29
    版主
  • 你好,

    我Debug了 在 InstallHookForKeyboard 和 Ondisconnection 中 Thread Id 是一致的。

    我看到你在回调过程中处理app.ActiveDocument.Content.Text,通常这不是一个好的方法。可以解释下吗?

    多线程问题? 我在研究研究。

    因为太笨,所以努力!
    2009年8月5日 9:14
  • 你好,

    我开始考虑是否Add-In和回调函数没有运行在UI线程下,以致会有多线程读写同一块数据的问题,但是现在这些都在一个线程下,问题就和多线程没有关系了。

    其次我不觉得这种方法是好方法,Word如何处理KeyBoard以便修改文档,现在的做法等于是在不通过Word,自己的代码先对文档进行了修改,Word的行为当然不正确了。

    请给出你程序的逻辑,让我们来看看有没有其他的办法。
    Please remember to mark the replies as answers if they help and unmark them if they provide no help.
    Welcome to the All-In-One Code Framework! If you have any feedback, please tell us.
    • 已标记为答案 孙绍棕 2009年8月7日 8:51
    2009年8月6日 5:49
    版主
  • 嗯. 在Word中有若干域,我在按Delete时判断选择点是否有域? 如果有则自动在文档的末尾删除一些文字。  (文档末尾的文字是我自动生成的一些内容。)

    流程是:

    当我按下键盘的 Delete按键时, 在如上钩子处理程序里面做我自己想做的事情,这个事情大致如下:
    1. 要删除的是否Word域?
    2. 如果是,则删除文档里面的一些内容。比如说删除为 “我”的全部字符。

    这个应该就可以表达程序的逻辑了。

    另: 你说的蛮对, 我这里就处理了文档,那么Word那边可能出于某些原因就无法处理了。 

    另: 我在多次按delete键后会出现 "文档太大,文档无法处理“的提示。 我猜想这里涉及到很复杂的Word的内部处理过程。

    因为太笨,所以努力!
    2009年8月6日 7:34
  • 请教了一位对Win32 API编程极富经验的同学后,转换了一种方式解决了问题。 

    大体思路为:

     1. 在插件加载后启动一个线程 
     2. 在线程里面调用 PeekMessage 函数接受消息,处理具体的逻辑。 
     3. 钩挂函数里只将接受到的消息通过函数 PostThreadMessage函数转发到我的线程。 

    另: 就该问题到目前为止也无一个明确的论断,那位同学的意见是 

    1. 最好不要在钩挂函数里面做具体的处理, Microsoft 这样说的。 
    2. 有可能是消息一直被阻塞。 

     那位同学只是提供了一个思路,问题就解决了。

     觉得自己还是太笨,再究其深层原因是基础不扎实,基础不扎实思考问题的时候就容易忽略细节,而只关注宏观上的事情。结果很多东西都做的不尽人意。 

     不过,比起07年的时候连 for 循环都搞不清楚是怎么回事,为什么这样,好的太多了! 

     用心,开心的学习,做事。 

     谢谢Microsoft 论坛上的朋友的关注,还有 这位斑竹的热心帮助。 ;)
    因为太笨,所以努力!
    • 已标记为答案 孙绍棕 2009年8月7日 8:51
    2009年8月7日 8:48