2012-02-01 13 views
1

メッセージボックスの場合、ユーザーはEnterキーを押してデフォルトオプションを受け入れることはできませんが、オプションのキーを押す必要があります。例えば。はい/いいえのオプションを持つMessageBoxを指定すると、ユーザーはYまたはNキーを押す必要があります。今私はキーボードのフックを使用してこれを実装しましたが、コードが返ってくると、KeyUpイベントも呼び出しコードに返されます。.NetキーボードフックExtra KeyUpイベント

質問:呼び出しコードに戻る前にすべてのキーボードイベントをフラッシュするにはどうすればよいですか?

私はボイラープレートのコードを削除しましたが、必要な場合は助言してください。

呼び出しコード:

private static ResultMsgBox MsgResultBaseNoEnter(string msg, string caption, uint options) 
    { 
     ResultMsgBox res; 
     _hookID = SetHook(_proc); 
     try 
     { 
      res = MessageBox(GetForegroundWindow(), msg, caption, options); 
     } 
     finally 
     { 
      UnhookWindowsHookEx(_hookID); 
     } 
     return res; 
    } 

とフックコード:

private static IntPtr HookCallback(int nCode, IntPtr wParam, IntPtr lParam) 
    { 
     if (nCode >= 0 && wParam == (IntPtr)WM_KEYDOWN) 
     { 
      int vkCode = Marshal.ReadInt32(lParam); 
      if (vkCode == VK_RETURN) 
       return (IntPtr)(-1); 
     } 
     return CallNextHookEx(_hookID, nCode, wParam, lParam); 
    } 
+1

これを30回以上プッシュしても問題ありません。もう一度プッシュバックしても構いません。このようなデフォルトの動作を無効にすることは*悪い*です。彼らはこのような動きごとにこのシステムを使用するすべてのユーザーのトレーニングコストを実際に増やしています。 –

+0

これは私が継承したカスタムビルドです。 – Cheval

+1

カスタムメッセージボックスが必要な場合は、カスタムメッセージボックスとしてキーボードフックを投げるのではなくカスタムメッセージボックスとして実装します。 – CodesInChaos

答えて

1

(または他のクラスで使用することができ、いくつかの静的なクラスで)どこかにあなたのクラスのコードのこれらの行を追加します。

[StructLayout(LayoutKind.Sequential)] 
public class MSG 
{ 
    public IntPtr hwnd; 
    public uint message; 
    public IntPtr wParam; 
    public IntPtr lParam; 
    public uint time; 
    int x; 
    int y; 
} 

[DllImport("user32")] 
public static extern bool PeekMessage([Out]MSG lpMsg, IntPtr hWnd, uint wMsgFilterMin, uint wMsgFilterMax, int wRemoveMsg); 

/// <summary> 
/// Examines the message queue for key messages. 
/// </summary> 
/// <param name="remove">If this parameter is true, the returned message is also removed from the queue.</param> 
/// <returns>Returns the next available key message, or null if there is no key message available.</returns> 
public static MSG PeekKeyMessage(bool remove) 
{ 
    MSG msg = new MSG(); 
    if (PeekMessage(msg, IntPtr.Zero, 0x0100 /*WM_KEYFIRST*/, 0x0109 /*WM_KEYLAST*/, remove ? 1 : 0)) 
     return msg; 
    return null; 
} 

public static void RemoveAllKeyMessages() 
{ 
    while (PeekKeyMessage(true) != null) ; // Empty body. Every call to the method removes one key message. 
} 

RemoveAllKeyMessages()を呼び出すには、正確に何をしたいん。

+0

私は少しの変更を加えて返信しますが、これは答えのようです。ありがとう。 – Cheval

0

実際にあなたがキーボードイベントをフラッシュすることはできませんが、スレッドのメッセージが受信するイベントを防ぐことができますループ。
WH_GETMESSAGEフックのハンドラをインストールする必要があります。フックプロシージャのlParamは、MSG構造体へのポインタです。構造体を調べた後、メッセージを呼び出し元のメッセージプロセッサに渡さないように構造体を変更することができます。メッセージをWM_NULLに変更する必要があります。
.NETでの実際の手順は少し時間がかかり、別の記事が必要です。

コピーし、このクラスを正確にそのまま、あなたのプロジェクトに新しいC#ファイルに:

using System; 
using System.Collections.Generic; 
using System.Diagnostics; 
using System.Runtime.InteropServices; 

namespace Unicorn 
{ 
    public static class HookManager 
    { 
     #region Fields 

     private delegate int HookDelegate(int ncode, IntPtr wParam, IntPtr lParam); 
     private static HookDelegate getMessageHookProc; 
     private static IntPtr getMessageHookHandle; 
     private static List<EventHandler<GetMessageHookEventArgs>> getMessageHandlers = 
      new List<EventHandler<GetMessageHookEventArgs>>(); 

     #endregion 
     #region Private Methods - Installation and Uninstallation 

     private static void InstallGetMessageHook() 
     { 
      if (getMessageHookProc != null) 
       return; 
      getMessageHookProc = new HookDelegate(GetMessageHookProc); 
      getMessageHookHandle = SetWindowsHookEx(WH_GETMESSAGE, getMessageHookProc, 0, GetCurrentThreadId()); 
     } 

     private static void UninstallGetMessageHook() 
     { 
      if (getMessageHookProc == null) 
       return; 
      UnhookWindowsHookEx(getMessageHookHandle); 
      getMessageHookHandle = IntPtr.Zero; 
      getMessageHookProc = null; 
     } 

     #endregion 
     #region Public Methods - Add and Remove Handlers 

     public static void AddGetMessageHookHandler(EventHandler<GetMessageHookEventArgs> handler) 
     { 
      if (getMessageHandlers.Contains(handler)) 
       return; 
      getMessageHandlers.Add(handler); 
      if (getMessageHandlers.Count == 1) 
       InstallGetMessageHook(); 
     } 

     public static void RemoveGetMessageHookHandler(EventHandler<GetMessageHookEventArgs> handler) 
     { 
      getMessageHandlers.Remove(handler); 
      if (getMessageHandlers.Count == 0) 
       UninstallGetMessageHook(); 
     } 

     #endregion 
     #region Private Methods - Hook Procedures 

     [DebuggerStepThrough] 
     private static int GetMessageHookProc(int code, IntPtr wParam, IntPtr lParam) 
     { 
      if (code == 0) // HC_ACTION 
      { 
       MSG msg = new MSG(); 
       Marshal.PtrToStructure(lParam, msg); 
       GetMessageHookEventArgs e = new GetMessageHookEventArgs() 
       { 
        HWnd = msg.hwnd, 
        Msg = msg.message, 
        WParam = msg.wParam, 
        LParam = msg.lParam, 
        MessageRemoved = (int)wParam == 1, 
        ShouldApplyChanges = false 
       }; 

       foreach (var handler in getMessageHandlers.ToArray()) 
       { 
        handler(null, e); 
        if (e.ShouldApplyChanges) 
        { 
         msg.hwnd = e.HWnd; 
         msg.message = e.Msg; 
         msg.wParam = e.WParam; 
         msg.lParam = e.LParam; 
         Marshal.StructureToPtr(msg, (IntPtr)lParam, false); 
         e.ShouldApplyChanges = false; 
        } 
       } 
      } 

      return CallNextHookEx(getMessageHookHandle, code, wParam, lParam); 
     } 

     #endregion 
     #region Win32 stuff 

     private const int WH_KEYBOARD = 2; 
     private const int WH_GETMESSAGE = 3; 
     private const int WH_CALLWNDPROC = 4; 
     private const int WH_MOUSE = 7; 
     private const int WH_CALLWNDPROCRET = 12; 

     [StructLayout(LayoutKind.Sequential)] 
     public class MSG 
     { 
      public IntPtr hwnd; 
      public uint message; 
      public IntPtr wParam; 
      public IntPtr lParam; 
      public uint time; 
      int x; 
      int y; 
     } 


     [DllImport("USER32.dll", CharSet = CharSet.Auto)] 
     private static extern IntPtr SetWindowsHookEx(int idHook, HookDelegate lpfn, int hMod, int dwThreadId); 

     [DllImport("USER32.dll", CharSet = CharSet.Auto)] 
     private static extern int CallNextHookEx(IntPtr hhk, int nCode, IntPtr wParam, IntPtr lParam); 

     [DllImport("USER32.dll", CharSet = CharSet.Auto)] 
     private static extern bool UnhookWindowsHookEx(IntPtr hhk); 

     [DllImport("kernel32.dll", ExactSpelling = true, CharSet = CharSet.Auto)] 
     private static extern int GetCurrentThreadId(); 
     #endregion 
    } 

    #region EventArgs 

    public class GetMessageHookEventArgs : EventArgs 
    { 
     public uint Msg { get; set; } 
     public IntPtr HWnd { get; set; } 
     public IntPtr WParam { get; set; } 
     public IntPtr LParam { get; set; } 

     public bool MessageRemoved { get; set; } 
     public bool ShouldApplyChanges { get; set; } 
    } 

    #endregion 
} 

これはあなたが必要なすべてを行うヘルパークラスですが、簡単に、ここでの方法です。私の実際のクラスはもう少し長くなり、さらにいくつかのフックタイプを扱うことができましたが、コードを小さくするためにコードをクリアしました。

この後、あなたのコードは次のようになります。

private static void GetMessageProcHook(object sender, Unicorn.GetMessageHookEventArgs e) 
{ 
    if (e.Msg == 0x100 && (Keys)e.WParam == Keys.Return) // WM_KEYDOWN 
    { 
     // swallow the message 
     e.Msg = 0; // WM_NULL 
     e.WParam = IntPtr.Zero; 
     e.LParam = IntPtr.Zero; 
     e.ShouldApplyChanges = true; // This will tell the HookManager to copy the changes back. 
    } 
} 

private static ResultMsgBox MsgResultBaseNoEnter(string msg, string caption, uint options) 
{ 
    ResultMsgBox res; 
    Unicorn.HookManager.AddGetMessageHookHandler(GetMessageProcHook); 
    try 
    { 
     res = MessageBox(GetForegroundWindow(), msg, caption, options); 
    } 
    finally 
    { 
     Unicorn.HookManager.RemoveGetMessageHookHandler(GetMessageProcHook); 
    } 
    return res; 
} 

あなたが他の問題が発生した場合、私に知らせてください。

+0

残念ながら、それは問題を解決しませんでした。 Yキーを押すと、それはまだメッセージボックスから戻っていて、KeyUpイベントは呼び出し元のウィンドウで起動しています。ウィンドウのKeyUpイベントハンドラでは、イベントはデータ内のY文字をリストします。 – Cheval

+0

私はまた、 "else if(e.Msg == WM_KEYUP)//上記のようにメッセージコードを飲み込もうとしましたが"、それでもイベントを防ぐことはできませんでした。 – Cheval

+0

戻りキーは正常に機能しました。また、別の要件があります。「Y」ボタンを押した後で、keyupイベントが呼び出し元のウィンドウに表示されないようにする必要があります。そのようにすることはできません。なぜなら、 'y'キーがメッセージボックスで受信されると、メッセージボックスは閉じられ、前のウィンドウに戻るとフックがアンインストールされるからです。あなたの2番目のコメントで言及したことはうまくいかないでしょう。私は上で作業し、私の答えにそれを添付しますが、あなたの質問にあなたの新しい要件を言及する方が良いです。 –

0

ありがとうMD.Unicorn。 PeekMessageメソッドとRemoveAllKeyMessagesメソッドは、マイナーチェンジを除いてうまくいきます。

私はこの問題についてより多くの調査をしてきました。明らかに、MessageBoxがKeyDownイベントの入力オプションを受け入れて、それを閉じてしまう既知の問題です。戻り値のウィンドウは、後でKeyUpイベントを受け取ります。

私が知っているように、このKeyUpイベントは将来、ある時点で発生しますが、すぐには発生しません。 (RemoveAllKeyMessagesだけでは問題は解決しませんでした)。次のようにメソッドをポーリングするように調整しました。 MessageBoxの問題のカスタム使用を示すためにメソッドの名前を変更しました。(メッセージボックスは、KeyUpイベントを送信しない場合を除く)、明白な欠陥がある場合を除き

public static void RemoveMessageBoxKeyMessages() 
    { 
     //Loop until the MessageBox KeyUp event fires 
     var timeOut = DateTime.Now; 
     while (PeekKeyMessage(false) == null && DateTime.Now.Subtract(timeOut).TotalSeconds < 1) 
      System.Threading.Thread.Sleep(100); 

     while (PeekKeyMessage(true) != null) ; // Empty body. Every call to the method removes one key message. 
    } 

が、これは同様の問題を持つ他の人のためのソリューションでなければなりません。

+0

@Chevel:ユーザーがキーを押してからキーを離すと、キーが押されている時間が少しあります。だからあなたは正しい。キーアップイベントは、キーダウンイベントの直後に送信されず、メッセージボックスを閉じます。しかし、ユーザーが100ミリ秒以上キーを保持している状況では、ソリューションが機能しないことがあります。この状況では、何か他のことをしなければならないかもしれません。たとえば、keyupイベントを待機します。しかし、アプリケーションがフリーズすることがあります。 –

+0

ユーザーが100ミリ秒以上キーを押さえているケースをテストしました。はい、KeyUpイベントも呼び出しコードに送られました。これは、おそらくイベントが最初に通過することを許可する理由です。潜在的な凍結についても良い点。私はタイムアウトのためにコードを更新しました。 – Cheval

関連する問題