Weird DirectSound Behaviour (was COM Aggregation (C++))

Started by
5 comments, last by Evil Steve 16 years, 6 months ago
Hi all, I'm trying to get my copy of Dungeon Keeper 2 to work properly. I noticed that some sounds stutter, and I've seen this happen before in my own engine. It's caused by my on-board audio (And it occurs on other on-board audio too) not handing hardware sound buffer notifications properly. The fix is pretty simple - Don't create a hardware sound buffer; create a software one instead. So, I'm trying to write my own dsound.dll to act as a proxy between Dungeon Keeper 2 and the real DirectSound DLL. I've done this before with D3D, but I've never had to deal with this OLE and COM aggregation crap before. The problem is that DK2 doesn't call DirectSoundCreate(), it uses DllGetClassObject() to get a CLSID_DirectSound interface, and then calls CreateSoundBuffer() with the pUnkOuter parameter non-NULL, and I don't know how to handle that. This is what I have just now (Which I know is wrong):

HRESULT WINAPI DSoundWrapper::CreateSoundBuffer(LPCDSBUFFERDESC pcDSBufferDesc, LPDIRECTSOUNDBUFFER* ppDSBuffer, LPUNKNOWN pUnkOuter)
{
	HRESULT hResult = m_pDSound->CreateSoundBuffer(pcDSBufferDesc, ppDSBuffer, pUnkOuter);
	if(SUCCEEDED(hResult))
	{
		DSoundBufferWrapper* pWrapper = new DSoundBufferWrapper(*ppDSBuffer);
		*ppDSBuffer = pWrapper;
	}
	return hResult;
}


My question is, what should I do to handle this aggregation? I had a quick look on Google, but I really didn't understand much. I understand the concept of aggregation, but not the implementation details. Essentially, all I want to do is have my own IDirectSoundBuffer version (DSoundBufferWrapper) returned instead of the real one. Using my above code, I get calls like so:
Quote: DllGetClassObject(): I return 0x01b70e80 CreateSoundBuffer(NULL, 0x0013c780 (-> 0x279afa83), 0x0013bdf0 (-> 0x00000000)): Returns pUnkOuter of 0x034a1338, my buffer allocated at 0x01b723c8 IDirectSound::Release() DllGetClassObject(): I return 0x01b70e80 CreateSoundBuffer(NULL, 0x180242e8 (-> 0x279afa83), 0x0013c784 (-> 0x01b70e80)): Returns pUnkOuter of 0x034a193c, my buffer allocated 0x01b723fc Crashes in this call deteferencing the ppDSBuffer pointer.
So, my question is - how do I handle this? And/or does anyone have any good links I could read? Cheers, Steve [Edited by - Evil Steve on October 10, 2007 3:31:21 AM]
Advertisement
For the benifit of anyone coming across this topic (It's #5 on Google when searching for "COM Aggregation" after a mere 9 hours), This MSDN Article seems to make sense to me. I'll have another go at it tonight.
Ok, that MSDN article didn't help at all. The main problem I'm having is that CreateSoundBuffer() takes an IUnknown* as an aggregate interface pointer, but the "real" CreateSoundBuffer() function ends up filling in the vtable for the interface. I could understand if it was an IUnknown**, but not a single pointer.
I've tried doing some horrible hacks to copy the vtable and stuff, but they all end up in access violations.

Does anyone have any ideas on what on earth I should be doing to handle this aggregation stuff? The source code for CreateSoundBuffer() would be good, but something tells me that isn't available [smile]
This doesn’t make sense at all. DirectSound doesn’t support aggregation. If you pass anything else the NULL there it should return an error.
Quote:Original post by Demirug
This doesn’t make sense at all. DirectSound doesn’t support aggregation. If you pass anything else the NULL there it should return an error.
Yup - that's what confuses me [smile]

I can only assume that this is undocumented, weird internal behaviour that I'm seeing. I've made a sample app that almost shows what's going on - except CreateSoundBuffer() is returning E_INVALIDARG in my code, and it succeeds when called through OLE on my desktop (I'm on my laptop now):
#define INITGUID#include <windows.h>#include <dsound.h>typedef HRESULT (WINAPI *LPDllGetClassObject)(REFCLSID rclsid, REFIID riid, LPVOID* ppv);DEFINE_GUID(MyGuid, 0x279afa83, 0x4981, 0x11ce, 0xa5, 0x21, 0x00, 0x20, 0xaf, 0x0b, 0xe5, 0x60);int main(int, char**){	HMODULE hDll = LoadLibrary("dsound.dll");	if(!hDll) return -1;	LPDllGetClassObject pfn = (LPDllGetClassObject)GetProcAddress(hDll, "DllGetClassObject");	if(!pfn) return -1;	IDirectSound* pDSound = NULL;	HRESULT h = pfn(CLSID_DirectSound, IID_IClassFactory, (void**)&pDSound);	if(FAILED(h)) return -1;	char buffer[512];	memset(buffer, 0, sizeof(buffer));	IUnknown* pUnknown = (IUnknown*)buffer;	IDirectSoundBuffer** ppBuffer = (IDirectSoundBuffer**)&MyGuid;	h = pDSound->CreateSoundBuffer(NULL, ppBuffer, pUnknown);	if(FAILED(h)) return -1;}
The IUnknown stuff there looks like what I see inside my proxy DLL - a pointer which is non-null, but with a null vtable.

I don't understand why this sample code doesn't work, but what appears to be the same code works on my desktop.
I thought it might be me casting an interface pointer wrongly, and screwing up the vtable - and ending up calling the wrong function, but I can't see how that would happen...
I'll have another tinker with this tomorrow morning (It's nearly midnight here, and I'm knackered and ill)...

Ok, I got annoyed and came back to it. It seems like the buffer pointer passed in is a pointer to a GUID, and only this GUID works. The above code now succeeds and has the exact same effect as what I'm seeing - the pUnknown pointer becomes intialised to a seemingly valid interface.

[Edited by - Evil Steve on October 9, 2007 5:09:07 PM]
Yet another update (I've renamed the thread too - Not sure if this would be better suited to the DirectX forum).

The GUID going in seems to be IID_IDirectSound. If I change the call to:
h = pDSound->CreateSoundBuffer(NULL, ppBuffer, (LPUNKNOWN)&pUnknown);
then I seem to get a valid pointer back out, which I assume is an IDirectSound interface.

Now I just need to figure out what I'm supposed to do with this interface - since it looks like a bungled QueryInterface() or something...
Ok, sorted - I'm abusing DllGetClassObject(), casting to the wrong interface, and stamping all over the vtable.

DllGetClassObject() in my code is asking for an IClassFactory, not an IDirectSound interface. It just so happens that the function in the same place in the vtable as CreateSoundBuffer() in IClassFactory is:
virtual /* [local] */ HRESULT STDMETHODCALLTYPE CreateInstance(     /* [unique][in] */ IUnknown *pUnkOuter,    /* [in] */ REFIID riid,    /* [iid_is][out] */ void **ppvObject) = 0;

Which perfectly explains the parameters I'm seeing.

Oh well - lesson learned, make sure a pointer is really what you expect before doing an unsafe cast...

*sigh*

This topic is closed to new replies.

Advertisement