3

I want to remove the window borders of another process in C#; I used RemoveMenu to remove the borders. It almost works but I have 2 problems left:

  • I need to remove the borders twice, the first time the menu bar still exists.
  • I can’t restore the menu’s

This is what I already wrote:

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));
    }

Can someone show me what I did wrong? I already tried to save the MenuHandle and restore it later, but that doesn't work.

lukiffer
  • 11,025
  • 8
  • 46
  • 70
Laurence
  • 1,815
  • 4
  • 22
  • 35
  • 1
    You need to `RedrawWindow` with `RDW_FRAMECHANGED` to repaint with the new borders. Shouldn't you be using the `FormBorderStyle` property instead of whacking styles? – Raymond Chen Apr 26 '12 at 18:00
  • Thanks, but I don't get the borders back with that function. I used: "RedrawWindowFlags.Invalidate And RedrawWindowFlags.Frame" – Laurence Apr 26 '12 at 18:20
  • 1
    Oops, memory failed me. Should have been `SWP_FRAMECHANGED`. But why are you screwing with windows that belong to another process? That's kind of rude. – Raymond Chen Apr 26 '12 at 18:53
  • That doesn't work either: SetWindowPos(WindowHandle, 0, 0, 0, 0, 0, 0x20 | 0x02 | 0x01); I'm making Multiboxing software. I want to remove the borders of all games, that way you can place more games on one screen. (Yes Multiboxing is legal: http://www.wowwiki.com/Multiboxing ). – Laurence Apr 27 '12 at 06:11
  • 1
    I'm not saying whether or not it's legal. I'm saying it's not polite. Maybe the application is resetting its style back after you mess with it. Maybe `SWP_FRAMECHANGED` doesn't work cross-process. Maybe you got the constants wrong. (For example, you don't say whether you're compiling as 32-bit or 64-bit.) – Raymond Chen Apr 27 '12 at 06:29
  • That is true, I'm compiling in 64bit. But is there a beater way to accomplish this? – Laurence Apr 27 '12 at 11:56
  • 3
    The parameters to `GetWindowLong` are different on 64-bit. There is no better way to accomplish this because this is not supposed to be accomplished. – Raymond Chen Apr 27 '12 at 13:16
  • Ah, that is why. Thanks for your great help. – Laurence Apr 27 '12 at 15:42

3 Answers3

1
  • I can't restore the menu's

This is because that your MenuHandle is local variable.

When the first call to your method RemoveBorders ends, the Garbage Collector deletes MenuHandle, and free memory.

The second time you call RemoveBorders, MenuHandle recreated as new local variable, and reassigned to the current state of the menu of your window - a menu with no menu items.

As a result:

MenuHandle doesn't save the previous state of your window's menu, and this explains why you cannot restore the window's menu.

My advice to you is to make MenuHandle global variable, and define it out of RemoveBorders method definition.

You can define it as a private, protected or public field and also define another property for it, but this is optional, and not necessary. You also can define it as static, if this attribute is better for you.

Here are some examples for MenuHandle's definition:

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...

You'll have to move the line:

IntPtr MenuHandle = GetMenu(WindowHandle);

Inside:

if (Remove)

and before the call to GetMenuItemCount function.

You'll also have to modify that line, and at least remove the IntPtr, to declare that MenuHandle is not local variable, and to refer to the MenuHandle field, which is defined out of RemoveBorders method. IntelliSense still will recognize it as a field, and won't alert you the undefined error.

If MenuHandle is not static, then you also can add the this. keyword after removing the IntPtr before MenuHandle (In other words, you can replace IntPtr with this.), to memorize yourself that MenuHandle is not local variable anymore, so the Garbage Collector won't delete it whenever RemoveBorders finishes the job.

When you will start your program, MenuHandle will be assigned to IntPtr.Zero as default value. When you call RemoveBorders for the first time, MenuHandle's value will be set to the returned value of the GetMenu function in the if (Remove).

When RemoveBorders finishes for the first time, MenuHandle is not deleted, and saves the previous state of the window's menu, before all it's items removed.

So when you call RemoveBorders for the second time in order to restore the menu, the executor will reach if (Remove) code and jump immediately to the else code, because remove = false, and in there you call SetMenu function, when you give it the previous state of the window's menu since the first call to RemoveBorders. This way you'll be able finally to restore window's menu.

I still don't realize why you need to remove the borders twice, the first time the menu bar still exist. I want to help you to solve this problem too, but have no idea. Your code is correct in this case. Sorry, but I hope that other people can solve this problem for you and give you the solution for this too.

0

Try this. This works for me. In this example the border and menu removing is done inside the app it self. But with minor adjustments you can make it work for an external window.

These are some constants I declare in my code



    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;

For the window border



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);
    }
}

For the menu you can do this.



    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();
    }

Also notice I use GetWindowLongPtr, SetWindowLongPtr and SetWindowPosPtr with IntPtr as arguments instead of GetWindowLong, SetWindowLong and SetWindowPos int/uint. This is because of x86/x64 compatibility.

Here is how I do the import 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));
        }
    }

Hope this helps.

Paciente8159
  • 181
  • 2
0

I solved your problem about the following point:

I can’t restore the menu’s

About your problem in the other point

I need to remove the borders twice, the first time the menu bar still exists.

I have no solution for that sorry, but I hope that other people will help with that.

Delete all code that defines your RemoveBorders method, which you posted in your question, and then select all the following code, that I posted below (use Ctrl + A if it works), copy it (right mouse button click => select "Copy" or just press Ctrl + C quicker), and then paste it (right mouse button click => select "Paste" or just press Ctrl + V quicker) to your code. Make sure that the position of the cursor in the code editor stands in the right place where your old code that defined your RemoveBorder method was, before you paste the new code. My new code that redefines RemoveBorders is:

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;
}

Changes that were made from your old version to my new version of RemoveBorders method:

First of all, the return value of the method changed from void to IntPtr, so the code line

return MenuHandle;

was added below your last call to SetWindowLong function. The purpose of this change was to program RemoveBorders to return the handle of the menu (as IntPtr type in C#) that was belong to the window, before its borders removed from it. This is important, because next time you recall RemoveBorders again in order to restore window's border, you need to give back the handle of its menu. So this is the reason that the second argument of RemoveBorders (bool remove) was changed to IntPtr MenuHandle, to allow you to give back window's menu and restore its borders. As a result, I had to delete the IntPtr before MenuHandle in the first code line, to declare that MenuHandle is not local variable anymore, but it is an argument now. Set this argument to IntPtr.Zero (which means NULL in C++) whenever you want to remove window's borders. Because of that the remove argument was replaced and so doesn't exist in my new version anymore, the code line if (remove) was changed to

if (MenuHandle == IntPtr.Zero)

It checks that you didn't give any handle of menu to the window, then you want to remove its borders. The action is done inside the if statement. If you give back the window's menu, so MenuHandle is not NULL (i.e. IntPtr.Zero), and then the code is brought into the else statement to do the restoration. Last very important change was to move the first code line, where the IntPtr was deleted and where you call GetMenu function, inside the if statement block, before you call to GetMenuItemCount function, because we need to get the window's menu and keep it, before its borders are going to be removed, and not always. Without this change, you won't be able to restore back the window's borders, because the first line of code will "discard" the window's menu that you gave back, because you call GetMenu function when the window doesn't have menu, so the return value of this function will be NULL (IntPtr.Zero in C#), so in the else block, you set window's menu to IntPtr.Zero (which means NULL in C++). With this change the new RemoveBorders method should work properly, and allow you restore window's borders.

From now on, you should use my new version to RemoveBorders instead of your old version, in order to be able to restore the window's borders. I hope you understood my idea in this change. The instructions are simple: Define new variable of type IntPtr, and whenever you call RemoveBorders in order to remove window's border, assign that variable to the return value of the method, so it will keep and save the window's menu, after its borders were removed. Later you recall RemoveBorders again, but now to restore the window's borders, so you set the second argument to the variable that keeps the window's menu, i.e. the return value of the previous call to RemoveBorders method. Hope that helps you!