This is another question I have related to setWindowsHookEx, I have written small Application to hook dll into windows calculator applicatio. Basically, exe application inject the dll into calculator and it monitors all the mouse down events happening at the calculator application. And currently , I am able to disable all the mouse down events happening at the calculator. however, I want to get it done based on the location the mouse is pointing. Specifically, when the mouse button is clicking on specific menu bar item. I tried to get this done by using GetWindowText to retrieve the text where button is pointing, but most of the time it return the window text instead of the window item ( menu item) it is pointing. Following is the code that I have written for this. Any idea on how to disable a particular window element on menu bar.
EXE code which call SetWindowsHookEx
// program.exe.cpp : Defines the entry point for the console application.
//
#include "stdafx.h"
#include <Windows.h>
#include <string>
#include <io.h>
using namespace std;
void Usage()
{
printf("Usage: InjectDLL pid path-to-dll [-privilege]");
}
int _tmain(int argc, char* argv[])
{
/*
* Load library in which we'll be hooking our functions.
*/
HMODULE dll = LoadLibrary(L"C:\\drivers\\dllinject.dll");
if (dll == NULL) {
printf("The DLL could not be found.\n");
getchar();
return -1;
}
/*
* Get the address of the function inside the DLL.
* _hookmethod#12 is used to hook into 32bit applications
*/
//HOOKPROC addr = (HOOKPROC)GetProcAddress(dll, "_hookmethod#12");
HOOKPROC addr = (HOOKPROC)GetProcAddress(dll, "hookmethod");
if (addr == NULL) {
printf("The function was not found.\n");
getchar();
return -1;
}
/*
* Hook the function.
*/
DWORD procID=0;
HWND targetWnd = FindWindowA("CalcFrame", "Calculator");
wchar_t msgBuf[1024] = L"";
wchar_t msgBuf2[1024] = L"";
wsprintf(msgBuf, L"the thread Id is : %d , process id is : %d ", threadID, procID);
OutputDebugString(msgBuf);
//
//WH_KEYBOARD_LL
HHOOK handle = SetWindowsHookEx(WH_GETMESSAGE, addr, dll, threadID);
//HHOOK handle = SetWindowsHookEx(WH_CALLWNDPROC, addr, dll, threadID);
//HHOOK handle = SetWindowsHookEx(WH_MSGFILTER, addr, dll, threadID);
DWORD x = GetLastError();
wsprintf(msgBuf2, L"the last error is %d", x);
OutputDebugString(msgBuf2);
if (handle == NULL) {
printf("The KEYBOARD could not be hooked.\n");
}
else{
printf("Program successfully hooked.\nPress enter to unhook the function and stop the program.\n");
}
/*
* Unhook the function.
*/
getchar();
UnhookWindowsHookEx(handle);
return 0;
}
dll which is used to block the clicking of menu bar item.
#include "stdafx.h"
#include <stdio.h>
#include <windows.h>
static HWND s_hWndButton;
INT APIENTRY DllMain(HMODULE hDLL, DWORD Reason, LPVOID Reserved) {
switch (Reason) {
case DLL_PROCESS_ATTACH:
OutputDebugString(L"DLL attach function called11.\n");
break;
case DLL_PROCESS_DETACH:
OutputDebugString(L"DLL detach function called.\n");
break;
case DLL_THREAD_ATTACH:
OutputDebugString(L"DLL thread attach function called.\n");
break;
case DLL_THREAD_DETACH:
OutputDebugString(L"DLL thread detach function called.\n");
break;
}
return TRUE;
}
extern "C" __declspec(dllexport) LRESULT __stdcall hookmethod(int code, WPARAM wParam, LPARAM lParam) {
TCHAR msgBuftext[65536];
TCHAR msgBufhandle[65536];
TCHAR msgBufhandletext[65536];
TCHAR msgBufwintext[2048];
if (code >= 0) {
MSG* cp = (MSG*)lParam;
int txtlen = GetWindowTextLengthW(cp->hwnd);
GetWindowText(cp->hwnd, msgBufhandletext, txtlen);
wsprintf(msgBufhandle, L"Handle %X", cp->hwnd);
wsprintf(msgBufwintext, L"Window Text %s", msgBufhandletext);
LPMSG msg = (LPMSG)lParam;
if (msg->message == WM_LBUTTONDOWN) {
OutputDebugString(msgBufwintext);
msg->message = WM_NULL;
}
}
return(CallNextHookEx(NULL, code, wParam, lParam));
}
For those who are interested, I would like to post the answer that I was able to implement. Basically , instead of listening to WH_GETMESSAGE, I changed the hook chain to WH_MSGFILTER. Reason because, it allows me to find the menu item id when I move the mouse over menu item and when the button up event happens, I can check the menu item id and disable mouse click by changing the message to WM_NULL. following is the working code sample. It also enable u to block the event if user is navigat
dll code
#include "stdafx.h"
#include <stdio.h>
#include <windows.h>
#include <Winuser.h>
#include <Lmcons.h>
#include "nxlrunner.h"
WORD wMenuItemID;
INT APIENTRY DllMain(HMODULE hDLL, DWORD Reason, LPVOID Reserved) {
switch (Reason) {
case DLL_PROCESS_ATTACH:
OutputDebugString(L"DLL attach function called11.\n");
break;
case DLL_PROCESS_DETACH:
OutputDebugString(L"DLL detach function called.\n");
break;
case DLL_THREAD_ATTACH:
OutputDebugString(L"DLL thread attach function called.\n");
break;
case DLL_THREAD_DETACH:
OutputDebugString(L"DLL thread detach function called.\n");
break;
}
return TRUE;
}
extern "C" __declspec(dllexport) LRESULT __stdcall meconnect(int code, WPARAM wParam, LPARAM lParam) {
switch (code)
{
case MSGF_MENU: // message is for a menu
{
MSG* pMSG = (MSG*)lParam;
switch (pMSG->message)
{
case WM_MENUSELECT:
{
wMenuItemID = LOWORD(pMSG->wParam);
break;
}
case WM_LBUTTONDOWN:
{
break;
}
case WM_LBUTTONUP:
{
if (wMenuItemID == 10353){
wchar_t usernamew[UNLEN];
DWORD username_len = UNLEN + 1;
GetUserNameW(usernamew, &username_len);
MessageBoxW(NULL, usernamew, L"Title", MB_OK);
pMSG->message = WM_NULL;
}
break;
}
case WM_LBUTTONDBLCLK:
{
if (wMenuItemID == 10353){
pMSG->message = WM_NULL;
}
break;
}
case WM_KEYDOWN:
{
switch (pMSG->wParam)
{
case VK_RETURN:
{
if (wMenuItemID == 10353){
MessageBoxW(NULL, L"Message to user", L"Title.", MB_OK);
pMSG->message = WM_NULL;
}
break;
}
}
break;
}
case WM_KEYUP:
{
switch (pMSG->wParam)
{
case VK_RETURN:
{
if (wMenuItemID == 10353){
MessageBoxW(NULL, L"Message to user", L"Title", MB_OK);
pMSG->message = WM_NULL;
}
break;
}
}
break;
}
}
break;
}
}
return false;
}
windows hooking
system message hooks
Call MSG Filter function
msg structure
sample code
keyboard codes
sample code from internet
Related
I'm using IContextMenu with "delete" verb to erase items from recycle bin, but it brings up the UI dialog even if I useCMIC_MASK_FLAG_NO_UI flag. Is there another way to delete files from recycle bin without showing the dialog?
IContextMenu* contextMenu;
recycleBin->GetUIObjectOf(0, 1, (LPCITEMIDLIST*)(&pidl), IID_IContextMenu, 0, (LPVOID *)&contextMenu);
CMINVOKECOMMANDINFO ci;
memset(&ci, 0, sizeof(ci));
ci.fMask = CMIC_MASK_FLAG_NO_UI;
ci.cbSize = sizeof(CMINVOKECOMMANDINFO);
ci.lpVerb = "delete";
contextMenu->InvokeCommand(&ci);
contextMenu->Release();
The full code including recycle bin object initialization and items enumeration is on gist
You can install a WH_CBT hook, and in its callback watch for an nCode of HCBT_CREATEWND. If you get a matching class name ('#32770 (Dialog)' ?) and a matching title either return a nonzero value from the callback, you can send BM_CLICK to click Yes button and the dialog will be closed automatically.
Code Sample:
main.cpp
#include <iostream>
#include <windows.h>
#include <Ole2.h>
#include <shlobj.h>
#include <shlwapi.h>
#include <string>
#pragma comment(lib,"Shlwapi.lib")
HHOOK g_hhook;
int main()
{
OleInitialize(0);
HINSTANCE hinstDLL = LoadLibrary(TEXT("D:\\xxx\\Dll_WH_CBT\\Debug\\Dll_WH_CBT.dll"));
HHOOK(*AttachHookProc)(DWORD);
AttachHookProc = (HHOOK(*)(DWORD)) GetProcAddress(hinstDLL, "AttachHook");
void(*RemoveHooks)(DWORD);
RemoveHooks = (void(*)(DWORD))GetProcAddress(hinstDLL, "RemoveHooks");
IShellFolder* desktop;
if (FAILED(SHGetDesktopFolder(&desktop))) {
std::cerr << "Failed to get desktop fodler" << std::endl;
return -1;
}
...
IEnumIDList* enumFiles;
if (FAILED(recycleBin->EnumObjects(0, SHCONTF_FOLDERS | SHCONTF_NONFOLDERS |
SHCONTF_INCLUDEHIDDEN, &enumFiles)))
{
std::cerr << "Could not enumerate recycle bin objects" << std::endl;
recycleBin->Release();
return -1;
}
AttachHookProc(0);
...
RemoveHooks(0);
enumFiles->Release();
recycleBin->Release();
OleUninitialize();
return 0;
}
Dll:
// dllmain.cpp : Defines the entry point for the DLL application.
#include "pch.h"
#include <Windows.h>
HMODULE thisModule;
HHOOK g_hhook;
LRESULT CALLBACK CBTProc(int nCode, WPARAM wParam, LPARAM lParam)
{
switch (nCode)
{
case HCBT_CREATEWND:
{
HWND File_Window = FindWindow(L"#32770", L"Delete File");
HWND Folder_Window = FindWindow(L"#32770", L"Delete Folder");
if (File_Window)
{
HWND yes = FindWindowEx(File_Window, NULL, L"Button", L"&Yes");
SendMessage(yes, BM_CLICK, 0, 0);
SendMessage(yes, BM_CLICK, 0, 0);
}
if (Folder_Window)
{
HWND yes = FindWindowEx(Folder_Window, NULL, L"Button", L"&Yes");
SendMessage(yes, BM_CLICK, 0, 0);
SendMessage(yes, BM_CLICK, 0, 0);
}
break;
}
default:
break;
}
return CallNextHookEx(g_hhook, nCode, wParam, lParam);
}
#ifdef __cplusplus //If used by C++ code.
extern "C" { //we need to export the C interface
#endif
_declspec(dllexport) HHOOK AttachHook(DWORD threadID) {
g_hhook = SetWindowsHookEx(WH_CBT, CBTProc, thisModule, threadID);
return g_hhook;
}
#ifdef __cplusplus
}
#endif
extern "C" { //we need to export the C interface
__declspec(dllexport) void RemoveHooks(DWORD)
{
UnhookWindowsHookEx(g_hhook);
}
}
BOOL APIENTRY DllMain( HMODULE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
thisModule = hModule;
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
case DLL_PROCESS_DETACH:
break;
}
return TRUE;
}
I'm trying to use SetWindowsHookEx to install WH_CBT hook on global to inject the DLL into other process. Now I have the inject.exe, ExampleWindow.exe, inject.dll, let me show you the code.
1.code piece of inject.exe
#include <windows.h>
int main()
{
HHOOK hhook = NULL;
HMODULE dll = LoadLibrary(_T("D:\\projects\\CppDemon\\Release\\HookDemoDll.dll"));
HOOKPROC pfn = (HOOKPROC)GetProcAddress(dll, "MyCBTProc");
if (pfn == NULL)
{
printf("can not get MyCBTProc,press any key to exit\n");
getchar();
return 0;
}
printf("press any key to continue hook\n");
getchar();
hhook = SetWindowsHookEx(WH_CBT, pfn, dll, 0);
if (NULL == hhook)
{
printf("%d\n", GetLastError());
}
printf("press any key to unhook\n");
getchar();
UnhookWindowsHookEx(hhook);
return 0;
}
2.code piece of inject.dll
#ifdef __cplusplus
extern "C"
{
#endif
extern "C" __declspec(dllexport) LRESULT MyCBTProc(_In_ int nCode, _In_ WPARAM wParam, _In_ LPARAM lParam);
#ifdef __cplusplus
}
#endif
LRESULT MyCBTProc(_In_ int nCode, _In_ WPARAM wParam, _In_ LPARAM lParam)
{
return CallNextHookEx(NULL, nCode, wParam, lParam);
}
BOOL APIENTRY DllMain( HMODULE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
DWORD pid = 0;
TCHAR buffer[128] = { 0 };
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
case DLL_THREAD_ATTACH:
pid = GetCurrentProcessId();
_stprintf_s(buffer, 128, _T("[+] PID = %d"), pid);
OutputDebugString(buffer);
case DLL_THREAD_DETACH:
case DLL_PROCESS_DETACH:
break;
}
return TRUE;
}
The ExampleWindow.exe is a test case for inject when the named window is created.
When I run inject.exe and everything goes well, the inject.dll has been loaded and the the debug message is output correct, and the PCHUNTER(this is an ARK tool) can detect the WH_CBT message hook in module inject.exe. Then, I run the ExampleWindow.exe, there also can output the debug message.
After this action, I refresh the message hook windows in PCHUNTER ,the WH_CBT hook message has disappeared from the control, and the inject.dll not injected in the ExampleWindow.exe at all.THis is not my expect result.
I don't know how this happens. I hope someone can help me find the cause of this problem.
i've created s simple W32 application to cicle through two applications(hard coded window classes atm).
When i call the start button (IDC_Start) everything works fine, but when i change the focus to
hwnd the application hangs an cannot be closed. I just need a simple and clean method to
stop the loop, that start when calling IDC_Start. Any Help would be greatly apreciated!
#include <stdio.h>
#include <stdlib.h>
#include <conio.h>
#include <windows.h>
#include <resource.h>
#include <iostream>
using namespace std;
BOOL CALLBACK DlgProc(HWND hwnd, UINT Message, WPARAM wParam, LPARAM lParam)
{
HWND hwnd1,hwnd2;
hwnd1 = FindWindow("Notepad++",0);
hwnd2 = FindWindow("Notepad",0);
BOOL bDone;
switch(Message)
{
case WM_INITDIALOG:
// This is where we set up the dialog box, and initialise any default values
{
SetDlgItemInt(hwnd, IDC_NUMBER, 5, FALSE);
HICON hIcon, hIconSm;
hIcon = (HICON)LoadImage(NULL, "e32.ico", IMAGE_ICON, 32, 32, LR_LOADFROMFILE);
if(hIcon)
SendMessage(hwnd, WM_SETICON, ICON_BIG, (LPARAM)hIcon);
else
MessageBox(hwnd, "Could not load large icon!", "Error", MB_OK | MB_ICONERROR);
hIconSm = (HICON) LoadImage(NULL, "e16.ico", IMAGE_ICON, 16, 16, LR_LOADFROMFILE);
if(hIconSm)
SendMessage(hwnd, WM_SETICON, ICON_SMALL, (LPARAM)hIconSm);
else
MessageBox(hwnd, "Could not load small icon!", "Error", MB_OK | MB_ICONERROR);
}
break;
case WM_COMMAND:
switch(LOWORD(wParam))
{
case IDC_START:
{
BOOL bSuccess;
int nDelay = GetDlgItemInt(hwnd, IDC_NUMBER, &bSuccess, FALSE);
nDelay= nDelay*1000;
int i=1;
ShowWindow(hwnd,SW_MINIMIZE);
if(bSuccess)
{
if (hwnd1 != 0&&hwnd2 != 0)
{
SetThreadExecutionState(ES_CONTINUOUS | ES_DISPLAY_REQUIRED);
while(i)
{
if(bDone!=TRUE)
{
SetDlgItemInt(hwnd, IDC_SHOWCOUNT, nDelay/1000, FALSE);
Sleep(nDelay);
SetForegroundWindow(hwnd1);
Sleep(nDelay);
SetForegroundWindow(hwnd2);
i++;
}
else
{
SetThreadExecutionState(ES_CONTINUOUS);
MessageBox(hwnd, "Stopped", "Warning", MB_OK);
break;
}
}
}
else
{
MessageBox(hwnd,"Cannot find suitable Window","AppDia",MB_OK);
}
}
else
{
MessageBox(hwnd, "Number not identified", "Warning", MB_OK);
}
}
break;
case IDC_STOP:
bDone==TRUE;
break;
}
break;
case WM_CLOSE:
EndDialog(hwnd, 0);
break;
default:
return FALSE;
}
return TRUE;
}
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
LPSTR lpCmdLine, int nCmdShow)
{
return DialogBox(hInstance, MAKEINTRESOURCE(IDD_MAIN), NULL, DlgProc);
}
Your logic loops with Sleeps, it doesn't give the dialog box a way to process it's messages, so it seems to hang -- try using a Timer instead, with something like:
static BOOL bWnd1 = TRUE;
case WM_COMMAND:
switch(LOWORD(wParam))
{ case IDC_START:
{ int nDelay = GetDlgItemInt(hwnd, IDC_NUMBER, &bSuccess, FALSE);
nDelay= nDelay*1000;
ShowWindow(hwnd,SW_MINIMIZE);
SetTimer(hwnd, 1, nDelay, NULL);
break;
}
case IDC_STOP:
KillTimer(hwnd, 1);
break;
}
break;
case WM_TIMER:
{ HWND hwnd = (bWnd1 ? FindWindow("Notepad++",0) : FindWindow("Notepad",0));
SetForegroundWindow(hwnd);
bWnd1 = !bWnd1;
break;
}
case WM_CLOSE:
KillTimer(hwnd, 1);
EndDialog(hwnd, 0);
break;
default:
return FALSE;
This is just a sample code, you'll need to add error checking to it...
I'm trying to get this simple windows dialog to spawn but as soon as I start it it reports "Error x715" which means hDialog was not correctly made in the int WINAPI WinMain() function. It compiles just fine.
I'm working in Visual Studio 2010 and it's a "Visual C++ -> Empty Project" project.
This is the full main.cpp file, which is the only file in the project:
#include "windows.h"
#define DLG_MAIN 200 // ID for dialog
#define DLG_ICON 30000 // IDs for icons
#define DLG_ICON_S 30000
#define IDC_QUIT 1011 // ID for "quit"-button
#define IDC_INFO 2000 // ID for "info"-button
#define ID_TIMER 1 // ID for timer
#define IDC_STATIC -1 // ID for all labels
#define TIMER_INTERRUPT 500 // timer msg interval in msec
HINSTANCE TheInstance = 0; // instance handle of this program
// Our main function
void lalala(HWND hwnd)
{
/*Doesn't do anything yet.*/
}
// Windows passes messages to application windows to indicate "something"
// needs to be done
BOOL CALLBACK DialogProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_INITDIALOG:
// set the time to generate a timer message (WM_TIMER)
SetTimer(hwnd, ID_TIMER, TIMER_INTERRUPT, NULL);
return TRUE;
case WM_TIMER:
// a timer msg was received so call our main function!
lalala(hwnd);
return TRUE;
case WM_COMMAND:
switch (LOWORD(wParam))
{
case IDC_INFO:
// the info button on the window was pressed
MessageBox(hwnd, "<show some info>", "The Jonas Brothers are way better than Nick Cave ever was.", MB_OK);
return TRUE;
case IDC_QUIT:
// the quit button on the window was pressed
PostQuitMessage(0);
return TRUE;
}
return TRUE;
case WM_DESTROY:
// this app is about to be closed so kill the timer
KillTimer(hwnd, ID_TIMER);
PostQuitMessage(0);
return TRUE;
case WM_CLOSE:
// destroy the window
DestroyWindow (hwnd);
return TRUE;
}
return FALSE;
}
int WINAPI WinMain(HINSTANCE hInst, HINSTANCE hPrevInst, char * cmdParam, int cmdShow)
{
TheInstance = hInst;
HWND hDialog = 0;
hDialog = CreateDialog (hInst, MAKEINTRESOURCE (DLG_MAIN), 0, (DLGPROC)DialogProc);
if (!hDialog)
{
char buf [100];
wsprintf (buf, "Error x%x", GetLastError ());
MessageBox (0, buf, "CreateDialog", MB_ICONEXCLAMATION | MB_OK);
return 1;
}
HICON hIcon = LoadIcon (TheInstance, MAKEINTRESOURCE (DLG_ICON));
SendMessage (hDialog, WM_SETICON, WPARAM (TRUE), LPARAM (hIcon));
hIcon = LoadIcon (TheInstance, MAKEINTRESOURCE (DLG_ICON_S));
SendMessage (hDialog, WM_SETICON, WPARAM (FALSE), LPARAM (hIcon));
MSG msg;
int status;
while ((status = GetMessage (&msg, 0, 0, 0)) != 0)
{
if (status == -1) return -1;
if (!IsDialogMessage (hDialog, &msg))
{
TranslateMessage ( &msg );
DispatchMessage ( &msg );
}
}
return msg.wParam;
}
Can anyone tell me why it fails at hDialog = CreateDialog (hInst, MAKEINTRESOURCE (DLG_MAIN), 0, (DLGPROC)DialogProc); ?
Error 0x715 is ERROR_RESOURCE_NAME_NOT_FOUND, which you get when it fails to find the dialog with the name that you supplied, in the resource section. Instead of declaring the macros for each resources, just use #include "resource.h"
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.