Solved

COM with classes

Posted on 2001-07-22
10
258 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
Courses: Start Training Online With Pros, Today

Brush up on the basics or master the advanced techniques required to earn essential industry certifications, with Courses. Enroll in a course and start learning today. Training topics range from Android App Dev to the Xen Virtualization Platform.

 
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

Announcing the Most Valuable Experts of 2016

MVEs are more concerned with the satisfaction of those they help than with the considerable points they can earn. They are the types of people you feel privileged to call colleagues. Join us in honoring this amazing group of Experts.

Question has a verified solution.

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

Suggested Solutions

Title # Comments Views Activity
Save copy of document in Word 4 81
Slow process to read Excel 15 120
Changing the installation path of this MSI 5 101
Need to impress with my knowledge of .NET 5 89
This article shows a few slightly more advanced techniques for Windows 7 gadget programming, including how to save and restore user settings for your gadget and how to populate the "details" panel that is displayed in the Windows 7 gadget gallery.  …
This article surveys and compares options for encoding and decoding base64 data.  It includes source code in C++ as well as examples of how to use standard Windows API functions for these tasks. We'll look at the algorithms — how encoding and decodi…
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…
Sending a Secure fax is easy with eFax Corporate (http://www.enterprise.efax.com). First, just open a new email message. In the To field, type your recipient's fax number @efaxsend.com. You can even send a secure international fax — just include t…

813 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

Need Help in Real-Time?

Connect with top rated Experts

12 Experts available now in Live!

Get 1:1 Help Now