为什么我的tb_insertbutton消息导致comctl32投掷

Why is my TB_INSERTBUTTON message causing comctl32 to throw?

本文关键字:comctl32 投掷 消息 insertbutton 我的 tb 为什么      更新时间:2023-10-16

我正在尝试将一个附加按钮添加到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

它正在失败,因为您发送了一条包含在过程边界上的指针的消息。请注意您通过地址的事实:

LRESULT insertButtonResult = SendMessage(hWndToolbar, TB_INSERTBUTTON, 0, 
    (LPARAM)&buttonToAdd);

该最终参数是您的流程地址地址空间中的一个地址。但是收件人是一个不同的过程,您通过的地址在另一个过程的地址空间中没有任何意义。

例如,WM_SETTEXT的一些消息将其有效载荷通过系统编组为另一个过程。但是TB_INSERTBUTTON不属于该类别。TB_INSERTBUTTON的规则之一是,您通过的指针在拥有收件人窗口的过程中具有含义。

您可以使用VirtualAllocWriteProcessMemory等解决此问题,以在其他过程中分配并写入内存。

警告说,这是正确的任务。特别是这两个过程是否具有相同的位置非常重要。结构的布局在32和64位之间不同。确保您发送正确布局的最简单方法是以与目标过程相同的位置编译过程。

到目前为止,做类似此类操作的最简单方法是在目标过程中。如果您要编写插件,那么您就不必处理任何这些问题,也将能够使用正式支持的API进行扩展。

正如雷蒙德(Raymond)所说的那样,您正在尝试的是很危险,您会很好地了解他的建议。