/// <summary>
        /// Subclasses the window.
        /// </summary>
        /// <remarks>
        /// You must call <see cref="Dispose()"/> to undo the subclassing before
        /// the window is destroyed.
        /// </remarks>
        /// <returns></returns>
        public void Open()
        {
            if (_disposed)
            {
                throw new ObjectDisposedException(nameof(WindowSubclassHandler));
            }
            if (_opened)
            {
                throw new InvalidOperationException();
            }

            // Replace the existing window procedure with our one
            // ("instance subclassing").
            // We need to explicitely clear the last Win32 error and then retrieve
            // it, to check if the call succeeded.
            WindowSubclassHandlerNativeMethods.SetLastError(0);
            _originalWindowProc = WindowSubclassHandlerNativeMethods.SetWindowLongPtr(
                _handle,
                WindowSubclassHandlerNativeMethods.GWLP_WNDPROC,
                _windowProcDelegatePtr);
            if (_originalWindowProc == IntPtr.Zero && Marshal.GetLastWin32Error() != 0)
            {
                throw new Win32Exception();
            }

            Debug.Assert(_originalWindowProc != _windowProcDelegatePtr);

            _opened = true;
        }
        protected virtual void Dispose(bool disposing)
        {
            if (!_disposed)
            {
                // We cannot do anything from the finalizer thread since we have
                // resoures that must only be accessed from the GUI thread.
                if (disposing && _opened)
                {
                    // Check if the current window procedure is the correct one.
                    // We need to explicitely clear the last Win32 error and then
                    // retrieve it, to check if the call succeeded.
                    WindowSubclassHandlerNativeMethods.SetLastError(0);
                    IntPtr currentWindowProcedure = WindowSubclassHandlerNativeMethods.GetWindowLongPtr(
                        _handle,
                        WindowSubclassHandlerNativeMethods.GWLP_WNDPROC);
                    if (currentWindowProcedure == IntPtr.Zero && Marshal.GetLastWin32Error() != 0)
                    {
                        throw new Win32Exception();
                    }

                    if (currentWindowProcedure != _windowProcDelegatePtr)
                    {
                        throw new InvalidOperationException(
                                  "The current window procedure is not the expected one.");
                    }

                    // Undo the subclassing by restoring the original window
                    // procedure.
                    WindowSubclassHandlerNativeMethods.SetLastError(0);
                    if (WindowSubclassHandlerNativeMethods.SetWindowLongPtr(
                            _handle,
                            WindowSubclassHandlerNativeMethods.GWLP_WNDPROC,
                            _originalWindowProc) == IntPtr.Zero &&
                        Marshal.GetLastWin32Error() != 0)
                    {
                        throw new Win32Exception();
                    }

                    // Ensure to keep the delegate alive up to the point after we
                    // have undone the subclassing.
                    KeepCallbackDelegateAlive();
                }

                _disposed = true;
            }
        }
        protected virtual IntPtr WndProc(
            int msg,
            IntPtr wParam,
            IntPtr lParam)
        {
            // Call the original window procedure to process the message.
            if (_originalWindowProc != IntPtr.Zero)
            {
                return(WindowSubclassHandlerNativeMethods.CallWindowProc(
                           _originalWindowProc,
                           _handle,
                           msg,
                           wParam,
                           lParam));
            }

            return(IntPtr.Zero);
        }