1 // Permission is hereby granted, free of charge, to any person obtaining
2 // a copy of this software and associated documentation files (the
3 // "Software"), to deal in the Software without restriction, including
4 // without limitation the rights to use, copy, modify, merge, publish,
5 // distribute, sublicense, and/or sell copies of the Software, and to
6 // permit persons to whom the Software is furnished to do so, subject to
7 // the following conditions:
9 // The above copyright notice and this permission notice shall be
10 // included in all copies or substantial portions of the Software.
12 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
13 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
14 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
15 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
16 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
17 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
18 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
20 // Copyright (c) 2004-2008 Novell, Inc.
23 // Peter Dennis Bartok pbartok@novell.com
24 // Ivan N. Zlatev <contact@i-nz.net>
30 //#define ExternalExceptionHandler
32 using System.Runtime.Remoting;
33 using System.Runtime.InteropServices;
34 using System.Runtime.CompilerServices;
35 using System.Threading;
36 using System.Collections;
37 using System.Diagnostics;
40 namespace System.Windows.Forms
42 public class NativeWindow : MarshalByRefObject, IWin32Window
44 IntPtr window_handle = IntPtr.Zero;
45 static Hashtable window_collection = new Hashtable();
48 static NativeWindow WindowCreating;
50 #region Public Constructors
53 window_handle=IntPtr.Zero;
55 #endregion // Public Constructors
57 #region Public Instance Properties
58 public IntPtr Handle {
63 #endregion // Public Instance Properties
65 #region Public Static Methods
66 public static NativeWindow FromHandle(IntPtr handle)
68 return FindFirstInTable (handle);
70 #endregion // Public Static Methods
72 #region Private and Internal Methods
73 internal void InvalidateHandle()
75 RemoveFromTable (this);
76 window_handle = IntPtr.Zero;
80 #region Public Instance Methods
81 public void AssignHandle(IntPtr handle)
83 RemoveFromTable (this);
84 window_handle = handle;
89 private static void AddToTable (NativeWindow window)
91 IntPtr handle = window.Handle;
92 if (handle == IntPtr.Zero)
95 lock (window_collection) {
96 object current = window_collection[handle];
97 if (current == null) {
98 window_collection.Add (handle, window);
100 NativeWindow currentWindow = current as NativeWindow;
101 if (currentWindow != null) {
102 if (currentWindow != window) {
103 ArrayList windows = new ArrayList ();
104 windows.Add (currentWindow);
105 windows.Add (window);
106 window_collection[handle] = windows;
108 } else { // list of windows
109 ArrayList windows = (ArrayList) window_collection[handle];
110 if (!windows.Contains (window))
111 windows.Add (window);
117 private static void RemoveFromTable (NativeWindow window)
119 IntPtr handle = window.Handle;
120 if (handle == IntPtr.Zero)
123 lock (window_collection) {
124 object current = window_collection[handle];
125 if (current != null) {
126 NativeWindow currentWindow = current as NativeWindow;
127 if (currentWindow != null) {
128 window_collection.Remove (handle);
129 } else { // list of windows
130 ArrayList windows = (ArrayList) window_collection[handle];
131 windows.Remove (window);
132 if (windows.Count == 0)
133 window_collection.Remove (handle);
134 else if (windows.Count == 1)
135 window_collection[handle] = windows[0];
141 private static NativeWindow FindFirstInTable (IntPtr handle)
143 if (handle == IntPtr.Zero)
146 NativeWindow window = null;
147 lock (window_collection) {
148 object current = window_collection[handle];
149 if (current != null) {
150 window = current as NativeWindow;
151 if (window == null) {
152 ArrayList windows = (ArrayList) current;
153 if (windows.Count > 0)
154 window = (NativeWindow) windows[0];
161 public virtual void CreateHandle(CreateParams cp)
164 WindowCreating = this;
165 window_handle=XplatUI.CreateWindow(cp);
166 WindowCreating = null;
168 if (window_handle != IntPtr.Zero)
174 public void DefWndProc(ref Message m)
176 m.Result=XplatUI.DefWndProc(ref m);
179 public virtual void DestroyHandle()
181 if (window_handle != IntPtr.Zero) {
182 XplatUI.DestroyWindow(window_handle);
186 public virtual void ReleaseHandle()
188 RemoveFromTable (this);
189 window_handle=IntPtr.Zero;
193 #endregion // Public Instance Methods
195 #region Protected Instance Methods
200 protected virtual void OnHandleChange()
204 protected virtual void OnThreadException(Exception e)
206 Application.OnThreadException(e);
209 protected virtual void WndProc(ref Message m)
214 internal static IntPtr WndProc(IntPtr hWnd, Msg msg, IntPtr wParam, IntPtr lParam)
216 IntPtr result = IntPtr.Zero;
217 Message m = new Message();
222 m.Result = IntPtr.Zero;
225 Console.WriteLine("NativeWindow.cs ({0}, {1}, {2}, {3}): result {4}", hWnd, msg, wParam, lParam, m.Result);
227 NativeWindow window = null;
230 object current = null;
231 lock (window_collection) {
232 current = window_collection[hWnd];
235 window = current as NativeWindow;
237 window = EnsureCreated (window, hWnd);
239 if (window != null) {
240 window.WndProc (ref m);
242 } else if (current is ArrayList) {
243 ArrayList windows = (ArrayList) current;
245 if (windows.Count > 0) {
246 window = EnsureCreated ((NativeWindow)windows[0], hWnd);
247 window.WndProc (ref m);
248 // the first one is the control's one. all others are synthetic,
249 // so we want only the result from the control
251 for (int i=1; i < windows.Count; i++)
252 ((NativeWindow)windows[i]).WndProc (ref m);
256 result = XplatUI.DefWndProc (ref m);
259 catch (Exception ex) {
260 #if !ExternalExceptionHandler
261 if (window != null) {
262 if (msg == Msg.WM_PAINT && window is Control.ControlNativeWindow) {
263 // Replace control with a red cross
264 var control = ((Control.ControlNativeWindow)window).Owner;
266 var redCross = new Control (control.Parent, string.Empty);
267 redCross.BackColor = Color.White;
268 redCross.ForeColor = Color.Red;
269 redCross.Bounds = control.Bounds;
270 redCross.Paint += HandleRedCrossPaint;
272 window.OnThreadException (ex);
279 Console.WriteLine("NativeWindow.cs: Message {0}, result {1}", msg, m.Result);
285 private static void HandleRedCrossPaint (object sender, PaintEventArgs e)
287 var control = sender as Control;
288 using (var pen = new Pen (control.ForeColor, 2)) {
289 var paintRect = control.DisplayRectangle;
290 e.Graphics.DrawRectangle (pen, paintRect.Left + 1,
291 paintRect.Top + 1, paintRect.Width - 1, paintRect.Height - 1);
292 // NOTE: .NET's drawing of the red cross seems to have a bug
293 // that draws the bottom and right of the rectangle only 1 pixel
294 // wide. We would get a nicer rectangle using the following code,
295 // but that runs into a problem with libgdiplus.
296 //var paintRect = control.DisplayRectangle;
297 //paintRect.Inflate (-1, -1);
298 //e.Graphics.DrawRectangle (pen, paintRect);
299 e.Graphics.DrawLine (pen, paintRect.Location,
300 paintRect.Location + paintRect.Size);
301 e.Graphics.DrawLine (pen, new Point (paintRect.Left, paintRect.Bottom),
302 new Point (paintRect.Right, paintRect.Top));
306 private static NativeWindow EnsureCreated (NativeWindow window, IntPtr hWnd)
308 // we need to do this AssignHandle here instead of relying on
309 // Control.WndProc to do it, because subclasses can override
310 // WndProc, install their own WM_CREATE block, and look at
311 // this.Handle, and it needs to be set. Otherwise, we end up
312 // recursively creating windows and emitting WM_CREATE.
313 if (window == null && WindowCreating != null) {
314 window = WindowCreating;
315 WindowCreating = null;
316 if (window.Handle == IntPtr.Zero)
317 window.AssignHandle (hWnd);
321 #endregion // Protected Instance Methods