我偶然发现了Windows 10崩溃,这可能是Windows 10更新到2004年以来的新崩溃。
问题是COM启动时ntdll崩溃。要使用Core Audio,需要COM。
触发它的代码:
IMMDeviceEnumerator* enumerator = nullptr;
CoInitializeEx(nullptr, COINIT_MULTITHREADED); // Also with STA
HRESULT hr = CoCreateInstance( CLSID_MMDeviceEnumerator, 0, CLSCTX_ALL, IID_IMMDeviceEnumerator,
(void**)&enumerator);
弹出窗口:
MicTest.exe中0x00007FFE7846D3D1(ntdll.dll)的未处理的异常: LIST_ENTRY已损坏(即两次删除)。
Callstack:
ntdll.dll!LdrpInsertDataTableEntry()
ntdll.dll!LdrpMapDllWithSectionHandle()
ntdll.dll!LdrpMapDllNtFileName()
ntdll.dll!LdrpMapDllFullPath()
ntdll.dll!LdrpProcessWork()
ntdll.dll!LdrpLoadDllInternal()
ntdll.dll!LdrpLoadForwardedDll()
ntdll.dll!LdrpGetDelayloadExportDll()
ntdll.dll!LdrpHandleProtectedDelayload()
ntdll.dll!LdrResolveDelayLoadedAPI()
combase.dll!00007ffe769ab1c2()
combase.dll!00007ffe769c56fd()
combase.dll!00007ffe769b3b27()
ntdll.dll!RtlRunOnceExecuteOnce()
KernelBase.dll!InitOnceExecuteOnce()
combase.dll!00007ffe7696c11f()
combase.dll!00007ffe7696baf8()
combase.dll!00007ffe7696b9e6()
combase.dll!00007ffe76907df2()
combase.dll!00007ffe76906fce()
combase.dll!00007ffe76907928()
combase.dll!00007ffe76907718()
MicTest.exe!Microphone::Microphone() Line 23 C++
[External Code]
MicTest.exe!main(int argc, char * * argv) Line 67 C++
那里不是100%可复制的,有时崩溃是在IAudioClient::Initialize
之后的几行中,当ntdll在加载器锁定下崩溃时
> ntdll.dll!LdrpInitializeThread()
ntdll.dll!_LdrpInitialize()
ntdll.dll!LdrpInitialize()
ntdll.dll!LdrInitializeThunk()
这发生在Windows本身用来加载DLL的新Windows 10线程池线程中。令人反感的代码显示为AudioSes.dll!CAudioClient::CreateRemoteStream
(注意:AudioSes.dll是Core Audio,根据Microsoft Symbol Server的符号。)
这是一个已知问题吗?有解决方法吗?
为完整起见,请完整代码:
Microphone::Microphone()
{
IMMDeviceEnumerator* enumerator = nullptr;
CoInitializeEx(nullptr, COINIT_MULTITHREADED); // crash near here
HRESULT hr = CoCreateInstance( CLSID_MMDeviceEnumerator, 0, CLSCTX_ALL, IID_IMMDeviceEnumerator, (void**)&enumerator);
if (!enumerator) throw std::system_error(hr, std::system_category());
IMMDevice* device = nullptr;
hr = enumerator->GetDefaultAudioEndpoint(eCapture, eConsole, &device);
enumerator->Release();
if (!device) throw std::system_error(hr, std::system_category());
hr = device->Activate(IID_IAudioClient, CLSCTX_ALL, NULL, (void**)&client);
device->Release();
if (!client) throw std::system_error(hr, std::system_category());
WAVEFORMATEXTENSIBLE *pwfx = nullptr;
hr = client->GetMixFormat(reinterpret_cast<WAVEFORMATEX**>(&pwfx));
if (SUCCEEDED(hr) && pwfx)
{
this->span.sampleRate = pwfx->Format.nSamplesPerSec;
this->blockAlign = pwfx->Format.nBlockAlign;
// In general, this is NOT pwfx->wBitsPerSample;
if (pwfx->SubFormat == KSDATAFORMAT_SUBTYPE_IEEE_FLOAT)
{
this->format = fmt_FP32;
}
else if (pwfx->SubFormat == KSDATAFORMAT_SUBTYPE_PCM)
{
this->format = fmt_PCM;
}
else
{
// Can't deal with that format, and Core Audio ought to support FP32.
pwfx->SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
this->format = fmt_FP32;
}
}
// or crash here
hr = client->Initialize(AUDCLNT_SHAREMODE_SHARED, 0, 1000000, 0, &pwfx->Format, NULL);
if (!SUCCEEDED(hr))
{
throw std::system_error(hr, std::system_category());
}
CoTaskMemFree(pwfx);
// Start capture
hr = client->GetService( IID_IAudioCaptureClient, (void**)&capture);
client->Start();
答案 0 :(得分:0)
正如RbMm所指出的,这需要查看程序集。事实证明Windows 10 2004破坏了其PEB_LDR_DATA
数据结构。这是每个过程的结构,其中包含重要的数据。特别是,LdrpInitializeThread
在获取装载机锁之后立即使用PEB_LDR_DATA::InLoadOrderModuleList
。出于不清楚的原因,这可能是空指针。
由于这是在默认线程池中由Windows创建的线程中发生的,因此它与用户代码无关,也不与用户代码同步。崩溃的确切点可能会有所不同-PEB_LDR_DATA
结构的损坏似乎更为广泛。
虽然这不是CoInitializeEx
崩溃的解决方案,但似乎可以通过较小的更改来避免由于PEB_LDR_DATA::InLoadOrderModuleList
损坏而导致的第二次崩溃:
hr = client->Initialize(AUDCLNT_SHAREMODE_SHARED, 0, 0, 0, &pwfx->Format, NULL);
UINT32 bufSize = 0;
hr = client->GetBufferSize(&bufSize);
GetBuffer
auto samplePollRatio = pwfx->Format.nSamplesPerSec / 2666;
auto nextPoll = std::chrono::system_clock::now() +
std::chrono::milliseconds(bufSize / samplePollRatio);
std::this_thread::sleep_until(nextPoll);
auto hr = capture->GetBuffer(&buf, &samples, &flags, nullptr, nullptr);
这是一个合理的模式。 MSDN example也有一个Sleep
调用,但是sleep_until
在处理过程中的可变延迟方面更强大。 (例如,在第一次调用时,打开WAV文件以保存结果时,您可能会有更多延迟)。但是,通过延迟首次采样的获取,您可以绕开Windows 10中明显的竞争条件。