using System; using System.Collections.Generic; using System.Text; using System.Runtime.InteropServices; using System.Drawing; // Created by Yousef El-Dardiry // yousef.dardiry.com // Please let me know when you're using // this code in one of your projects namespace Advanced_toolbar_buttons { public class BHO : IObjectWithSite { private Win32.WndProc newWndProc; private Win32.WndProc newWndProcParent; private IntPtr oldWndProc = (IntPtr)null; private IntPtr oldWndProcParent = (IntPtr)null; private int imageProperty; // The command identifier of the button we're going to add. // We should make sure this one isn't being used already instead of just picking this number private uint BUTTON_CID = 1000; public BHO() { IntPtr hwnd = GetToolBarHWND(); // Make sure the button hasn't been added yet if (Win32.SendMessage(hwnd, Win32.TB_COMMANDTOINDEX, BUTTON_CID, 0) == -1) { newWndProc = new Win32.WndProc(WindowProc); oldWndProc = Win32.SetWindowLong(hwnd, Win32.GWL_WNDPROC, Marshal.GetFunctionPointerForDelegate(newWndProc)); newWndProcParent = new Win32.WndProc(WindowProcParent); oldWndProcParent = Win32.SetWindowLong(GetToolBarParentHWND(), Win32.GWL_WNDPROC, Marshal.GetFunctionPointerForDelegate(newWndProcParent)); } } // Implement these functions yourself using a tool like // Spy++ for the toolbar where you want to add your button private IntPtr GetToolBarParentHWND() { return IntPtr.Zero; } private IntPtr GetToolBarHWND() { return IntPtr.Zero; } private Win32.TBBUTTON getButton() { Win32.TBBUTTON button = new Win32.TBBUTTON(); button.fsStyle = Win32.BTNS_WHOLEDROPDOWN | Win32.BTNS_SHOWTEXT; button.iString = (int)Marshal.StringToHGlobalAuto("Button"); button.idCommand = (int)BUTTON_CID; button.iBitmap = imageProperty; return button; } private int WindowProc( IntPtr hwnd, uint msg, uint wParam, int lParam) { if (msg == Win32.TB_SETIMAGELIST) { // Obtain the Bitmap of the Icon we want to insert into the image list // This example works with resources, but you can do this however you like Bitmap bmp = Resource1.Normal.ToBitmap(); // Add it into the Toolbar's image list int count = Win32.ImageList_GetImageCount((IntPtr)lParam); int res = Win32.ImageList_Add((IntPtr)lParam, bmp.GetHbitmap(), IntPtr.Zero); // Save this value for future use in the buttons iBitmap property imageProperty = Win32.MakeLong(res, (int)wParam); } else if (msg == Win32.TB_ADDBUTTONS) { // When TB_ADDBUTTONS gets called, we want to make sure our button gets added as well if (Win32.SendMessage(hwnd, Win32.TB_COMMANDTOINDEX, BUTTON_CID, 0) == -1) { object[] buttons = MarshalEx.PtrToStructureArray((IntPtr)lParam, typeof(Win32.TBBUTTON), (int)wParam++); object[] newbuttons = new object[wParam]; // Copy all buttons to newbuttons for (int i = 0; i < wParam - 1; i++) { newbuttons[i] = buttons[i]; } // Add our own button newbuttons[wParam - 1] = getButton(); // Send newbuttons as lParam instead of the old buttons structure lParam = (int)Marshal.AllocCoTaskMem((int)(wParam * Marshal.SizeOf(typeof(Win32.TBBUTTON)))); MarshalEx.StructureArrayToPtr((IntPtr)lParam, newbuttons); } } else if (msg == Win32.WM_NCDESTROY) { // Make sure we clean up before the window gets completely destroyed Win32.SetWindowLong(hwnd, Win32.GWL_WNDPROC, oldWndProc); } return Win32.CallWindowProc( oldWndProc, hwnd, msg, wParam, lParam); } private int WindowProcParent(IntPtr hwnd, uint msg, uint wParam, int lParam) { if (msg == Win32.WM_COMMAND) { // Handle WM_COMMAND like you would do in a normal application // When one of your buttons gets clicked, you can notify other instances of your BHO of this // by letting them handle a static event, and fire the event here. } else if (msg == Win32.WM_NOTIFY) { Win32.NMHDR nm = new Win32.NMHDR(); Marshal.PtrToStructure((IntPtr)lParam, nm); if (nm.code == Win32.TBN_DROPDOWN) { Win32.NMTOOLBAR nmtoolbar = (Win32.NMTOOLBAR)System.Runtime.InteropServices.Marshal.PtrToStructure((IntPtr)lParam, typeof(Win32.NMTOOLBAR)); if (nmtoolbar.iItem == BUTTON_CID) { // Show your menu using TrackPopupMenuEx here! } } else if (nm.code == Win32.TBN_GETBUTTONINFO) { // To make sure our button gets listed into the 'customize toolbar' form // we simply add it as the first button requested Win32.NMTOOLBAR toolbar = (Win32.NMTOOLBAR)Marshal.PtrToStructure((IntPtr)lParam, typeof(Win32.NMTOOLBAR)); String s = Marshal.PtrToStringAuto((IntPtr)toolbar.tbButton.iString); if (toolbar.iItem == 1) { Win32.TBBUTTON button = getButton(); toolbar.tbButton = button; toolbar.cchText = 6; toolbar.pszText = (IntPtr)button.iString; Marshal.StructureToPtr(toolbar, (IntPtr)lParam, true); return 1; } } } else if (msg == Win32.WM_NCDESTROY) { // Once again, make sure we clean up before the window gets completely destroyed Win32.SetWindowLong(hwnd, Win32.GWL_WNDPROC, oldWndProcParent); } return Win32.CallWindowProc( oldWndProcParent, hwnd, msg, wParam, lParam); } } // We need these helper functions in the TB_ADDBUTTONS hook public class MarshalEx { public static object[] PtrToStructureArray(IntPtr pointer, Type structureType, int len) { //----- Creates the array object[] array = new object[len]; for (int i = 0; i < len; i++) { //----- Get the structure pointed from pointer parameter address array[i] = Marshal.PtrToStructure(pointer, structureType); //----- Add to the pointer parameter address the size of structure //----- After that, pointer parameter //----- points to the next structure in memory pointer = (IntPtr)(pointer.ToInt32() + Marshal.SizeOf(array[i])); } return array; } public static void StructureArrayToPtr(IntPtr pointer, object[] array) { for (int i = 0; i < array.Length; i++) { Marshal.StructureToPtr(array[i], pointer, true); pointer = (IntPtr)(pointer.ToInt32() + Marshal.SizeOf(array[i])); } } } public class Win32 { public delegate int WndProc(IntPtr hwnd, uint msg, uint wParam, int lParam); #region Win32 function imports [DllImport("user32.dll")] public extern static int SendMessage( IntPtr hwnd, uint msg, uint wParam, uint lParam); [DllImport("user32.dll")] public extern static int SendMessage( IntPtr hwnd, uint msg, uint wParam, ref TBBUTTONINFO lParam); [DllImport("user32.dll")] public extern static IntPtr SetWindowLong( IntPtr hwnd, int nIndex, IntPtr dwNewLong); [DllImport("comctl32")] public extern static int ImageList_Add( IntPtr himl, IntPtr hbmImage, IntPtr hbmMask ); [DllImport("comctl32")] public extern static int ImageList_GetImageCount( IntPtr himl); [DllImport("user32.dll")] public extern static int CallWindowProc( IntPtr lpPrevWndFunc, IntPtr hwnd, uint msg, uint wParam, int lParam); #endregion public static int MakeLong(int LoWord, int HiWord) { return (HiWord << 16) | (LoWord & 0xffff); } #region Win32 Constants public const int GWL_WNDPROC = -4; public const uint WM_NOTIFY = 0x4E; public const uint WM_USER = 0x0400; public const uint WM_NCDESTROY = 0x0082; public const uint WM_COMMAND = 0x0111; public const uint TB_COMMANDTOINDEX = WM_USER + 25; public const uint TB_SETBUTTONINFO = WM_USER + 64; public const uint TB_ADDBUTTONS = WM_USER + 68; public const uint TB_SETIMAGELIST = WM_USER + 48; public static uint SH = 700; public static uint ZERO = 0; public static uint TBN_FIRST = ZERO - SH; public static uint TBN_DROPDOWN = TBN_FIRST - 10; public static uint TBN_GETBUTTONINFO = TBN_FIRST - 20; public const byte BTNS_SHOWTEXT = 0x0040; public const byte BTNS_DROPDOWN = 0x0008; public const byte BTNS_WHOLEDROPDOWN = 0x0080; public const uint TBIF_IMAGE = 0x00000001; public const uint TBIF_TEXT = 0x00000002; public const uint TBIF_STATE = 0x00000004; public const uint TBIF_STYLE = 0x00000008; public const uint TBIF_LPARAM = 0x00000010; public const uint TBIF_COMMAND = 0x00000020; public const uint TBIF_SIZE = 0x00000040; public const uint TBIF_BYINDEX = 0x80000000; #endregion #region Win32 Struct definitions [StructLayout(LayoutKind.Sequential, Pack = 1)] public struct TBBUTTON { public int iBitmap; public int idCommand; public byte fsState; public byte fsStyle; public byte bReserved0; public byte bReserved1; public int dwData; public int iString; } [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)] public struct TBBUTTONINFO { public int cbSize; public int dwMask; public int idCommand; public int iImage; public byte fsState; public byte fsStyle; public short cx; public IntPtr lParam; public IntPtr pszText; public int cchText; } [StructLayout(LayoutKind.Sequential)] public class NMHDR { private IntPtr hwndFrom; public uint idFrom; public uint code; } [StructLayout(LayoutKind.Sequential)] public struct NMTOOLBAR { public NMHDR hdr; public int iItem; public TBBUTTON tbButton; public int cchText; public IntPtr pszText; public RECT rcButton; } [StructLayout(LayoutKind.Sequential)] public struct RECT { public int left; public int top; public int right; public int bottom; } #endregion } }