質問者
イベントの無効化

質問
-
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
すべての返信
-
pachiryです。
Yutie様、稍丼様、ご回答ありがとうございました。
> WndProc をフックしたとのことですが、どのメッセージをフックしました?
WM_KILLFOCUSで試してみましたが、うまくいきませんでした。
> アンマネジドでもいいのなら,
> WH_CBT を指定して SetWindowsHookEx を呼び出せば,
> CBTProcコールバックプロシージャ内で,
> nCode が HCBT_ACTIVATE で,wParam が自フォームのハンドル以外を
> はじけば( 1 を返す)いいんですが...。大変参考になります。ちょっとやってみます。
貴重なご意見、ありがとうございました。
-
定義は,だいたい以下のような感じです。
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();
}
-
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);
}
}