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

标签 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的调用栈,是这样的:

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 所说,你正在尝试的事情相当危险,你最好听从他的建议。

关于c++ - 为什么我的 TB_INSERTBUTTON 消息导致 comctl32 抛出?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/21483214/

相关文章:

C++ 着色控制台输出

c - 如果设置了 SIF_DISABLENOSCROLL,SetScrollInfo() 是否会隐藏滚动条?

c++ - 将委托(delegate)作为回调传递给 native C++ API 调用

c# - 在 C++(win32 应用程序)中使用 C# 类时出现 EEFileLoadException

c++ - 有多少数据段内存与 C 中的代码段一起加载

c++ - 如何在arduino中从sd卡创建变量

c++ - MFC中如何显示指针的值?

multithreading - 托管 C++ 中的 C# 的 lock()

python - 如何通过 Python 使用 COM INetCfg 对象?

c++ - 将 ofstream 分配给 ostream