为什么我的TB_INSERTBUTTON消息导致comctl32抛出?

时间:2014-01-31 15:05:29

标签: c++ winapi managed-c++

我正在尝试add an additional button into a toolbar in 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的callstack,看起来像这样:

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 个答案:

答案 0 :(得分:7)

它失败了,因为您发送的消息包含跨进程边界的指针。请注意您传递地址的事实:

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

最后一个参数是进程地址空间中的地址。但是收件人是一个不同的进程,你传递的地址在另一个进程的地址空间中没有任何意义。

某些消息(例如WM_SETTEXT)将使其有效负载由系统编组到其他进程。但TB_INSERTBUTTON不属于该类别。 TB_INSERTBUTTON的一个规则是你传递的指针在拥有收件人窗口的过程中具有意义。

您可以使用VirtualAllocWriteProcessMemory等来解决此问题,以便在该其他进程中分配和写入内存。

请注意,要做到这一点,这是一项有点困难的任务。特别重要的是,这两个过程是否具有相同的位数。结构的布局在32位和64位之间不同。确保发送正确布局的最简单方法是使用与目标进程相同的位数编译进程。

到目前为止,执行此类操作的最简单方法是进入目标进程。如果您要编写插件,那么您就不必处理任何这些问题,也可以使用官方支持的API进行扩展。

正如雷蒙德所说,你所尝试的是相当危险的,你应该听从他的建议。