All About Handles in Windows Forms
(Repost from JFo's Talkin)
Handle Creation
What is a Handle?
A handle (HWND) is the return value from CreateWindowEx which the Windows Operating System uses to identify a window. A "window" in win32 is a much broader concept than you may think - each individual button, combobox, listbox etc comprises a window. (For more information see About Window Classes ) NOTE: there are other things known as "Handles" in the Framework - e.g. GDI Handles from a Bitmap or Handles to Device Contexts (HDCs) - this article discusses HWNDs only.
Why does Control have a Handle property?
The System.Windows.Forms.Control class actually wraps (subclasses) a HWND. This allows Control to take advantage of several operating system features such as painting, mouse hit testing, etc.
How do I specify arguments to CreateWindowEx? (What are CreateParams?)
The parameters to CreateWindowEx are actually controlled by the CreateParams property - for example - creating a progress bar is as simple as switching the class name in the CreateParams:
public class MyProgressBar : Control {
protected override CreateParams CreateParams {
get {
CreateParams cp = base.CreateParams;
cp.ClassName = "msctls_progress32";
return cp;
}
}
}
When does a Control create its handle? (When does a control call CreateWindowEx?)
A control tries as much as possible to defer creating its handle. This is because setting properties forces chatty interop between the CLR and user32.
Typically the handles for all the controls are created before the Form.Load event is called. Handles can also be created if the "Handle" property is called and the handle has not yet been created, or CreateControl() is called.
When does a Control destroy its handle? (When does a control call DestroyWindow)?
The protected virtual function DestroyHandle calls DestroyWindow (which destructs the window and all its children via the WM_DESTROY message). This is typically called from the Dispose method when someone is trying to clean up the control.
A window can also be destroyed when RecreateHandle is called.
When would a Control want to RecreateHandle?
Sometimes setting a property can force a handle recreation. This is because some window styles in CreateParams can only be set (bug-free) when calling CreateWindowEx. e.g. Setting RightToLeft forces the handle to recreate.
Recreating a handle sounds expensive, is there any other way to pick up changes to CreateParams?
Yes - you can do the equivilant of SetWindowLong on the style and the exStyle bits by calling UpdateStyles() instead of calling RecreateHandle().
How does a control finalize?
When a control is no longer parented and is not referenced by another CLR object, it becomes elligble for garbage collection. A control's window handle still exists even after it is removed from its parent. During finalization, a control must take special care to destroy its window handle before its memory is reclaimed by the garbage collector, or else the native window handle will crash as soon as it tries to route a message to the control. Within a control's finalize method, it first routes the window procedure away from WndProc and back to native code. Then, because a window can only be destroyed from the thread it was created on, and finalization runs on its own thread, the control posts a WM_CLOSE to its window handle. When the WM_CLOSE is processed by the native window, the window is destroyed.
Summary of the methods related to handle creation:
The CreateControl method forces a handle to be created for the control and its child controls. This method is used when you need a handle immediately for manipulation of the control or its children; simply calling a control's constructor does not create the Handle. |
|
You typically should not call the CreateHandle method directly. The preferred method is to call the CreateControl method, which forces a handle to be created for the control and its child controls when the control is created. |
|
An IntPtr that contains the window handle (HWND) of the control. (Result of CreateWindowEx) |
|
Specifies whether CreateHandle() has been called yet. |
|
Occurs when the handle has been created. Great place to start calling SendMessage to update the control with additional information. |
|
Occurs when the handle has been destroyed. |
|
Called whenever something needs to be changed to CreateParams and UpdateStyles doesnt do the trick. Calls DestroyHandle and CreateHandle, sets RecreatingHandle to true. |
|
|
If true, specifies that the control is in the middle of the RecreateHandle function. |
Subclassing
What is subclassing?
Subclassing is a way of snarfling window messages not necessarily directed towards you. For example, if you're a fancy button, you may want to know when the form has been activated and paint yourself a different way. Problem is WM_ACTIVATEAPP is a message that's only sent to toplevel windows. The solution (which should be a last resort) is to subclass the form and preprocess all the messages for the form.
Subclassing is achieved in managed code by using the NativeWindow class. Never subclass a window using SetWindowLong. Always use NativeWindow as it knows how to correctly unsubclass itself during garbage collection, app domain unloads, and process shutdown. Calling SetWindowLong yourself to subclass a window may introduce crashes at shutdown or garbage collection time.
For more background on subclassing, read About Window Procedures
Avoiding subclassing
If you can override the WndProc of the control you know is going to get the the particular window message - this is the best solution for handling the window message.
If the message is posted to another control - your control can implement IMessageFilter to interject yourself into the the message pump processing by calling Application.AddMessageFilter.
For information on the difference between SendMessage (window message usually goes direct to WndProc) and PostMessage (window message is processed through a message pump), read Using Messages and Message Queues.
Hooks
What are window hooks?
Hooks are a way of getting window messages even before it's sent to the WndProc (and hence even before a subclass). More information on how to do this via interop is available at this kb article .
(Special thanks to Brian for helping gather this info).
Comments
- Anonymous
November 24, 2004
Blog link of the week 40