IPortableDeviceEventCallback не работает должным образом (странное поведение)

Я хочу получить обратный вызов в моем приложении, когда была сделана фотография на подключенном мобильном телефоне (WPD_EVENT_OBJECT_ADDED). Я реализовал клиент WPD и IPortableDeviceEventCallback, как показано в Центре разработки Windows.

Теперь моя проблема заключается в том, что метод IPortableDeviceEventCallback::OnEvent() вызывается только в том случае, если я открыл папку DCIM/Camera на телефоне через проводник Windows и проводник начал создавать миниатюры для изображений. После того, как я сделал это один раз, я получаю каждое событие с телефона, пока не отключу и не подключу его снова. Но если я этого не сделаю, onEvent() никогда не вызывается, за исключением того, что я отключаю телефон.

Протестировано на разных телефонах Android и iOS. У кого-нибудь есть идея, что происходит?

DeviceEventsCallback::DeviceEventsCallback(MyPortableDevice* parent) : IPortableDeviceEventCallback(), cRef(1)
{
    parentDevice = parent;
}

DeviceEventsCallback::~DeviceEventsCallback()
{

}

HRESULT __stdcall DeviceEventsCallback::QueryInterface(const IID& riid, LPVOID* ppvObj)
{
    static const QITAB qitab[] = {
        QITABENT(DeviceEventsCallback, IPortableDeviceEventCallback),
        { },
    };

    return QISearch(this, qitab, riid, ppvObj);

    //    HRESULT hr = S_OK;
    //    if (ppvObj == NULL) {
    //        hr = E_INVALIDARG;
    //        return hr;
    //    }

    //    if ((riid == IID_IUnknown) ||
    //        (riid == IID_IPortableDeviceEventCallback)) {
    //        AddRef();
    //        *ppvObj = this;
    //    }
    //    else {
    //        *ppvObj = NULL;
    //        hr = E_NOINTERFACE;
    //    }
    //    return hr;
}

ULONG __stdcall DeviceEventsCallback::AddRef()
{
    InterlockedIncrement((long*) &cRef);
    return cRef;
}

ULONG __stdcall DeviceEventsCallback::Release()
{
    ULONG refCount = cRef - 1;
    long ref = InterlockedDecrement(&cRef);

    if (ref == 0) {
        delete this;
        return 0;
    }

    return refCount;
}

HRESULT __stdcall DeviceEventsCallback::OnEvent(IPortableDeviceValues* pEventParameters)
{       
    HRESULT hr = S_OK;

    if (pEventParameters == NULL) {
        hr = E_POINTER;
        return hr;
    }

    // The pEventParameters collection contains information about the event that was
    // fired. We'll at least need the EVENT_ID to figure out which event was fired
    // and based on that retrieve additional values from the collection

    // Display the event that was fired
    GUID EventId;

    if (EventId == WPD_EVENT_DEVICE_CAPABILITIES_UPDATED) {
        return S_OK;
    }

    if (hr == S_OK) {
        hr = pEventParameters->GetGuidValue(WPD_EVENT_PARAMETER_EVENT_ID, &EventId);
    }

    if (EventId == WPD_EVENT_DEVICE_REMOVED) {
        return S_OK;
    }

    LPWSTR pwszEventId = NULL;

    if (hr == S_OK) {
        hr = StringFromCLSID(EventId, &pwszEventId);
    }

    if (pwszEventId != NULL) {
        CoTaskMemFree(pwszEventId);
    }

    // Display the ID of the object that was affected
    // We can also obtain WPD_OBJECT_NAME, WPD_OBJECT_PERSISTENT_UNIQUE_ID,
    // WPD_OBJECT_PARENT_ID, etc.
    LPWSTR pwszObjectId = NULL;

    if (hr == S_OK) {
        hr = pEventParameters->GetStringValue(WPD_OBJECT_ID, &pwszObjectId);
    }

    if (parentDevice != nullptr && pwszObjectId != nullptr && EventId == WPD_EVENT_OBJECT_ADDED) {
        qDebug() << "invoked method";
        QMetaObject::invokeMethod(parentDevice, "onNewFileOnDevice", Qt::DirectConnection, Q_ARG(QString, QString::fromStdWString(pwszObjectId)));
    }

    if (pwszObjectId != NULL) {
        CoTaskMemFree(pwszObjectId);
    }

    // Note that we intentionally do not call Release on pEventParameters since we
    // do not own it

    return hr;
}  

И в моей реализации MTP я регистрирую свой DeviceEventsCallback() eventNotifier

void MyPortableDevice::registerEventNotification(ComPtr<IPortableDevice> pDevice)
{
    HRESULT hr = S_OK;
    PWSTR tempEventCookie = nullptr;

    if (pwszEventCookie != nullptr || pDevice == nullptr) {
        return;
    }

    eventNotifier = new(std::nothrow) DeviceEventsCallback(this);

    if (eventNotifier == nullptr) {
        hr = E_OUTOFMEMORY;
    }

    if (hr == S_OK) {
        hr = pDevice->Advise(0, eventNotifier, nullptr, &tempEventCookie);
    }

    if (hr == S_OK) {
        pwszEventCookie = tempEventCookie;
        tempEventCookie = nullptr; // relinquish memory to the caller
    }
    else {
        // Free the event registration cookie because some error occurred
        CoTaskMemFree(tempEventCookie);
        tempEventCookie = nullptr;
    }
}

void MyPortableDevice::unregisterEventsNotification(ComPtr<IPortableDevice> pDevice)
{
    if (pDevice == nullptr || pwszEventCookie == nullptr) {
        return;
    }

    HRESULT hr = pDevice->Unadvise(pwszEventCookie);

    CoTaskMemFree(pwszEventCookie);
    pwszEventCookie = nullptr;
}

Моя функция open() выглядит так:

bool MyPortableDevice::open(IPortableDevice** ppDevice)
{
    retrieveClientInformation(&clientInformation);
    IPortableDevice* pDevice = nullptr;

    HRESULT hr = CoCreateInstance(CLSID_PortableDeviceFTM, nullptr, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&pDevice));

    if (SUCCEEDED(hr)) {
        wchar_t* wID = new wchar_t[ID.length() + 1];
        ID.toWCharArray(wID);
        wID[ID.length()] = '\0';

        hr = pDevice->Open(wID, clientInformation.Get());

        if (hr == E_ACCESSDENIED) {
            qDebug() << "Failed to Open the device for Read Write access, will open it for Read-only access instead" << hr;
            clientInformation->SetUnsignedIntegerValue(WPD_CLIENT_DESIRED_ACCESS, GENERIC_READ);
            hr = pDevice->Open(wID, clientInformation.Get());

            readOnly = true;
        }
        if (SUCCEEDED(hr)) {
            // The device successfully opened, obtain an instance of the Device into
            // ppDevice so the caller can be returned an opened IPortableDevice.
            hr = pDevice->QueryInterface(IID_IPortableDevice, (VOID**)ppDevice);
            if (FAILED(hr)) {
                qDebug() << "Failed to QueryInterface the opened IPortableDevice";
                return  false;
            }
        }

        if (pDevice != nullptr) {
            pDevice->Release();
            pDevice = nullptr;
        }

        delete [] wID;
        wID = nullptr;

        if (clientInformation != nullptr) {
            clientInformation.Reset();
            clientInformation = nullptr;
        } 

        return true;
    }
    else {
        qDebug() << "! Failed to CoCreateInstance CLSID_PortableDeviceFTM, hr = 0x%lx\n" << hr;
        return false;
    }
} 

0 ответов

Другие вопросы по тегам