当我在Delphi 7中创建基于TPanel(没有添加代码)的ActiveX控件时,我可以将它添加到MFC C ++应用程序并使其运行正常。
当我使用完全相同的代码并在Delphi XE4(和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_METHOD | DISPATCHPROPERTYGET。
之后一切似乎都能正常工作(如果从XE4开始,鼠标事件会导致类似问题,但D7不包含它们)。
我在Visual Studio 2010和Visual Studio 2012中都尝试过这种方法。在MFC中,我正在创建一个新的MFC对话框应用程序,右键单击并选择添加ActiveX控件。我对MFC比较陌生,所以我做错了。
Win 7 x64系统中的主机系统。
我不能在代码中留下断言,并且真的希望这个能够正常工作,以便将来可以重用一堆Delphi代码。
任何有关正在发生的事情的想法,或者任何人都可以指出我的方向略好于敲击键盘?
更新:2013.09.18
下面的Remy答案是正确的,但这里有更多信息。
从XE4开始,似乎主要问题是发送回控制主机的事件(即OnClickEvent,OnMouseEnter,OnMouseLeave,OnConstrainedResize,OnCanResize或OnResizeEvent)。
我找到了3个可能的解决方案(如果我找到的话会再次更新):
我使用类似下面的内容来围绕调用事件的位置:
FEvents <> nil then
try
SetDispatchByCallID(True);
FEvents.OnClick;
finally
SetDispatchByCallID(False);
end;
答案 0 :(得分:3)
唯一允许同时指定DISPATCH_METHOD
和DISPATCH_PROPERTYGET
的时间是调用者正在调用Invoke()
,因为被调用者同时具有方法和属性一样的名字。在这种情况下,COleControlSite::XEventSink
仅允许自己作为方法调用。这可以非常简单地修复XEvenSink
方 - 只需将ASSERT(wFlags == DISPATCH_METHOD)
更改为ASSERT(wFlags & DISPATCH_METHOD)
即可。至于为什么Delphi会以这种方式调用XEventSink
,我在Delphi中唯一能够一起使用这些标志的是DispatchInvoke()
单元的ComObj
函数中的以下逻辑:
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()
中的逻辑至少会一直回到Delphi 5。但是在与XE4对象使用相同的条件下,早期版本中ArgCount
可能不是0或Result
是nil
吗?很难确定,因为DispatchInvoke()
在整个RTL中的许多不同位置被调用,所以你必须通过调用堆栈来跟踪以找出实际调用XEventSink
的人以及调用者的原因指定特定的标志组合。