简体   繁体   English

将 Window 脱钩到原来的 State

[英]Unhook Window into its original State

I hook a window into a Panel using this code:我使用以下代码将 window 挂接到面板中:

SetParent(win, pnlApp.Handle);
SetWindowLong(win, GWL_STYLE, WS_VISIBLE);
MoveWindow(win, 0, 0, pnlApp.Width, pnlApp.Height, true);

where win is the Window handle, as an IntPtr .其中win是 Window 句柄,作为IntPtr Here think about Notepad, it will hook very well into a Panel.在这里想想记事本,它会很好地钩入一个面板。
But when I try to release (unhook) it using this code:但是当我尝试使用以下代码释放(取消挂钩)它时:

SetParent(win, (IntPtr)0);

It's shown without the Window frame, I mean it will release into the Desktop again.它显示没有 Window 框架,我的意思是它会再次发布到桌面。 but it has not any Window frame.但它没有任何 Window 框架。

How to solve this issue?如何解决这个问题?

In relation to the code in the question:关于问题中的代码:

  • SetWindowLong() should be replace by SetWindowLongPtr() - see the warning in the Docs. SetWindowLong()应替换为SetWindowLongPtr() - 请参阅文档中的警告 The latter calls the former in case the calling code requires it.后者在调用代码需要时调用前者。

  • You need to call GetWindowLongPtr() to get the current Window Styles, then add or remove Styles as needed;需要调用GetWindowLongPtr()获取当前的Window Styles,然后根据需要添加或删除Styles; store the original value: it will be used to restore the previous status.存储原始值:用于恢复之前的状态。
    ► In the example, the WS_SYSMENU Style is removed, hence the Window will not show the System Menu and all the Buttons usually located in the Caption. ► 在示例中,删除了WS_SYSMENU样式,因此 Window 不会显示系统菜单和通常位于标题中的所有按钮。 It's just an example, to see how this works (it may not be effective with all Windows).这只是一个例子,看看它是如何工作的(它可能不适用于所有 Windows)。

  • SetParent() returns the Handle of the previous Parent. SetParent()返回前一个 Parent 的句柄。 Store this value, it will be needed to restore the Window to its previous owner, whatever that is.存储此值,将需要将 Window 恢复为其先前的所有者,无论它是什么。 If the Window is a top-level Window, the previous Parent appears to be the Desktop, so you expect IntPtr.Zero .如果 Window 是顶级 Window,则先前的 Parent似乎是 Desktop,因此您期望IntPtr.Zero It might not be, you can get a Handle that is not IntPtr.Zero .可能不是,您可以获得一个不是IntPtr.Zero的句柄。 Just store the returned value, see what that is, if you're interested:)只需存储返回的值,如果您有兴趣,请查看它是什么:)

  • You should check the return value of both GetWindowLongPtr() and SetWindowLongPtr() : if it's 0 , then there was an error.您应该检查GetWindowLongPtr()SetWindowLongPtr()的返回值:如果它是0 ,那么有一个错误。 You can use Marshal.GetLastWin32Error() to get the error code.您可以使用Marshal.GetLastWin32Error()来获取错误代码。 Note that SetWindowLongPtr() doesn't clear the error code, so you have to clear it yourself, calling SetLastError(0) .请注意, SetWindowLongPtr()不会清除错误代码,因此您必须自己清除它,调用SetLastError(0)

  • You can use MoveWindow() to reposition the Windows inside the new Parent's bounds;您可以使用MoveWindow()将 Windows 重新定位在新父级的范围内; it's probably not required.这可能不是必需的。 Anyway, I suggest to call SetWindowPos() instead, you have more options.无论如何,我建议改为调用SetWindowPos() ,您有更多选择。 See the code here.请参阅此处的代码。

Disclaimer : keep in mind what's in Raymond Chen's blog post:免责声明:请记住 Raymond Chen 的博客文章中的内容:
Is it legal to have a cross-process parent/child or owner/owned window relationship 拥有跨进程父/子或所有者/拥有的 window 关系是否合法
In other words, it's a just because you asked thing:)换句话说,这只是因为你问的事情:)


Set these Fields (or whatever fits):设置这些字段(或任何合适的):

  • windowdHwnd is the Handle of the Window to re-parent windowdHwnd是 Window 的句柄 to re-parent
  • oldParent is the Handle of the previous Parent, returned by SetParent() oldParent是前一个 Parent 的句柄,由SetParent()返回
  • oldStyles will store the previous Window's Styles, returned by GetWWindowLongPtr() oldStyles将存储上一个 Window 的 Styles,由GetWWindowLongPtr()返回
IntPtr windowdHwnd = IntPtr.Zero;
IntPtr oldParent = (IntPtr)(-1);
int oldStyles = 0;
// [...]

When you have the Handle of the Window you care about, set windowdHwnd to this value.当你有你关心的 Window 的 Handle 时,将windowdHwnd设置为这个值。 Call this code after (it could be a method, pass the Window Handle and the Container Control that will be the new Parent - here, a Control named somePanel ).之后调用此代码(它可能是一个方法,传递 Window 句柄和将成为新父级的容器控件 - 这里是一个名为somePanel的控件)。

// Already parented, resets the previous styles, clean up and return
if (windowdHwnd != IntPtr.Zero && oldParent != (IntPtr)(-1)) {
    NativeMethods.SetParent(windowdHwnd, oldParent);
    NativeMethods.SetWindowLongPtr(windowdHwnd, NativeMethods.GWL_Flags.GWL_STYLE, new IntPtr(oldStyles));
    windowdHwnd = IntPtr.Zero;
    oldParent = (IntPtr)(-1);
    return;
}

// Store the existing Styles, to restore when the Window is dismissed
oldStyles = NativeMethods.GetWindowLongPtr(windowdHwnd, NativeMethods.GWL_Flags.GWL_STYLE);
if (oldStyles == IntPtr.Zero) {
    int error = Marshal.GetLastWin32Error();  // Show the error code or throw an exception
    return;
}

// Removes the System Menu from the Window: it will also remove the Buttons from the Caption
int newStyle = oldStyles.ToInt32()^ (int)NativeMethods.WinStyles.WS_SYSMENU;

NativeMethods.SetLastError(0);
// Sets the new Styles
IntPtr result = NativeMethods.SetWindowLongPtr(windowdHwnd, NativeMethods.GWL_Flags.GWL_STYLE, (IntPtr)newStyle);
if (result == IntPtr.Zero) {
    int error = Marshal.GetLastWin32Error();  // Show the error code or throw an exception
    return;
}

oldParent = NativeMethods.SetParent(windowdHwnd, somePanel.Handle);

// Repositions the Window and shows it, if needed
var flags = NativeMethods.SWP_Flags.SWP_ASYNCWINDOWPOS | NativeMethods.SWP_Flags.SWP_SHOWWINDOW;
NativeMethods.SetWindowPos(windowdHwnd, IntPtr.Zero, 0, 0, somePanel.Width, somePanel.Height, flags);

You can write (this just removes):你可以写(这只是删除):

int newStyle = oldStyles &~(int)NativeMethods.WinStyles.WS_SYSMENU;

instead of (this switches on/off):而不是(这会打开/关闭):

int newStyle = oldStyles ^ (int)NativeMethods.WinStyles.WS_SYSMENU;

NativeMethods class: NativeMethods class:

using System.Runtime.InteropServices;

public class NativeMethods
{
    [Flags]
    public enum SWP_Flags : uint
    {
        /// <summary>Retains the current size (ignores the cx and cy parameters).</summary>
        SWP_NOSIZE = 0x0001,
        /// <summary>Retains the current position (ignores X and Y parameters).</summary>
        SWP_NOMOVE = 0x0002,
        /// <summary>Retains the current Z order (ignores the hWndInsertAfter parameter).</summary>
        SWP_NOZORDER = 0x0004,
        /// <summary>Does not redraw changes. If this flag is set, no repainting of any kind occurs. This applies to
        /// the client area, the nonclient area (including the title bar and scroll bars), and any part of the parent
        /// window uncovered as a result of the window being moved. When this flag is set, the application must
        /// explicitly invalidate or redraw any parts of the window and parent window that need redrawing.</summary>
        SWP_NOREDRAW = 0x0008,
        /// <summary>Does not activate the window. If this flag is not set, the window is activated and moved to the
        /// top of either the topmost or non-topmost group (depending on the setting of the hWndInsertAfter
        /// parameter).</summary>
        SWP_NOACTIVATE = 0x0010,
        /// <summary>Draws a frame (defined in the window's class description) around the window.</summary>
        SWP_DRAWFRAME = 0x0020,
        /// <summary>Applies new frame styles set using the SetWindowLong function. Sends a WM_NCCALCSIZE message to
        /// the window, even if the window's size is not being changed. If this flag is not specified, WM_NCCALCSIZE
        /// is sent only when the window's size is being changed.</summary>
        SWP_FRAMECHANGED = 0x0020,
        /// <summary>Displays the window.</summary>
        SWP_SHOWWINDOW = 0x0040,
        /// <summary>Hides the window.</summary>
        SWP_HIDEWINDOW = 0x0080,
        /// <summary>Discards the entire contents of the client area. If this flag is not specified, the valid
        /// contents of the client area are saved and copied back into the client area after the window is sized or
        /// repositioned.</summary>
        SWP_NOCOPYBITS = 0x0100,
        /// <summary>Does not change the owner window's position in the Z order.</summary>
        SWP_NOOWNERZORDER = 0x0200,
        /// <summary>Same as the SWP_NOOWNERZORDER flag.</summary>
        SWP_NOREPOSITION = 0x0200,
        /// <summary>Prevents the window from receiving the WM_WINDOWPOSCHANGING message.</summary>
        SWP_NOSENDCHANGING = 0x0400,
        /// <summary>Internal use.</summary>
        SWP_NOCLIENTSIZE = 0x0800,
        /// <summary>Internal use.</summary>
        SWP_NOCLIENTMOVE = 0x1000,
        /// <summary>Prevents generation of the WM_SYNCPAINT message.</summary>
        SWP_DEFERERASE = 0x2000,
        /// <summary>If the calling thread and the thread that owns the window are attached to different input queues,
        /// the system posts the request to the thread that owns the window. This prevents the calling thread from
        /// blocking its execution while other threads process the request.</summary>
        SWP_ASYNCWINDOWPOS = 0x4000
    }

    [Flags]
    public enum WinStyles : uint
    {
        WS_BORDER = 0x00800000,                     //The window has a thin-line border.
        WS_CAPTION = 0x00C00000,                    //The window has a title bar (includes the WS_BORDER style).
        WS_CHILD = 0x40000000,                      //The window is a child window. A window with this style cannot have a menu bar. This style cannot be used with the WS_POPUP style.
        WS_CHILDWINDOW = 0x40000000,                //Same as the WS_CHILD style.
        WS_CLIPCHILDREN = 0x02000000,               //Excludes the area occupied by child windows when drawing occurs within the parent window. This style is used when creating the parent window.
        WS_CLIPSIBLINGS = 0x04000000,               //Clips child windows relative to each other; that is, when a particular child window receives a WM_PAINT message, the WS_CLIPSIBLINGS style clips all other overlapping child windows out of the region of the child window to be updated. If WS_CLIPSIBLINGS is not specified and child windows overlap, it is possible, when drawing within the client area of a child window, to draw within the client area of a neighboring child window.
        WS_DISABLED = 0x08000000,                   //The window is initially disabled. A disabled window cannot receive input from the user. To change this after a window has been created, use the EnableWindow function.
        WS_DLGFRAME = 0x00400000,                   //The window has a border of a style typically used with dialog boxes. A window with this style cannot have a title bar.
        WS_GROUP = 0x00020000,                      //The window is the first control of a group of controls. The group consists of this first control and all controls defined after it, up to the next control with the WS_GROUP style. The first control in each group usually has the WS_TABSTOP style so that the user can move from group to group. The user can subsequently change the keyboard focus from one control in the group to the next control in the group by using the direction keys.
                                                    //You can turn this style on and off to change dialog box navigation. To change this style after a window has been created, use the SetWindowLong function.
        WS_HSCROLL = 0x00100000,                    //The window has a horizontal scroll bar.
        WS_ICONIC = 0x20000000,                     //The window is initially minimized. Same as the WS_MINIMIZE style.
        WS_MAXIMIZE = 0x01000000,                   //The window is initially maximized.
        WS_MAXIMIZEBOX = 0x00010000,                //The window has a maximize button. Cannot be combined with the WS_EX_CONTEXTHELP style. The WS_SYSMENU style must also be specified.
        WS_MINIMIZE = 0x20000000,                   //The window is initially minimized. Same as the WS_ICONIC style.
        WS_MINIMIZEBOX = 0x00020000,                //The window has a minimize button. Cannot be combined with the WS_EX_CONTEXTHELP style. The WS_SYSMENU style must also be specified.
        WS_OVERLAPPED = 0x00000000,                 //The window is an overlapped window. An overlapped window has a title bar and a border. Same as the WS_TILED style.
        WS_OVERLAPPEDWINDOW = WS_OVERLAPPED |       //The window is an overlapped window. Same as the WS_TILEDWINDOW style.
                              WS_CAPTION |
                              WS_SYSMENU |
                              WS_THICKFRAME |
                              WS_MINIMIZEBOX |
                              WS_MAXIMIZEBOX,
        WS_POPUP = 0x80000000,                      //The windows is a pop-up window. This style cannot be used with the WS_CHILD style.
        WS_POPUPWINDOW = WS_POPUP |                 //The window is a pop-up window. The WS_CAPTION and WS_POPUPWINDOW styles must be combined to make the window menu visible.
                         WS_BORDER |
                         WS_SYSMENU,
        WS_SIZEBOX = 0x00040000,                    //The window has a sizing border. Same as the WS_THICKFRAME style.
        WS_SYSMENU = 0x00080000,                    //The window has a window menu on its title bar. The WS_CAPTION style must also be specified.
        WS_TABSTOP = 0x00010000,                    //The window is a control that can receive the keyboard focus when the user presses the TAB key. Pressing the TAB key changes the keyboard focus to the next control with the WS_TABSTOP style.
                                                    //You can turn this style on and off to change dialog box navigation. To change this style after a window has been created, use the SetWindowLong function. For user-created windows and modeless dialogs to work with tab stops, alter the message loop to call the IsDialogMessage function.
        WS_THICKFRAME = 0x00040000,                 //The window has a sizing border. Same as the WS_SIZEBOX style.
        WS_TILED = 0x00000000,                      //The window is an overlapped window. An overlapped window has a title bar and a border. Same as the WS_OVERLAPPED style.
        WS_TILEDWINDOW = WS_OVERLAPPED |            //The window is an overlapped window. Same as the WS_OVERLAPPEDWINDOW style.
                         WS_CAPTION |
                         WS_SYSMENU |
                         WS_THICKFRAME |
                         WS_MINIMIZEBOX |
                         WS_MAXIMIZEBOX,
        WS_VISIBLE = 0x10000000,                   //The window is initially visible. This style can be turned on and off by using the ShowWindow or SetWindowPos function.
        WS_VSCROLL = 0x00200000,                   //The window has a vertical scroll bar.
    }

    public enum GWL_Flags : int
    {
        GWL_USERDATA = -21,
        GWL_EXSTYLE = -20,
        GWL_STYLE = -16,
        GWL_ID = -12,
        GWLP_HWNDPARENT = -8,
        GWLP_HINSTANCE = -6,
        GWL_WNDPROC = -4,
        DWLP_MSGRESULT = 0x0,
        DWLP_DLGPROC = 0x4,
        DWLP_USER = 0x8
    }


    [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
    internal static extern IntPtr GetWindowLongPtr(IntPtr hWnd, GWL_Flags nIndex);

    [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
    internal static extern IntPtr SetWindowLongPtr(IntPtr hWnd, GWL_Flags nIndex, IntPtr dwNewLong);

    [DllImport("user32.dll", SetLastError = true)]
    internal static extern IntPtr SetParent(IntPtr hWndChild, IntPtr hWndNewParent);

    [DllImport("user32.dll", SetLastError = true)]
    internal static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int x, int y, int cx, int cy, SWP_Flags uFlags);

    [DllImport("kernel32.dll")]
    internal static extern void SetLastError(uint dwErrCode);
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM