Заменить пункт меню на свой через HANDLE

Ответить

Код подтверждения
Введите код в точности так, как вы его видите. Регистр символов не имеет значения.

BBCode ВКЛЮЧЁН
[img] ВКЛЮЧЁН
[url] ВКЛЮЧЁН
Смайлики ОТКЛЮЧЕНЫ

Обзор темы
   

Развернуть Обзор темы: Заменить пункт меню на свой через HANDLE

char_ser » 24 ноя 2004, 16:33

В общем, сделаю наверное так:
Есть такая фигня "Detours": http://research.microsoft.com/sn/detours/
Она позволяет перехватывать любые Win32 API функции, она специально для этого и разработана. (COOL)
Я перехватываю TrackPopupMenu(), в параметре hMenu у нее убиваю нужный мне пункт и передаю управление оригинальной TrackPopupMenu() :)

Eugie » 24 ноя 2004, 16:17

Речь об обычном контекстном меню, в эмулированном, ессно, все может быть

Romeo » 24 ноя 2004, 14:54

В последнем случае нужно ловить WM_COMMAND, который отсылает сэмулированный айтем главного меню. Ведь если разработчики сэмулировали выпадение подменю, то соответствующий айтем уже не popup, а обычный, как следствие он шлёт WM_COMMAND. Именно в command xэндлере этого айтема происходит вызов TrackPopupMenu.

Eugie » 24 ноя 2004, 13:45

Видимо, вопрос естественным образом закрылся, но на всякий случай: 1) если контекстное меню системное (system menu, отображается при клике в левом вернем углу заголовка), оно посылает сообщения WM_SYSCOMMAND, их тоже можно ловить; 2) если оно пользоватьское - все зависит от способа вызова - указан или нет флажок TPM_NONOTIFY в TrackPopupMenu (в последнем случае, действительно, ловить нечего)

char_ser » 22 ноя 2004, 15:33

А что делать в случае с контекстным меню? Ведь оно не посылает сообщения WM_COMMAND...

char_ser » 22 ноя 2004, 14:14

ОГРОМНОЕ СПАСИБО!!!

Eugie » 22 ноя 2004, 13:39

Вот пример, как это можно сделать (код для dll: когда она загружена, перехватывается обращение к пункту меню с именем "&About ...", вместо него вызывается MessageBox):

Код: Выделить всё

HHOOK hkMsg;
int nTargetCmdID;

// Searching for the main window of the current app.
BOOL CALLBACK EnumWindowsProc(
  HWND hwnd,      // handle to parent window
  LPARAM lParam   // application-defined value
)
{
	DWORD dwPID, dwTID = GetWindowThreadProcessId(hwnd, &dwPID);
	DWORD dwCurPID = GetCurrentProcessId();
	BOOL bProceed = dwCurPID != dwPID;
	if (!bProceed) 
		*(HWND*)lParam = hwnd;
	return bProceed;
}


HWND GetAppTopWindow()
{
	HWND hwnd = NULL;
	EnumWindows(EnumWindowsProc, (LPARAM)&hwnd);
	return hwnd;
}

int FindMenuItemID(HMENU hmMenu, LPCTSTR lpszMenuItemText)
{
	int id = -1;
	TCHAR buf[100];
	MENUITEMINFO mii;
	mii.cbSize = sizeof(MENUITEMINFO);
	mii.fMask = MIIM_STRING;
	mii.dwTypeData = buf;
	mii.cch = sizeof(buf)/sizeof(TCHAR);

	for(int i = 0; i < GetMenuItemCount(hmMenu); i++)
	{
		GetMenuItemInfo(hmMenu, i, TRUE, &mii);
		if (strcmp(mii.dwTypeData, lpszMenuItemText) == 0)
			return GetMenuItemID(hmMenu, i);
	  mii.fMask = MIIM_SUBMENU;
		GetMenuItemInfo(hmMenu, i, TRUE, &mii);
		if (mii.hSubMenu)
			id = FindMenuItemID(mii.hSubMenu, lpszMenuItemText);
	}

	return id;
}

int GetMenuCmdMsgID(LPCTSTR lpszMenuItemText)
{
	HWND hwnd = GetAppTopWindow();
	HMENU hmenu = GetMenu(hwnd);
	return FindMenuItemID(hmenu, lpszMenuItemText);
}

LRESULT CALLBACK GetMsgProc(
  int code,       // hook code
  WPARAM wParam,  // removal option
  LPARAM lParam   // message
)
{
	if (code == HC_ACTION)
	{
		MSG* pmsg = (MSG*)lParam;
		wParam = PM_REMOVE;

	  nTargetCmdID = GetMenuCmdMsgID("&About ...");
		if(pmsg->hwnd == GetAppTopWindow() &&
		   pmsg->message == WM_COMMAND &&
		   HIWORD(pmsg->wParam) == 0 &&
		   LOWORD(pmsg->wParam) == nTargetCmdID)
		{
			pmsg->wParam = -1; // to prevent passing to standard handler
			MessageBox(NULL, "Target menu command called.", "Info", MB_OK);
			return 0;
		}

	}
	return CallNextHookEx(hkMsg, code, wParam, lParam);
}

void InstallHook()
{
	hkMsg = SetWindowsHookEx(WH_GETMESSAGE, GetMsgProc, NULL, GetCurrentThreadId()); 
}

void UninstallHook()
{
	UnhookWindowsHookEx(hkMsg); 
}

BOOL APIENTRY DllMain( HANDLE hModule, 
                       DWORD  ul_reason_for_call, 
                       LPVOID lpReserved
					 )
{
    switch (ul_reason_for_call)
	{
		case DLL_PROCESS_ATTACH:
			InstallHook();
			break;
		case DLL_PROCESS_DETACH:
			UninstallHook();
			break;
		case DLL_THREAD_ATTACH:
			break;
		case DLL_THREAD_DETACH:
			break;
    }
    return TRUE;
}

char_ser » 22 ноя 2004, 12:34

Теперь другая проблема вылезла :(
В общем заменил я пункт меню на свой, а вот чтобы теперь к нему добавить вызываемую функцию по этому пункту меню...
Ведь существует карта сообщений у программы. Я ее не знаю.. Мой новый элемент меню имеет свой ID. Как мне изменить эту карту так, чтобы она по этому ID вызывала мою функцию??? Как к этой карте получить доступ?

Eugie » 19 ноя 2004, 16:23

2Eugie: мне кажется, что это реально, т.к. этот процесс пользуется моей dll -> я нахожусь тоже в этом процессе -> подменить обработчик элемента меню несложно
В этом случае действительно можно: при загрузке dll ставить хук типа WH_GETMESSAGE и ловить сообщение WM_COMMAND с заданным id. Ессно, при выгрузке dll хук нужно удалять.

Romeo » 19 ноя 2004, 15:02

Так бы оно и было, если бы DLL была COM-овской. Что-то у меня необъяснимое ощущение, что DLL-ка загружается комплексом, как плагин. Т.е. делает LoadLibrary, потом GetProcAddress, вызывает полученный метод, а потом делает FreeLibrary. Это, конечно, утрированный пример, но технология, думаю, именно такова. А, поскольку, меняя стандартный обработчик на свой, dllcounter ты не изменяешь, то при вызове FreeLibrary произойдёт выгрузка и никаких других вариантов быть просто не может.

Вернуться к началу