views:

317

answers:

1

Could someone point me whats wrong with this code please? I'm having a very hard experience in mixing C++ and MC++. I have read a lot of blogs and tutorial regarding this subject (passing delegates) but now that looks my code is ok (its compiling and runs well when in debug mode and step by step) it crashs.

The main problem is that it needs to have a Delegate that is a member function (which needs to access other class members).

I remembered that theres a note in waveInProc documentation which says that inside the callback you cannot call any system function. Should be this what's crashing the application since it tryies to use other members and the managed environment takes place here calling other system methods?

ref class CWaveIn
{
public:
 void CWaveIn::Open(int currentInputDeviceId)
private:
 void AllocateBuffer(void);
 void WaveInProc(HWAVEIN hwi, UINT uMsg, DWORD_PTR dwInstance, DWORD_PTR dwParam1, DWORD_PTR dwParam2);
 delegate void CallBack(HWAVEIN hwi, UINT uMsg, DWORD_PTR dwInstance, DWORD_PTR dwParam1, DWORD_PTR dwParam2);
 CallBack^ myDelegate;
protected:
 WAVEFORMATEX* waveFormat;
 int bufferDuration; // in seconds
 BYTE* waveInBuffer;
 int bufferSize;
};

void CWaveIn::AllocateBuffer(void)
{
 free(waveInBuffer);
 bufferSize = waveFormat->nAvgBytesPerSec * bufferDuration;
 waveInBuffer = new BYTE[bufferSize];
 Debug::WriteLine("BufferSize: " + bufferSize);
}

void CWaveIn::WaveInProc(HWAVEIN hwi, UINT uMsg, DWORD_PTR dwInstance, DWORD_PTR dwParam1, DWORD_PTR dwParam2) 
{
 switch(uMsg) {
  case WIM_CLOSE:
   Debug::WriteLine("WIM_CLOSE");
   break;
  case WIM_DATA:
   for(int i=0;i<bufferSize; i++) {
    Debug::Write(waveInBuffer[i] + " ");
   }
   Debug::WriteLine("WIM_DATA");
   break;
  case WIM_OPEN:
   Debug::WriteLine("WIM_OPEN");
   break;
 }
}

void CWaveIn::Open(int currentInputDeviceId) 
{
 MMRESULT result = ::waveInOpen(0, currentInputDeviceId, waveFormat, 0, 0, WAVE_FORMAT_QUERY);
 Debug::WriteLine(L"CWaveIn::Open() WAVE_FORMAT_QUERY: device " + currentInputDeviceId.ToString());
 DebugError(result);
 if(result == MMSYSERR_NOERROR)
 {
  myDelegate = gcnew CallBack(this, &CWaveIn::WaveInProc);
  pin_ptr<CallBack^> ptrMyDelegate= &myDelegate;
  IntPtr delegatePointer = System::Runtime::InteropServices::Marshal::GetFunctionPointerForDelegate(myDelegate);

  HWAVEIN hWaveIn;
  MMRESULT result = ::waveInOpen(&hWaveIn, currentInputDeviceId, waveFormat, (DWORD_PTR)delegatePointer.ToPointer(), 0, CALLBACK_FUNCTION | WAVE_FORMAT_DIRECT);
  Debug::WriteLine(L"CWaveIn::Open() : device " + currentInputDeviceId.ToString());
  DebugError(result);

  AllocateBuffer();

  WAVEHDR WaveInHdr;
  WaveInHdr.lpData = (LPSTR)waveInBuffer;
  WaveInHdr.dwBufferLength = bufferSize;
  WaveInHdr.dwBytesRecorded=0;
  WaveInHdr.dwUser = 0L;
  WaveInHdr.dwFlags = 0L;
  WaveInHdr.dwLoops = 0L;
  ::waveInPrepareHeader(hWaveIn, &WaveInHdr, sizeof(WAVEHDR));
  result = ::waveInAddBuffer(hWaveIn, &WaveInHdr, sizeof(WAVEHDR));

  result = ::waveInStart(hWaveIn);
  Debug::WriteLine(L"CWaveIn::Start() : device " + currentInputDeviceId.ToString());
  DebugError(result);
 }
}
A: 

your are declaring the pin_ptr on the managed heap and then you pass it to an un-managed function all the managed references to this pointer are inside CWaveIn::Open(int currentInputDeviceId) so I guess the GC sees no reason to keep this object after CWaveIn::Open exits.

try to create it in the class scope instead of the function scope

Alon
right, thanks. That's my point. I have no idea on how to perform this. The compiler does not like a pin_ptr declared as a data member.
Ruben Trancoso
what error do you get ?
Alon
added this line to the end of CWaveIn::Open and looks like it now works.while((WaveInHdr.dwFlags Not a god solution that it lead us to one. What it does is stay inside open() until the record callback finish.
Ruben Trancoso
Error 2 error C3831: 'ptrMyDelegate': 'CWaveIn' cannot have a pinned data member or a member function returning a pinning pointer.
Ruben Trancoso
such a loop is a CPU hog
Alon
yeap, take this as a proof of concept :). Need to think in something better.
Ruben Trancoso
try to set the delegate to a reference in the class context that way it wont get blown away by the GC e.g m_myDelegate = myDelegate where m_myDelegate is a member
Alon
I found this. Will work on it. Thanks a lot by the help Alon! http://bytes.com/topic/net/answers/465689-pin_ptr-global-class
Ruben Trancoso
You are helping too much, you don't need to pin nor create the unmanaged delegate function pointer. The P/Invoke marshaller already does this for you, just pass myDelete as-is.
Hans Passant
hi nobugz, if so I get this error: Error 1 error C2664: 'waveInOpen' : cannot convert parameter 4 from 'CWaveIn::CallBack ^' to 'DWORD_PTR'
Ruben Trancoso