Windows::Devices::Enumeration::DeviceWatcher->Stop() 在扫描时崩溃
Windows::Devices::Enumeration::DeviceWatcher->Stop() crashes when scanning
我正在使用 C++ 编写 JNI DLL,为我们的 Java 应用程序提供一种访问 Windows 10 和 macOS 上的蓝牙 LE 设备的方法。它使用 C++/CX 访问 Windows 10 上的蓝牙 LE 对象,并使用 Objective-C++ 访问 macOS 上的等效项。但是,这个问题集中在我在 Windows 10 上遇到的崩溃,因此不会考虑特定于 macOS 的代码。当通过 JNI 或通过单独的 C 导出(使用命令)使用我的 DLL 时,会发生此崩溃我为简化调试而编写的线路测试应用程序)。
注意:CentralManagerCppCxShim
是一个 ref class
,它只是在触发事件时回调到 CentralManagerImpl
。
崩溃发生在 CentralManagerImpl
对象在扫描 运行 时被销毁(即 StartScan()
被调用):当 _deviceWatcher
被销毁时它崩溃(无论我将其设置为 nullptr
或不设置都没有区别)。我对 BluetoothLEAdvertisementWatcher
有类似的问题,但正确删除析构函数中的事件处理程序使崩溃消失了。
代码摘录:
CentralManagerImpl.h:
class CentralManagerImpl: public CentralManager
{
private:
CentralManagerCppCxShim ^_shim;
DeviceWatcher ^_deviceWatcher;
BluetoothLEAdvertisementWatcher ^_watcher;
EventRegistrationToken _deviceWatcherAdded;
EventRegistrationToken _deviceWatcherRemoved;
EventRegistrationToken _deviceWatcherUpdated;
EventRegistrationToken _watcherReceived;
public:
CentralManagerImpl();
~CentralManagerImpl();
void StartScan();
void StopScan();
}
CentralManagerImpl.cpp:
CentralManagerImpl::CentralManagerImpl()
{
_shim = ref new CentralManagerCppCxShim(this);
_deviceWatcher = DeviceInformation::CreateWatcher(BluetoothLEDevice::GetDeviceSelectorFromPairingState(true));
_deviceWatcherAdded = _deviceWatcher->Added += ref new TypedEventHandler<DeviceWatcher ^, DeviceInformation ^>(_shim, &CentralManagerCppCxShim::OnDeviceAdded);
_deviceWatcherRemoved = _deviceWatcher->Removed += ref new TypedEventHandler<DeviceWatcher ^, DeviceInformationUpdate ^>(_shim, &CentralManagerCppCxShim::OnDeviceRemoved);
_deviceWatcherUpdated = _deviceWatcher->Updated += ref new TypedEventHandler<DeviceWatcher ^, DeviceInformationUpdate ^>(_shim, &CentralManagerCppCxShim::OnDeviceUpdated);
_deviceWatcher->Stopped += ref new TypedEventHandler<DeviceWatcher ^, Object ^>(_shim, &CentralManagerCppCxShim::OnDeviceWatcherStopped);
_watcher = ref new BluetoothLEAdvertisementWatcher();
BluetoothLEManufacturerData ^manufacturerData = ref new BluetoothLEManufacturerData();
manufacturerData->CompanyId = MAKEWORD(HIBYTE(OUR_COMPANY_IDENTIFIER), LOBYTE(OUR_COMPANY_IDENTIFIER));
_watcher->AdvertisementFilter->Advertisement->ManufacturerData->Append(manufacturerData);
_watcher->Stopped += ref new TypedEventHandler<BluetoothLEAdvertisementWatcher ^, BluetoothLEAdvertisementWatcherStoppedEventArgs ^>(_shim, &CentralManagerCppCxShim::OnAdvertisementWatcherStopped);
_watcherReceived = _watcher->Received += ref new TypedEventHandler<BluetoothLEAdvertisementWatcher ^, BluetoothLEAdvertisementReceivedEventArgs ^>(_shim, &CentralManagerCppCxShim::OnAdvertisementReceived);
}
CentralManagerImpl::~CentralManagerImpl()
{
_watcher->Received -= _watcherReceived;
_watcher = nullptr;
_deviceWatcher->Updated -= _deviceWatcherUpdated;
_deviceWatcher->Removed -= _deviceWatcherRemoved;
_deviceWatcher->Added -= _deviceWatcherAdded;
_deviceWatcher = nullptr; // this crashes :(
}
void CentralManagerImpl::StartScan()
{
_deviceWatcher->Start();
_watcher->Start();
}
void CentralManagerImpl::StopScan()
{
_watcher->Stop();
_deviceWatcher->Stop();
}
这是我得到的调用堆栈(是的,它很讨厌):
ntdll.dll!_TppRaiseInvalidParameter@0()
ntdll.dll!_TpPostWork@4()
cfgmgr32.dll!TQuery::CloseQuery()
cfgmgr32.dll!_DevCloseObjectQuery@4()
Windows.Devices.Enumeration.dll!Watcher<class Windows::Devices::Enumeration::DeviceWatcher,struct Windows::Devices::Enumeration::IDeviceWatcher,struct Windows::Devices::Enumeration::IDeviceWatcher2,class Windows::Devices::Enumeration::DeviceInformation,struct Windows::Devices::Enumeration::IDeviceInformation,struct Windows::Devices::Enumeration::IDeviceInformation2,class DeviceInformationServer,class Windows::Devices::Enumeration::DeviceInformationUpdate,struct Windows::Devices::Enumeration::IDeviceInformationUpdate,class DeviceInformationUpdateServer,&unsigned short const * const RuntimeClass_Windows_Devices_Enumeration_DeviceWatcher>::Impl::EndQuery(void)
Windows.Devices.Enumeration.dll!Watcher<class Windows::Devices::Enumeration::DeviceWatcher,struct Windows::Devices::Enumeration::IDeviceWatcher,struct Windows::Devices::Enumeration::IDeviceWatcher2,class Windows::Devices::Enumeration::DeviceInformation,struct Windows::Devices::Enumeration::IDeviceInformation,struct Windows::Devices::Enumeration::IDeviceInformation2,class DeviceInformationServer,class Windows::Devices::Enumeration::DeviceInformationUpdate,struct Windows::Devices::Enumeration::IDeviceInformationUpdate,class DeviceInformationUpdateServer,&unsigned short const * const RuntimeClass_Windows_Devices_Enumeration_DeviceWatcher>::Impl::Stop(bool)
Windows.Devices.Enumeration.dll!Watcher<class Windows::Devices::Enumeration::DeviceWatcher,struct Windows::Devices::Enumeration::IDeviceWatcher,struct Windows::Devices::Enumeration::IDeviceWatcher2,class Windows::Devices::Enumeration::DeviceInformation,struct Windows::Devices::Enumeration::IDeviceInformation,struct Windows::Devices::Enumeration::IDeviceInformation2,class DeviceInformationServer,class Windows::Devices::Enumeration::DeviceInformationUpdate,struct Windows::Devices::Enumeration::IDeviceInformationUpdate,class DeviceInformationUpdateServer,&unsigned short const * const RuntimeClass_Windows_Devices_Enumeration_DeviceWatcher>::~Watcher<class Windows::Devices::Enumeration::DeviceWatcher,struct Windows::Devices::Enumeration::IDeviceWatcher,struct Windows::Devices::Enumeration::IDeviceWatcher2,class Windows::Devices::Enumeration::DeviceInformation,struct Windows::Devices::Enumeration::IDeviceInformation,struct Windows::Devices::Enumeration::IDeviceInformation2,class DeviceInformationServer,class Windows::Devices::Enumeration::Devic()
Windows.Devices.Enumeration.dll!Watcher<class Windows::Devices::Enumeration::DeviceWatcher,struct Windows::Devices::Enumeration::IDeviceWatcher,struct Windows::Devices::Enumeration::IDeviceWatcher2,class Windows::Devices::Enumeration::DeviceInformation,struct Windows::Devices::Enumeration::IDeviceInformation,struct Windows::Devices::Enumeration::IDeviceInformation2,class DeviceInformationServer,class Windows::Devices::Enumeration::DeviceInformationUpdate,struct Windows::Devices::Enumeration::IDeviceInformationUpdate,class DeviceInformationUpdateServer,&unsigned short const * const RuntimeClass_Windows_Devices_Enumeration_DeviceWatcher>::`vector deleting destructor'(unsigned int)
Windows.Devices.Enumeration.dll!Microsoft::WRL::Details::RuntimeClass<struct Microsoft::WRL::Details::InterfaceList<struct Windows::Devices::Enumeration::IDeviceWatcher,struct Microsoft::WRL::Details::InterfaceList<struct Windows::Devices::Enumeration::IDeviceWatcher2,struct Microsoft::WRL::Details::InterfaceList<struct Windows::Foundation::IClosable,struct Microsoft::WRL::Details::InterfaceList<class Microsoft::WRL::FtmBase,class Microsoft::WRL::Details::Nil> > > >,struct Microsoft::WRL::RuntimeClassFlags<3>,1,1,0>::Release(void)
mydll.dll!__abi_winrt_ptr_assign(void * * __ppTargetArg, const volatile Platform::Object ^ __objArg) Line 405
mydll.dll!CentralManagerImpl::~CentralManagerImpl() Line 64
[External Code]
//…
经过深入调查,我得出以下结论:
当 DLL 作为 UWP 工作线程被卸载(DLL_PROCESS_DETACH
)时,系统无法在 CentralManagerImpl
C++ class 的默认析构函数中销毁 DeviceWatcher
已经被系统终止了。我找到的唯一可行的解决方案是向我的 DLL 添加一个新的导出,以明确拆除我的 C++ class.
我会将此标记为已回答,因为我认为没有其他解决方法。
我正在使用 C++ 编写 JNI DLL,为我们的 Java 应用程序提供一种访问 Windows 10 和 macOS 上的蓝牙 LE 设备的方法。它使用 C++/CX 访问 Windows 10 上的蓝牙 LE 对象,并使用 Objective-C++ 访问 macOS 上的等效项。但是,这个问题集中在我在 Windows 10 上遇到的崩溃,因此不会考虑特定于 macOS 的代码。当通过 JNI 或通过单独的 C 导出(使用命令)使用我的 DLL 时,会发生此崩溃我为简化调试而编写的线路测试应用程序)。
注意:CentralManagerCppCxShim
是一个 ref class
,它只是在触发事件时回调到 CentralManagerImpl
。
崩溃发生在 CentralManagerImpl
对象在扫描 运行 时被销毁(即 StartScan()
被调用):当 _deviceWatcher
被销毁时它崩溃(无论我将其设置为 nullptr
或不设置都没有区别)。我对 BluetoothLEAdvertisementWatcher
有类似的问题,但正确删除析构函数中的事件处理程序使崩溃消失了。
代码摘录:
CentralManagerImpl.h:
class CentralManagerImpl: public CentralManager
{
private:
CentralManagerCppCxShim ^_shim;
DeviceWatcher ^_deviceWatcher;
BluetoothLEAdvertisementWatcher ^_watcher;
EventRegistrationToken _deviceWatcherAdded;
EventRegistrationToken _deviceWatcherRemoved;
EventRegistrationToken _deviceWatcherUpdated;
EventRegistrationToken _watcherReceived;
public:
CentralManagerImpl();
~CentralManagerImpl();
void StartScan();
void StopScan();
}
CentralManagerImpl.cpp:
CentralManagerImpl::CentralManagerImpl()
{
_shim = ref new CentralManagerCppCxShim(this);
_deviceWatcher = DeviceInformation::CreateWatcher(BluetoothLEDevice::GetDeviceSelectorFromPairingState(true));
_deviceWatcherAdded = _deviceWatcher->Added += ref new TypedEventHandler<DeviceWatcher ^, DeviceInformation ^>(_shim, &CentralManagerCppCxShim::OnDeviceAdded);
_deviceWatcherRemoved = _deviceWatcher->Removed += ref new TypedEventHandler<DeviceWatcher ^, DeviceInformationUpdate ^>(_shim, &CentralManagerCppCxShim::OnDeviceRemoved);
_deviceWatcherUpdated = _deviceWatcher->Updated += ref new TypedEventHandler<DeviceWatcher ^, DeviceInformationUpdate ^>(_shim, &CentralManagerCppCxShim::OnDeviceUpdated);
_deviceWatcher->Stopped += ref new TypedEventHandler<DeviceWatcher ^, Object ^>(_shim, &CentralManagerCppCxShim::OnDeviceWatcherStopped);
_watcher = ref new BluetoothLEAdvertisementWatcher();
BluetoothLEManufacturerData ^manufacturerData = ref new BluetoothLEManufacturerData();
manufacturerData->CompanyId = MAKEWORD(HIBYTE(OUR_COMPANY_IDENTIFIER), LOBYTE(OUR_COMPANY_IDENTIFIER));
_watcher->AdvertisementFilter->Advertisement->ManufacturerData->Append(manufacturerData);
_watcher->Stopped += ref new TypedEventHandler<BluetoothLEAdvertisementWatcher ^, BluetoothLEAdvertisementWatcherStoppedEventArgs ^>(_shim, &CentralManagerCppCxShim::OnAdvertisementWatcherStopped);
_watcherReceived = _watcher->Received += ref new TypedEventHandler<BluetoothLEAdvertisementWatcher ^, BluetoothLEAdvertisementReceivedEventArgs ^>(_shim, &CentralManagerCppCxShim::OnAdvertisementReceived);
}
CentralManagerImpl::~CentralManagerImpl()
{
_watcher->Received -= _watcherReceived;
_watcher = nullptr;
_deviceWatcher->Updated -= _deviceWatcherUpdated;
_deviceWatcher->Removed -= _deviceWatcherRemoved;
_deviceWatcher->Added -= _deviceWatcherAdded;
_deviceWatcher = nullptr; // this crashes :(
}
void CentralManagerImpl::StartScan()
{
_deviceWatcher->Start();
_watcher->Start();
}
void CentralManagerImpl::StopScan()
{
_watcher->Stop();
_deviceWatcher->Stop();
}
这是我得到的调用堆栈(是的,它很讨厌):
ntdll.dll!_TppRaiseInvalidParameter@0()
ntdll.dll!_TpPostWork@4()
cfgmgr32.dll!TQuery::CloseQuery()
cfgmgr32.dll!_DevCloseObjectQuery@4()
Windows.Devices.Enumeration.dll!Watcher<class Windows::Devices::Enumeration::DeviceWatcher,struct Windows::Devices::Enumeration::IDeviceWatcher,struct Windows::Devices::Enumeration::IDeviceWatcher2,class Windows::Devices::Enumeration::DeviceInformation,struct Windows::Devices::Enumeration::IDeviceInformation,struct Windows::Devices::Enumeration::IDeviceInformation2,class DeviceInformationServer,class Windows::Devices::Enumeration::DeviceInformationUpdate,struct Windows::Devices::Enumeration::IDeviceInformationUpdate,class DeviceInformationUpdateServer,&unsigned short const * const RuntimeClass_Windows_Devices_Enumeration_DeviceWatcher>::Impl::EndQuery(void)
Windows.Devices.Enumeration.dll!Watcher<class Windows::Devices::Enumeration::DeviceWatcher,struct Windows::Devices::Enumeration::IDeviceWatcher,struct Windows::Devices::Enumeration::IDeviceWatcher2,class Windows::Devices::Enumeration::DeviceInformation,struct Windows::Devices::Enumeration::IDeviceInformation,struct Windows::Devices::Enumeration::IDeviceInformation2,class DeviceInformationServer,class Windows::Devices::Enumeration::DeviceInformationUpdate,struct Windows::Devices::Enumeration::IDeviceInformationUpdate,class DeviceInformationUpdateServer,&unsigned short const * const RuntimeClass_Windows_Devices_Enumeration_DeviceWatcher>::Impl::Stop(bool)
Windows.Devices.Enumeration.dll!Watcher<class Windows::Devices::Enumeration::DeviceWatcher,struct Windows::Devices::Enumeration::IDeviceWatcher,struct Windows::Devices::Enumeration::IDeviceWatcher2,class Windows::Devices::Enumeration::DeviceInformation,struct Windows::Devices::Enumeration::IDeviceInformation,struct Windows::Devices::Enumeration::IDeviceInformation2,class DeviceInformationServer,class Windows::Devices::Enumeration::DeviceInformationUpdate,struct Windows::Devices::Enumeration::IDeviceInformationUpdate,class DeviceInformationUpdateServer,&unsigned short const * const RuntimeClass_Windows_Devices_Enumeration_DeviceWatcher>::~Watcher<class Windows::Devices::Enumeration::DeviceWatcher,struct Windows::Devices::Enumeration::IDeviceWatcher,struct Windows::Devices::Enumeration::IDeviceWatcher2,class Windows::Devices::Enumeration::DeviceInformation,struct Windows::Devices::Enumeration::IDeviceInformation,struct Windows::Devices::Enumeration::IDeviceInformation2,class DeviceInformationServer,class Windows::Devices::Enumeration::Devic()
Windows.Devices.Enumeration.dll!Watcher<class Windows::Devices::Enumeration::DeviceWatcher,struct Windows::Devices::Enumeration::IDeviceWatcher,struct Windows::Devices::Enumeration::IDeviceWatcher2,class Windows::Devices::Enumeration::DeviceInformation,struct Windows::Devices::Enumeration::IDeviceInformation,struct Windows::Devices::Enumeration::IDeviceInformation2,class DeviceInformationServer,class Windows::Devices::Enumeration::DeviceInformationUpdate,struct Windows::Devices::Enumeration::IDeviceInformationUpdate,class DeviceInformationUpdateServer,&unsigned short const * const RuntimeClass_Windows_Devices_Enumeration_DeviceWatcher>::`vector deleting destructor'(unsigned int)
Windows.Devices.Enumeration.dll!Microsoft::WRL::Details::RuntimeClass<struct Microsoft::WRL::Details::InterfaceList<struct Windows::Devices::Enumeration::IDeviceWatcher,struct Microsoft::WRL::Details::InterfaceList<struct Windows::Devices::Enumeration::IDeviceWatcher2,struct Microsoft::WRL::Details::InterfaceList<struct Windows::Foundation::IClosable,struct Microsoft::WRL::Details::InterfaceList<class Microsoft::WRL::FtmBase,class Microsoft::WRL::Details::Nil> > > >,struct Microsoft::WRL::RuntimeClassFlags<3>,1,1,0>::Release(void)
mydll.dll!__abi_winrt_ptr_assign(void * * __ppTargetArg, const volatile Platform::Object ^ __objArg) Line 405
mydll.dll!CentralManagerImpl::~CentralManagerImpl() Line 64
[External Code]
//…
经过深入调查,我得出以下结论:
当 DLL 作为 UWP 工作线程被卸载(DLL_PROCESS_DETACH
)时,系统无法在 CentralManagerImpl
C++ class 的默认析构函数中销毁 DeviceWatcher
已经被系统终止了。我找到的唯一可行的解决方案是向我的 DLL 添加一个新的导出,以明确拆除我的 C++ class.
我会将此标记为已回答,因为我认为没有其他解决方法。