views:

690

answers:

5

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

SendMessage(          
   HWND hWnd,
   UINT Msg,
   WPARAM wParam,
   LPARAM lParam
);

SetClipboardViewer

HWND SetClipboardViewer(
   HWND hWndNewViewer
);

SetTimer

UINT_PTR SetTimer(
   HWND hWnd,
   UINT_PTR nIDEvent,
   UINT uElapse,
   TIMERPROC lpTimerFunc
);

IProgressDialog::StartProgressDialog

HRESULT StartProgressDialog(          
   HWND hwndParent,
   IUnknown *punkEnableModless,
   DWORD dwFlags,
   LPCVOID pvReserved
);

Shell_NotifyIcon

BOOL Shell_NotifyIcon(          
   DWORD dwMessage,
   PNOTIFYICONDATA lpdata  //<--hWnd in there
);

AnimateWindow

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
A: 

I think that the form's OnClose function (or related event) should be overriden.

Alternative - each form implements IDisposable - why don't you add code to the Dispose method of the form that belong to that handle.

Dror Helper
Because the handle can be destroyed while the form still exists (i.e. it's never disposed)
Ian Boyd
A: 

You might want to use a GCHandle for this (System.Runtime.InteropServices.GCHandle). A GCHandle can be used to "pin" an object so that .NET keeps it in one memory location. I use this extensively for interacting with the waveOut API; without pinning, the app just mysteriously crashes once in a while.

Obviously this wouldn't be useful in a situation where the window is actually destroyed and then re-created.

MusiGenesis
+1  A: 

Have you looked at the functionality in the NativeWindow class?

kirkus
i'd never heard of this class. It looks like it's not a visual thinb, but as a non-visual window that can be used to receive messages. This might be good for solving problems (SetClipboardViewer) where i need an hWnd that can recieve messages, but doesn't solve the larger issue.
Ian Boyd
Updated original question to include this suggestion as solution for some cases - perhaps it might even be preferable to have a non-visual window as the listener for messages - rather than using a visual form.
Ian Boyd
The sample code for NativeWindow involves overriding the OnHandleCreated and OnHandleDestroyed events, as mentioned by slimCODE, and that is what had caught my eye.
kirkus
Yeah, i see that now too - which makes me think it doesn't do anything. i thought it was the .net framework equivalent of a helper that was available in another language. i thought it creates a window for you, where you can override the window proc, and use it to receive messages.
Ian Boyd
+1  A: 

Could you live with overriding the OnHandleCreated and OnHandleDestroyed on your form, and act accordingly?

Martin Plante
That might just be the answer i need. After i've tested it i'll vote the answer one way or the other
Ian Boyd
A: 

i can name one way in which a form's handle becomes invalid:

this.RightToLeft = RightToLeft.Yes;

I know another way:

this.ShowInTaskbar = false;
Serghei