window| Menu
First, the preface
In this article, a C # class Systemmenu is designed by invoking the Windows API to implement the traditional operation of the system menu, for cases where there is no direct systemmenu-like attribute or a GetSystemMenu member function in C#.net.
Second, the system menu introduction
The system menu pops up when you click the window icon or right-click the window's title bar. that contains the default behavior of the current window. The system menu for different windows looks a little different, such as a normal window's system menu that looks like a menu on a Toolbar Child dialog window.
To modify the benefits of the system menu:
• Add the menu items that the application defines itself.
• When WW is minimized, SS is a good place to place the action and can be accessed because SS can be displayed by right-clicking on the taskbar window icon.
• Make a menu item lose its ability, such as removing "maximize" from the system menu, "Minimize" "close", etc. Since this change also affects the three buttons in the upper-right corner of the window, this is a good way to make the upper-right corner of the window "X" lose its ability.
Manipulating System Menus
By invoking the API function GetSystemMenu, you retrieve a copy of the system menu. The second parameter of the function indicates whether you want to reset the system menu to its default state. Plus a few other API menu functions such as AppendMenu, InsertMenu, and so on, you can achieve a flexible control of the system menu.
Let me just briefly explain how to add a menu item and how to implement the new item's interaction with the user.
Iii. Introduction of Systemmenu class
The implementation of the Systemmenu class makes the entire system menu access easier. You can use this class to modify a window's menu. By calling the static member function fromform you get an object that requires a Form object or a class that inherits from form as its argument. It then creates a new object, which, of course, throws a Nosystemmenuexception exception if the GetSystemMenu API call fails.
Note that each Windows API Menu function requires a menu handle to facilitate operation. Because the menu handle is actually a C + + pointer, the. NET you want to use IntPtr to manipulate it. Many functions also require a bitmask flag to indicate the action or form of a new menu item. Fortunately, you don't have to use a series of bitmask flags to define the inclusion of a head file, as in VC + +. NET already provides a ready-made public enumeration class Itemflags. A few important members of this class are described below:
Mfstring―― tells the subsystem to display the string passed by the "item" argument in the menu item.
mfseparator―― the "ID" and "Item" arguments are ignored at this time.
· Mfbarbreak―― when used for menu bars, it functions as mfbreak; when used for drop-down menus, submenus, or shortcut menus, a new column is separated from the old column by a line of vertical lines.
· Mfbreak―― put the current item on a new line (menu bar) or a new column (Drop-down menu, submenu, or shortcut menu).
Note: If you specify more than one flag, you should connect with the bitwise operator operator | (OR). For example:
A menu item "Test" is created and the item is selected (checked)
Mysystemmenu.appendmenu (MyID, "Test", itemflags.mfstring | itemflags.mfchecked);
The item argument specifies the text to be displayed in the new item, and its ID must be a unique number-used to flag the menu item.
Note: Make sure that the ID of the new item is greater than 0 and less than 0xf000. Because a range greater than or equal to 0xf000 is reserved for system commands. You can also call the class Systemmenu static method Verifyitemid to verify that your ID is correct.
In addition, there are two constants that need to be explained: Mfbycommand and mfbyposition.
First, by default, Mfbycommand is used. Second, the "Pos" interpretation relies on these flags: If you specify Mfbycommand, the "pos" parameter is the ID of the item before the new entry is inserted; If you specify Mfbyposition, the "pos" argument is the relative position of the new item that starts with a 0 index If it is-1 and specifies Mfbyposition, the item will be inserted at the end. This is why AppendMenu () can be replaced by InsertMenu ().
Four, Systemmenu class code Analysis
Using System;
Using System.Windows.Forms;
Using System.Diagnostics;
Using System.Runtime.InteropServices;
public class NoSystemMenuException:System.Exception
{}
These values are from MSDN
public enum Itemflags
{
The item ...
mfunchecked = 0x00000000,//Not checked
mfstring = 0x00000000,//... contains a string as label
mfdisabled = 0x00000002,//... is disabled
mfgrayed = 0x00000001,//... is grayed
mfchecked = 0x00000008,//... is checked
Mfpopup = 0x00000010,///... is a popup menu. Pass the
Menu handle of the Popup
Menu into the ID parameter.
Mfbarbreak = 0x00000020,//... is a bar break
Mfbreak = 0x00000040,//...
Mfbyposition = 0x00000400,//... is identified by the position
Mfbycommand = 0x00000000,//... is identified by its ID
Mfseparator = 0x00000800//... is a seperator (String and
ID parameters are ignored).
}
public enum Windowmessages
{
Wmsyscommand = 0x0112
}
//
The definition of the class that helps implement the operating system menu
///.
Note: When calling unmanaged functions in a dynamic-link library using P/invoke, you should perform the following steps:
1, locate the DLL that contains the function.
2, load the DLL library into memory.
3, locate the function address that is about to be invoked, and press all the fields into the stack.
4, call the function.
//
public class Systemmenu
{
Tip: C # declares functions as external, and uses property dllimport to specify the DLL
And any other parameters that might be required.
First, we need the GetSystemMenu () function
Note that this function does not have a Unicode version
[DllImport ("USER32", entrypoint= "GetSystemMenu", Setlasterror=true,
CharSet=CharSet.Unicode, Exactspelling=true,
CALLINGCONVENTION=CALLINGCONVENTION.WINAPI)]
private static extern IntPtr Apigetsystemmenu (IntPtr windowhandle,int breset);
AppendMenu () is also required. Since. NET uses Unicode,
We should select its Unicode version.
[DllImport ("USER32", entrypoint= "Appendmenuw", Setlasterror=true,
CharSet=CharSet.Unicode, Exactspelling=true,
CALLINGCONVENTION=CALLINGCONVENTION.WINAPI)]
private static extern int Apiappendmenu (IntPtr menuhandle, int flags,int NewID, String Item);
You may also need InsertMenu ()
[DllImport ("USER32", entrypoint= "Insertmenuw", Setlasterror=true,
CharSet=CharSet.Unicode, Exactspelling=true,
CALLINGCONVENTION=CALLINGCONVENTION.WINAPI)]
private static extern int Apiinsertmenu (IntPtr hmenu, int position,int Flags, int newid,string Item);
Private IntPtr m_sysmenu = IntPtr.Zero; System Menu Handle
Public Systemmenu ()
{}
Inserts a separator bar at the given position (starting at 0)
public bool InsertSeparator (int Pos)
{
Return (InsertMenu (Pos, Itemflags.mfseparator | Itemflags.mfbyposition, 0, ""));
}
Simplified InsertMenu (), if the ――pos parameter is a relative index position at the beginning of 0
public bool InsertMenu (int Pos, int ID, String Item)
{
Return (InsertMenu (Pos, Itemflags.mfbyposition | Itemflags.mfstring, ID, Item));
}
Inserts a menu item at a given location. The exact location of the insertion depends on the flags
public bool InsertMenu (int Pos, itemflags Flags, int ID, String Item)
{
Return (Apiinsertmenu (M_sysmenu, Pos, (Int32) Flags, IDs, Item) = = 0);
}
Add a separator bar
public bool Appendseparator ()
{
Return AppendMenu (0, "", itemflags.mfseparator);
}
Use itemflags.mfstring as the default value
public bool AppendMenu (int ID, String Item)
{
Return AppendMenu (ID, Item, itemflags.mfstring);
}
superseded functions
public bool AppendMenu (int ID, String Item, Itemflags Flags)
{
Return (Apiappendmenu (m_sysmenu, (int) Flags, ID, Item) = = 0);
}
Retrieving a new object from a Form object
public static Systemmenu Fromform (Form Frm)
{
Systemmenu Csysmenu = new Systemmenu ();
Csysmenu.m_sysmenu = Apigetsystemmenu (frm.handle, 0);
if (Csysmenu.m_sysmenu = = IntPtr.Zero)
{//Once failed, throws an exception
throw new Nosystemmenuexception ();
}
return csysmenu;
}
Current Window menu restore public static void Resetsystemmenu (Form Frm)
{
Apigetsystemmenu (Frm.handle, 1);
}
Checks whether a given ID is within the range of the system menu ID
public static bool Verifyitemid (int ID)
{
Return (BOOL) (ID < 0xf000 && ID > 0);
}
}
You can use the static method to resetsystemmenu the window's system menu to its original state-which is useful when the application encounters an error or does not modify the menu correctly.
V. Use of Systemmenu class
Systemmenu objects
Private Systemmenu m_systemmenu = null;
ID constant Definition
Private Const int m_aboutid = 0x100;
Private Const int m_resetid = 0X101;
private void Frmmain_load (object sender, System.EventArgs e)
{
Try
{
M_systemmenu = Systemmenu.fromform (this);
Add a separator ...
M_systemmenu.appendseparator ();
Add the About menu item
M_systemmenu.appendmenu (M_aboutid, "about");
At the top of the menu, add the Reset menu item
M_systemmenu.insertseparator (0);
M_systemmenu.insertmenu (0, M_resetid, "Reset system Menu");
}
catch (nosystemmenuexception/* err */)
{
Build your error processor
}
}
Vi. Detect whether a custom menu item is clicked
This is a more difficult part to implement. Because you have to overload your WndProc member function from the form or control inheritance class. You can achieve this:
protected override void WndProc (ref message MSG)
{
Base. WndProc (ref msg);
}
Note that the WndProc implementation of the base class must be called, otherwise it will not work correctly.
Now, let's analyze how to overload WndProc. First you should intercept the WM_SYSCOMMAND message. When the user clicks on an item in the system menu or selects the "Maximize" button, the "Minimize" button or the "Close" button, we retrieve the message. In particular, note that the wparam parameter of the message object contains exactly the ID of the clicked menu item. So we can implement the following overloads:
protected override void WndProc (ref message MSG)
{
By intercepting the WM_SYSCOMMAND message and processing it
Note that the message wm_syscommand is defined in the Windowmessages enumeration class
The wparam parameter of the message contains the ID of the item clicked
This value is the same as passed by the InsertMenu () or appendmenu () member function of the above class
if (Msg. MSG = = (int) windowmessages.wmsyscommand)
{
Switch (MSG. Wparam.toint32 ())
{
Case M_RESETID://Reset menu item ID
{
if (MessageBox.Show, "\tare you sure?", "question", messageboxbuttons.yesno) = =
Dialogresult.yes)
{//Reset system Menu
Systemmenu.resetsystemmenu (this);
}
} break;
Case M_aboutid:
{//About menu item
MessageBox.Show (This, "Author: Zhu Xianzhong \ n" + "e-mail:sdmyzxz@163.com", "about");
} break;
Here you can design the process for another menu item
}
}
Call base class function
Base. WndProc (ref msg);
}
Vii. Summary
Another possible way to achieve this is by creating an event OnSysCommand and activating it when the message wm_syscommand, and then passing the property wparam to the handle of the event. Readers can programmatically authenticate themselves.
In short, this article through a simple system menu modification example, analyzed in C # using the. NET platform invoke mechanism to invoke the unmanaged function in the DLL the basic steps and considerations. In addition, the attached source is in Windows2000 Server/vs. NET2003 debugging Pass.