В приложении Node.js я должен быть уведомлен, когда аудиоустройство по умолчанию было изменено. Эта программа будет использоваться в Windows 7.
В настоящее время я пытаюсь сделать это путем создания надстройки C ++ для узла, которая через метод IMMNotificationClient :: OnDefaultDeviceChanged из Windows Core Audio API генерирует событие, которое может быть получено отправителем события Node.
Вот пример того, как будет выглядеть метод обратного вызова Windows Core Audio API:
HRESULT STDMETHODCALLTYPE OnDefaultDeviceChanged(EDataFlow flow, ERole role, LPCWSTR pwstrDeviceID)
{
//User written code that emits a node Event.
}
Вот некоторый идеальный псевдокод в стиле c ++, который я хотел бы иметь в приведенной выше функции обратного вызова:
EventEmitter.emit(v8::String::NewFromUtf8("defaultDeviceChanged"), deviceName);
Я не уверен, как это сделать, поэтому вот мой вопрос: как передать событие в приложение Node.js через функцию обратного вызова C ++?
Я открыт для других решений до тех пор, пока они могут работать на ноде (IE через надстройку) и будут работать в Windows 7, это означает, что внешние вызовы приложений, таких как nircmd, не обсуждаются.
РЕДАКТИРОВАТЬ
Это некоторый экспериментальный код, чтобы попытаться помочь описать, что я делаю:
AudioDeviceEmitter.h:
#pragma once
#include <stdio.h>
#include <wchar.h>
#include <tchar.h>
#include <time.h>
#include "windows.h"#include "Mmdeviceapi.h"#include "Propidl.h"#include "Functiondiscoverykeys_devpkey.h"#include <vector>
#include <string>
#include <comdef.h>
#define NAPI_DISABLE_CPP_EXCEPTIONS
#include <napi.h>
#include <vector>
class AudioDeviceEmitter : public Napi::ObjectWrap<AudioDeviceEmitter>, public IMMNotificationClient {
public:
static Napi::Object Init(Napi::Env env, Napi::Object exports);
AudioDeviceEmitter(const Napi::CallbackInfo& info);Napi::Value AudioDeviceEmitter::enrollInNotifications(const Napi::CallbackInfo& info);
Napi::Value AudioDeviceEmitter::unenrollInNotifications(const Napi::CallbackInfo& info);
//WIN API
~AudioDeviceEmitter()
{
if (_pEnumerator != NULL) {
_pEnumerator->UnregisterEndpointNotificationCallback(this);
_pEnumerator->Release();
}
}// IUnknown methods -- AddRef, Release, and QueryInterface
ULONG STDMETHODCALLTYPE AddRef()
{
return InterlockedIncrement(&_cRef);
}
ULONG STDMETHODCALLTYPE Release()
{
ULONG ulRef = InterlockedDecrement(&_cRef);
if (0 == ulRef)
{
delete this;
}
return ulRef;
}
HRESULT STDMETHODCALLTYPE QueryInterface(
REFIID riid, VOID **ppvInterface)
{
if (IID_IUnknown == riid)
{
AddRef();
*ppvInterface = (IUnknown*)this;
}
else if (__uuidof(IMMNotificationClient) == riid)
{
AddRef();
*ppvInterface = (IMMNotificationClient*)this;
}
else
{
*ppvInterface = NULL;
return E_NOINTERFACE;
}
return S_OK;
}
// Callback methods for device-event notifications.
//------------THIS IS A FUNCTION I AM TRYING TO GET TO WORK----------
//------------It doesnt currently work, but hopefully shows----------
//------------ what I'm trying to accomplish ----------
HRESULT STDMETHODCALLTYPE OnDefaultDeviceChanged(
EDataFlow flow, ERole role,
LPCWSTR pwstrDeviceId)
{
if (flow == eRender) {
_locked = true;
std::string name = "";
IMMDevice * pDevice = NULL;
HRESULT hr = _pEnumerator->GetDevice(pwstrDeviceId, &pDevice);
if(SUCCEEDED(hr)){
name = getFriendlyNameString(pDevice);
pDevice->Release();
}
for(int i = 0; i < _stillEnrolled.size(); i++) {
if(_stillEnrolled.at(i)) {
Napi::CallbackInfo & info = _enrolledSessions.at(i);
Napi::Env env = info.Env();
Napi::Function emit = info.This().As<Napi::Object>()
.Get("emit").As<Napi::Function>();
emit.Call(info.This(), { Napi::String::New(env, "defaultDeviceChanged"),
Napi::String::New(env, name.c_str())});
}
}
_locked = false;
}
return S_OK;
}
HRESULT STDMETHODCALLTYPE OnDeviceAdded(LPCWSTR pwstrDeviceId)
{
return S_OK;
};
HRESULT STDMETHODCALLTYPE OnDeviceRemoved(LPCWSTR pwstrDeviceId)
{
return S_OK;
}
HRESULT STDMETHODCALLTYPE OnDeviceStateChanged(
LPCWSTR pwstrDeviceId,
DWORD dwNewState)
{
return S_OK;
}
HRESULT STDMETHODCALLTYPE OnPropertyValueChanged(
LPCWSTR pwstrDeviceId,
const PROPERTYKEY key)
{
return S_OK;
}
private:
std::string getFriendlyNameString(IMMDevice *pDevice) {
HRESULT hr;
IPropertyStore *pStore;
hr = pDevice->OpenPropertyStore(STGM_READ, &pStore);
PROPVARIANT variant;
PropVariantInit(&variant);
hr = pStore->GetValue(PKEY_Device_FriendlyName, &variant);
size_t strlen = wcslen((wchar_t *)variant.pwszVal);
int throwAwaylen;
char *pOutBuffer = (char *)malloc(strlen);
wcstombs_s((size_t *)&throwAwaylen, pOutBuffer, size, wideCharArray, size);
std::string toReturn = pOutBuffer;
free(pOutBuffer);
PropVariantClear(&variant);
pStore->Release();
return toReturn;
}
LONG _cRef;
IMMDeviceEnumerator *_pEnumerator;
static Napi::FunctionReference constructor;
std::vector<Napi::CallbackInfo> _enrolledSessions;
std::vector<bool> _stillEnrolled;
bool _locked;
};
AudioDeviceEmitter.cpp
#include "AudioDeviceEmitter.h"
Napi::FunctionReference AudioDeviceEmitter::constructor;
Napi::Object AudioDeviceEmitter::Init(Napi::Env env, Napi::Object exports) {
Napi::HandleScope scope(env);
Napi::Function func = DefineClass(env, "AudioDeviceEmitter", {
InstanceMethod("enrollInNotifications", &AudioDeviceEmitter::enrollInNotifications),
InstanceMethod("unenrollInNotifications", &AudioDeviceEmitter::unenrollInNotifications)
});
constructor = Napi::Persistent(func);
constructor.SuppressDestruct();
exports.Set("AudioDeviceEmitter", func);
return exports;
}
AudioDeviceEmitter::AudioDeviceEmitter(const Napi::CallbackInfo& info)
: Napi::ObjectWrap<AudioDeviceEmitter>(info), _locked(false), _cRef(1),
_pEnumerator(NULL)
{
HRESULT hr = CoInitialize(NULL);
hr = CoCreateInstance(__uuidof(MMDeviceEnumerator), NULL,
CLSCTX_ALL, __uuidof(IMMDeviceEnumerator), (void**)&_pEnumerator);
_pEnumerator->RegisterEndpointNotificationCallback(this);
}
//------------THIS IS A FUNCTION I AM TRYING TO GET TO WORK----------
//------------It doesnt currently work, but hopefully shows----------
//------------ what I'm trying to accomplish ----------
Napi::Value AudioDeviceEmitter::enrollInNotifications(const Napi::CallbackInfo& info) {
while(_locked){}
_locked = true;
int currentPos = _enrolledSessions.size();
_enrolledSessions.push_back(info);
_stillEnrolled.push_back(true);
_locked = false;
while(_stillEnrolled.at(currentPos)){}
return Napi::String::New(info.Env(), "OK");
}//------------THIS IS A FUNCTION I AM TRYING TO GET TO WORK----------
//------------It doesnt currently work, but hopefully shows----------
//------------ what I'm trying to accomplish ----------
Napi::Value AudioDeviceEmitter::unenrollInNotifications(const Napi::CallbackInfo& info) {
while(_locked){}
for(int i = 0; i < _enrolledSessions.size(); i++) {
if (info.This() == _enrolledSessions.at(i).This()) {
_stillEnrolled.at(i) = false;
}
}
return Napi::String::New(info.Env(), "OK");
}
binding.cpp:
#include <napi.h>
#include "AudioDeviceEmitter.h"
Napi::Object InitNAPI(Napi::Env env, Napi::Object exports) {
AudioDeviceEmitter::Init(env, exports);
return exports;
}
NODE_API_MODULE(WinDefaultAudioDevice, InitNAPI)
Этот код выдает ошибки и не компилируется, мы надеемся более подробно объяснить мою проблему.
Реализуйте один из классов Nan’s AsyncWorker. Для некоторого примера кода о том, как это сделать, посмотрите на этот ответ. после реализации работника назначьте функцию обратного вызова асинхронному работнику.
Других решений пока нет …