none
イベントの無効化 RRS feed

  • 質問

  • pachiryといいます。お世話になります。

    FormA と FormB の2つの Form を表示しており、FormA 上には TextBoxA が配置してあります。
    (FormA と FormB は同一プロセスからモードレスで表示しています。)

    通常、FormA がアクティブの時 FormB をクリックすると FormB がアクティブになりますが、
    FormA がある状態の時には FormA をディアクティブにできない
    動きを実現したいと思ってます。(FormA が適切な状態になるまでFormB をアクティブにできない)

    例えば、TextBoxAの内容が空の場合はFormAをディアクティブにできないという動きです。

    1. FormA がディアクティブになるイベントを横取りし、
    2. FormA がディアクティブになれる状態か判断し、
    3. ディアクティブになれない状態であれば、FormA をアクティブにする。
    という手続きを踏めばよいのかと思い、Application.AddMessageFilter() やForm.WndProc() 等を使ってチャレンジしてみたのですが、思うような動きを実現できずに困っています。

    このような場合どのような方法を用いればよいのでしょうか。
    よろしくお願いします。

    環境
    ・ WindowsXP Professional SP2
    ・ VisualStudio 2005
    ・ .NET Framework 2.0

    2006年3月31日 8:14

すべての返信

  • Owner のすり替えでうまくできないですかね?
    Owner が設定されている時は、Form 自体を非活性にしておけばできそうですが。

    2006年3月31日 8:44
  • じゃんぬねっと様、ご回答ありがとうございます。

    すり替えですが、具体的にどのような手順になるのでしょうか?

    またFormの非活性ですが、Enabledプロパティをfalseにするということでしょうか?Formの見た目は変えたくないのでそれはさけたいのですが・・・。

    2006年3月31日 9:40
  • WndProc をフックしたとのことですが、どのメッセージをフックしました? どのようなコードを書いたのでしょうか。多分 Msg の処理がうまく言っていれば、先ほどの動作は可能だと思うのですが。

    ご存知だと思いますが、Spy++ を利用してウィンドウが切り替わる際のメッセージを監視することができますので、やってみてはどうでしょうか。
    2006年4月4日 16:11
  • アンマネジドでもいいのなら,
    WH_CBT を指定して SetWindowsHookEx を呼び出せば,
    CBTProcコールバックプロシージャ内で,
    nCode が HCBT_ACTIVATE で,wParam が自フォームのハンドル以外を
    はじけば( 1 を返す)いいんですが...。

    マネジドだと,どうやるんでしょうね。

    2006年4月6日 0:14
  • pachiryです。

    Yutie様、稍丼様、ご回答ありがとうございました。

    > WndProc をフックしたとのことですが、どのメッセージをフックしました?

    WM_KILLFOCUSで試してみましたが、うまくいきませんでした。

    > アンマネジドでもいいのなら,
    > WH_CBT を指定して SetWindowsHookEx を呼び出せば,
    > CBTProcコールバックプロシージャ内で,
    > nCode が HCBT_ACTIVATE で,wParam が自フォームのハンドル以外を
    > はじけば( 1 を返す)いいんですが...。

    大変参考になります。ちょっとやってみます。

    貴重なご意見、ありがとうございました。

    2006年4月6日 1:42
  • 定義は,だいたい以下のような感じです。

        static class WinAPI

        {

            public enum HookType

            {

                WH_MSGFILTER       = (-1),

                WH_JOURNALRECORD   = 0,

                WH_JOURNALPLAYBACK = 1,

                WH_KEYBOARD        = 2,

                WH_GETMESSAGE      = 3,

                WH_CALLWNDPROC     = 4,

                WH_CBT             = 5,

                WH_SYSMSGFILTER    = 6,

                WH_MOUSE           = 7,

                WH_HARDWARE        = 8,  // defined(_WIN32_WINDOWS)

                WH_DEBUG           = 9,

                WH_SHELL           = 10,

                WH_FOREGROUNDIDLE  = 11,

                WH_CALLWNDPROCRET  = 12,  // (WINVER >= 0x0400)

                WH_KEYBOARD_LL     = 13,  // (_WIN32_WINNT >= 0x0400)

                WH_MOUSE_LL        = 14   // (_WIN32_WINNT >= 0x0400)

            }

     

            public enum CBTHookCode

            {

                HCBT_MOVESIZE     = 0,

                HCBT_MINMAX       = 1,

                HCBT_QS           = 2,

                HCBT_CREATEWND    = 3,

                HCBT_DESTROYWND   = 4,

                HCBT_ACTIVATE     = 5,

                HCBT_CLICKSKIPPED = 6,

                HCBT_KEYSKIPPED   = 7,

                HCBT_SYSCOMMAND   = 8,

                HCBT_SETFOCUS     = 9

            }

     

            //LRESULT CALLBACK CBTProc(

            //    int nCode,

            //    WPARAM wParam,

            //    LPARAM lParam

            //);

            public delegate int CBTProcDelegate(int uCode, uint wParam, int lParam);

     

            //HHOOK SetWindowsHookEx(         

            //    int idHook,

            //    HOOKPROC lpfn,

            //    HINSTANCE hMod,

            //    DWORD dwThreadId

            //);

            [DllImport("user32.dll"/*, EntryPoint="SetWindowsHookExA"*/)]

            public static extern IntPtr SetWindowsHookEx(
    int idHook, CBTProcDelegate lpfn, IntPtr hmod, uint dwThreadId);

     

            [DllImport("user32.dll")]

            public static extern int UnhookWindowsHookEx(IntPtr hHook);

     

            [DllImport("user32.dll")]

            public static extern int CallNextHookEx(IntPtr hHook, int nCode, uint wParam, int lParam);

     

            [DllImport("kernel32.dll")]

            public static extern uint GetCurrentThreadId();

     

        }

    2006年4月6日 3:22
  • CBTProcDelegateは,直接 new して,渡してしまうと
    GCされてしまう... とかで,怒られます。
    なので,一旦,前もってどこかに入れておかないといけないようです。
    (あと,Uninstallの呼び出しは,必ず担保する必要があります。)
    AppDomain.GetCurrentThreadId() も,注意されるので...

        public partial class Form2 : Form

        {

            IntPtr _hForm = IntPtr.Zero;

            IntPtr _hHook = IntPtr.Zero;

            WinAPI.CBTProcDelegate _cbtProcDelegate;

     

            public Form2()

            {

                InitializeComponent();

                _cbtProcDelegate = new WinAPI.CBTProcDelegate(CBTProc);

            }

     

            //private void button1_Click(object sender, EventArgs e)

            //{

            //    bool rc = Install();

            //    if (!rc)

            //    {

            //        MessageBox.Show("フックに失敗");

            //    }

            //}

     

            //private void button2_Click(object sender, EventArgs e)

            //{

            //    bool rc = Uninstall();

            //}

     

            private bool Install()

            {

                if (_hHook != IntPtr.Zero)

                {

                    return true;

                }

     

                _hForm = this.Handle;

                uint hThreadId = 0;

                //hThreadId = AppDomain.GetCurrentThreadId()

                hThreadId = WinAPI.GetCurrentThreadId();

     

                _hHook = WinAPI.SetWindowsHookEx((int)WinAPI.HookType.WH_CBT,
    _cbtProcDelegate,
    IntPtr.Zero,
    hThreadId);

                if (_hHook == IntPtr.Zero)

                {

                    return false;

                }

                return true;

            }

     

            private bool Uninstall()

            {

                if (_hHook == IntPtr.Zero)

                {

                    return true;

                }

     

                int rc = WinAPI.UnhookWindowsHookEx(_hHook);

                if (rc == 0)

                {

                    return false;

                }

                _hHook = IntPtr.Zero;

                return true;

            }

     

     

            private int CBTProc(int nCode, uint wParam, int lParam)

            {

                // 0 未満は,すぐに渡す

                if (nCode < 0)

                {

                    return WinAPI.CallNextHookEx(_hHook, nCode, wParam, lParam);

                }

     

                if (nCode == (int)WinAPI.CBTHookCode.HCBT_ACTIVATE &&
    wParam != _hForm.ToInt32())

                {

                    return 1;  //prevent

                }

     

                return WinAPI.CallNextHookEx(_hHook, nCode, wParam, lParam);

            }

      

        }

    2006年4月6日 3:57
  • 最大化,最小化とかができてしまうので,
    HCBT_MINMAX とかもブロックしないといけないようです。
    ここまできたら後はなんとかなるでしょう。

    (追記: 上のコードは,直接フォームクラスに書いてしまっているけど,
    ちゃんとクラスにわけて再利用可能にした方がいいですね...)

    2006年4月6日 4:08
  • pachiryです。

    稍丼様、ご回答ありがとうございます。

    サンプルコードをご提示していただきまして、方法がよくわかりました。
    まさに私が求めていた理想の動作です。

    この技術はいろいろな用途に応用できそうですね。
    ありがとうございました。

     

    2006年4月6日 9:14