How to handle NM_CLICK for Syslink control
-
10-06-2021 - |
Question
Did quite a bit of research trying to figure out how to get this to work properly. I read that if you filter WM_NOTIFY -> NM_CLICK, it would catch the actual click event of the syslink. Trouble is that it catches the event, though it gets stuck in a endless recursion and in a few seconds you have hundreds of browser windows or w/e the link open's up as.
Steps taken:
- Create Syslink on dialog
- Add title to Syslink control link and change IDD to IDC_LINK1
Filter WM_NOTIFY for NM_CLICK events
case WM_NOTIFY: //case NM_CLICK: switch(LOWORD(wParam)) { case NM_CLICK: switch(LOWORD(wParam)) { case IDC_LINK1: // Standard ShellExecute with added check for IsLinkCtrl to make sure its the right kind of control. OpenLink(hWndDlg, LOWORD(wParam)); break; } break; } break;
I guess my real question is how do I do this properly? I don't see any good examples that show how a Syslink is properly filtered to execute a link as a url.
Solution 2
I found a successful way to get this to work without going into recursion. The message loop seems to keep reading in a continuous loop and if you don't filter by using the correct structure it will fall into a infinite recursion. This works for me. If you have better input by all means please do addon.
The correct steps to take.
- Create WM_NOTIFY Message filter event.
- Create Switch statement using NMHDR to parse the code for NM_CLICK.
- Check wParam to identify correct control being clicked.
- Execute link with ShellExecute.
The following code was used.
case WM_NOTIFY:
//NMHDR* pHeader = (NMHDR*)lParam;
//NMLINK* pNMLink = (NMLINK*)lParam;
//LITEM iItem = pNMLink->item;
switch(((NMHDR *)lParam)->code)
{
case NM_CLICK:
{ // Included to avoid "case" skip statements.
times++;
NMLINK* pNMLink = (NMLINK*)lParam;
LITEM iItem = pNMLink->item;
// Custom OutputDebugString
winapi::Output("NM_CLICK: Fired %d time%s!\n", times, (times <= 1) ? L"" : L"s");
#ifdef DEBUG
assert(iItem.szID);
MessageBox(NULL, (LPCWSTR)lParam, L"Assert", MB_OK|MB_ICONINFORMATION);
#endif
if(wParam == IDC_LINK1)
{
winapi::Output("Success!");
OpenLink(hWndDlg, LOWORD(wParam));
}
Note:Use NMLINK structure if you rely on your link containing HTML attributes (link or here) to feed Shellexecute its url path.
OTHER TIPS
Just to clarify, I do not think you are using managed C++, there for I am not sure why you are referring to a NM_CLICK notificaton code as an event. Besides, you do not have to catch anything, you are not handling any exceptions, you are just handling a WM_NOTIFY message.
Anyway, I am not sure why you experience this particular behavior since I do not see what you do outside of the code snippet but I know what is causing it. In our code snippet you use wParam to determine a notification code and this is incorrect. This control does not use wParam. To determine notification code you should do the following:
NMHDR* pHeader = (NMHDR*)lParam;
switch (pHeader->code)
pHeader->code holds the notification code value you should use and pHeader->hwndFrom is the handle of the control that was clicked.
Furthermore, you using LOWORD(wParam) again and pass it as the parameter to function call. You are not showing what you do in this function but I can conclude that the code is also not correct. The same lParam is a pointer to the NMLINK structure. First member of this structure is mentioned above NMHDR, the next member is LITEM structure that should be used to determine what the URL is.
NMLINK* pNMLink = (NMLINK*)lParam;
LITEM iItem = pNMLink->item;
item.szUrl is the URL you should use in call to ShellExecute call open.
Either
your
OpenLink
function results in another message being sent (most likely), orThis isn't the only code where
OpenLink
is called, oryou reach this code not from
WM_NOTIFY
, but from fall-through of the case above, oryou call
DefWindowProc
for the message, even though you already handled it.
Try
break; // make sure there's no fall-through here
case NM_CLICK:
switch(LOWORD(wParam))
{
case IDC_LINK1:
// Standard ShellExecute with added check for IsLinkCtrl to make sure its the right kind of control.
OpenLink(hWndDlg, LOWORD(wParam));
return TRUE; // handled, don't pass to DefWindowProc
}
break;