【问题标题】:Passing a Delegate as Callback to a Native C++ API Call将委托作为回调传递给本机 C++ API 调用
【发布时间】:2023-03-25 09:59:01
【问题描述】:

有人能指出我这段代码有什么问题吗?我在混合 C++ 和 MC++ 方面有过非常艰难的经历。我已经阅读了很多关于这个主题的博客和教程(通过委托),但现在看起来我的代码还可以(它在调试模式下编译和运行良好,并且逐步运行)它崩溃了。

主要问题是它需要有一个作为成员函数的Delegate(需要访问其他类成员)。

我记得 waveInProc 文档中有一条注释说在回调中不能调用任何系统函数。这应该是导致应用程序崩溃的原因吗,因为它尝试使用其他成员并且托管环境发生在这里调用其他系统方法?

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);
 }
}

【问题讨论】:

  • 如果能提供一些有关您在调试器崩溃时从调试器获得的消息的更多信息,以及在哪一行堆栈转储等方面,它可能会有所帮助。
  • 我现在想到的一个想法是,当 CWaveIn::Open 调用完成时 pin_ptr 超出范围。那么,我怎样才能将它固定在类范围内呢?
  • 异常代码是 C0000005 ACCESS_VIOLATION。故障模块名称:msvcrt.dll。没有关于线路的信息。调用回调时,它可能发生在 waveInOpen 内部。有帮助吗?

标签: c++ winapi audio delegates managed-c++


【解决方案1】:

您需要使用 GCHandle 而不是 pin_ptr。见http://msdn.microsoft.com/en-us/library/367eeye0(v=vs.80).aspx

【讨论】:

    【解决方案2】:

    您在托管堆上声明 pin_ptr,然后将其传递给非托管函数 对此指针的所有托管引用都在 CWaveIn::Open(int currentInputDeviceId) 内 所以我猜 GC 在CWaveIn::Open 退出后认为没有理由保留这个对象。

    尝试在类作用域而不是函数作用域中创建它

    【讨论】:

    • 好的,谢谢。这就是我的观点。我不知道如何执行此操作。编译器不喜欢将 pin_ptr 声明为数据成员。
    • 将此行添加到 CWaveIn::Open 的末尾,看起来它现在可以工作了。而((WaveInHdr.dwFlags & WHDR_DONE)!= WHDR_DONE){};不是一个上帝的解决方案,它引导我们找到一个。它所做的就是留在 open() 中,直到记录回调完成。
    • 错误 2 错误 C3831: 'ptrMyDelegate': 'CWaveIn' 不能有固定数据成员或返回固定指针的成员函数。
    • 是的,将此作为概念证明:)。需要更好地思考问题。
    • 您帮助太多了,您不需要固定或创建非托管委托函数指针。 P/Invoke marshaller 已经为您完成了这项工作,只需按原样传递 myDelete。
    猜你喜欢
    • 1970-01-01
    • 2020-08-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-07-12
    • 2011-05-29
    • 1970-01-01
    相关资源
    最近更新 更多