Release版本中EnterCriticalSection调用致程序冻结问题求助
问题
我正在开发基于Microsoft Media Foundation的openframeworks扩展,Debug构建下运行正常,但Release构建中调用EnterCriticalSection后程序冻结。
Release构建无编译错误,启动ofApp后控制台持续挂起,永远输出不了"Pass 2b"行。
相关代码如下:
HRESULT ofxMMF::SetSourceReader(IMFActivate * device) { HRESULT hr = S_OK; IMFMediaSource * source = NULL; IMFAttributes * attributes = NULL; std::cout << "Pass 2a" << std::endl; EnterCriticalSection(&criticalSection); std::cout << "Pass 2b" << std::endl; hr = device -> ActivateObject(__uuidof(IMFMediaSource), (void **)&source); //get symbolic link for the device if (SUCCEEDED(hr)) hr = device ->GetAllocatedString(MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_VIDCAP_SYMBOLIC_LINK, &wSymbolicLink, &cchSymbolicLink); //Allocate attributes if (SUCCEEDED(hr)) hr = MFCreateAttributes(&attributes, 2); //get attributes if (SUCCEEDED(hr)) hr = attributes -> SetUINT32(MF_READWRITE_DISABLE_CONVERTERS, TRUE); // Set the callback pointer. if (SUCCEEDED(hr)) hr = attributes -> SetUnknown(MF_SOURCE_READER_ASYNC_CALLBACK, this); //Create the source reader if (SUCCEEDED(hr)) hr = MFCreateSourceReaderFromMediaSource(source, attributes, &sourceReader); EnumerateCaptureFormats(source); bool foundCorrectMediaType = false; // Try to find a suitable output type. if (SUCCEEDED(hr)) { HRESULT nativeTypeErrorCode = S_OK; DWORD count = 0; UINT32 streamIndex = 0; //2688 x 1512 UINT32 requiredWidth = reqW; UINT32 requiredheight = reqH; while (nativeTypeErrorCode == S_OK) { IMFMediaType * nativeType = NULL; nativeTypeErrorCode = sourceReader->GetNativeMediaType(streamIndex, count, &nativeType); if (nativeTypeErrorCode != S_OK) continue; GUID nativeGuid = { 0 }; hr = nativeType->GetGUID(MF_MT_SUBTYPE, &nativeGuid); if (FAILED(hr)) return hr; UINT32 cwidth, cheight; hr = MFGetAttributeSize(nativeType, MF_MT_FRAME_SIZE, &cwidth, &cheight); if (FAILED(hr)) return hr; UINT32 yuv_video_matrix = 0; yuv_video_matrix = MFGetAttributeUINT32(nativeType, MF_MT_YUV_MATRIX, yuv_video_matrix); UINT32 sample_size = 0; sample_size = MFGetAttributeUINT32(nativeType, MF_MT_SAMPLE_SIZE, sample_size); UINT32 curstride = 0; curstride = MFGetAttributeUINT32(nativeType, MF_MT_DEFAULT_STRIDE, curstride); GUID subtype = GUID_NULL; LONG tempStride = 0; nativeType->GetGUID(MF_MT_SUBTYPE, &subtype); MFGetStrideForBitmapInfoHeader(subtype.Data1, cwidth, &tempStride); WCHAR * pGuidValName; hr = GetGUIDName(nativeGuid, &pGuidValName); std::cout << count << ": " << ConvertWCharToString(pGuidValName) << "\t" << cwidth << " x " << cheight << " : yuv_decode: " << yuv_video_matrix << " : sample size: " << sample_size << ": stride: " << curstride << ": tmpstride: " << tempStride << std::endl; if (nativeGuid == MFVideoFormat_NV12 && cwidth == requiredWidth && cheight == requiredheight) { // found native config, set it hr = IsMediaTypeSupported(nativeType); if (FAILED(hr)) { std::cout << "Media type is not supported" << std::endl; //return hr; } hr = sourceReader->SetCurrentMediaType(streamIndex, NULL, nativeType); if (FAILED(hr)) { std::cout << "Failed to set Media type" << std::endl; //return hr; } MFGetAttributeSize(nativeType, MF_MT_FRAME_SIZE, &width, &height); foundCorrectMediaType = true; break; } count++; } } if (!foundCorrectMediaType) { hr = E_FAIL; std::cout << "Cant find appropriate mediatype,exiting" << std::endl; } if (SUCCEEDED(hr)) { // Ask for the first sample. hr = sourceReader -> ReadSample((DWORD)MF_SOURCE_READER_FIRST_VIDEO_STREAM, 0, NULL, NULL, NULL, NULL); } if (FAILED(hr)) { if (source) { source -> Shutdown(); } //Close(); } if (source) { source -> Release(); source = NULL; } if (attributes) { attributes -> Release(); attributes = NULL; } LeaveCriticalSection(&criticalSection); return hr; }
链接器通过以下指令引入库:
#pragma comment(lib, "Mfplat.lib") #pragma comment(lib, "Mf.lib") #pragma comment(lib, "Mfreadwrite.lib") #pragma comment(lib, "mfuuid.lib") #pragma comment(lib, "shlwapi.lib")
如果这在Release中不合适,我本以为会出现链接错误。
排查思路
- 检查临界区初始化:Release模式下,
criticalSection是否正确调用InitializeCriticalSection或InitializeCriticalSectionAndSpinCount完成初始化?Debug模式下编译器可能自动初始化全局临界区,但Release模式不会,未初始化的临界区调用EnterCriticalSection会直接导致死锁或冻结。 - 排查死锁场景:确认是否有其他线程已持有该临界区,且在等待当前线程释放资源。比如
MF_SOURCE_READER_ASYNC_CALLBACK回调函数中是否也尝试进入同一个临界区?Media Source Reader的回调在后台线程执行,若回调里调用EnterCriticalSection,而主线程进入临界区后调用ReadSample触发回调,就会形成死锁。 - 检查Release优化导致的变量异常:确认
criticalSection的存储类型,若为局部变量或类成员变量,排查是否被编译器优化后内存地址异常,或类实例在Release模式下被提前释放、内存被覆盖。 - 排除输出缓冲干扰:
std::cout在Release模式下默认是缓冲输出,"Pass 2b"可能已写入缓冲区但未刷新到控制台。可以在输出后添加std::cout.flush();,确认是否真的未执行到该行,排除假冻结情况。 - 核对Media Foundation初始化:Debug和Release模式下,是否都正确调用了
MFStartup?MFStartup必须在使用任何MF接口前调用,且两种模式的初始化参数需一致,否则Release模式下可能出现隐性错误。 - 验证指针有效性:检查
device指针在Release模式下是否有效,是否存在Debug模式下合法但Release下被优化为野指针的情况,比如传入的IMFActivate*是否在调用前已被释放。 - 检查异常处理设置:Release模式下是否禁用了异常捕获,导致隐性异常触发后程序进入无响应状态?可在VS项目属性中开启C异常捕获(路径:C/C->代码生成->启用C++异常),排查是否存在未处理的异常。
- 修复代码结构问题:注意到
EnumerateCaptureFormats(source);一行未被大括号包裹,无论MFCreateSourceReaderFromMediaSource是否成功都会执行。若source为NULL(比如ActivateObject失败),调用该函数会导致访问违规,在Release模式下可能表现为冻结而非崩溃,需将该行放入if块内。
内容的提问来源于stack exchange,提问作者Epoxy
相关产品推荐
相关产品推荐

