MFC应用程序中Delphi 7和Delphi XE4之间的ActiveX差异
ActiveX differences between Delphi 7 and Delphi XE4 in an MFC application
当我在Delphi7中基于TPanel(没有添加代码)创建ActiveX控件时,我可以将其添加到MFC C++应用程序中,并使其正常运行。
当我使用完全相同的代码并在DelphiXE4(和XE2)中编译它时,MFC会抛出一个断言。我确认了唯一的更改是在dcu、ocx和res文件中。
断言发生在occsite.cpp中的ASSERT(wFlags == DISPATCH_METHOD);
上(我包含了它的来源)。
STDMETHODIMP COleControlSite::XEventSink::Invoke(
DISPID dispid, REFIID, LCID, unsigned short wFlags,
DISPPARAMS* pDispParams, VARIANT* pvarResult,
EXCEPINFO* pExcepInfo, unsigned int* puArgError)
{
UNUSED(wFlags);
METHOD_PROLOGUE_EX(COleControlSite, EventSink)
ASSERT(pThis->m_pCtrlCont != NULL);
ASSERT(pThis->m_pCtrlCont->m_pWnd != NULL);
ASSERT(wFlags == DISPATCH_METHOD);
AFX_EVENT event(AFX_EVENT::event, dispid, pDispParams, pExcepInfo,
puArgError);
pThis->OnEvent(&event);
if (pvarResult != NULL)
::VariantClear(pvarResult);
return event.m_hResult;
}
wFlags的值为DISPATCH_MMETHOD|DISPATCHROPERTYGET。
在那之后,一切似乎都正常工作(如果您从XE4开始,鼠标事件会导致类似的问题,但D7不包括它们)。
我在Visual Studio 2010和Visual Studio 2012中都尝试过这种方法。在MFC中,我正在创建一个新的MFC对话框应用程序,右键单击并选择添加ActiveX控件。我是MFC的新手,所以我可能做错了。
Win 7 x64系统中的主机系统。
我不能把断言留在代码中,我真的想让它正常工作,这样我就可以在未来重用一堆Delphi代码。
你知道发生了什么吗?或者有人能给我一个比敲击键盘更好的方向吗?
更新:2013.09.18
雷米下面的答案是正确的,但这里有更多信息。
从XE4开始,这方面的主要问题似乎是发送回控制主机的事件(即OnClickEvent、OnMouseEnter、OnMousLeave、OnConstrainedResize、OnCanResize或OnResizeEvent)。
我找到了3种可能的解决方案(如果我找到了,将再次更新):
- 注释掉调用这些事件的代码(我没有说它们是好的解决方案)
- 注释掉ComObjs.DispatchInvoke中导致设置此项的行
- 修改ComObj以具有备用DispatchInvoke和DispCallByID
- 备用DispCallID需要调用备用DispatchInvoke
- 备用DispatchInvoke需要删除更改标志的代码
- 事件使用全局变量DispCallByIDProc时,需要将其设置为备用DispCallByID过程
- DispCallByIDProc需要在设置为备用之后进行设置(我将其作为备用DispCallByID中的第一行进行设置)
我用下面这样的东西来围绕事件的调用位置:
FEvents <> nil then
try
SetDispatchByCallID(True);
FEvents.OnClick;
finally
SetDispatchByCallID(False);
end;
唯一允许像这样同时指定DISPATCH_METHOD
和DISPATCH_PROPERTYGET
的时间是,如果调用者正在调用Invoke()
,因为被调用者具有相同名称的方法和属性。在这种情况下,COleControlSite::XEventSink
只允许将其本身作为一个方法调用。这在XEvenSink
侧非常简单——只需将ASSERT(wFlags == DISPATCH_METHOD)
更改为ASSERT(wFlags & DISPATCH_METHOD)
即可。至于为什么Delphi会以这种方式调用XEventSink
,我在Delphi中唯一能找到的将这些标志一起使用的是ComObj
单元的DispatchInvoke()
函数中的以下逻辑:
procedure DispatchInvoke(const Dispatch: IDispatch; CallDesc: PCallDesc;
DispIDs: PDispIDList; Params: Pointer; Result: PVariant);
var
..., InvKind: Integer;
...
begin
...
InvKind := CallDesc^.CallType;
...
if InvKind = DISPATCH_PROPERTYPUT then
begin
...
end
else if (InvKind = DISPATCH_METHOD) and (CallDesc^.ArgCount = 0) and (Result <> nil) then
InvKind := DISPATCH_METHOD or DISPATCH_PROPERTYGET; // <-- HERE
...
Status := Dispatch.Invoke(..., InvKind, ..., Result, ...);
...
end;
然而,这种逻辑存在于DispatchInvoke()
中,至少可以追溯到Delphi5。但是,在与XE4对象使用的条件相同的条件下,早期版本中的ArgCount
可能不是0,或者Result
是nil
?很难说,因为DispatchInvoke()
在整个RTL的许多不同位置被调用,所以您必须跟踪调用堆栈,以找出谁真正在调用XEventSink
,以及调用者为什么指定特定的标志组合。
- C++中std::resize(n)和std::shrink_to_fit之间的区别
- int(c) 和 c-'0' 之间的区别。C++
- 在cuda线程之间共享大量常量数据
- 在c代码之间共享数据的最佳方式
- Mix_Init和Mix_OpenAudio SDL之间的区别是什么
- C++ 使用 assign 函数的字符串与直接使用 '=' 更改值的字符串之间的区别
- VSOMEIP-2个设备之间的通信(TCP/UDP)不工作
- std::atomic和std::condition_variable wait,notify_*方法之间的区别
- 大小相等但成员数量不同的结构之间的性能差异
- 类与私有变量的其他类之间的线程安全性
- 如何在cpp文件之间切换窗口?在Qt中
- 线程之间的布尔停止信号
- 我是C++编程的新手,这些代码之间有什么区别,我应该使用哪一个
- 在 const 函数中通过引用和指针返回之间的区别
- 我想知道长双倍和双倍之间的区别
- 如何防止clang格式在流运算符调用之间添加换行符<<
- 如何将unicode字符串从C++传递到delphi
- 在两台机器之间进行时间戳的最佳c++chrono函数是什么
- Delphi链接器和C++链接器之间的区别
- MFC应用程序中Delphi 7和Delphi XE4之间的ActiveX差异