Reputation: 156
When I create an ActiveX control based on a TPanel (with no added code) in Delphi 7, I am able to add this to a MFC C++ application and have it run fine.
When I take the exact same code and compile it in Delphi XE4 (and XE2), MFC throws an assertion. I confirmed that the only changes are in the dcu, ocx and res files.
The assertion is happening on ASSERT(wFlags == DISPATCH_METHOD);
in occsite.cpp (I included the source to this).
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;
}
The value of wFlags is DISPATCH_METHOD | DISPATCHPROPERTYGET.
Everything seems to work correctly after that (mouse events cause similar issues if you start in XE4, but D7 doesn't include them).
I tried this in both Visual Studio 2010 and Visual Studio 2012. In MFC, I am creating a new MFC dialog application, right clicking and selecting add ActiveX control. I am relatively new to MFC so I could be doing it wrong.
The host system in a Win 7 x64 system.
I can't leave the assertions in the code and really want to get this to work correctly so I can reuse a bunch of Delphi code in the future.
Any ideas what is happening or can anyone point me in a slightly better direction than head banging on a keyboard?
Update: 2013.09.18
Remy's answer below is correct, but here is some more information.
As of XE4, it seems the primary issues with this are the events sent back to the control host (i.e. OnClickEvent, OnMouseEnter, OnMouseLeave, OnConstrainedResize, OnCanResize or OnResizeEvent).
I found 3 possible solutions (will update again if I find anymore):
I used something like the following to surround where the event was being called:
FEvents <> nil then
try
SetDispatchByCallID(True);
FEvents.OnClick;
finally
SetDispatchByCallID(False);
end;
Upvotes: 3
Views: 641
Reputation: 595320
The only time that DISPATCH_METHOD
and DISPATCH_PROPERTYGET
are allowed to be specified together like that is if the caller is calling Invoke()
because the callee has both a method and a property that have the same name. In this case, COleControlSite::XEventSink
is allow itself to be invoked only as a method. This would be very simple to fix on the XEvenSink
side - simply change ASSERT(wFlags == DISPATCH_METHOD)
to ASSERT(wFlags & DISPATCH_METHOD)
instead. As for why Delphi would invoke XEventSink
this way, the only thing I can find in Delphi that uses those flags together is the following logic in the DispatchInvoke()
function of the ComObj
unit:
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;
However, that logic exists in DispatchInvoke()
going all the way back to Delphi 5, at least. But maybe ArgCount
was not 0 or Result
was nil
in earlier versions under the same conditions as your XE4 object is using? Hard to say for sure, as DispatchInvoke()
gets called in a bunch of different places throughout the RTL, so you would have to track through the call stack to find out who is actually calling XEventSink
and why the caller is specifying that particular flag combination.
Upvotes: 4