Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
144 views
in Technique[技术] by (71.8m points)

c# - Unhook Window into its original State

I hook a window into a Panel using this code:

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. 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, but it has not any Window frame.

How to solve this issue?

question from:https://stackoverflow.com/questions/65842979/unhook-window-into-its-original-state

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

In relation to the code in the question:

  • SetWindowLong() should be replace by SetWindowLongPtr() - see the warning in the Docs. 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; 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. It's just an example, to see how this works (it may not be effective with all Windows).

  • SetParent() returns the Handle of the previous Parent. Store this value, it will be needed to restore the Window to its previous owner, whatever that is. If the Window is a top-level Window, the previous Parent appears to be the Desktop, so you expect IntPtr.Zero. It might not be, you can get a Handle that is not 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. You can use Marshal.GetLastWin32Error() to get the error code. Note that SetWindowLongPtr() doesn't clear the error code, so you have to clear it yourself, calling SetLastError(0).

  • You can use MoveWindow() to reposition the Windows inside the new Parent's bounds; it's probably not required. Anyway, I suggest to call SetWindowPos() instead, you have more options. See the code here.

Disclaimer: keep in mind what's in Raymond Chen's blog post:
Is it legal to have a cross-process parent/child or owner/owned window relationship
I don't know what you want to do with all this; just because you asked :)


Set these Fields (or whatever fits):

  • windowdHwnd is the Handle of the Window to re-parent
  • oldParent is the Handle of the previous Parent, returned by SetParent()
  • oldStyles will store the previous Window's Styles, returned by GetWWindowLongPtr()
IntPtr windowdHwnd = IntPtr.Zero;
IntPtr oldParent = IntPtr.Zero;
int oldStyles = 0;
// [...]

When you have the Handle of the Window you care about, set windowdHwnd to this value. 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).

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

// Store the existing Styles, to restore when the Window is dismissed
oldStyles = NativeMethods.GetWindowLongPtr(windowdHwnd, NativeMethods.GWL_Flags.GWL_STYLE).ToInt32();
if (oldStyles == 0) {
    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^ (int)NativeMethods.WinStyles.WS_SYSMENU;

NativeMethods.SetLastError(0);
// Sets the new Styles
IntPtr result = NativeMethods.SetWindowLongPtr(windowdHwnd, NativeMethods.GWL_Flags.GWL_STYLE, new 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:

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,  

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...