在MFC应用程序中Delphi 7和Delphi XE4之间的ActiveX差异

时间:2013-08-15 21:07:32

标签: c++ visual-studio delphi mfc activex

当我在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个可能的解决方案(如果我找到的话会再次更新):

  1. 注释掉调用这些事件的代码(我没有说它们是很好的解决方案)。
  2. 注释掉ComObjs.DispatchInvoke中的行,导致设置它。
  3. 修改ComObjs以使用备用DispatchInvoke和DispCallByID
    • 备用DispCallID需要调用备用DispatchInvoke。
    • 备用DispatchInvoke需要更改标志的代码
    • 全局变量DispCallByIDProc在被事件使用时需要设置为备用DispCallByID过程。
    • DispCallByIDProc需要在之后被设置为替代(我将其作为备用DispCallByID中的第一行)进行设置。
  4. 我使用类似下面的内容来围绕调用事件的位置:

    FEvents <> nil then
    try
        SetDispatchByCallID(True);
        FEvents.OnClick;
    finally
        SetDispatchByCallID(False);
    end;
    

1 个答案:

答案 0 :(得分:3)

唯一允许同时指定DISPATCH_METHODDISPATCH_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或Resultnil吗?很难确定,因为DispatchInvoke()在整个RTL中的许多不同位置被调用,所以你必须通过调用堆栈来跟踪以找出实际调用XEventSink的人以及调用者的原因指定特定的标志组合。