短縮版
ウィンドウ ハンドルが有効なままであることを保証できない場合、API 呼び出しを使用するにはどうすればよいですか?
私は自分のフォームへの参照を保持していることを保証できます (そのため、フォームは破棄されません)。これは、フォームのハンドルが常に有効であることを保証するものではありません。
フォームが破棄されていないにもかかわらず、フォームのウィンドウ ハンドルが無効になるのはなぜですか?
フォームの基になる Windows ウィンドウが破棄され、再作成されたためです。
ロングバージョン
hwnd (ウィンドウへのハンドル) を必要とする API を P/Invoke したい。hWnd を必要とする API 呼び出しの例を次に示します。
IVRWindowlessControl::SetVideoClippingWindow
HRESULT SetVideoClippingWindow(
HWND hwnd
);
SendMessage(
HWND hWnd,
UINT Msg,
WPARAM wParam,
LPARAM lParam
);
HWND SetClipboardViewer(
HWND hWndNewViewer
);
UINT_PTR SetTimer(
HWND hWnd,
UINT_PTR nIDEvent,
UINT uElapse,
TIMERPROC lpTimerFunc
);
IProgressDialog::StartProgressDialog
HRESULT StartProgressDialog(
HWND hwndParent,
IUnknown *punkEnableModless,
DWORD dwFlags,
LPCVOID pvReserved
);
BOOL Shell_NotifyIcon(
DWORD dwMessage,
PNOTIFYICONDATA lpdata //<--hWnd in there
);
BOOL AnimateWindow(
HWND hwnd,
DWORD dwTime,
DWORD dwFlags
);
注:これらの API 呼び出しの中には、同等のものを管理しているものもあれば、管理していないものもありますが、その事実は私の質問には関係ありません。
説明
これらの API 関数のいずれかを呼び出すことができますが、これには長期的なウィンドウ ハンドルが必要です。
private void TellTheGuyToDoTheThing()
{
SendMessage(this.Handle,
WM_MyCustomMessage,
paramOneForTheThing,
paramTwoForTheThing);
}
上記の SendMessage の呼び出しは、ウィンドウ ハンドルが管理されていないため、危険であることが示唆されています。彼らは、hwnd を HandleRef オブジェクトにラップすることを提案しています。
private void TellTheGuyToDoTheThing()
{
SendMessage(new HandleRef(this, this.Handle),
WM_MyCustomMessage,
paramOneForTheThing, paramTwoForTheThing);
こうすることで、ウィンドウ ハンドルは、SendMessage の呼び出し中に有効であることが保証されます。しかし、いつもそのようにうまくいくとは限りません。次の API 呼び出しでは、ウィンドウ ハンドルに長時間アクセスする必要があります。
private void RegisterWithTheThing()
{
this.nextClipboardViewerInChain = SetClipboardViewer(
new HandleRef(this, this.Handle));
}
ハンドルを HandleRef でラップしても、(その後の秒、分、時間、日、週、月、または年で) フォームのウィンドウ ハンドルが無効になる可能性があります。これは、フォームの基になる Windows ウィンドウが破棄され、新しいウィンドウが作成されたときに発生します。これは、HandleRef でフォームのハンドルを保護したにもかかわらずです。
フォームのハンドルが無効になる1 つの方法を挙げることができます。
this.RightToLeft = RightToLeft.Yes;
フォームのウィンドウが再作成され、古い hwnd は無効になります。
問題は、ウィンドウ ハンドルを必要とする API 呼び出しを使用する方法です。
できませんか?
私は答えを期待しています:あなたはこれを行うことはできません. フォームのハンドルを保持する必要がある限り有効であることを保証するために、フォームのハンドルを保護するためにできることは何もありません。
これは、ハンドルがいつ破棄されるかを知る必要があることを意味するため、Windows にそれを手放すように指示できます。
protected override void TheHandleIsAboutToBeDestroyed()
{
ChangeClipboardChain(this.Handle, this.nextClipboardViewerInChain);
}
新しいハンドルが作成されると、次のように通知されます。
protected override void TheHandleWasJustCreated()
{
RegisterTheThing();
}
ただし、そのような祖先メソッドは存在しません。
別の質問:ウィンドウのハンドルがいつ破棄されようとしているのか、いつ作成されたのかを知るためにオーバーライドできるメソッドはありますか?
ハンドルを再作成する .NET WinForms カプセル化を破らなければならないのは醜いですが、それが唯一の方法ですか?
アップデートワン
フォームのClose / OnCloseイベントを処理するだけでは不十分です。
- IDisposable の処理
- フォームを固定する GC
フォームを閉じたり破棄したりせずに、フォームの基になるウィンドウ ハンドルを無効にできるためです。例えば:
private void InvalidThisFormsWindowHandleForFun()
{
this.RightToLeft = RightToLeft.Yes;
}
注: Windows ウィンドウ ハンドルは破棄しますが、破棄はしません。.NET のオブジェクトは破棄されるものです。Form オブジェクトの場合は、Windows ウィンドウ ハンドルの破棄が必要になる可能性が高くなります。
Windows は Microsoft の製品です。
ウィンドウはメッセージ ループを伴うもので、画面に何かを表示することがあります。
アップデート 2
me.yahoo.com/a/BrYwgは、 NativeWindowオブジェクトを使用して、メッセージをリッスンするために使用される hWnd を必要とするアイテムのリスナーとして機能することについて良い提案をしました。これは、次のようないくつかの問題を解決するために使用できます。
- SetClipboardViewer
- タイマー設定
- IProgressDialgo::StartProgressDialog
- Shell_NotifyIcon
しかし、うまくいきません
- AnimateWindow
- メッセージを送る
- IVRWindowlessControl::SetVideoClippingWindow