Почему мое сообщение TB_INSERTBUTTON вызывает вызов comctl32?
Я пытаюсь добавить дополнительную кнопку в панель инструментов в Internet Explorer.
Я предполагал, что реализация будет прямой, и в настоящее время я использую этот код:
TBBUTTON buttonToAdd;
ZeroMemory( &buttonToAdd, sizeof( TBBUTTON ) );
buttonToAdd.iBitmap = 1;
buttonToAdd.idCommand = 1;
buttonToAdd.fsState = TBSTATE_ENABLED;
buttonToAdd.fsStyle = BTNS_BUTTON|BTNS_AUTOSIZE;
LRESULT insertButtonResult = SendMessage( hWndToolbar, TB_INSERTBUTTON, 0, (LPARAM)&buttonToAdd );
Когда сообщение отправлено, Internet Explorer столкнется 90% времени (10% времени, я получаю несколько сломанную кнопку на панели инструментов) со следующим исключением:
Unhandled exception at 0x000007FEFB97DDFA (comctl32.dll) in iexplore.exe: 0xC000041D: An unhandled exception was encountered during a user callback.
Учитывая, что результаты несовместимы, я предположил, что проблема с макетом памяти. Поэтому я попытался отправить TB_INSERTBUTTONA
вместо (мое приложение по умолчанию - TB_INSERTBUTTONW
), но это не влияет на проблему.
Я также пробовал как 32, так и 64 сборки моего приложения, оба имеют одинаковый результат.
Я посмотрел на столбец iexplore.exe
, который выглядит так:
comctl32.dll!CToolbar::TBInputStruct(struct _TBBUTTONDATA *,struct _TBBUTTON const *) Unknown
comctl32.dll!CToolbar::TBInsertButtons(unsigned int,unsigned int,struct _TBBUTTON *,int) Unknown
comctl32.dll!CToolbar::ToolbarWndProc(struct HWND__ *,unsigned int,unsigned __int64,__int64) Unknown
comctl32.dll!CToolbar::s_ToolbarWndProc(struct HWND__ *,unsigned int,unsigned __int64,__int64) Unknown
user32.dll!UserCallWinProcCheckWow() Unknown
user32.dll!DispatchClientMessage() Unknown
user32.dll!__fnDWORD() Unknown
ntdll.dll!KiUserCallbackDispatcherContinue() Unknown
user32.dll!NtUserPeekMessage() Unknown
user32.dll!PeekMessageW() Unknown
...
Я нашел это несколько интересным, потому что я предполагаю, что метод в верхней копии данных из моей входной структуры во внутреннюю структуру и что-то пойдет не так. Но что не так с моей структурой входных данных?
Исходный код доступен в GitHub по адресу: https://github.com/oliversalzburg/ie-button
Ответы
Ответ 1
Ошибка, потому что вы отправляете сообщение, содержащее указатель на границе процесса. Обратите внимание на то, что вы передаете адрес:
LRESULT insertButtonResult = SendMessage(hWndToolbar, TB_INSERTBUTTON, 0,
(LPARAM)&buttonToAdd);
Этот конечный параметр - это адрес в адресном пространстве процессов. Но получатель - это другой процесс, и адрес, который вы передаете, не имеет смысла в адресном пространстве другого процесса.
Некоторые сообщения, например WM_SETTEXT
, будут иметь свою полезную нагрузку, сопоставленную с другим процессом системой. Но TB_INSERTBUTTON
не попадает в эту категорию. Одним из правил TB_INSERTBUTTON
является то, что указатель, который вы передаете, имеет смысл в процессе, который имеет окно получателя.
Вы можете решить эту проблему, используя VirtualAlloc
, WriteProcessMemory
и т.д., чтобы выделить и записать в память в этом другом процессе.
Будьте предупреждены, что это довольно сложная задача, чтобы получить право. В частности, очень важно, имеют ли эти два процесса одинаковые биты. Макет структуры отличается от 32 до 64 бит. Самый простой способ убедиться, что вы отправляете правильный макет, - это скомпилировать ваш процесс с той же биттой, что и целевой процесс.
Самый простой способ сделать что-то вроде этого - находиться внутри целевого процесса. Если вы хотите написать плагин, вам не придется разбираться ни с одной из этих проблем, а также сможет использовать официально поддерживаемые API для расширения.
Как говорит Раймонд, то, что вы пытаетесь, довольно опасно, и вам было бы неплохо прислушаться к его советам.