Почему мое сообщение 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 для расширения.

Как говорит Раймонд, то, что вы пытаетесь, довольно опасно, и вам было бы неплохо прислушаться к его советам.