I have a GUI consists of 3 tab control
when I click on each tab the controls on that tab appears (there is a dialog for each tab that I show when that tab clicked)
the application has another dialog, when I change the focus on that dialog or any other window or program and return back to the main program I just can see the tab I left before and when I click the other tabs they don't show up.
I initialize tabs in WM_INITDIALOG and I show when each one is clicked in WM_NOTIFY like this:
case WM_NOTIFY:
switch (((LPNMHDR)lParam)->code)
{
case TCN_SELCHANGING:
{
// Return FALSE to allow the selection to change.
return FALSE;
}
break;
case TCN_SELCHANGE:
{
if( TabCtrl_GetCurSel( ( ( LPNMHDR ) lParam) -> hwndFrom ) == 0 ) {
ShowWindow( hwndTimeFrame, SW_HIDE );
ShowWindow( hwndAR, SW_HIDE );
ShowWindow( hwndInsFeed, SW_SHOW );
}
if( TabCtrl_GetCurSel( ( ( LPNMHDR ) lParam) -> hwndFrom ) == 1 )
{
ShowWindow( hwndInsFeed, SW_HIDE );
ShowWindow( hwndAR, SW_HIDE );
ShowWindow( hwndTimeFrame, SW_SHOW );
}
if( TabCtrl_GetCurSel( ( ( LPNMHDR ) lParam) -> hwndFrom ) == 2 )
{
ShowWindow( hwndInsFeed, SW_HIDE );
ShowWindow( hwndTimeFrame, SW_HIDE );
ShowWindow( hwndAR, SW_SHOW );
}
}
break;
}
break;
any suggestion ?
I think I figured out this.
I initialized the tab in WM_INITDIALOG which turned out to be the problem, because each time the application showing the tab dialogs it send this message and initialize them again. I remove this part from initdialog to initialization of the application before entering the message loop
Related
I need to handle the event of changing caret pos in edit control (not richedit control).
I know how to handle event of changing text in the edit, but I don't know how to handle the event of changing caret position in edit control.
I hope someone can help me. Thanks to all.
Once I have done this for displaying line number on status bar by subclassing Edit Control (SetWindowLongPtr) and sending message to parent window on messages that could potentialy move caret.
#define CARET_MOVED_COMMAND_ID 50001
WNDPROC OriginalEditProc = 0;
HWND OriginalEditParent = 0;
LRESULT WINAPI MyEditProc( HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam )
{
// Handle message by original edit control procedure.
LRESULT result = CallWindowProc( OriginalEditProc, hwnd, msg, wparam, lparam );
switch ( msg )
{
case WM_CHAR:
case WM_KEYDOWN:
case WM_KEYUP:
case WM_LBUTTONDOWN:
case WM_LBUTTONUP:
case WM_LBUTTONDBLCLK:
case WM_RBUTTONDOWN:
case WM_RBUTTONUP:
case WM_RBUTTONDBLCLK:
case WM_MBUTTONDOWN:
case WM_MBUTTONUP:
case WM_MBUTTONDBLCLK:
case EM_SETSEL:
// And maybe others...
// Notify parent of posible caret move.
// You can recreate EN_SELCHANGE behavior, I have been too lazy
// and was just sending command (like from menu).
SendMessage( OriginalEditParent, WM_COMMAND, MAKEWPARAM( CARET_MOVED_COMMAND_ID, 0 ), 0 );
break;
}
return result;
}
// ---- In initialization code ----
// subclass
OriginalEditParent = MyMainWindow;
OriginalEditProc = (WNDPROC) SetWindowLongPtr( EditBoxHwnd, GWLP_WNDPROC, (LONG_PTR)MyEditProc );
// ---- In parent window message handling ----
case WM_COMMAND:
switch ( LOWORD( wparam ) )
{
case CARET_MOVED_COMMAND_ID:
{
// You sould execute this code in EN_CHANGE handler,
// so it can handle cuting, pasting, undo etc.
int line = (int) SendMessage( EditBoxHwnd, EM_LINEFROMCHAR, -1, 0 );
// Update status bar. Could be optimized by remembering
// displayed line number and updating only when it changes.
wchar_t buff[50];
wsprintf( buff, L"%d", line );
SendMessage( StatusBarHwnd, SB_SETTEXT, MAKEWPARAM( 1, SBT_NOBORDERS ), (LPARAM) buff );
}
break;
}
break;
You can set the position of the caret in the edit control by sending an EM_SETSEL message, that selects from the desired position to the same position:
SendMessage(hWnd, EM_SETSEL, pos, pos);
Getting the position of the caret requires to send an EM_GETSEL message. Note that the if the start and the end are at same position, you know the precise position of the caret. If there's a difference, it means that text is selected, and you could take as position the last one.
There's no specific event for tracking only caret/selection changes. In your event loop, you need to catch key press and mouse events, and check if they changed the position. But wouldn't it possible for you to read the caret position only when you need it instead of actively tracking it ?
I have a very specific problem involving a modeless dialog box in my application.
The dialog box freezes and becomes unresponsive to any messages sent to it by other functions in my application. What is interesting is that my debugging tells me that it freezes when the dialog procedure has received just around 5000 messages that it DID NOT handle. The only explanation I can think of is that the Windows Message Queue may be full and it is more or less confirmed by the fact that the stream of messages going through the dialog box seem to tone down immensely.
Now - I've never used dialog boxes in conjunction with an ordinary main window before, and so I may be making illegal moves. By this I mean that I update the dialog box's controls (static texts and a list box) directly by sending the specific controls messages using SendMessage or SetWindowText functions.
What I think is weird is, that this technique works perfectly until 5000 messages have passed.
The main loop sends messages to the dialog box via the parent window handle and use of IsDialogMessage function.
Both the Main window and the dialog box still receives messages, but the dialog box freezes.
Is there a way for me to empty the message queue manually or check its current volume to check if that is actually the problem? I use PeekMessage function to retrieve my messages, which according to MSDN should remove a message from the bottom of the Message queue.
Here is how I've implemented My main loop ( I am pretty sure it's completely legal ):
while (true) //while there is a message
{
//if there was a windows message
if ( PeekMessage( &msg, NULL, 0, 0, PM_REMOVE ) )
{
if ( msg.message == WM_QUIT ) //if the message was WM_QUIT
return 0; //Exit the message loop
if ( !IsDialogMessage( m_StatusHwnd, &msg ) )
{
TranslateMessage( &msg );
DispatchMessage( &msg );
}
}
else
{
advanceFrame();
}
}
I really hope one of you have an idea about what is wrong, because this is REALLY hard hard to debug!
The Dialog procedure is implemented like so: ( Sorry that you have to see my actual code )
First the static dialog procedure redirects the messages to a custom method:
BOOL CALLBACK DXCore::statusDlgProc( HWND hwnd,
UINT msg,
WPARAM wParam,
LPARAM lParam )
{
if ( msg == WM_INITDIALOG ) SetWindowLongPtr( hwnd, DWLP_USER, lParam);
DXCore * pCore = reinterpret_cast<DXCore*>( GetWindowLongPtr( hwnd, DWLP_USER ) ) ;
if ( pCore ) return pCore->displayStatusDlgProc( hwnd, msg, wParam, lParam );
//return the message for windows to handle it
return FALSE;
}
Then the actual procedure looks like this:
BOOL DXCore::displayStatusDlgProc( HWND hwnd,
UINT msg,
WPARAM wParam,
LPARAM lParam )
{
HBRUSH brush = CreateSolidBrush( COLORREF( RGB( 255, 0, 0 ) ) ); //red
HPEN blackPen = CreatePen( PS_SOLID, 2, COLORREF( RGB(0,0,0 ) ) );
HDC hdc; PAINTSTRUCT ps;
RECT clientArea;
GetClientRect( hwnd, &clientArea );
int gizmoRadius= 5;
m_GismoOrigon.x = clientArea.left + 150;
m_GismoOrigon.y = clientArea.top + 460;
//OutputDebugString( "Dillermand\n" );
dlgProcCounter += 1;
switch ( msg )
{
case WM_INITDIALOG:
m_FPSCount = GetDlgItem( hwnd, IDC_STATIC_FPS );
if ( !m_FPSCount ) MessageBox( NULL, "ghFPSCount", "DAMN", MB_OK );
m_CamPosX = GetDlgItem( hwnd, IDC_CAMPOSX );
if ( !m_CamPosX ) MessageBox( NULL, "ghCamPosX", "DAMN", MB_OK );
m_CamPosY = GetDlgItem( hwnd, IDC_CAMPOSY );
if ( !m_CamPosY ) MessageBox( NULL, "ghCamPosY", "DAMN", MB_OK );
m_CamPosZ = GetDlgItem( hwnd, IDC_CAMPOSZ );
if ( !m_CamPosZ ) MessageBox( NULL, "ghCamPosZ", "DAMN", MB_OK );
m_hStatusMessages = GetDlgItem( hwnd, IDSTATUS_PROGMSG );
if ( !m_hStatusMessages ) MessageBox( NULL, "ghStatusMessages", "DAMN", MB_OK );
else
{
SetParent( m_hStatusMessages, hwnd );
}
m_RunButton = GetDlgItem( hwnd, IDCSTATUS_RUN_BTN );
if ( !m_RunButton ) MessageBox( NULL, "ghRunButton ", "DAMN", MB_OK );
m_PauseButton = GetDlgItem( hwnd, IDSTATUS_PAUSE_BTN );
if ( !m_PauseButton ) MessageBox( NULL, "ghPauseButton", "DAMN", MB_OK );
SetWindowText( m_CamPosX, "0" );
SetWindowText( m_CamPosY, "0" );
SetWindowText( m_CamPosZ, "0" );
return TRUE;
case WM_PAINT:
hdc = BeginPaint( hwnd, &ps );
SelectObject( hdc, brush );
SelectObject( hdc, blackPen );
Ellipse( hdc, m_GismoOrigon.x - gizmoRadius, m_GismoOrigon.y - gizmoRadius, m_GismoOrigon.x + gizmoRadius, m_GismoOrigon.y + gizmoRadius ) ;
EndPaint( hwnd, &ps );
return TRUE;
case WM_COMMAND:
return TRUE;
case WM_NOTIFY:
return TRUE;
case WM_CTLCOLORSTATIC:
return TRUE;
case WM_TIMER:
return TRUE;
case WM_DESTROY:
if ( MessageBox( hwnd, "Exit Program?", "Do Not Want!", MB_YESNO ) == IDYES )
{
PostQuitMessage( 0 );
}
else ShowWindow(m_StatusHwnd, true );
return TRUE;
case WM_CLOSE:
DestroyWindow( m_StatusHwnd );
return TRUE;
default:
string s = std::to_string( dlgProcCounter ) + " Unhandled Dlg message: " + std::to_string( msg ) + "\n";
OutputDebugString( s.c_str( ) );
return (INT_PTR)FALSE;
}
return FALSE;
}
Your dialog procedure is creating two GDI objects, a brush and pen, every time it's called. It never destroys these objects. By default there's a 10,000 per process limit on GDI objects. Once you reach that limit the calls to create the objects will fail. Your code will then try to draw using invalid handle values, making it appear that your window has frozen.
The solution is to only create the objects once when handling the WM_INITDIALOG message. Also always check the return value of the functions you call for errors. If you had checked the return values to CreateSolidBrush and CreatePen you potentially could have figured this out earlier.
When a user clicks on window B the window C becomes visible.
Situation 1 : While window C is visible and user clicks anywhere on window A or B or any other child windows of window A, window C becomes invisible.
Situation 2 : While window C is visible and user clicks on window C then program does few things and makes window C invisible. [window C has more child windows]
How do I achieve this functionality ?
The SetCapture won't work for Situation 2.
In the window B this is what I got, it works for Situation 1, but obviously not for Situation 2.
LRESULT WindowB::LButtonDown ( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam )
{
if ( !capture )
{
::SetCapture ( hwnd );
capture = true;
windowC->show ( );
}
else
{
::ReleaseCapture ( );
capture = false;
windowC->hide ( );
}
return 0;
};
P.S : I am using pure winapi with oop c++, and my own wrapper for windows and message handling.
EDIT: More code added as requested.
LRESULT WindowC::lButton ( UINT message, WPARAM wParam, LPARAM lParam )
{
doStuff ( );
::PostMessage ( hParent, WM_COMMAND, WPARAM ( BN_CLICKED ), LPARAM ( hwnd ) );
return 0;
};
I create a tab control in WM_INITDIALOG this way:
INITCOMMONCONTROLSEX icex = {0};
icex.dwSize = sizeof(INITCOMMONCONTROLSEX);
icex.dwICC = ICC_TAB_CLASSES;
InitCommonControlsEx(&icex);
TCITEM tie;
LPSTR text = "my tab";
tie.mask = TCIF_TEXT|TCIF_IMAGE;
tie.iImage = -1;
tie.pszText = text;
hTab = CreateWindow(WC_TABCONTROL, "", WS_CHILD |WS_CLIPSIBLINGS| WS_VISIBLE,
0,0, 400, 350, hWnd,NULL, g_hInstance, NULL);
TabCtrl_InsertItem(hTab,0,&tie);
TabCtrl_InsertItem(hTab,1,&tie);
TabCtrl_InsertItem(hTab,2,&tie);
and also I create two dialog here to show in each tab as content of the tab. I create them with toolbox selecting formview dialog:
hwndTabcontentDialog1 = CreateDialogParam( GetModuleHandle( NULL ),
MAKEINTRESOURCE( IDD_FORMVIEW1 ), hTab, (DLGPROC)Proc1,lParam );
hwndTabcontentDialog2 = CreateDialogParam( GetModuleHandle( NULL ),
MAKEINTRESOURCE( IDD_FORMVIEW ), hTab, (DLGPROC)Proc2,lParam );
now in WM_NOTIFY I am doing this to content of each tab when its clicked:
case WM_NOTIFY:
switch (((LPNMHDR)lParam)->code)
{
case TCN_SELCHANGE:
{
if( TabCtrl_GetCurSel( ( ( LPNMHDR ) lParam) -> hwndFrom ) == 0 ) {
ShowWindow( hwndTabcontentDialog1, SW_SHOW );
ShowWindow( hwndTabcontentDialog2, SW_HIDE );
} else {
ShowWindow( hwndTabcontentDialog1, SW_HIDE );
ShowWindow( hwndTabcontentDialog2, SW_SHOW );
}
}
now the tab is created and everything is fine(content of current tab is not visible), but when I click on one of the tab items the dialog cover all the tab control and you cant see the tabs anymore.
what is wrong ? what should I modify ?
Immediately after you create the dialogs reposition/resize them with MoveWindow. The TCM_ADJUSTRECT tab control message can help you figure out the proper position/size to make the dialogs.
I added a notification icon to my dialog based application, and it received WM_LBUTTONDBLCLK when the icon is double clicked on, but it is not receiving WM_CONTEXTMENU when the icon is right clicked or when the icon is highlighted with the keyboard and the context menu key is pressed. I based my usage of the notification icon on the example in the Windows 7.1 SDK Samples. So, I have no idea where I'm going wrong or why this isn't working.
Note: If I change WM_CONTEXTMENU to WM_RBUTTONUP, it receives the event, but the cursor coordinates are wrong.
/******************************************************************************/
/* Menu Resource */
/******************************************************************************/
LANGUAGE LANG_NEUTRAL, SUBLANG_NEUTRAL
IDR_TRAYMENU MENU
{
POPUP ""
{
MENUITEM "&Show Status Window", IDM__SHOW_STATUS_WINDOW
MENUITEM "&About", IDM__ABOUT
MENUITEM SEPARATOR
MENUITEM "&Exit", IDM__EXIT
}
}
/******************************************************************************/
/* WinMain() */
/******************************************************************************/
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow )
{
// ... code unrelated to icon
// Enable Visual Styles
InitCommonControls();
// create the main dialog
if( NULL == (hWnd=CreateDialog(hInstance,MAKEINTRESOURCE(IDD_MAINDLG),NULL,(DLGPROC)WndProc)) )
{
MessageBox( NULL, "Error creating the main dialog!", NULL, MB_OK | MB_ICONERROR );
return -1;
}
// ... code unrelated to icon
MSG msg;
while( GetMessage(&msg,NULL,0,0) && IsWindow(hWnd) )
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return 0;
}
/******************************************************************************/
/* WndProc() */
/******************************************************************************/
BOOL CALLBACK WndProc(HWND hWndDlg, UINT Message, WPARAM wParam, LPARAM lParam)
{
switch(Message)
{
case WM_INITDIALOG:
{
// ... code unrelated to icon
hIcon = (HICON)LoadImage( GetModuleHandle(NULL), MAKEINTRESOURCE(IDI_DDCMP), IMAGE_ICON, 16, 16, LR_DEFAULTSIZE );
// Setup the system tray icon
memset( &nid, 0, sizeof(NOTIFYICONDATA) );
nid.cbSize = sizeof(NOTIFYICONDATA);
nid.hWnd = hWndDlg;
nid.uID = 0xDDC;
nid.uFlags = NIF_ICON | NIF_MESSAGE | NIF_TIP | NIF_SHOWTIP;
nid.uCallbackMessage = WM_APP + 0xDDC;
nid.hIcon = hIcon;
strcpy( nid.szTip, "DDCMP Driver" );
Shell_NotifyIcon( NIM_ADD, &nid );
// ... code unrelated to icon
return true;
} break;
case WM_APP + 0xDDC:
{
switch( LOWORD(lParam) )
{
case WM_CONTEXTMENU:
{
MessageBox( hWndDlg, "This message box never shows up.", NULL, MB_OK | MB_SYSTEMMODAL );
HMENU hMenu = LoadMenu(GetModuleHandle(NULL),MAKEINTRESOURCE(IDR_TRAYMENU));
if( hMenu )
{
HMENU hSubMenu = GetSubMenu(hMenu,0);
if( hSubMenu )
{
SetForegroundWindow( hWndDlg );
POINT pt = { LOWORD(wParam), HIWORD(wParam) };
UINT uFlags = TPM_RIGHTBUTTON;
if( 0 != GetSystemMetrics(SM_MENUDROPALIGNMENT) )
uFlags |= TPM_RIGHTALIGN;
else
uFlags |= TPM_LEFTALIGN;
TrackPopupMenuEx( hSubMenu, uFlags, pt.x, pt.y, hWndDlg, NULL );
}
DestroyMenu( hMenu );
}
} break;
case WM_LBUTTONDBLCLK:
if( IsWindowVisible(hWndDlg) )
ShowWindow( hWnd, SW_HIDE );
else
ShowWindow( hWnd, SW_SHOW );
break;
}
return true;
} break;
case WM_CLOSE:
ShowWindow( hWndDlg, SW_HIDE );
break;
case WM_DESTROY:
case WM_QUIT:
{
Shell_NotifyIcon( NIM_DELETE, &nid );
// ... code unrelated to icon
return true;
} break;
}
return false;
}
This is the WndProc from the Windows 7.1 SDK Sample
LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
static HWND s_hwndFlyout = NULL;
static BOOL s_fCanShowFlyout = TRUE;
switch (message)
{
case WM_CREATE:
// add the notification icon
if (!AddNotificationIcon(hwnd))
{
MessageBox(hwnd,
L"Please read the ReadMe.txt file for troubleshooting",
L"Error adding icon", MB_OK);
return -1;
}
break;
case WM_COMMAND:
{
int const wmId = LOWORD(wParam);
// Parse the menu selections:
switch (wmId)
{
case IDM_LOWINK:
ShowLowInkBalloon();
break;
case IDM_NOINK:
ShowNoInkBalloon();
break;
case IDM_PRINTJOB:
ShowPrintJobBalloon();
break;
case IDM_OPTIONS:
// placeholder for an options dialog
MessageBox(hwnd, L"Display the options dialog here.", L"Options", MB_OK);
break;
case IDM_EXIT:
DestroyWindow(hwnd);
break;
case IDM_FLYOUT:
s_hwndFlyout = ShowFlyout(hwnd);
break;
default:
return DefWindowProc(hwnd, message, wParam, lParam);
}
}
break;
case WMAPP_NOTIFYCALLBACK:
switch (LOWORD(lParam))
{
case NIN_SELECT:
// for NOTIFYICON_VERSION_4 clients, NIN_SELECT is prerable to listening to mouse clicks and key presses
// directly.
if (IsWindowVisible(s_hwndFlyout))
{
HideFlyout(hwnd, s_hwndFlyout);
s_hwndFlyout = NULL;
s_fCanShowFlyout = FALSE;
}
else if (s_fCanShowFlyout)
{
s_hwndFlyout = ShowFlyout(hwnd);
}
break;
case NIN_BALLOONTIMEOUT:
RestoreTooltip();
break;
case NIN_BALLOONUSERCLICK:
RestoreTooltip();
// placeholder for the user clicking on the balloon.
MessageBox(hwnd, L"The user clicked on the balloon.", L"User click", MB_OK);
break;
//
//
// As you can very plainly see, the Windows SDK Sample ONLY used WM_CONTEXTMNEU
//
//
case WM_CONTEXTMENU:
{
POINT const pt = { LOWORD(wParam), HIWORD(wParam) };
ShowContextMenu(hwnd, pt);
}
break;
}
break;
case WMAPP_HIDEFLYOUT:
HideFlyout(hwnd, s_hwndFlyout);
s_hwndFlyout = NULL;
s_fCanShowFlyout = FALSE;
break;
case WM_TIMER:
if (wParam == HIDEFLYOUT_TIMER_ID)
{
// please see the comment in HideFlyout() for an explanation of this code.
KillTimer(hwnd, HIDEFLYOUT_TIMER_ID);
s_fCanShowFlyout = TRUE;
}
break;
case WM_DESTROY:
DeleteNotificationIcon();
PostQuitMessage(0);
break;
default:
return DefWindowProc(hwnd, message, wParam, lParam);
}
return 0;
}
I think you should try changing the uVersion member of the NOTIFYICONDATA structure to NOTIFYICON_VERSION_4, the documentation states that this members value will tell how the uCallbackMessage parameters will be interpreted when passed on to your callback function.
You can also have a look at this: Basic use of Shell_NotifyIcon in Win32
I did a bit of research and the following should work for you:
memset(&nid, 0, sizeof(NOTIFYICONDATA));
nid.cbSize = sizeof(NOTIFYICONDATA);
nid.hWnd = hWndDlg;
nid.uID = 0xDDC;
nid.uFlags = NIF_ICON | NIF_MESSAGE | NIF_TIP | NIF_SHOWTIP;
nid.uCallbackMessage = WM_APP + 0xDDC;
nid.hIcon = hIcon;
nid.uVersion = NOTIFYICON_VERSION_4;
strcpy(nid.szTip, "DDCMP Driver");
Shell_NotifyIcon(NIM_ADD, &nid);
Shell_NotifyIcon(NIM_SETVERSION, &nid);
NIM_SETVERSION (MSDN):
Shell32.dll version 5.0 and later only. Instructs the notification
area to behave according to the version number specified in the
uVersion member of the structure pointed to by lpdata. The version
number specifies which members are recognized.
The notify icon has changed behaviour over the years. For reasons of compatibility with pre-existing code, you must opt-in to the new behaviour. If you don't opt-in then you don't get sent WM_CONTEXTMENU messages. Instead you have to respond to WM_RBUTTONUP. Even if you invoke the context menu from the keyboard, the system still sends WM_RBUTTONUP. You have to obtain the cursor position, in order to know where to show the menu, by calling GetCursorPos.
You can opt in to the new behaviour (and WM_CONTEXTMENU) as described in the documentation, by calling Shell_NotifyIcon passing NIM_SETVERSION after the NIM_ADD call. Presumably the SDK sample you are looking at does this somewhere. My guess is that is what is missing from your code.
The key extract from the documentation is in the remarks section:
As of Windows 2000 (Shell32.dll version 5.0), Shell_NotifyIcon mouse and keyboard events are handled differently than in earlier Shell versions found on Microsoft Windows NT 4.0, Windows 95, and Windows 98. The differences include the following:
If a user selects a notify icon's shortcut menu with the keyboard, the Shell now sends the associated application a WM_CONTEXTMENU message. Earlier versions send WM_RBUTTONDOWN and WM_RBUTTONUP messages.
If a user selects a notify icon with the keyboard and activates it with the SPACEBAR or ENTER key, the version 5.0 Shell sends the associated application an NIN_KEYSELECT notification. Earlier versions send WM_RBUTTONDOWN and WM_RBUTTONUP messages.
If a user selects a notify icon with the mouse and activates it with the ENTER key, the Shell now sends the associated application an NIN_SELECT notification. Earlier versions send WM_RBUTTONDOWN and WM_RBUTTONUP messages.