Solved

COM with classes

Posted on 2001-07-22
10
263 Views
Last Modified: 2013-12-03
I have a class that encapsulates a COM interface (IDirectSoundBuffer), like this:

class Sound
{
private:
    LPDIRECTSOUNDBUFFER lpBuf;

public:
    bool Create( char *wavefile );
    void Destroy();

    Sound() { lpBuf = 0; }
    ~Sound();
};


bool Sound::Create( char *wavefile )
{
    // Use IDirectSound::CreateSoundBuffer() to create
    // a buffer, then fill it with the wave data
}

void Sound::Destroy()
{
    if (lpBuf != 0)
        lpBuf->Release(), lpBuf = 0;
}

Sound::~Sound()
{
    Destroy();
}


When my program is started, it obtains an IDirectSound interface using the DirectSoundCreate() function. After that a Sound object is created, which creates a sound buffer interface.

If Sound::Destroy() is called before releasing the IDirectSound interface, then everything works properly. A problem arises, however, when the interface is released first and Destroy() is called afterwords (either manually or in the destructor). When IDirectSound is released, any buffers that were created through it are also released automatically. The Destroy() function can't detect this situation because its lpBuf member is still nonzero, so it tries to release it again. This causes an access violation exception.

Obviously the right thing to do is to just make sure to call Destroy properly, but I can't help but wonder if there is a way to detect the 'released-but-nonzero' situation and deal with it properly. If anyone has run into this problem before, I would really appreciate it if you told me how you delt with it.
0
Comment
Question by:TookH
  • 3
  • 2
  • 2
  • +2
10 Comments
 
LVL 5

Accepted Solution

by:
robpitt earned 100 total points
ID: 6307691
Hmmm, thats interesting and your right its a bit of a problem.

Effectively releasing the parent DirectSound object has caused the destruction of all the DirectSoundBuffers. I believe this to be completely counter the standard reference counting mechanism.

The only thing I can think of is to pass a reference to the IDirectSound interface to your CSound class. Your CSound class can then call AddRef() on the IDirectSound interface. This will ensure that IDirectSound isn't released until after the destructor at which point you CSound class can do a IDirectSound->Release().

0
 
LVL 9

Expert Comment

by:ShaunWilde
ID: 6308436
hmm it does seem to be that the directsound interface is keeping a reference to it without incrementing its own refcount and without tying them in to its own refcount - not an uncommon scenario - but I am surprised it deletes the buffer itself rather than let the the directsoundbuffer do it but then if you tell DirectSound that you no longer need it then it may be a default tidy up mechanism to tidy up resources left behind by 'bad' programmers
0
 
LVL 5

Expert Comment

by:robpitt
ID: 6308504
I'm pretty sure your right about it being a "default tidy up mechanism to tidy up resources left behind by 'bad' programmers"


Certainly it was the case that when the original DX was released lots of games programmers were completely ignorant of techniques such as reference counting.

Hence lots of them just took the samples and butchered them without any real understanding of what they should be releasing and when.

Rob
0
Salesforce Made Easy to Use

On-screen guidance at the moment of need enables you & your employees to focus on the core, you can now boost your adoption rates swiftly and simply with one easy tool.

 
LVL 3

Expert Comment

by:JackThornton
ID: 6309495
There's no valid or guaranteed way to detect that kind of situation, which (theoretically) isn't supposed to come up if reference counting is properly handled. What DX does, for whatever *good* reason, violates COM policy.

Three things you can do:

(1) Test pointer before releasing it by calling IsBadReadPtr against either lpBuf or a dereferenced lpBuf (given the assumption that, even with a proxy, the interface pointer points to a pointer), before releasing it. E.G.:

if (!IsBadReadPtr(lpBuf, 4) && !IsBadReadPtr(*((unsigned **)lpBuf), 1))
    lpBuf->Release();

Not guaranteed to work since the released memory could be reallocated and no longer hold the FEFEFEFE or BAADF00D that MS likes to fill the CoTaskMemFree'd memory with.

(2) Wrap the release in a try/catch block.

(3) Construct your sound buffer with a pointer to the "controlling" IDirectSound interface, and keep a addref'd reference to that interface in your buffer object. E.G.:

    lpBuf->Release();
    lpIDirectSound->Release();

This keeps the DirectSound object around until you've destroyed all your buffers yourself. This is the only "legitimate" way to handle this ugly situation.

- jack

0
 
LVL 5

Expert Comment

by:robpitt
ID: 6309534
(3) is what I suggested  :-)
0
 
LVL 3

Expert Comment

by:JackThornton
ID: 6309558
robpitt: oops! (that's what I get for not reading carefully. Or having kids and thereby destroying my few remaining brain cells).
0
 
LVL 9

Expert Comment

by:ShaunWilde
ID: 6309596
I agree about the kids - what was this question about again?
0
 
LVL 6

Expert Comment

by:snoegler
ID: 6316101
BTW: This behavior occurs in other parts of Windows API's as well ... for example, the IStorage COM interfaces behave exactly like that. If you obtain a IStream from a IStorage and you release the IStorage, the IStream is also released.

Sometimes i get the feeling that MS should only do COM after learning it :)
0
 
LVL 1

Author Comment

by:TookH
ID: 6316184
The AddRef solution looks like exactly what I need. Much thanks. :-)
0
 
LVL 1

Author Comment

by:TookH
ID: 6350427
Um... I just got about five notifs from this question. Anyone else?
0

Featured Post

Salesforce Made Easy to Use

On-screen guidance at the moment of need enables you & your employees to focus on the core, you can now boost your adoption rates swiftly and simply with one easy tool.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

This article describes how to programmatically preset the "Pages per Sheet" option that's available with most printer drivers.   This setting lets you do "n-Up" printing, where two, four, or more pages are printed on each sheet of paper. If your …
Whether you've completed a degree in computer sciences or you're a self-taught programmer, writing your first lines of code in the real world is always a challenge. Here are some of the most common pitfalls for new programmers.
This is Part 3 in a 3-part series on Experts Exchange to discuss error handling in VBA code written for Excel. Part 1 of this series discussed basic error handling code using VBA. http://www.experts-exchange.com/videos/1478/Excel-Error-Handlin…

820 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question