Short version
How do i use an API call when i cannot guarantee that the window handle will remain valid?
i can guarantee that i'm holding a reference to my form (so the form is not being disposed). That doesn't guarantee that the form's handle will stay valid all that time.
How can a form's window handle become invalid even though the form is not diposed?
Because the form's underlying Windows window was destroyed and recreated.
Long Version
i want to P/Invoke an API that requires a hwnd (a handle to a window). Some examples of API calls that requie an hWnd are:
IVMRWindowlessControl::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
);
Note: Some of these API calls have managed equivalents, some do not - but that fact is irrelavent for my question.
Explanation
i can call one of these API functions, which requires long term window handle, e.g.:
private void TellTheGuyToDoTheThing()
{
SendMessage(this.Handle,
WM_MyCustomMessage,
paramOneForTheThing,
paramTwoForTheThing);
}
It has been suggested that the above call to SendMessage is dangerous because of the unmanaged use of a window handle. They suggest that you wrap the hwnd in a HandleRef object:
private void TellTheGuyToDoTheThing()
{
SendMessage(new HandleRef(this, this.Handle),
WM_MyCustomMessage,
paramOneForTheThing, paramTwoForTheThing);
This way: the window handle is guaranteed to stay valid during the call to SendMessage. But it doesn't always work out that way. The following API call requires long-term access to a window handle:
private void RegisterWithTheThing()
{
this.nextClipboardViewerInChain = SetClipboardViewer(
new HandleRef(this, this.Handle));
}
Even though i wrapped the handle in a HandleRef, it is still possible (in the subsequest seconds, minutes, hours, days, weeks, months, or years) for the form's window handle to become invalid. This happens when the form's underlying Windows window is destroyed and a new one created. This, despite the fact that i protected the form's handle in a HandleRef.
i can name one way in which a form's handle becomes invalid:
this.RightToLeft = RightToLeft.Yes;
The form's window is re-created and old hwnd is now invalid.
So the question is: How to use an API call that requires a window handle?
Cannot be done?
i anticipate the answer: you cannot do this. There is nothing that can be done to protect the form's handle to ensure it is valid as long as you need to keep the handle.
This then means that i need to know when the handle is being destroyed, so i can tell Windows to let it go, e.g.:
protected override void TheHandleIsAboutToBeDestroyed()
{
ChangeClipboardChain(this.Handle, this.nextClipboardViewerInChain);
}
and then be told when the new handle is created:
protected override void TheHandleWasJustCreated()
{
RegisterTheThing();
}
Except no such ancestor methods exist.
Alternate question: Are there methods i can override so i know when a window's handle is about to be destroyed, and when it has just been creatd?
Having to break the .NET WinForms encapsulation of re-creating handles is ugly, but is it the only way?
Update One
Handling the Close/*OnClose* event of a form is insufficient, the same goes for
- handling IDisposable
- GC pinning the form
since i can make the form's underlying window handle invalid without closing or disposing form. e.g:
private void InvalidThisFormsWindowHandleForFun()
{
this.RightToLeft = RightToLeft.Yes;
}
Note: You destroy a Windows window handle, you don't dispose of it. Objects in .NET are the things that get disposed of; which, if it's a Form object, will most likely involve destroying the Windows window handle.
Windows is a product by Microsoft.
A window is a thing with a message loop and can sometimes show stuff on screen.
Update Two
me.yahoo.com/a/BrYwg had a good suggestion for the use of a NativeWindow object to act as a listener for items that require an hWnd which are used to listen for messages. This can be used to solve some issues, like:
- SetClipboardViewer
- SetTimer
- IProgressDialgo::StartProgressDialog
- Shell_NotifyIcon
but doesn't work for
- AnimateWindow
- SendMessage
- IVMRWindowlessControl::SetVideoClippingWindow