删除和恢复窗口边框

时间:2012-04-26 16:38:00

标签: c# windows winapi border

我想删除C#中另一个进程的窗口边框;我使用RemoveMenu删除边框。它几乎可以工作,但我还有两个问题:

  • 我需要删除边框两次,第一次菜单栏仍然是 存在。
  • 我无法恢复菜单

这就是我已经写过的:

public void RemoveBorders(IntPtr WindowHandle, bool Remove)
    {
        IntPtr MenuHandle = GetMenu(WindowHandle);

        if (Remove)
        {
            int count = GetMenuItemCount(MenuHandle);
            for (int i = 0; i < count; i++)
                RemoveMenu(MenuHandle, 0, (0x40 | 0x10));
        }
        else
        {
            SetMenu(WindowHandle,MenuHandle);
        }

        int WindowStyle = GetWindowLong(WindowHandle, -16);

        //Redraw
        DrawMenuBar(WindowHandle);
        SetWindowLong(WindowHandle, -16, (WindowStyle & ~0x00080000));
        SetWindowLong(WindowHandle, -16, (WindowStyle & ~0x00800000 | 0x00400000));
    }

有人能告诉我我做错了什么吗?我已经尝试保存MenuHandle并在以后恢复它,但这不起作用。

3 个答案:

答案 0 :(得分:1)

  
      
  • 我无法恢复菜单
  •   

这是因为您的MenuHandle是本地变量。

第一次调用方法RemoveBorders结束时,垃圾收集器会删除MenuHandle,并释放内存。

第二次时间调用RemoveBorders,MenuHandle重新创建为新的本地变量,并重新分配到窗口菜单的当前状态 - 没有菜单项的菜单

结果:

MenuHandle不会保存窗口菜单的上一个状态,这就解释了为什么无法恢复窗口的菜单。

我建议您使用MenuHandle 全局变量,并从RemoveBorders方法定义中定义它。

您可以将其定义为私有,受保护或公共字段,并为其定义另一个属性,但这是可选的,并且是必需的。如果此属性对您更好,您也可以将其定义为静态。

以下是MenuHandle定义的一些示例:

private IntPtr MenuHandle;
//or
IntPtr MenuHandle; //Defined outside of RemoveBorders, which is defined below this line, to show that MenuHandle is not local variable.
public void RemoveBorders(IntPtr WindowHandle, bool Remove)
//or
protected IntPtr MenuHandle;
//or
public IntPtr MenuHandle
//or
private static IntPtr MenuHandle
//or
static IntPtr MenuHandle
//etc...

你必须移动这条线:

IntPtr MenuHandle = GetMenu(WindowHandle);

内部:

if (Remove)

并在调用GetMenuItemCount函数之前。

您还必须修改该行,并至少删除IntPtr,以声明MenuHandle是 本地变量,并参考MenuHandle 字段,由RemoveBorders方法定义。 IntelliSense仍会将其识别为字段,并且不会提醒您未定义的错误。

如果MenuHandle 静态,那么您还可以添加this。在MenuHandle之前移除IntPtr后的关键字(换句话说,您可以将IntPtr替换为this.),以记住您自己MenuHandle 本地变量,所以当RemoveBorders完成作业时,垃圾收集器不会删除它。

当您启动程序时,MenuHandle将被指定为IntPtr.Zero作为默认值。当您在第一次时调用RemoveBorders时,MenuHandle的值将设置为if (Remove)中GetMenu函数的返回值。

当RemoveBorders在第一次时间结束时,不删除MenuHandle,并在删除所有项目之前保存窗口菜单的先前状态。

因此,当您为第二时间调用RemoveBorders以恢复菜单时,执行程序将到达if (Remove)代码并立即跳转到else代码,因为删除= false,并且在那里你调用SetMenu函数,当你从第一次调用RemoveBorders以来给它窗口菜单的上一个状态。 通过这种方式,您最终可以恢复窗口的菜单。

我仍然没有意识到为什么你需要两次删除边框,第一次菜单栏仍然存在。 我也想帮你解决这个问题,但不知道。 在这种情况下,您的代码是正确的。对不起,我希望其他人能为您解决这个问题并为您提供解决方案。

答案 1 :(得分:0)

试试这个。这适合我。在此示例中,边框和菜单删除在应用程序内部完成。但是通过微调可以使它适用于外部窗口。

这些是我在代码中声明的一些常量



    const uint WS_BORDER = 0x00800000;
    const uint WS_DLGFRAME = 0x00400000;
    const uint WS_THICKFRAME = 0x00040000;
    const uint WS_CAPTION = WS_BORDER | WS_DLGFRAME;
    const uint WS_MINIMIZE = 0x20000000;
    const uint WS_MAXIMIZE = 0x01000000;
    const uint WS_SYSMENU = 0x00080000;
    const uint WS_VISIBLE = 0x10000000;
    const int GWL_STYLE = -16;

用于窗口边框



Point originallocation = this.Location;
Size originalsize = this.Size;

public void RemoveBorder(IntPtr windowHandle, bool removeBorder)
{

    uint currentstyle = (uint)GetWindowLongPtr(this.Handle, GWL_STYLE).ToInt64();
    uint[] styles = new uint[] { WS_CAPTION, WS_THICKFRAME, WS_MINIMIZE, WS_MAXIMIZE, WS_SYSMENU };

    foreach (uint style in styles)
    {

        if ((currentstyle & style) != 0)
        {

            if(removeBorder)
            {

                currentstyle &= ~style;
            }
            else
            {

                currentstyle |= style;
            }
        }
    }

    SetWindowLongPtr(windowHandle, GWL_STYLE, (IntPtr)(currentstyle));
    //this resizes the window to the client area and back. Also forces the window to redraw.
    if(removeBorder)
    {

        SetWindowPosPtr(this.Handle, (IntPtr)0, this.PointToScreen(this.ClientRectangle.Location).X, this.PointToScreen(this.ClientRectangle.Location).Y, this.ClientRectangle.Width, this.ClientRectangle.Height, 0);
    }
    else
    {

        SetWindowPosPtr(this.Handle, (IntPtr)0, originallocation.X, originallocation.Y, originalsize.Width, originalsize.Height, 0);
    }
}

对于菜单,您可以执行此操作。



    public void RemoveMenu(IntPtr menuHandle, bool removeMenu)
    {
        uint menustyle = (uint)GetWindowLongPtr(menuStrip1.Handle, GWL_STYLE).ToInt64();

        SetWindowLongPtr(menuStrip1.Handle, GWL_STYLE, (IntPtr)(menustyle^WS_VISIBLE));
        // forces the window to redraw (makes the menu visible or not)
        this.Refresh();
    }

另请注意,我将GetWindowLongPtr,SetWindowLongPtr和SetWindowPosPtr与IntPtr一起用作参数,而不是GetWindowLong,SetWindowLong和SetWindowPos int / uint。这是因为x86 / x64的兼容性。

以下是我如何导入GetWindowLongPtr



    [DllImport("user32.dll", EntryPoint = "GetWindowLong")]
    public static extern int GetWindowLong(IntPtr hWnd, int nIndex);

    [DllImport("user32.dll", EntryPoint = "GetWindowLongPtr")]
    public static extern IntPtr GetWindowLong64(IntPtr hWnd, int nIndex);

    public static IntPtr GetWindowLongPtr(IntPtr hWnd, int nIndex)
    {

        if (IntPtr.Size == 8)
        {

            return GetWindowLong64(hWnd, nIndex);
        }
        else
        {

            return new IntPtr(GetWindowLong(hWnd, nIndex));
        }
    }

希望这有帮助。

答案 2 :(得分:0)

我解决了你关于以下问题的问题:

  

我无法恢复菜单的

关于你在另一点上的问题

  

我需要删除边框两次,第一次菜单栏仍然是   存在。

对于那个抱歉,我有没有解决方案,但我希望其他人能帮忙解决这个问题。

删除您在问题中发布的所有定义您的RemoveBorders方法的代码,然后选择以下所有代码,我在下面发布(如果有效,请使用Ctrl + A),复制它(鼠标右键单击=&gt;选择“复制”只需按Ctrl + C更快),然后粘贴它(鼠标右键单击=&gt;选择“粘贴”只需按Ctrl + V即可快速输入代码。在粘贴新代码之前,请确保代码编辑器中光标的位置位于定义RemoveBorder方法的旧代码所在的正确位置。我重新定义RemoveBorders的新代码是:

public IntPtr RemoveBorders(IntPtr WindowHandle, IntPtr MenuHandle)
{
    if (MenuHandle == IntPtr.Zero)
    {
        MenuHandle = GetMenu(WindowHandle);
        int count = GetMenuItemCount(MenuHandle);
        for (int i = 0; i < count; i++)
            RemoveMenu(MenuHandle, 0, (0x40 | 0x10));
    }
    else
    {
        SetMenu(WindowHandle,MenuHandle);
    }

    int WindowStyle = GetWindowLong(WindowHandle, -16);

    //Redraw
    DrawMenuBar(WindowHandle);
    SetWindowLong(WindowHandle, -16, (WindowStyle & ~0x00080000));
    SetWindowLong(WindowHandle, -16, (WindowStyle & ~0x00800000 | 0x00400000));
    return MenuHandle;
}

从旧版本到我的新版本RemoveBorders方法所做的更改:

首先,该方法的返回值已从void更改为IntPtr,因此代码行

return MenuHandle;

添加在您对SetWindowLong功能的最后一次调用之下。此更改的目的是对RemoveBorders进行编程,以便在从其中删除边框之前返回属于该窗口的菜单句柄(在C#中为IntPtr类型)。这很重要,因为下次再次调用RemoveBorders以恢复窗口的边框时,需要返回其菜单的句柄。因此,这就是RemoveBorders(bool remove)的第二个参数更改为IntPtr MenuHandle的原因,允许您返回窗口菜单并恢复其边框。因此,我必须在第一个代码行中删除MenuHandle之前的IntPtr,以声明MenuHandle不再是不是局部变量,但现在它是一个参数。无论何时想要删除窗口的边框,都要将此参数设置为IntPtr.Zero(在C ++中表示NULL)。因为删除了参数,因此我的新版本不再存在,代码行if (remove)已更改为

if (MenuHandle == IntPtr.Zero)

它会检查您是否未向窗口提供任何菜单句柄,然后您要删除其边框。该操作在if语句中完成。如果您返回窗口的菜单,那么MenuHandle不是NULL(即IntPtr.Zero),然后将代码带入else语句进行恢复。最后非常重要的更改是在调用GetMenuItemCount函数之前移动第一个代码行,其中删除了IntPtr以及在if语句块中调用GetMenu函数的位置,因为我们需要获取窗口的菜单并保留它,在它的边界将被删除之前,并不总是。没有这个改变,你将无法恢复窗口的边框,因为第一行代码将“丢弃”你回放的窗口菜单,因为你在窗口没有菜单时调用GetMenu函数,所以此函数的返回值将为NULL(C#中的IntPtr.Zero),因此在else块中,将窗口菜单设置为IntPtr.Zero(这意味着在C ++中为NULL)。通过此更改,新的RemoveBorders方法应该可以正常工作,并允许您恢复窗口的边框。

从现在开始,您应该将我的新版本用于RemoveBorders而不是旧版本,以便能够恢复窗口的边框。我希望你能理解我的想法。说明很简单:定义IntPtr类型的新变量,每当调用RemoveBorders以删除窗口的边框时,将该变量赋值给方法的返回值,这样它将保留并保存窗口的菜单,之后它的边界被删除了。稍后您再次调用RemoveBorders,但现在要恢复窗口的边框,因此您将第二个参数设置为保留窗口菜单的变量,即上一次调用RemoveBorders方法的返回值。希望对你有帮助!