I'm attempting to create a concrete instance of the IAudioEvents COM interface (available in Vista and later). This is my first foray into COM programming, so I'm probably just doing something stupid here. Anyway, the following code fails to compile with "C2259: 'AudioEndpointVolumeNotifierImpl' : cannot instantiate abstract class".
Class Definiton (AudioEndpointVolumeNotifierImpl.h):
class AudioEndpointVolumeNotifierImpl : public IAudioSessionEvents
{
private:
LONG _cRef;
public:
AudioEndpointVolumeNotifierImpl() : _cRef(1){}
~AudioEndpointVolumeNotifierImpl(){}
HRESULT STDMETHODCALLTYPE OnSimpleVolumeChanged(float NewVolume, BOOL NewMute,LPCGUID EventContext);
HRESULT STDMETHODCALLTYPE OnChannelVolumeChanged(DWORD ChannelCount, float NewChannelVolumeArray[], DWORD ChangedChannel, LPCGUID EventContext){return S_OK;}
HRESULT STDMETHODCALLTYPE OnDisplayNameChanged(LPCWSTR NewDisplayName, LPCGUID EventContext){return S_OK;}
HRESULT STDMETHODCALLTYPE OnGroupingParamChanged(LPCGUID NewGroupingParam, LPCGUID EventContext){return S_OK;}
HRESULT STDMETHODCALLTYPE OnIconPathChanged(LPWCHAR NewIconPath, LPCGUID EventContext){return S_OK;}
HRESULT STDMETHODCALLTYPE OnSessionDisconnected(AudioSessionDisconnectReason DisconnectReason){return S_OK;}
HRESULT STDMETHODCALLTYPE OnStateChanged(AudioSessionState NewState){ return S_OK; }
ULONG STDMETHODCALLTYPE AddRef()
{
return InterlockedIncrement(&_cRef);
}
ULONG STDMETHODCALLTYPE Release()
{
ULONG ulRef = InterlockedDecrement(&_cRef);
if (0 == ulRef)
{
delete this;
}
return ulRef;
}
HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, VOID **ppvInterface)
{
if (IID_IUnknown == riid)
{
AddRef();
*ppvInterface = (IUnknown*)this;
}
else if (__uuidof(IAudioSessionEvents) == riid)
{
AddRef();
*ppvInterface = (IAudioSessionEvents*)this;
}
else
{
*ppvInterface = NULL;
return E_NOINTERFACE;
}
return S_OK;
}
};
Corresponding .cpp:
HRESULT STDMETHODCALLTYPE AudioEndpointVolumeNotifierImpl::OnSimpleVolumeChanged(float NewVolume, BOOL NewMute, LPCGUID EventContext)
{
PostStatusChange(NewVolume);
return S_OK;
}
Fails in an IClassFactory instance on the following code:
...
AudioEndpointVolumeNotifierImpl* pObject = new AudioEndpointVolumeNotifierImpl();
if (pObject == NULL)
{
return E_OUTOFMEMORY ;
}
...
A good portion of this code IS lifted from some tutorials (the IUnknown stuff in particular). I'm not expecting this code to work just yet, but I can't see why it doesn't compile.
Thanks.
Oddly, although OnIconPathChanged is described as taking an LPWCHAR parameter here:
http://msdn.microsoft.com/en-us/library/dd370939(VS.85).aspx
It is shown taking an LPCWSTR in the example here:
http://msdn.microsoft.com/en-us/library/dd370797(VS.85).aspx
One of these is probably wrong; if we assume it is the former, and that the method actually takes an LPCWSTR (which makes more sense given the context anyway), that would explain your error. I would try changing your declaration to
HRESULT STDMETHODCALLTYPE OnIconPathChanged(LPCWSTR NewIconPath, LPCGUID EventContext){return S_OK;}
In addition to Eric Melski's answer (since you said you were a beginner, i assumed his answer would not be clear to you):
The reason why you get this error is because AudioEndpointVolumeNotifierImpl is an abstract class, which means that it has pure virtual methods, either directly defined in the class, or inherited from base class.
In your case, it's clearly the latter.
What you want to do is implement inherited pure virtual methods, which you have tried to do, but if the signature of the methods are not the same, you are simply defining an overload, and leaving the base pure virtual method unchanged. And your class is still abstract. Hence the error message.
When you get this error you can often look in the output console (i.e not the error list) for "due to following members:" and you'll see what it is that makes the class abstract.
Related
Background:
I hooked CoCreateObject through the DLL to Excel.
Within it, an IFileOpenDialog object was created through the original CoCreateObject function.
The orig variable below is a pointer to IFileOpenDialog.
static HRESULT WINAPI HookedCoCreateInstance(
REFCLSID rclsid,
LPUNKNOWN pUnkOuter,
DWORD dwClsContext,
REFIID riid,
LPVOID *ppv
) {
HRESULT hres = old_CoCreateInstance(rclsid, pUnkOuter, dwClsContext, riid, ppv);
if (IsEqualGUID(rclsid, CLSID_FileOpenDialog) && !hres) {
IFileOpenDialog *orig = reinterpret_cast<IFileOpenDialog *>(ppv);
DebugPrintf("HookedCoCreateInstance AA: orig=%p, pUnkOuter=%p, dwClsContext=%u\n", orig, pUnkOuter, dwClsContext);
orig->QueryInterface(...)
But when I call orig->QueryInterface the exception is caused.
Looking at the vtable below, each entry contains a pointer.
However, the pointer contains another pointer that is not a function instruction set, as shown in the second picture.
Can a vtable contain nested?
I am having a WCF (C# application) application that makes a call to a C++COM Dll. Now the method exposed by C++ COM dll has the following signature.
STDMETHODIMP MessageControl::CallMe(eEventType eventTypeVal, OUT long *pVal)
Now instead of returning a long *pVal I would like to return a interface which has multiple properties. Something like -
STDMETHODIMP MessageControl::CallMe(eEventType eventTypeVal, OUT IData *pVal)
So for the new interface how should I update the COM DLL? So Should I add a new entry in the IDL file for IData interface. I am new to this COM C++ Dll.
You need to have a function in your IDL like (assuming your interface is IMessageControl):
interface IMessageControl
{
STDMETHOD(CallMe)(eEventType eventTypeVal, [out,retval] IData** pVal);
}
or if you want option to return long or an object
interface IMessageControl
{
STDMETHOD(CallMe)(eEventType eventTypeVal, [out,retval] VARIANT* pVal);
}
Your C++ code would be something like:
STDMETHODIMP MessageControl::CallMe(eEventType eventTypeVal, IData** pVal)
{
return E_NOTIMPL;
}
or for variant version
STDMETHODIMP MessageControl::CallMe(eEventType eventTypeVal, VARIANT* pVal)
{
return E_NOTIMPL;
}
Obviously, once you implement, return S_OK for good return or an E_ type error code for a failure.
I have a transform filter which exposes a custom interface says IMyInit. This interface used to be configured some basic setup setting before streaming.
DECLARE_INTERFACE_(IMyInit, IUnknown) {
STDMETHOD HRESULT SetPath(const wchar_t* wcsPath) PURE;
STDMETHOD HRESULT SetMode(UINT uMode) PURE;
};
Client code like:
CComPtr<IBaseFilter> pMyFilter;
HRESULT hr = CoCreateInstance(CLSID_MYFILTER, IID_MYFILTER, ..., (void**)&pMyFilter);
// hr is S_OK
CComPtr<IMyInit> pMyInit;
hr = pMyFilter->QueryInterface(IID_IMyInit, (void**)&pMyInit);
// hr is S_OK
hr = pMyInit->SetMode(1);
// hr is 0x80040213/VFW_E_NO_CLOCK
In my CMyFilter::SetMode(UINT uMode), there are only E_POINTER, E_INVALIDARG for parameters checking, and S_OK if uMode is set. It is not possible to return such error code, VFW_E_NO_CLOCK, related to transform filter.
Why?
I have implemented the IMyInit interface dispatch in NonDelegatingQueryInterface, and it seems like
if(riid == IID_IMyInit) {
return GetInterface((IMyInit*)this, ppv);
}
But! I forgot to let my CMyFilter concrete class inherit from the IMyInit interface. So there is no connection between IMyInit and CMyFilter.
The C style casting, (IMyInit*)this, then casts the ppv to CMyFilter's some base class, may be the direct show's CTransformFilter. The unknown method pointered by IMyInit::SetMode(UINT) may require clock to exist. This is why the VFW_E_NO_CLOCK will return.
So I have tried to debug the program and as soon as I get into the Windows API function calls things get a little crazy, plus there isn't much help with debugging those files because I can't change them anyways. Basically what I am stuck on is the following two functions that I can change (FYI this is really old code and the program works in 32bit versions but when converted to 64bit this problem occurred):
void CSalvoPage::AdviseScrollingButtonPanel()
{
if ( m_SBPCookie == 0 )
{
IUnknown * pSinkUnk;
long * pCookie = &m_SBPCookie;
m_spSBPControlSink->QueryInterface(IID_IUnknown, (void **) &pSinkUnk);
if (pSinkUnk != NULL)
{
m_SalvoButtons.AddListener(pSinkUnk, pCookie);//here is the problem~~~~
pSinkUnk->Release();
}
}
}
Then we have the AddListener call which does this
void CNvButtonPanel::AddListener(LPUNKNOWN pUnk, long* pCookie)
{
static BYTE parms[] =
VTS_UNKNOWN VTS_PI4;
InvokeHelper(0x16, DISPATCH_METHOD, VT_EMPTY, NULL, parms,
pUnk, pCookie);
}
I know for a fact that the InvokeHelper function throws the exception through debugging. All I seem to understand is that parms[] lets the InvokeHelper know what types of parameters it's getting and how many. I looked up the definitions and found that in fact
VTS_UNKNOWN = "\x0D" //IUNKNOWN*
and
VTS_PI4 = "\x43" //a 'long*'
Therefore I am telling the InvokeHelper the correct types of parameters to expect so I don't understand why I get a Type Mismatch Error in a popup window everytime I run the program... Any ideas as to why my InvokeHelper throws the Type Mismatch Error?
I have tried to look into the InvokeHelper method documentation and it's really confusing... What I do know is that it throws the COleException mentioned in the documentation and the SCODE returned from the Invoke method is -2147352571
[id(22), helpstring("method AddListener")]
HRESULT AddListener(
[in] IUnknown * pUnk,
[out] IUnknown ** pCookie
);
I was able to fix the issue by doing what RbMm suggested which was to change the function AddListener and RemoveListener functions to match the types declared in the .idl file.
void AddListener(LPUNKNOWN pUnk, LPUNKNOWN* pCookie);
void RemoveListener(LPUNKNOWN pCookie);
The functions now correctly match the types defined in the .idl file
[id(22), helpstring("method AddListener")]
HRESULT AddListener(
[in] IUnknown * pUnk,
[out] IUnknown ** pCookie
);
[id(23), helpstring("method RemoveListener")]
HRESULT RemoveListener(
[in] IUnknown * pCookie
);
I have a DLL in unmanaged C++ :
EditArticleManagerFactory.h:
class __declspec(dllexport) EditArticleManagerFactory : public NamedClassFactory<SCEditArticleManager>,
public SCBLEditArticle:ICOMEditArticleManagerFactory
{
public:
STDMETHODIMP CreateManager(BSTR bstrName, SCBLEditArticle::ICOMEditArticleManager** pEditArticleManager);
}
interface ICOMEditArticleManagerFactory : IUnknown
{
HRESULT CreateManager([in]BSTR bstrName, [out]ICOMEditArticleManager** pEditArticleManager);
}
EditArticleManagerFactory.cpp:
STDMETHODIMP EditArticleManagerFactory::CreateManager(BSTR bstrName, SCBLEditArticle::ICOMEditArticleManager** pEditArticleManager)
{
manager = factory->createManager(bstrName);
return manager->QueryInterface(__uuidof(SCBLEditArticle::ICOMEditArticleManager), (void**)&pEditArticleManager);
}
I'd like to call this method from Delphi and it should return an interface to a created manager.
Delphi:
function CreateManager(bstrName: wideString; pEditArticleManager: ICOMEditArticleManager): HResult; stdcall; external 'SCBLEditArticle.dll';
procedure CreateManager;
var
hr:HResult;
mCOMEditArticleManager: ICOMEditArticleManager;
begin
hr := CreateManager('MANAGER1', mCOMEditArticleManager);
end;
The problem is I get an access violation when it reaches the end; in this delphi method.
Do you have any ideea what could be wrong?
Thx, rufusz
Edit:
But I'm using a macro for implementing Release and
EditArticleManagerFactory.h :
IMPLEMENT_UNKNOWN_NODELETE(EditArticleManagerFactory) BEGIN_INTERFACE_TABLE(EditArticleManagerFactory) IMPLEMENTS_INTERFACE(SCBLEditArticle::ICOMEditArticleManagerFactory) END_INTERFACE_TABLE()
Inttable.cpp:
define IMPLEMENT_UNKNOWN_NODELETE(ClassName) \
STDMETHODIMP QueryInterface(REFIID riid, void **ppv) \ { \ HRESULT hr = InterfaceTableQueryInterface(this, GetInterfaceTable##ClassName(), riid, ppv);\ __if_exists(InheritedQueryInterface##ClassName) { if ( FAILED(hr) ) hr = InheritedQueryInterface##ClassName(riid, ppv); } \ return hr; \ }\ STDMETHODIMP_(ULONG) AddRef(void) { return 2; } \ STDMETHODIMP_(ULONG) Release(void) { return 1; }
Furthermore :
When I debug from Delphi, I get the access violation in the UnsetExceptionHandler
004046F7 3901 cmp [ecx],eax.
Maybe this can help to diagnose the problem.
Also if I declared an external function outside my C++ class, and called that from Delphi, I didn't get the access violation, but didn't get the interface pointer neither.
Also:
If I do nothing in the C++ method, I still get the AccessViolation.
Should be
function CreateManager(bstrName: wideString; OUT pEditArticleManager: ICOMEditArticleManager): HResult; stdcall; external 'SCBLEditArticle.dll';
Notice the "OUT", you somehow dropped a indirection. (an interface is only one *, not two)
It's odd you try to declare STDMETHODIMP CreateManager within your class in C++, this may be causing trouble.
Also COM provides a standard way of having an object-factory (class-object), which responds to CoCreateInstance calls, you may want to have a look at that.
Most likely there's something wrong with the reference counting.
When the function exists, the reference count to all interfaces is decremented. The Delphi compiler automatically creates the necessary code to call ICOMEditArticleManager::Release.
Either its implementation is flawed or you are not returning a valid IUnknown interface.
You can try the following:
In VC++ set a breakpoint at the implementation of ICOMEditArticleManager::Release
In Delphi switch to the CPU mode and single-step through the disassembled code.
That way you should find the cause or at least narrow it down.