diff --git a/src/Common/src/Interop/User32/Interop.SetParent.cs b/src/Common/src/Interop/User32/Interop.SetParent.cs index 0643cfe52bc..0fe148de583 100644 --- a/src/Common/src/Interop/User32/Interop.SetParent.cs +++ b/src/Common/src/Interop/User32/Interop.SetParent.cs @@ -9,7 +9,7 @@ internal static partial class Interop { internal static partial class User32 { - [DllImport(Libraries.User32, ExactSpelling = true)] + [DllImport(Libraries.User32, ExactSpelling = true, SetLastError = true)] public static extern IntPtr SetParent(IntPtr hWndChild, IntPtr hWndChildNewParent); public static IntPtr SetParent(IntPtr hWndChild, HandleRef hWndChildNewParent) diff --git a/src/System.Windows.Forms/src/Resources/SR.resx b/src/System.Windows.Forms/src/Resources/SR.resx index aa10b110981..78ecc5d77e8 100644 --- a/src/System.Windows.Forms/src/Resources/SR.resx +++ b/src/System.Windows.Forms/src/Resources/SR.resx @@ -4513,7 +4513,7 @@ Stack trace where the illegal operation occurred was: Print preview - + PrintDocument to be previewed. @@ -6653,4 +6653,7 @@ Stack trace where the illegal operation occurred was: UpDown + + Failed to set Win32 parent window of the Control. + diff --git a/src/System.Windows.Forms/src/Resources/xlf/SR.cs.xlf b/src/System.Windows.Forms/src/Resources/xlf/SR.cs.xlf index 32a5c2cbf05..c2e6adbaf85 100644 --- a/src/System.Windows.Forms/src/Resources/xlf/SR.cs.xlf +++ b/src/System.Windows.Forms/src/Resources/xlf/SR.cs.xlf @@ -10881,6 +10881,11 @@ Trasování zásobníku, kde došlo k neplatné operaci: Šířka musí být větší než hodnota MinWidth. + + Failed to set Win32 parent window of the Control. + Failed to set Win32 parent window of the Control. + + SetCompatibleTextRenderingDefault must be called before the first IWin32Window object is created in the application. Před vytvořením prvního objektu IWin32Window v aplikaci je nutné volat metodu SetCompatibleTextRenderingDefault. diff --git a/src/System.Windows.Forms/src/Resources/xlf/SR.de.xlf b/src/System.Windows.Forms/src/Resources/xlf/SR.de.xlf index f100f1b05c3..91e176a8caf 100644 --- a/src/System.Windows.Forms/src/Resources/xlf/SR.de.xlf +++ b/src/System.Windows.Forms/src/Resources/xlf/SR.de.xlf @@ -10881,6 +10881,11 @@ Stapelüberwachung, in der der unzulässige Vorgang auftrat: "Width" muss größer als MinWidth sein. + + Failed to set Win32 parent window of the Control. + Failed to set Win32 parent window of the Control. + + SetCompatibleTextRenderingDefault must be called before the first IWin32Window object is created in the application. SetCompatibleTextRenderingDefault muss aufgerufen werden, bevor das erste IWin32Window-Objekt in der Anwendung erstellt wird. diff --git a/src/System.Windows.Forms/src/Resources/xlf/SR.es.xlf b/src/System.Windows.Forms/src/Resources/xlf/SR.es.xlf index eeacf250dc7..b3f55e0882b 100644 --- a/src/System.Windows.Forms/src/Resources/xlf/SR.es.xlf +++ b/src/System.Windows.Forms/src/Resources/xlf/SR.es.xlf @@ -10881,6 +10881,11 @@ El seguimiento de la pila donde tuvo lugar la operación no válida fue: El ancho debe ser mayor que MinWidth. + + Failed to set Win32 parent window of the Control. + Failed to set Win32 parent window of the Control. + + SetCompatibleTextRenderingDefault must be called before the first IWin32Window object is created in the application. SetCompatibleTextRenderingDefault se debe llamar antes de crear el primer objeto IWin32Window en la aplicación. diff --git a/src/System.Windows.Forms/src/Resources/xlf/SR.fr.xlf b/src/System.Windows.Forms/src/Resources/xlf/SR.fr.xlf index c7d31c428db..edbd41e6a57 100644 --- a/src/System.Windows.Forms/src/Resources/xlf/SR.fr.xlf +++ b/src/System.Windows.Forms/src/Resources/xlf/SR.fr.xlf @@ -10881,6 +10881,11 @@ Cette opération non conforme s'est produite sur la trace de la pile : La largeur doit être supérieure à MinWidth. + + Failed to set Win32 parent window of the Control. + Failed to set Win32 parent window of the Control. + + SetCompatibleTextRenderingDefault must be called before the first IWin32Window object is created in the application. SetCompatibleTextRenderingDefault doit être appelé avant la création du premier objet IWin32Window dans l'application. diff --git a/src/System.Windows.Forms/src/Resources/xlf/SR.it.xlf b/src/System.Windows.Forms/src/Resources/xlf/SR.it.xlf index 8ca0e05cbdb..d01f189cc28 100644 --- a/src/System.Windows.Forms/src/Resources/xlf/SR.it.xlf +++ b/src/System.Windows.Forms/src/Resources/xlf/SR.it.xlf @@ -10881,6 +10881,11 @@ Traccia dello stack da cui si è verificata l'operazione non valida: Width deve essere maggiore di MinWidth. + + Failed to set Win32 parent window of the Control. + Failed to set Win32 parent window of the Control. + + SetCompatibleTextRenderingDefault must be called before the first IWin32Window object is created in the application. SetCompatibleTextRenderingDefault deve essere chiamato prima della creazione del primo oggetto IWin32Window nell'applicazione. diff --git a/src/System.Windows.Forms/src/Resources/xlf/SR.ja.xlf b/src/System.Windows.Forms/src/Resources/xlf/SR.ja.xlf index 21268309ed3..12d0c4e1460 100644 --- a/src/System.Windows.Forms/src/Resources/xlf/SR.ja.xlf +++ b/src/System.Windows.Forms/src/Resources/xlf/SR.ja.xlf @@ -10881,6 +10881,11 @@ Stack trace where the illegal operation occurred was: 幅は MinWidth より大きくなければなりません。 + + Failed to set Win32 parent window of the Control. + Failed to set Win32 parent window of the Control. + + SetCompatibleTextRenderingDefault must be called before the first IWin32Window object is created in the application. 最初の IWin32Window オブジェクトがアプリケーションで作成される前に、SetCompatibleTextRenderingDefault が呼び出されなければなりません。 diff --git a/src/System.Windows.Forms/src/Resources/xlf/SR.ko.xlf b/src/System.Windows.Forms/src/Resources/xlf/SR.ko.xlf index 40d3e6176e0..9e95799e86e 100644 --- a/src/System.Windows.Forms/src/Resources/xlf/SR.ko.xlf +++ b/src/System.Windows.Forms/src/Resources/xlf/SR.ko.xlf @@ -10881,6 +10881,11 @@ Stack trace where the illegal operation occurred was: Width는 MinWidth보다 커야 합니다. + + Failed to set Win32 parent window of the Control. + Failed to set Win32 parent window of the Control. + + SetCompatibleTextRenderingDefault must be called before the first IWin32Window object is created in the application. SetCompatibleTextRenderingDefault는 응용 프로그램에 첫 번째 IWin32Window 개체가 만들어지기 전에 호출해야 합니다. diff --git a/src/System.Windows.Forms/src/Resources/xlf/SR.pl.xlf b/src/System.Windows.Forms/src/Resources/xlf/SR.pl.xlf index 4d360b9152e..4217e380b78 100644 --- a/src/System.Windows.Forms/src/Resources/xlf/SR.pl.xlf +++ b/src/System.Windows.Forms/src/Resources/xlf/SR.pl.xlf @@ -10881,6 +10881,11 @@ Stos śledzenia, w którym wystąpiła zabroniona operacja: Szerokość musi być większa od wartości elementu MinWidth. + + Failed to set Win32 parent window of the Control. + Failed to set Win32 parent window of the Control. + + SetCompatibleTextRenderingDefault must be called before the first IWin32Window object is created in the application. Należy wywołać element SetCompatibleTextRenderingDefault, aby pierwszy obiekt IWin32Window został utworzony w aplikacji. diff --git a/src/System.Windows.Forms/src/Resources/xlf/SR.pt-BR.xlf b/src/System.Windows.Forms/src/Resources/xlf/SR.pt-BR.xlf index 5412d8a708c..70d6e49d224 100644 --- a/src/System.Windows.Forms/src/Resources/xlf/SR.pt-BR.xlf +++ b/src/System.Windows.Forms/src/Resources/xlf/SR.pt-BR.xlf @@ -10881,6 +10881,11 @@ Rastreamento de pilha em que a operação ilegal ocorreu: A largura deve ser maior que MinWidth. + + Failed to set Win32 parent window of the Control. + Failed to set Win32 parent window of the Control. + + SetCompatibleTextRenderingDefault must be called before the first IWin32Window object is created in the application. É necessário chamar SetCompatibleTextRenderingDefault antes da criação do primeiro objeto IWin32Window no aplicativo. diff --git a/src/System.Windows.Forms/src/Resources/xlf/SR.ru.xlf b/src/System.Windows.Forms/src/Resources/xlf/SR.ru.xlf index 134a960ca19..0ae416ddf4f 100644 --- a/src/System.Windows.Forms/src/Resources/xlf/SR.ru.xlf +++ b/src/System.Windows.Forms/src/Resources/xlf/SR.ru.xlf @@ -10882,6 +10882,11 @@ Stack trace where the illegal operation occurred was: Значение ширины должно быть больше MinWidth. + + Failed to set Win32 parent window of the Control. + Failed to set Win32 parent window of the Control. + + SetCompatibleTextRenderingDefault must be called before the first IWin32Window object is created in the application. До создания первого объекта IWin32Window в приложении необходимо вызвать SetCompatibleTextRenderingDefault. diff --git a/src/System.Windows.Forms/src/Resources/xlf/SR.tr.xlf b/src/System.Windows.Forms/src/Resources/xlf/SR.tr.xlf index 8a19a2b62a4..d12838be314 100644 --- a/src/System.Windows.Forms/src/Resources/xlf/SR.tr.xlf +++ b/src/System.Windows.Forms/src/Resources/xlf/SR.tr.xlf @@ -10881,6 +10881,11 @@ Geçersiz işlemin gerçekleştiği yığın izi: Width değeri MinWidth değerinden büyük olmalı. + + Failed to set Win32 parent window of the Control. + Failed to set Win32 parent window of the Control. + + SetCompatibleTextRenderingDefault must be called before the first IWin32Window object is created in the application. Uygulamada ilk IWin32Window nesnesi oluşturulmadan önce SetCompatibleTextRenderingDefault çağrılmalıdır. diff --git a/src/System.Windows.Forms/src/Resources/xlf/SR.zh-Hans.xlf b/src/System.Windows.Forms/src/Resources/xlf/SR.zh-Hans.xlf index 72a5109a63d..717a07c9d00 100644 --- a/src/System.Windows.Forms/src/Resources/xlf/SR.zh-Hans.xlf +++ b/src/System.Windows.Forms/src/Resources/xlf/SR.zh-Hans.xlf @@ -10881,6 +10881,11 @@ Stack trace where the illegal operation occurred was: 宽度必须大于 MinWidth。 + + Failed to set Win32 parent window of the Control. + Failed to set Win32 parent window of the Control. + + SetCompatibleTextRenderingDefault must be called before the first IWin32Window object is created in the application. 在应用程序中创建第一个 IWin32Window 对象之前,必须调用 SetCompatibleTextRenderingDefault。 diff --git a/src/System.Windows.Forms/src/Resources/xlf/SR.zh-Hant.xlf b/src/System.Windows.Forms/src/Resources/xlf/SR.zh-Hant.xlf index 60f7b43f321..4acd436fc62 100644 --- a/src/System.Windows.Forms/src/Resources/xlf/SR.zh-Hant.xlf +++ b/src/System.Windows.Forms/src/Resources/xlf/SR.zh-Hant.xlf @@ -10881,6 +10881,11 @@ Stack trace where the illegal operation occurred was: 寬度必須大於 MinWidth。 + + Failed to set Win32 parent window of the Control. + Failed to set Win32 parent window of the Control. + + SetCompatibleTextRenderingDefault must be called before the first IWin32Window object is created in the application. 在應用程式中建立第一個 IWin32Window 物件之前,必須先呼叫 SetCompatibleTextRenderingDefault。 diff --git a/src/System.Windows.Forms/src/System/Windows/Forms/Control.ActiveXImpl.cs b/src/System.Windows.Forms/src/System/Windows/Forms/Control.ActiveXImpl.cs index b68134c086e..da76ca44413 100644 --- a/src/System.Windows.Forms/src/System/Windows/Forms/Control.ActiveXImpl.cs +++ b/src/System.Windows.Forms/src/System/Windows/Forms/Control.ActiveXImpl.cs @@ -918,7 +918,10 @@ internal unsafe void InPlaceActivate(Ole32.OLEIVERB verb) // If it doesn't, that means that the host // won't reflect messages back to us. HWNDParent = hwndParent; - User32.SetParent(new HandleRef(_control, _control.Handle), hwndParent); + if (User32.SetParent(new HandleRef(_control, _control.Handle), hwndParent) == IntPtr.Zero) + { + throw new Win32Exception(Marshal.GetLastWin32Error(), SR.Win32SetParentFailed); + } // Now create our handle if it hasn't already been done. _control.CreateControl(); diff --git a/src/System.Windows.Forms/src/System/Windows/Forms/Control.cs b/src/System.Windows.Forms/src/System/Windows/Forms/Control.cs index 3c916a918f1..93337cb2f58 100644 --- a/src/System.Windows.Forms/src/System/Windows/Forms/Control.cs +++ b/src/System.Windows.Forms/src/System/Windows/Forms/Control.cs @@ -7497,7 +7497,10 @@ internal virtual void OnParentHandleRecreated() { if (IsHandleCreated) { - User32.SetParent(new HandleRef(this, Handle), new HandleRef(parent, parent.Handle)); + if (User32.SetParent(new HandleRef(this, Handle), new HandleRef(parent, parent.Handle)) == IntPtr.Zero) + { + throw new Win32Exception(Marshal.GetLastWin32Error(), SR.Win32SetParentFailed); + } UpdateZOrder(); } } @@ -9755,118 +9758,154 @@ protected void RecreateHandle() internal virtual void RecreateHandleCore() { - // lock (this) { - if (IsHandleCreated) + if (!IsHandleCreated) { + // Do nothing if the handle is not created yet. + return; + } - bool focused = ContainsFocus; + bool focused = ContainsFocus; #if DEBUG - if (CoreSwitches.PerfTrack.Enabled) - { - Debug.Write("RecreateHandle: "); - Debug.Write(GetType().FullName); - Debug.Write(" [Text="); - Debug.Write(Text); - Debug.Write("]"); - Debug.WriteLine(""); - } + if (CoreSwitches.PerfTrack.Enabled) + { + Debug.Write("RecreateHandle: "); + Debug.Write(GetType().FullName); + Debug.Write(" [Text="); + Debug.Write(Text); + Debug.Write("]"); + Debug.WriteLine(""); + } #endif - bool created = (_state & States.Created) != 0; - if (GetState(States.TrackingMouseEvent)) - { - SetState(States.MouseEnterPending, true); - UnhookMouseEvent(); - } + bool created = GetState(States.Created); + if (GetState(States.TrackingMouseEvent)) + { + SetState(States.MouseEnterPending, true); + UnhookMouseEvent(); + } - HandleRef parentHandle = new HandleRef(this, User32.GetParent(this)); + HandleRef parentHandle = new HandleRef(this, User32.GetParent(this)); - try - { - Control[] controlSnapshot = null; - _state |= States.Recreate; + Control[] controlSnapshot = null; + SetState(States.Recreate, true); - try - { - // Inform child controls that their parent is recreating handle. + try + { + // Inform child controls that their parent is recreating handle. - // The default behavior is to now SetParent to parking window, then - // SetParent back after the parent's handle has been recreated. - // This behavior can be overridden in OnParentHandleRecreat* and is in ListView. + // The default behavior is to now SetParent to parking window, then + // SetParent back after the parent's handle has been recreated. + // This behavior can be overridden in OnParentHandleRecreat* and is in ListView. - //fish out control collection w/o demand creating one. - ControlCollection controlsCollection = (ControlCollection)Properties.GetObject(s_controlsCollectionProperty); - if (controlsCollection != null && controlsCollection.Count > 0) + //fish out control collection w/o demand creating one. + ControlCollection controlsCollection = (ControlCollection)Properties.GetObject(s_controlsCollectionProperty); + if (controlsCollection != null && controlsCollection.Count > 0) + { + controlSnapshot = new Control[controlsCollection.Count]; + for (int i = 0; i < controlsCollection.Count; i++) + { + Control childControl = controlsCollection[i]; + if (childControl != null && childControl.IsHandleCreated) { - controlSnapshot = new Control[controlsCollection.Count]; - for (int i = 0; i < controlsCollection.Count; i++) - { - Control childControl = controlsCollection[i]; - if (childControl != null && childControl.IsHandleCreated) - { - // SetParent to parking window - childControl.OnParentHandleRecreating(); + // SetParent to parking window + childControl.OnParentHandleRecreating(); - // if we were successful, remember this control - // so we can raise OnParentHandleRecreated - controlSnapshot[i] = childControl; - } - else - { - // put in a null slot which we'll skip over later. - controlSnapshot[i] = null; - } - } + // if we were successful, remember this control + // so we can raise OnParentHandleRecreated + controlSnapshot[i] = childControl; } - - // do the main work of recreating the handle - DestroyHandle(); - CreateHandle(); - } - finally - { - _state &= ~States.Recreate; - - // inform children that their parent's handle has recreated - if (controlSnapshot != null) + else { - for (int i = 0; i < controlSnapshot.Length; i++) - { - Control childControl = controlSnapshot[i]; - if (childControl != null && childControl.IsHandleCreated) - { - // SetParent back to the new Parent handle - childControl.OnParentHandleRecreated(); - } - } + // put in a null slot which we'll skip over later. + controlSnapshot[i] = null; } } - if (created) - { - CreateControl(); - } } - finally + + // do the main work of recreating the handle + DestroyHandle(); + + // Note that CreateHandle --> _window.CreateHandle may fail due to DPI awareness setting. + // By carefully choosing the correct parking window / keeping this and this.Parent DPI awareness untouched, + // the call shouldn't fail. + // However, it could fail if this.CreateParams.Parent is changed outside our control. + CreateHandle(); + } + catch (Exception) + { + // this.DestroyHandle succeeded, but CreateHandle failed. + // The control is actually destroyed. + if (_window.Handle == IntPtr.Zero) { - if (parentHandle.Handle != IntPtr.Zero // the parent was not null - && (FromHandle(parentHandle.Handle) == null || _parent == null) // but wasnt a windows forms window - && UnsafeNativeMethods.IsWindow(parentHandle)) - { // and still is a window - // correctly parent back up to where we were before. - // if we were parented to a proper windows forms control, CreateControl would have properly parented - // us back. - User32.SetParent(new HandleRef(this, Handle), parentHandle); + SetState(States.Created, false); + } + + throw; + } + finally + { + SetState(States.Recreate, false); + + // Inform children their parent's handle has been created. + // This means + // an Exception gets thrown before/during the invocation of DestroyHandle. + // In this case, GetState(States.Created) == true. + // We will restore the Parent value of the (visited) child controls. + // - or - + // an Exception gets thrown in CreateHandle. + // In this case, _window.Handle will be IntPtr.Zero, + // and we should have GetState(States.Created) == false. + // Do not go through this if CreateHandle fails (and an Exception is probably on its way bubbling up). + // - or - + // CreateHandle is successful. + // We will move the child controls to the new parent. + if (controlSnapshot != null && GetState(States.Created)) + { + for (int i = 0; i < controlSnapshot.Length; i++) + { + Control childControl = controlSnapshot[i]; + if (childControl != null && childControl.IsHandleCreated) + { + // Re-parent the control. + // If the control fails to re-parent itself, + // It and its next siblings will keep States.ParentRecreating state, + // parked in ParkingWindow. + // We let the error bubble up immediately. + childControl.OnParentHandleRecreated(); + } } } + } - // Restore control focus - if (focused) + if (created) + { + CreateControl(); + } + + if (// The window has a parent Win32 window before re-creation + parentHandle.Handle != IntPtr.Zero + // But the parent is not a managed WinForm Control, or this.Parent == null + && (FromHandle(parentHandle.Handle) == null || _parent == null) + // Still, parentHandle is a valid native Win32 window handle, e.g. the desktop window. + && UnsafeNativeMethods.IsWindow(parentHandle)) + { + // correctly parent back up to where we were before. + // if we were parented to a proper windows forms control, CreateControl would have properly parented + // us back. + if (User32.SetParent(new HandleRef(this, Handle), parentHandle) == IntPtr.Zero) { - Focus(); + // Somehow we failed to SetParent due to, e.g., different DPI awareness setting. + throw new Win32Exception(Marshal.GetLastWin32Error(), SR.Win32SetParentFailed); } } + + // Restore control focus + if (focused) + { + Focus(); + } } } @@ -10918,7 +10957,13 @@ private void SetParentHandle(IntPtr value) } else { - User32.SetParent(new HandleRef(_window, Handle), value); + if (User32.SetParent(new HandleRef(_window, Handle), value) == IntPtr.Zero) + { + // Somehow we failed to SetParent, e.g. due to different DPI awareness setting. + // Throwing exception will keep the handle parked inside ParkingWindow if recreate == true. + throw new Win32Exception(Marshal.GetLastWin32Error(), SR.Win32SetParentFailed); + } + if (_parent != null) { _parent.UpdateChildZOrder(this); @@ -10932,7 +10977,11 @@ private void SetParentHandle(IntPtr value) // The handle was previously parented to the parking window. Its TopLevel property was // then changed to true so the above call to GetParent returns null even though the parent of the control is // not null. We need to explicitly set the parent to null. - User32.SetParent(new HandleRef(_window, Handle), IntPtr.Zero); + if (User32.SetParent(new HandleRef(_window, Handle), IntPtr.Zero) == IntPtr.Zero) + { + throw new Win32Exception(Marshal.GetLastWin32Error(), SR.Win32SetParentFailed); + } + Application.UnparkHandle(new HandleRef(_window, Handle), _window.DpiAwarenessContext); } } diff --git a/src/System.Windows.Forms/src/System/Windows/Forms/NativeWindow.cs b/src/System.Windows.Forms/src/System/Windows/Forms/NativeWindow.cs index 8a2285eb63c..b51a0ce13b8 100644 --- a/src/System.Windows.Forms/src/System/Windows/Forms/NativeWindow.cs +++ b/src/System.Windows.Forms/src/System/Windows/Forms/NativeWindow.cs @@ -438,8 +438,8 @@ public virtual void CreateHandle(CreateParams cp) IntPtr createResult = IntPtr.Zero; int lastWin32Error = 0; - // Parking window dpi awarness context need to match with dpi awarenss context of control being - // parented to this parkign window. Otherwise, reparenting of control will fail. + // Parking window dpi awareness context need to match with dpi awareness context of control being + // parented to this parking window. Otherwise, reparenting of control will fail. using (DpiHelper.EnterDpiAwarenessScope(DpiAwarenessContext)) { IntPtr modHandle = Kernel32.GetModuleHandleW(null);