DirectSound and C

I accidentally deleted the InitDirectSound function when converting the from .cpp to c from my following along code. So I decided to use it as a refresher. But when rewriting the function in c, I ran into this problem, LPDIRECTBUFFER doesn't actually contain functions for some reason. I searched around and found this https://github.com/id-Software/Qu...ob/master/WinQuake/snd_win.c#L340.

On these lines they are accessing a member of LPDIRECTBUFFER called lpVtbl, when putting this into my code it doesn't make the compiler complain. Only when using C do I have to access lpVtbl. I could not find the docs for the version of DirectSound we are using, does anyone know what lpVtbl is?

I have not actually tested the code to make sure it still works. I'm actually going to try that right after writing this.

Thanks, Connor <3.
DirectSound is based on C++ COM object model. This means having public abstract C++ class. And real implementation inherits it and implements all methods.

The way how you use it in C++ are calls to regular C++ methods. In C you don't have methods available in structures/classes. But you access C++ virtual table and call function pointers yourself.

In C++ any COM interface is implemented as abstract class. But in C COM interface is implemented as a struct with one member - lpVtbl pointer. This pointer contains all the methods object implements.

So if you are compiling code as C++ then you can use methods directly, but if you compile code in C (or any other language) you need to access virtual table yourself and call needed function pointer.

To see how it is implemented look in dsound.h and combaseapi.h headers. dsound.h contains declaration of DirectSound interfaces and combaseapi.h contains macros which expands differently depending if you compile code as C or C++.

Look here:
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
DECLARE_INTERFACE_(IDirectSound, IUnknown)
{
    // IUnknown methods
    STDMETHOD(QueryInterface)       (THIS_ _In_ REFIID, _Outptr_ LPVOID*) PURE;
    STDMETHOD_(ULONG,AddRef)        (THIS) PURE;
    STDMETHOD_(ULONG,Release)       (THIS) PURE;

    // IDirectSound methods
    ...
    STDMETHOD(SetCooperativeLevel)  (THIS_ HWND hwnd, DWORD dwLevel) PURE;
    ...
};

#define IDirectSound_QueryInterface(p,a,b)       IUnknown_QueryInterface(p,a,b)
#define IDirectSound_AddRef(p)                   IUnknown_AddRef(p)
#define IDirectSound_Release(p)                  IUnknown_Release(p)

#if !defined(__cplusplus) || defined(CINTERFACE)
...
#define IDirectSound_SetCooperativeLevel(p,a,b)  (p)->lpVtbl->SetCooperativeLevel(p,a,b)
...
#else // !defined(__cplusplus) || defined(CINTERFACE)
...
#define IDirectSound_SetCooperativeLevel(p,a,b)  (p)->SetCooperativeLevel(a,b)
...
#endif // !defined(__cplusplus) || defined(CINTERFACE)


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
#if defined(__cplusplus) && !defined(CINTERFACE)
...
#define __STRUCT__ struct
#define interface __STRUCT__
#define STDMETHOD(method)        virtual COM_DECLSPEC_NOTHROW HRESULT STDMETHODCALLTYPE method
...
#define DECLARE_INTERFACE_(iface, baseiface)            interface DECLSPEC_NOVTABLE iface : public baseiface
...

#else

#define interface               struct
#define STDMETHOD(method)       HRESULT (STDMETHODCALLTYPE * method)

#define DECLARE_INTERFACE(iface)    typedef interface iface { \
                                    const struct iface##Vtbl FAR* lpVtbl; \
                                } iface; \
                                typedef const struct iface##Vtbl iface##Vtbl; \
                                const struct iface##Vtbl
...
#endif


You can see that DECLARE_INTERFACE is either C++ struct with virtual methods or C structure with virtual table pointer to actual function pointers. All wrapped in #ifdef __cplusplus.

You can also see that you can write calls using macros:
1
IDirectSound_SetCooperativeLevel(obj, arg1, arg2);

Which will expand to correct C or C++ code. That is if you want to switch between C and C++ in your code.

Edited by Mārtiņš Možeiko on
Thanks for the information! That's what I needed to know! PS. I have noticed that you are answering a lot of questions and just wanted to say I appreciate it!
No problem, glad I can help :)