Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

ATL inheritance mistake

226 views
Skip to first unread message

Barzo

unread,
Dec 9, 2009, 11:28:36 AM12/9/09
to
Hi,

in my project I have to code this situation:

AudioDevice : public IAudioDevice;
CAudioRecorder : public AudioDevice, public IAudioRecorder;
CAudioDecoder : public CAudioRecorder, public IAudioDecoder

The IDL file that represent this situation is:

/////////////////////////////////////////////////////////////////

[
object,
uuid(AB566C6C-AF54-11DE-B5BE-00A0D15E9B20),
oleautomation,
nonextensible,
helpstring("IAudioDevice Interface"),
pointer_default(unique)
]
interface IAudioDevice : IUnknown
{
.....
}


[
object,
uuid(AB566C6E-AF54-11DE-B5BE-00A0D15E9B20),
dual,
oleautomation,
nonextensible,
helpstring("Dual interface for AudioRecorder objects"),
pointer_default(unique)
]
interface IAudioRecorder : IDispatch
{
......
}


[
uuid(AB566C76-AF54-11DE-B5BE-00A0D15E9B20),
dual,
nonextensible,
helpstring("Dual interface for AudioDecoder objects"),
hidden
]
interface IAudioDecoder : IDispatch
{
[propget, id(40), helpstring("Returns a reference to a
IAudioRecorder interface.")]
HRESULT Recorder([out, retval] IAudioRecorder** pVal);
[propget, id(41), helpstring("Returns a reference to a IAudioDevice
interface.")]
HRESULT Device([out, retval] IAudioDevice** pVal);
};


[
uuid(AB566C66-AF54-11DE-B5BE-00A0D15E9B20),
version(1.1),
helpstring("Audio Library")
]
library AxAudioLib4
{
importlib("stdole2.tlb");

[
uuid(AB566C74-AF54-11DE-B5BE-00A0D15E9B20),
helpstring("AudioRecorder Class")
]
coclass AudioRecorder
{
interface IAudioDevice;
//interface IAudioRecorder;
[default] interface IAudioRecorder;
[default, source] dispinterface _IAudioRecorderEvents;
};

//-----------------------------------------------------------------------

[
uuid(AB566C77-AF54-11DE-B5BE-00A0D15E9B20),
helpstring("AudioDecoder Class")
]
coclass AudioDecoder
{
interface IAudioDevice;
interface IAudioRecorder;
[default] interface IAudioDecoder;
[default, source] dispinterface _IAudioDecoderEvents;
};

/////////////////////////////////////////////////////////////////


The AudioDevice class simply implements the IAudioDevice interface (it
is not an ATL derived class).
The CRecorder class ha the following signature:


/////////////////////////////////////////////////////////////////

class ATL_NO_VTABLE CAudioRecorder :
public CComObjectRootEx<CComSingleThreadModel>,
public CComCoClass<CAudioRecorder, &CLSID_AudioRecorder>,
public ISupportErrorInfo,
public IConnectionPointContainerImpl<CAudioRecorder>,
public CProxy_IAudioRecorderEvents<CAudioRecorder>,
public AudioDevice,
public IDispatchImpl<IAudioRecorder, &__uuidof(IAudioRecorder),
&LIBID_AxAudioLib4, /*wMajor =*/ 1, /*wMinor =*/ 0>
{
public:
CAudioRecorder();

DECLARE_REGISTRY_RESOURCEID(IDR_AUDIORECORDER)


BEGIN_COM_MAP(CAudioRecorder)
COM_INTERFACE_ENTRY(IAudioDevice)
COM_INTERFACE_ENTRY(IAudioRecorder)
COM_INTERFACE_ENTRY(IDispatch)
COM_INTERFACE_ENTRY(ISupportErrorInfo)
COM_INTERFACE_ENTRY(IConnectionPointContainer)
END_COM_MAP()

/////////////////////////////////////////////////////////////////


Now I have some difficult to define the CAudioDecoder class.
Since It has to implements IAudioDevice and IAudioRecorder I thought
to do something like this:

/////////////////////////////////////////////////////////////////
class ATL_NO_VTABLE CAudioDecoder :
public CComObjectRootEx<CComSingleThreadModel>,
public CComCoClass<CAudioDecoder, &CLSID_AudioDecoder>,
public ISupportErrorInfo,
public IConnectionPointContainerImpl<CAudioDecoder>,
public CProxy_IAudioDecoderEvents<CAudioDecoder>,
public AudioDevice,
public CAudioRecorder,
public IDispatchImpl<IAudioDecoder, &__uuidof(IAudioDecoder),
&LIBID_AxAudioLib4, /*wMajor =*/ 1, /*wMinor =*/ 0>
{
public:
CAudioDecoder();


DECLARE_REGISTRY_RESOURCEID(IDR_AUDIODECODER)


BEGIN_COM_MAP(CAudioDecoder)
COM_INTERFACE_ENTRY(IAudioDevice)
COM_INTERFACE_ENTRY(IAudioRecorder)
COM_INTERFACE_ENTRY(IAudioDecoder)
COM_INTERFACE_ENTRY2(IDispatch, IAudioDecoder)
COM_INTERFACE_ENTRY(ISupportErrorInfo)
COM_INTERFACE_ENTRY(IConnectionPointContainer)
END_COM_MAP()
/////////////////////////////////////////////////////////////////


But it doesn't works.
Could someone give me some suggestion?

Thanks in advance,
Daniele.

Barzo

unread,
Dec 9, 2009, 12:40:43 PM12/9/09
to
Like suggested in vcfaq#7, I moved the IAudioRecoder implementation to
a standalone one:

template <typename T>
class IAudioRecorderImpl : public IAudioRecorder, public AudioDevice
{..}


and in the CAudioRecorder class:


class ATL_NO_VTABLE CAudioRecorder :
public CComObjectRootEx<CComSingleThreadModel>,
public CComCoClass<CAudioRecorder, &CLSID_AudioRecorder>,
public ISupportErrorInfo,
public IConnectionPointContainerImpl<CAudioRecorder>,
public CProxy_IAudioRecorderEvents<CAudioRecorder>,

public IAudioRecorderImpl<CAudioRecorder>,


public IDispatchImpl<IAudioRecorder, &__uuidof(IAudioRecorder),
&LIBID_AxAudioLib4, /*wMajor =*/ 1, /*wMinor =*/ 0>
{
public:
CAudioRecorder(){};

DECLARE_REGISTRY_RESOURCEID(IDR_AUDIORECORDER)

DECLARE_NOT_AGGREGATABLE(CAudioRecorder)

BEGIN_COM_MAP(CAudioRecorder)
COM_INTERFACE_ENTRY(IAudioDevice)
COM_INTERFACE_ENTRY(IAudioRecorder) <<<< ERROR 1
COM_INTERFACE_ENTRY(IDispatch) <<<< ERROR 2
COM_INTERFACE_ENTRY(ISupportErrorInfo)
COM_INTERFACE_ENTRY(IConnectionPointContainer)
END_COM_MAP()


but building I get the following:

1>CAudioRecorder.cpp
1>d:\src\eurocom\audiolib_4\axaudiolib4\src\caudiorecorder.h(44) :
error C2594: 'static_cast' : ambiguous conversions from
'CAudioRecorder::_ComMapClass *' to 'IAudioRecorder *'
1>d:\src\eurocom\audiolib_4\axaudiolib4\src\caudiorecorder.h(46) :
error C2594: 'static_cast' : ambiguous conversions from
'CAudioRecorder::_ComMapClass *' to 'IDispatch *'

Any Idea?

Thanks,
Daniele.

Igor Tandetnik

unread,
Dec 9, 2009, 2:26:16 PM12/9/09
to
Barzo <dba...@gmail.com> wrote:
> in my project I have to code this situation:
>
> AudioDevice : public IAudioDevice;
> CAudioRecorder : public AudioDevice, public IAudioRecorder;
> CAudioDecoder : public CAudioRecorder, public IAudioDecoder

You have CAudioDecoder implement IAudioRecorder and IAudioDecoder, which are both derived from IDispatch. Generally, one object cannot implement two dual interfaces. For example, if the client queries for IDispatch, which of the two different IDispatch implementations are you going to give out?

I suggest you reconsider your design.
--
With best wishes,
Igor Tandetnik

With sufficient thrust, pigs fly just fine. However, this is not necessarily a good idea. It is hard to be sure where they are going to land, and it could be dangerous sitting under them as they fly overhead. -- RFC 1925

Barzo

unread,
Dec 10, 2009, 3:21:19 AM12/10/09
to
On 9 Dic, 20:26, "Igor Tandetnik" <itandet...@mvps.org> wrote:
>
> I suggest you reconsider your design.

Hi Igor!

I change a little my interface:

Now IAudioDecoder derives from IUnknown:
------------------------------------------------------
[
uuid(AB566C76-AF54-11DE-B5BE-00A0D15E9B20),
//dual,
oleautomation,
nonextensible,
helpstring("interface for AudioDecoder objects")
]
interface IAudioDecoder : IUnknown
------------------------------------------------------

so IAudioRecorder is the only interface that derives from IDispatch:

------------------------------------------------------


[
uuid(AB566C74-AF54-11DE-B5BE-00A0D15E9B20),
helpstring("AudioRecorder Class")
]
coclass AudioRecorder
{
interface IAudioDevice;

[default] interface IAudioRecorder;
[default, source] dispinterface _IAudioRecorderEvents;
};

[


uuid(AB566C77-AF54-11DE-B5BE-00A0D15E9B20),
helpstring("AudioDecoder Class")
]
coclass AudioDecoder
{
interface IAudioDevice;
interface IAudioRecorder;
[default] interface IAudioDecoder;
[default, source] dispinterface _IAudioDecoderEvents;
};

------------------------------------------------------

But, using the COM_INTERFACE_ENTRY2(IDispatch, IAudioRecoder) I get
the following error:


error C2594: 'static_cast' : ambiguous conversions from
'CAudioRecorder::_ComMapClass *' to 'IAudioRecorder *'

Why it is ambiguous?

Thanks,
Daniele.


Igor Tandetnik

unread,
Dec 10, 2009, 7:49:07 AM12/10/09
to
Barzo wrote:
> But, using the COM_INTERFACE_ENTRY2(IDispatch, IAudioRecoder) I get
> the following error:
>
>
> error C2594: 'static_cast' : ambiguous conversions from
> 'CAudioRecorder::_ComMapClass *' to 'IAudioRecorder *'
>
> Why it is ambiguous?

class ATL_NO_VTABLE CAudioRecorder :
public IAudioRecorderImpl<CAudioRecorder>,
public IDispatchImpl<IAudioRecorder, ...>

Your CAudioRecorder class derives from IAudioRecorder twice, once via IAudioRecorderImpl and again via IDispatchImpl. You want this instead:

class ATL_NO_VTABLE CAudioRecorder :
public IDispatchImpl<IAudioRecorderImpl<CAudioRecorder>, ...>

Oh, and since you no longer derive from IDispatch twice, you don't need COM_INTERFACE_ENTRY2 - plain old COM_INTERFACE_ENTRY would do.

Barzo

unread,
Dec 10, 2009, 8:33:30 AM12/10/09
to
On 10 Dic, 13:49, "Igor Tandetnik" <itandet...@mvps.org> wrote:
> Barzo wrote:
> > But, using the COM_INTERFACE_ENTRY2(IDispatch, IAudioRecoder) I get
> > the following error:
>
> > error C2594: 'static_cast' : ambiguous conversions from
> > 'CAudioRecorder::_ComMapClass *' to 'IAudioRecorder *'
>
> > Why it is ambiguous?
>
> class ATL_NO_VTABLE CAudioRecorder :
>   public IAudioRecorderImpl<CAudioRecorder>,
>   public IDispatchImpl<IAudioRecorder, ...>
>
> Your CAudioRecorder class derives from IAudioRecorder twice, once via IAudioRecorderImpl and again via IDispatchImpl. You want this instead:
>
> class ATL_NO_VTABLE CAudioRecorder :
>   public IDispatchImpl<IAudioRecorderImpl<CAudioRecorder>, ...>
>
> Oh, and since you no longer derive from IDispatch twice, you don't need COM_INTERFACE_ENTRY2 - plain old COM_INTERFACE_ENTRY would do.

Thanks a lot! It works!
Daniele.

Barzo

unread,
Dec 29, 2009, 5:33:34 AM12/29/09
to
On 10 Dic, 14:33, Barzo <dba...@gmail.com> wrote:
>
> Thanks a lot! It works!
> Daniele.

Hi,

regarding this thread I have some trouble when I use my DLL with VB6.
To summarize I have the following IDL:

//////////////////////////////////////////////////////////////
[
object,
uuid(AB566C6C-AF54-11DE-B5BE-00A0D15E9B20),
oleautomation,
nonextensible,
helpstring("IAudioDevice Interface"),
pointer_default(unique)
]
interface IAudioDevice : IUnknown
{
....
}

//-----------------------------------------------------------------------

[
object,
uuid(AB566C6E-AF54-11DE-B5BE-00A0D15E9B20),
dual,
oleautomation,
nonextensible,
helpstring("Dual interface for AudioRecorder objects"),
pointer_default(unique)
]
interface IAudioRecorder : IDispatch
{
...
}

//-----------------------------------------------------------------------

[
uuid(AB566C76-AF54-11DE-B5BE-00A0D15E9B20),
oleautomation,
nonextensible,
helpstring("IAudioDecoder Interface")
]
interface IAudioDecoder : IUnknown


{
[propget, id(40), helpstring("Returns a reference to a
IAudioRecorder interface.")]
HRESULT Recorder([out, retval] IAudioRecorder** pVal);

...
}


//-----------------------------------------------------------------------

library AxAudioLib4
{


[
uuid(AB566C74-AF54-11DE-B5BE-00A0D15E9B20),
helpstring("AudioRecorder Class")
]
coclass AudioRecorder
{
interface IAudioDevice;
[default] interface IAudioRecorder;
[default, source] dispinterface _IAudioRecorderEvents;
};

//-----------------------------------------------------------------------

[
uuid(AB566C77-AF54-11DE-B5BE-00A0D15E9B20),
helpstring("AudioDecoder Class")
]
coclass AudioDecoder
{
interface IAudioDevice;
interface IAudioRecorder;
[default] interface IAudioDecoder;
[default, source] dispinterface _IAudioDecoderEvents;
};

}
//////////////////////////////////////////////////////////////

The get_Recorder method of IAudioDecoder is:

STDMETHODIMP CAudioDecoder::get_Recorder(IAudioRecorder** pVal)
{
if (pVal) {
return this->QueryInterface(IID_IAudioRecorder,
reinterpret_cast<void**>(pVal));
}
else
return E_POINTER;
};


Now, in my VB6 code I use an EventCollection to store objects.
If I wrote this:

Dim Recorder As IAudioRecorder
Set Recorder = New AxAudioLib4.AudioRecorder
RecColl.Add Recorder, "KEY"

It works with no problem, but if I do this:

Dim withevents Decoder As AudioDecoder
Dim Recorder As IAudioRecorder

Set Decoder = New AxAudioLib4.AudioDecoder
Set Recorder = Decoder.Recorder
RecColl.Add Recorder, "KEY"
Debug.Print RecColl("KEY").object is Nothing

I get an error "Type mismatch" reading the "object" property.
This property is implemented like:

Public Property Get Object() As Object
Set Object = m_oObject
End Property

where m_oObject is an olelib.IUnknown and is created in the
RecColl.Add method:

Public Sub Add( _
Item As Object, _
Optional SourceIID As String, _
Optional Key As Variant)

Dim oObjectInfo As ObjectInfo
Dim oCPC As IConnectionPointContainer
Dim oEnm As IEnumConnectionPoints
Dim oCP As IConnectionPoint
Dim oUnk As olelib.IUnknown
Dim tIID As olelib.UUID
Dim lCookie As Long

' Get the IConnectionPointContainer interface
Set oCPC = Item

If LenB(SourceIID) = 0 Then
' Get connection point enumerator
Set oEnm = oCPC.EnumConnectionPoints
' Get the first connection point
oEnm.Next 1, oCP
' Get the IID
oCP.GetConnectionInterface tIID
Else
' Convert from string to UUID
olelib.CLSIDFromString SourceIID, tIID
' Get the connection point
Set oCP = oCPC.FindConnectionPoint(tIID)
End If

' Create the ObjectInfo object
Set oObjectInfo = New ObjectInfo
' Create the event sink object
Set oUnk = CreateEventSinkObj(tIID, oObjectInfo, Me)
' Connect the sink object with
' the source object
lCookie = oCP.Advise(oUnk)

On Error GoTo Disconnect

' Add the object to the collection
m_oCollection.Add oObjectInfo, Key
' Initialize the ObjectInfo object
oObjectInfo.frInitialize Key, _
m_oCollection.Count, _
lCookie, _
Item, _
tIID

Exit Sub

Disconnect:
oCP.Unadvise lCookie
Err.Raise Err.Number, , Err.Description
End Sub


And where the CreateEventSinkObj function is:

Public Function CreateEventSinkObj( _
EventIID As olelib.UUID, _
ByVal ObjInfo As ObjectInfo, _
ByVal Coll As EventCollection) As Object

Dim lEventSinkPtr As Long
Dim lOldProt As Long
Dim EventSink As EventSinkData

With EventSink
' Set the initial reference count to 1
.RefCount = 1
' Save the ID of the events interface
.EventIID = EventIID
' Save a pointer to the parent collection
MoveMemory .EvntColl, Coll, 4&
' Store the object info
MoveMemory .ObjInf, ObjInfo, 4&
' Set the vtable
.lvtablePtr = VarPtr(vtable(0))
End With

' Allocate memory for the object
lEventSinkPtr = GlobalAlloc(GPTR, LenB(EventSink))

If lEventSinkPtr Then
' Copy the structure to the allocated memory
MoveMemory ByVal lEventSinkPtr, EventSink, LenB(EventSink)
' Copy the pointer to the return value
MoveMemory CreateEventSinkObj, lEventSinkPtr, 4
Else
' Raise the error
Err.Raise 7, "CreateEventSinkObj"
End If

End Function

Could someone help me?
Tnx,
Daniele.

Igor Tandetnik

unread,
Dec 29, 2009, 8:23:41 AM12/29/09
to

Barzo wrote:
> Now, in my VB6 code I use an EventCollection to store objects.
>
> Set Decoder = New AxAudioLib4.AudioDecoder
> Set Recorder = Decoder.Recorder
> RecColl.Add Recorder, "KEY"
> Debug.Print RecColl("KEY").object is Nothing
>
> I get an error "Type mismatch" reading the "object" property.

I'm afraid I don't know VB well enough to help you here. I don't see anything obviously wrong in your C++ code. Consider asking in microsoft.public.vb.com .

Barzo

unread,
Dec 29, 2009, 9:27:25 AM12/29/09
to
On 29 Dic, 14:23, "Igor Tandetnik" <itandet...@mvps.org> wrote:
>
> I'm afraid I don't know VB well enough to help you here. I don't see anything obviously wrong in your C++ code. Consider asking in microsoft.public.vb.com .

Thanks for you intresting Igor.
I'm doing some tests, unfortunatelly I'm not an expert on ATL..
I'm thinking that the problem could be because I wanna use events from
an AudioDecoder object like it was an AudioRecorder.

So I thought: how can I use and AudioDecoder like it was an
AudioRecorder?
I try to change the AudioDecoder coclass to implement also the
IAudioRecorderEvents...

coclass AudioDecoder
{
interface IAudioDevice;
interface IAudioRecorder;
[default] interface IAudioDecoder;

[source] dispinterface _IAudioRecorderEvents;
[default, source] dispinterface _IAudioDecoderEvents;
};

But, now? How can I code for this new design? I try to add a new
CONNECTION_POINT_ENTRY:

BEGIN_CONNECTION_POINT_MAP(CAudioDecoder)
CONNECTION_POINT_ENTRY(__uuidof(_IAudioDecoderEvents))
CONNECTION_POINT_ENTRY(__uuidof(_IAudioRecorderEvents))
END_CONNECTION_POINT_MAP()

But I get:

1>\src\audiodecoder.h(71) : error C2440: 'static_cast' : cannot
convert from 'CAudioDecoder::_atl_conn_classtype *' to
'ATL::_ICPLocator<piid> *'
1> with
1> [
1> piid=& DIID__IAudioRecorderEvents
1> ]
1> Types pointed to are unrelated; conversion requires
reinterpret_cast, C-style cast or function-style cast

Have you some suggestions?
BR,
Daniele.

Igor Tandetnik

unread,
Dec 29, 2009, 10:03:21 AM12/29/09
to
Barzo wrote:
> I try to change the AudioDecoder coclass to implement also the
> IAudioRecorderEvents...
>
> coclass AudioDecoder
> {
> interface IAudioDevice;
> interface IAudioRecorder;
> [default] interface IAudioDecoder;
>
> [source] dispinterface _IAudioRecorderEvents;
> [default, source] dispinterface _IAudioDecoderEvents;
> };
>
> But, now? How can I code for this new design? I try to add a new
> CONNECTION_POINT_ENTRY:
>
> BEGIN_CONNECTION_POINT_MAP(CAudioDecoder)
> CONNECTION_POINT_ENTRY(__uuidof(_IAudioDecoderEvents))
> CONNECTION_POINT_ENTRY(__uuidof(_IAudioRecorderEvents))
> END_CONNECTION_POINT_MAP()

You also need to derive from two proxies (should be named something like _IAudioDecoderEventsCP and _IAudioRecorderEventsCP). You probably already derive from one - just add the other.

Barzo

unread,
Dec 29, 2009, 10:25:36 AM12/29/09
to
On 29 Dic, 16:03, "Igor Tandetnik" <itandet...@mvps.org> wrote:
>
> > BEGIN_CONNECTION_POINT_MAP(CAudioDecoder)
> >  CONNECTION_POINT_ENTRY(__uuidof(_IAudioDecoderEvents))
> >  CONNECTION_POINT_ENTRY(__uuidof(_IAudioRecorderEvents))
> > END_CONNECTION_POINT_MAP()
>
> You also need to derive from two proxies (should be named something like _IAudioDecoderEventsCP and _IAudioRecorderEventsCP). You probably already derive from one - just add the other.

Yes, it seems to work!

Sinche two events has the same signature, I make them virtual in the
RecorderProxy and I change the DecoderProxy deriving from it:


#include "_IAudioRecorderEvents_CP.h"

template <class T>
class CProxy_IAudioDecoderEvents : public IConnectionPointImpl<
T,
&__uuidof
( _IAudioDecoderEvents ),
CComDynamicUnkArray_GIT>,
public
CProxy_IAudioRecorderEvents<T>
{
HRESULT Fire_OnStatusChanged( E_DEVICE_STATE new_state, short
channel)..
HRESULT Fire_OnDebugMessage(BSTR msg)...
}

Thanks!
Daniele.

0 new messages