[RESOLVED] Modify right-click context menu in standard controls
So I haven't been able to find any solutions in the small number of threads from years back addressing it, so I'll give it a go again.
I want to modify, not replace, the standard context menu that pops up in edit controls (textbox, combobox edit). Would like to add my own items to it just like the standard GetSystemMenu/AppendMenu functionality for the title bar's context menu.
Now obviously the first thought is to simply intercept WM_CONTEXTMENU and substitute my own menu replicating the functionality... however modern Windows has a number of options relating to Unicode, and since I've spent quite a deal of time adding basic unicode support, these options need to be retained. The functions in question are pictured below:
http://i.imgur.com/8gvlwYD.jpg
So I suppose the question could also be, how do I implement right to left reading order, open IME, and reconversion (not even sure what that is). If that's possible, replacing the menu with my own would then be a viable alternative. The control and form are already subclassed (see tons of 'well, youd have to subclass it...' comments like that's a reason to give up and do it another way).
Thanks in advance.
Re: Modify right-click context menu in standard controls
Last time I messed with something like that was over 10 years ago, so am a bit foggy here.
You probably don't want to replace the context menu with your own. If you do, it will no longer be scalable (i.e., change depending on what O/S your app is running on). Also, some menu items may not apply depending on the O/S, and you'd have to decide which submenu items should be disabled and enabled vs. just letting the textbox control do that for you.
Sounds great if you can get the handle to the popup menu after it is created and before it appears. Getting that handle was a pain, if I recall correctly. Others may provide a link with sample source, but I don't have one ready. From what I do recall, is I needed to enumerate the desktop windows until I found one with the class name: #32768. Prior to XP this was a piece of cake, find the window & hook/subclass it, grab the menu before it is displayed & modify it. But Windows got smart and later created more than one of these, using an application like Spy++ you should see more than one. That made it much harder to determine which #32768 was the correct one.
I know this is not much help. Was hoping I had some old code from back then that I might be able to post, but no luck. Maybe my comment might trigger some memories from others on the forum. Maybe searching/googling for WM_ContextMenu & #32768 might be helpful?
Re: Modify right-click context menu in standard controls
So I'm trying to find 32768, but no such luck.
Code:
Dim hMenu As Long, hMenuX
Dim hw As Long, hwx As Long
hw = FindWindow("#32768", "")
Debug.Print "hw=" & CStr(hw)
hwx = FindWindowEx(hCBSed, 0, "#32768", "")
Debug.Print "hwx=" & CStr(hwx)
hMenu = SendMessage(hw, MN_GETHMENU, 0, ByVal 0&)
Debug.Print "hMenu=" & CStr(hMenu)
hMenuX = SendMessage(hwx, MN_GETHMENU, 0, ByVal 0&)
Debug.Print "hMenuX=" & CStr(hMenu)
I put that as the code for WM_RBUTTONUP; and it returns all zeros.
Re: Modify right-click context menu in standard controls
Re: Modify right-click context menu in standard controls
Quote:
Originally Posted by
fafalone
So I'm trying to find 32768, but no such luck.
The window class may only be created on demand with modern O/S? Found some old notes of mine and this is what I have. Should get you in the right direction
1. In order to find the #32768 window I established a hook (WindowsHookEx with the WH_CBT parameter), looking for a HCBT_CREATEHWND message. The hook procedure (like a Windows procedure) wParam parameter is the window handle. But to ensure the window created is #32768, the API GetClassName should be used. The only thing I didn't find in my notes is when to set this hook and release it. I think it should be set when WM_ContextMenu is received, then WM_ContextMenu passed along via CallWindowProc and hook released when that API returns. Just don't know off top of my head whether window is created before/after WM_ContextMenu is posted. May have to play with the timing. If the timing is correct, be cautious. You'll probably be modifying the menu in the hook procedure & only want to modify the first instance of that hooked window class. If submenus popup, likely more #32768 windows & don't want to modify those I would think. May need a flag inside that hook procedure to prevent you from double dipping.
2. With the #32768 class' window's handle, you should be able to get the menu handle via a SendMessage call to that window handle with the MN_GetMenu message. Note that the lParam of the hook procedure contains a pointer to a CBT_CREATEWND structure which has a hMenu member. This should be the same value as MN_GetMenu returns. Note: Per MSDN, when HCBT_CREATEWND is sent, window is already created but may not be completely sized. Before you try to subclass it, first ensure it is in your process: GetWindowThreadProcessId
3. Assuming you got that far, you could then append menu items to the menu via AppendMenu API & any submenus to that added menu via CreatePopupMenu API. Ensure you add a unique value for the menu ID(s). When done, you should release the hook
4. The menu ID(s) you added would be trapped by subclassing that #32768 (or the textbox?) window & reacting to the WM_MENUSELECT message. After menu closes, you should release the subclassing. You should also destroy any created menus
Edited: Just whipped up a quick sanity check and what I posted, applies well. I might be able to post a simple sample tomorrow afternoon. Won't be fancy, but goal will be to display textbox menu with appended custom menu items & be able to respond to those. By the way, this much I did verify (working off of Vista, tomorrow off of XP):
1) Subclass textbox for wm_contextmenu
2) When received, set wh_cbt hook
3) Forward wm_contextmenu
4) feedback showed #32768 & a shadow window being created
at this point in the sanity check, simply unhooked & unsubclassed. Appears my 10+ year old memory ain't too shabby yet :)
Re: Modify right-click context menu in standard controls
Ok, here's a working example. Important notes follow the code
1. In a new project, add: 2 command buttons, 1 textbox, 1 module. Leave all controls their default names
2. In the module, paste this:
Code:
Option Explicit
Private Declare Function SetWindowsHookEx Lib "user32.dll" Alias "SetWindowsHookExA" (ByVal idHook As Long, ByVal lpfn As Long, ByVal hmod As Long, ByVal dwThreadId As Long) As Long
Private Declare Function UnhookWindowsHookEx Lib "user32.dll" (ByVal hHook As Long) As Long
Public Declare Function CallNextHookEx Lib "user32.dll" (ByVal hHook As Long, ByVal nCode As Long, ByVal wParam As Long, ByVal lParam As Long) As Long
Public Declare Function CallWindowProc Lib "user32.dll" Alias "CallWindowProcA" (ByVal lpPrevWndFunc As Long, ByVal hWnd As Long, ByVal msg As Long, ByVal wParam As Long, ByVal lParam As Long) As Long
Private Declare Function SetWindowLong Lib "user32.dll" Alias "SetWindowLongA" (ByVal hWnd As Long, ByVal nIndex As Long, ByVal dwNewLong As Long) As Long
Private Const GWL_WNDPROC = (-4)
Private Const WM_DESTROY As Long = &H2
Private Const WH_CALLWNDPROC As Long = 4
Private m_HookProc As Long
Private m_WndProc As Long
Private m_SubclassedHwnd As Long
Public Function SetSubclass(hWnd As Long) As Boolean
' pass zero to stop subclassing
If hWnd = 0& Then
If m_SubclassedHwnd <> 0& Then
If SetWindowLong(m_SubclassedHwnd, GWL_WNDPROC, m_WndProc) <> 0& Then
m_WndProc = 0&
SetSubclass = True
End If
End If
Else
If m_WndProc <> 0& Then Call SetSubclass(0&)
m_WndProc = SetWindowLong(hWnd, GWL_WNDPROC, AddressOf pvWNDProc)
If m_WndProc <> 0& Then
m_SubclassedHwnd = hWnd
SetSubclass = True
End If
End If
End Function
Public Function SetHook(Initiate As Boolean) As Boolean
' pass Initiate as False to stop hook
If Initiate Then
If m_HookProc = 0& Then
m_HookProc = SetWindowsHookEx(WH_CALLWNDPROC, AddressOf pvCBTProc, App.hInstance, App.ThreadID)
SetHook = (m_HookProc <> 0)
End If
ElseIf m_HookProc <> 0 Then
SetHook = (UnhookWindowsHookEx(m_HookProc) <> 0)
m_HookProc = 0
End If
End Function
Private Function pvWNDProc(ByVal hWnd As Long, ByVal wMsg As Long, ByVal wParam As Long, ByVal lParam As Long) As Long
' subclassing procedure
If wMsg = WM_DESTROY Then
m_SubclassedHwnd = 0&
Call SetWindowLong(hWnd, GWL_WNDPROC, m_WndProc)
pvWNDProc = CallWindowProc(m_WndProc, hWnd, wMsg, wParam, lParam)
ElseIf m_SubclassedHwnd <> 0& Then
Dim bSupress As Boolean
pvWNDProc = Form1.HandleWindowMessage(hWnd, wMsg, wParam, lParam, m_WndProc Or 0&, bSupress)
If Not bSupress Then pvWNDProc = CallWindowProc(m_WndProc, hWnd, wMsg, wParam, lParam)
End If
End Function
Private Function pvCBTProc(ByVal nCode As Long, ByVal wParam As Long, ByVal lParam As Long) As Long
' hooking procedure
If nCode < 0& Then
pvCBTProc = CallNextHookEx(m_HookProc, nCode, wParam, lParam)
Else
Dim bSupress As Boolean
pvCBTProc = Form1.HandleHookMessage(WH_CALLWNDPROC, nCode, wParam, lParam, m_HookProc Or 0&, bSupress)
If Not bSupress Then pvCBTProc = CallNextHookEx(m_HookProc, nCode, wParam, lParam)
End If
End Function
3. In the form, paste this:
Code:
Option Explicit
Private Declare Sub CopyMemory Lib "kernel32.dll" Alias "RtlMoveMemory" (ByRef Destination As Any, ByRef Source As Any, ByVal Length As Long)
Private Declare Function SendMessage Lib "user32.dll" Alias "SendMessageA" (ByVal hWnd As Long, ByVal wMsg As Long, ByVal wParam As Long, ByRef lParam As Any) As Long
Private Declare Function AppendMenu Lib "user32.dll" Alias "AppendMenuA" (ByVal hMenu As Long, ByVal wFlags As Long, ByVal wIDNewItem As Long, ByVal lpNewItem As Any) As Long
Private Declare Function CreatePopupMenu Lib "user32.dll" () As Long
Private Declare Function DestroyMenu Lib "user32.dll" (ByVal hMenu As Long) As Long
Private Type CWPSTRUCT
lParam As Long
wParam As Long
message As Long
hWnd As Long
End Type
Private Type CREATESTRUCT
lpCreateParams As Long
hInstance As Long
hMenu As Long
hWndParent As Long
cy As Long
cx As Long
y As Long
x As Long
style As Long
lpszName As Long
lpszClass As Long
ExStyle As Long
End Type
Private Const WM_APP As Long = &H8000&
Private Const MN_GETHMENU As Long = &H1E1
Private Const WM_CONTEXTMENU As Long = &H7B
Private Const WM_CREATE As Long = &H1
Private Const MF_STRING As Long = &H0&
Private Const MF_SEPARATOR As Long = &H800&
Private Const MF_CHECKED As Long = &H8&
Private Const MF_GRAYED As Long = &H1&
Private Const MF_DISABLED As Long = &H2& Or MF_GRAYED
Private Const MF_POPUP As Long = &H10&
Public Function HandleHookMessage(HookType As Long, nCode As Long, wParam As Long, lParam As Long, HookProc As Long, Supress As Boolean) As Long
' called from the module for each hook message
Static hMenuOwner As Long
If nCode = 0& Then ' must handle
Dim CWP As CWPSTRUCT
CopyMemory CWP, ByVal lParam, 16& ' get structure
Select Case CWP.message ' test a couple messages
Case WM_CREATE
Dim CS As CREATESTRUCT ' window being created
CopyMemory CS, ByVal CWP.lParam, Len(CS)
If CS.lpszClass = 32768 Then ' is it #32768 (32768 is Atom)
hMenuOwner = CWP.hWnd ' cache window handle
End If
Case MN_GETHMENU ' we are sending this; ignore it
Case Else
If CWP.hWnd = hMenuOwner Then ' message for #32768?
If hMenuOwner <> 0 Then ' got a menu handle yet?
Dim hMenu As Long, puMenu As Long
hMenu = SendMessage(hMenuOwner, MN_GETHMENU, 0&, ByVal 0&)
If hMenu <> 0 Then ' if so, add our menu(s)
AppendMenu hMenu, MF_SEPARATOR Or MF_DISABLED, 0&, ByVal 0&
AppendMenu hMenu, MF_STRING, WM_APP Or 110, "&LaVolpe Added Menu Item"
puMenu = CreatePopupMenu
AppendMenu hMenu, MF_STRING Or MF_POPUP, puMenu, "LaVolpe Added Menu Item w/Submenus"
AppendMenu puMenu, MF_STRING, WM_APP Or 111, "Submenu A"
AppendMenu puMenu, MF_STRING, WM_APP Or 112, "Submenu B"
hMenuOwner = 0&
SetHook False ' terminate hooking; done here
End If
End If
End If
End Select
End If
End Function
Public Function HandleWindowMessage(hWnd As Long, wMsg As Long, wParam As Long, lParam As Long, WndProc As Long, Supress As Boolean) As Long
' called from the module for each window message
If wMsg = WM_CONTEXTMENU Then
SetHook True
HandleWindowMessage = CallWindowProc(WndProc, hWnd, wMsg, wParam, lParam)
SetHook False
Supress = True
ElseIf (wMsg And WM_APP) = WM_APP Then
MsgBox "Selected custom add-on menu ID (" & (wMsg Xor WM_APP) & ")", vbOKOnly
Supress = True
End If
End Function
Private Sub Command1_Click()
SetSubclass Text1.hWnd
End Sub
Private Sub Command2_Click()
SetSubclass 0&
End Sub
Private Sub Form_Load()
Command1.Caption = "Start Subclassing"
Command2.Caption = "Stop Subclassing"
End Sub
Private Sub Form_Unload(Cancel As Integer)
SetHook False ' in case didn't do it before closing
SetSubclass 0&
End Sub
Notes...
1. IMPORTANT IMPORTANT IMPORTANT
The context menu item IDs used for textboxes are actual windows messages. So the ID for the Paste menu item will be WM_Paste. When menu item is chosen, that ID is sent to the parent as a windows message. So, you need to ensure you NEVER use actual window messages as menu item IDs for any items you append to the context menu. For example. Using a menu item ID of 16 is the value for WM_Close & if selected, good bye parent (textbox).
Wouldn't bet this is the case for all control-types. If not, you'll probably receive a WM_Command message once an item is selected. Probably should presume that and add a line of code in the textbox window procedure, something like:
Code:
ElseIf wMsg = WM_Command Then
If (wParam and &HFFFF0000) = 0 Then
' menu item ID selected is ((wParam And &HFFFF&) Xor WM_App)
End If
ElseIf wMsg = WM_MENUCOMMAND Then
' wParam = zero-based index of menu item selected
' lParam = handle to the menu
2. I think you will be able to follow the code pretty well. Just some points & you can play
a. Menu item IDs: Use WM_APP Or [ID value > 0]. Example: WM_APP Or 100, WM_APP Or 125
b. Do not let the hook run longer than needed. Even if you are subclassing multiple textboxes, you only need one hook. It can be called when WM_ContextMenu received & released inside the hook procedure or after WM_ContextMenu has been processed
c. If creating a menu item with submenus, you would use CreatePopupMenu as in the above code sample. Per MSDN, that popup menu must be destroyed using DestroyMenu unless the menu is assigned to a window. You'll want to research this to verify. But I believe the menu is assigned to that #32678 window. If not, you'll want to cache the popup menus you create so they can be destroyed once you get a WM_MenuSelect with a wParam containing this mask: &HFFFF0000 indicating menu closed.
Edited: Tested on XP only, will try it later tonite with Vista, but not expecting any issues
Follow up: Worked well on Vista but, Vista seems to have stolen some of the WM_APP range for its use. Per MSDN, WM_APP thru &HBFFF would not conflict with system messages. But when used values closer to WM_APP, Vista took some action. I raised the IDs from < 20 to > 100 & that works well
Re: Modify right-click context menu in standard controls
Looks very awesome. I love how everyone each has their own subclassing methods, which is making things very difficult to follow (i subclass to 1 wndproc and handle every message there, instead of forwarding back to another procedure). Working things out now, if you get back to this thread before I update this post, is there any way you could elaborate a bit on the code flow? I'm not as smart as I used to be and am having just a little trouble figuring out the sequence of events etc. (modifying a copy/paste only has limited value, i want to completely understand things, for both knowledges sake and integrating this with all the other extensive subclass code--since supporting unicode and some other things neccessitated using a pure-api control made with createwindowex)
Edit, nevermind. Got it working! Thanks very much for the help. Also, I'm on Win7 x64... so if it works on XP too, I'd imagine it should work on Vista.
Re: Modify right-click context menu in standard controls
Quote:
Originally Posted by
fafalone
is there any way you could elaborate a bit on the code flow?
1) Wait for a WM_ContextMenu message
2) When received, start a hook to look for newly created windows
3) Forward the WM_ContextMenu message
4) In hook procedure, look for #32768 creation and keep monitoring its messages until the menu is created
5) When menu created, append/modify menu items, stop hook
6) After WM_ContextMenu finishes being forwarded, stop hook just in case it wasn't stopped earlier
7) When user selects a menu item, a message is forwarded to the textbox
Limitations: This method should work well for menus created via the TrackPopupMenu API. Many, if not most, context menus are done this way. If the desired menu isn't part of a #32768 window class, then the above won't work for that particular menu. For example, standard menus (at top of window) and system menus generally won't use that method.
P.S. I rarely handle any subclassing/hooking procedures within the actual procedure and NEVER callback directly to a form. Did so here just because it was a 'simple' workup. Personally, I use classes. The procedures make calls to the classes and the classes do the work or forward the messages via Public Events or via Implements. Each subclassed window has its own class. Each hook type has its own class. The idea is that your subclass/hook procedure remains generic, just a repeater. This allows each class, or form via Implements, to customize responses to the messages.
Re: [RESOLVED] Modify right-click context menu in standard controls
Well thanks for using the 'simple' one lol. I have Subclass() and a WndProc for each item, and that's it as far as Case WM_ statements; can use the same WndProc for multiple items too; like with this both my subclassed combobox's edit windows go to CBEWndProc, and I'm checking the hWnd to determine which menu options I'm appending. Seems by far the most flexible way; but not handling some messages in different procedures has its own tradeoff in complex WndProc functions.
Re: [RESOLVED] Modify right-click context menu in standard controls
Hi LaVolpe.
I would like to achieve the same think with JAVA and tried to understand your call back routine.
I don't get it why in your code the execution point it gets back in to the "Case Else"?
Can you helm me please by explain me exactly what happens if you remember of course in your routine
Form1.HandleHookMessage(WH_CALLWNDPROC, nCode, wParam, lParam, m_HookProc Or 0&, bSupress)
This is my WindProc and never gets back in ...
@Override
public LRESULT callback(int nCode, WPARAM wParam,CWPStruct info)
{
if (nCode>=0)
{
switch(info.message)
{
case User32.WM_CREATE :
String clazName="";
char[] windowClaz = new char[8];
User32.INSTANCE.GetClassName(info.handle, windowClaz, 8);
getLastError();
clazName = Native.toString(windowClaz);
if (clazName.equals("#32768"))
{
System.out.println(" INFO :" + info.toString(true));
System.out.println(" INFO lParam " + info.lParam.toString());
System.out.println(" INFO wParam " + info.wParam.toString());
System.out.println(" INFO HANDLE : " + info.handle.toString());
System.out.println(" WParam : " + wParam.toString());
hMenuOwner = info.handle; // cache the window handle
}
break;
case MN_GETHMENU : System.out.println("I DID BRODCASTED"); break;
default:
if(info.handle==hMenuOwner)
{
if(hMenuOwner!=null)
{
LRESULT result = IWinUser32.INSTANCE.SendMessage(hMenuOwner, MN_GETHMENU, new WPARAM(0), new LPARAM(0));
if (result !=null)
{
HMENU hmnu = new HMENU(result.toPointer());
System.out.println(" HMNU " + hmnu);
IWinUser32.INSTANCE.AppendMenu(hmnu,0, 666, "Florin SAMSON ITEM");
getLastError();
}
}
}
break;
}
}
User32.INSTANCE.PostQuitMessage(0);
quit = true;
return User32.INSTANCE.CallNextHookEx(hHook, nCode, wParam, info.getPointer());
}
};
Re: [RESOLVED] Modify right-click context menu in standard controls
I noticed this Thread is Resolved.
What was/is the final solution to the request and does it work. I want to do the same thing with the WebBrowser control
Re: [RESOLVED] Modify right-click context menu in standard controls
Quote:
Originally Posted by
jmsrickland
I noticed this Thread is Resolved.
What was/is the final solution to the request and does it work. I want to do the same thing with the WebBrowser control
http://vbcity.com/forums/t/80776.aspx - have you tried this? That was the first result that I clicked on while searching via Google.
We're currently hijacking this thread, I apologize for that.
Re: [RESOLVED] Modify right-click context menu in standard controls
That's not what I'm looking for. I already know how to override the context menu - what I am looking is how to add/modify to the existing menu which is what the OP requested. Besides, that code doesn't work anyway and what I have is much simplier - not all that other stuff which I have no idea what it is for.
Thanks, anyway
Re: [RESOLVED] Modify right-click context menu in standard controls
I'm not following what's going on... I don't know about doing the same in JS like flsam asked; jmsrickland, were you talking about the final solution to that javascript question, or the final solution to my original question? Because the code absolutely does work:
http://i.imgur.com/ZjPu5D2.jpg
Re: [RESOLVED] Modify right-click context menu in standard controls
Quote:
Originally Posted by
fafalone
I'm not following what's going on... I don't know about doing the same in JS like flsam asked; jmsrickland, were you talking about the final solution to that javascript question, or
the final solution to my original question? Because the code absolutely does work:
http://www.vbforums.com/images/ieimages/2015/08/1.jpg
the final solution to your original question
Is it post #6
Re: [RESOLVED] Modify right-click context menu in standard controls
Yes #6. LaVolpe did it on XP, and I'm using 7, so I can't speak for higher versions of Windows but it definitely works on those... what exactly is happening when you try to use it?
Note that you have to make sure to subclass the hWnd that gets the context menu messages; like I'm using it for a comboboxex; I have to first get the hwnd of the edit box with CBEM_GETEDITCONTROL; I can't just subclass combobox.hwnd. So first make sure what you're subclassing actually gets WM_CONTEXTMENU.
Edit: Also you've gotta be careful if you're integrating it into a different subclassing method. Say for example you're subclassing your edit hwnd directly to a single procedure (Subclass(edithwnd, AddressOf CBEWndProc)); then in CBEWndProc you'll handle both the WM_APP custom message processing:
Code:
If (uMsg And WM_APP) Then
Dim idCmdB As Long, idx As Long
Dim sz As String
idCmdB = (uMsg Xor WM_APP)
Debug.Print "Got WM_APP; xor=" & CStr(uMsg Xor WM_APP) & ",idcmdb=" & idCmdB
Select Case idCmdB
'[...] - handle your menu clicks here, but do not exit or call defwndprocs
End Select
CBEWndProc = 1 'CANCEL MESSAGE; WE HANDLED IT
Exit Function
and the WM_CONTEXTMENU message:
Code:
Case WM_CONTEXTMENU
m_HookhWnd = hWnd
SetHook True, AddressOf pvCBTProc
CBEWndProc = DefSubclassProc(hWnd, uMsg, wParam, lParam)
SetHook False, AddressOf pvCBTProc
Exit Function
The final version of the other functions wound up looking like this:
Code:
Public Function SetHook(Initiate As Boolean, lpfn As Long) As Boolean
' pass Initiate as False to stop hook
If Initiate Then
If m_HookProc = 0& Then
m_HookProc = SetWindowsHookEx(WH_CALLWNDPROC, lpfn, App.hInstance, App.ThreadID)
SetHook = (m_HookProc <> 0)
End If
ElseIf m_HookProc <> 0 Then
SetHook = (UnhookWindowsHookEx(m_HookProc) <> 0)
m_HookProc = 0
End If
End Function
Private Function pvCBTProc(ByVal nCode As Long, ByVal wParam As Long, ByVal lParam As Long) As Long
' hooking procedure
If nCode < 0& Then
pvCBTProc = CallNextHookEx(m_HookProc, nCode, wParam, lParam)
Else
Dim bSupress As Boolean
pvCBTProc = HandleHookMessage(WH_CALLWNDPROC, nCode, wParam, lParam, m_HookProc Or 0&, bSupress)
If Not bSupress Then pvCBTProc = CallNextHookEx(m_HookProc, nCode, wParam, lParam)
End If
End Function
Public Function HandleHookMessage(HookType As Long, nCode As Long, wParam As Long, lParam As Long, HookProc As Long, Supress As Boolean) As Long
' called from the module for each hook message
Static hMenuOwner As Long
If nCode = 0& Then ' must handle
Dim CWP As CWPSTRUCT
CopyMemory CWP, ByVal lParam, 16& ' get structure
Select Case CWP.Message ' test a couple messages
Case WM_CREATE
Dim CS As CREATESTRUCT ' window being created
CopyMemory CS, ByVal CWP.lParam, Len(CS)
If CS.lpszClass = 32768 Then ' is it #32768 (32768 is Atom)
hMenuOwner = CWP.hWnd ' cache window handle
End If
Case MN_GETHMENU ' we are sending this; ignore it
Case Else
If CWP.hWnd = hMenuOwner Then ' message for #32768?
If hMenuOwner <> 0 Then ' got a menu handle yet?
Dim hMenu As Long, puMenu As Long
Dim i As Long
Dim MII As MENUITEMINFO
hMenu = SendMessage(hMenuOwner, MN_GETHMENU, 0&, ByVal 0&)
If hMenu <> 0 Then ' if so, add our menu(s)
If m_HookhWnd = hCBSed Then 'search hwnd
ModifyCBXMenu hCBSed, hMenu
ElseIf m_HookhWnd = hCBPed Then
Debug.Print "MenuHook.Construct for hCBPed"
ModifyCBXMenu hCBPed, hMenu
End If 'm_HookhWnd
hMenuOwner = 0&
SetHook False, AddressOf pvCBTProc ' terminate hooking; done here
End If 'hMenu <> 0
End If 'hMenuOwner <> 0
End If 'CWP.hWnd = hMenuOwner
End Select
End If 'nCode=0
End Function
ModifyCBXMenu (edit control hwnd), hMenu just performs the actual menu modifications, depending on which edit box is being clicked. It's a normal hMenu; you can use AppendMenu to add items with MF_STRING Or MF_POPUP to add a submenu.
Re: [RESOLVED] Modify right-click context menu in standard controls
Quote:
Originally Posted by
fafalone
... what exactly is happening when you try to use it?
I haven't tried anything yet.
I want to modify the context menu when I right mouse click on WebBrowser. I know how to eliminate the context menu or create my own but that's not the same as modifying it like what you asked for in your post #1
Re: [RESOLVED] Modify right-click context menu in standard controls
The only difference would be which hWnd you subclass (whichever receives WM_CONTEXTMENU), and which class you look for (the normal edit context menu is 32768-- the line If CS.lpszClass = 32768 Then ).