STDAPI XAsyncGetResult()

in Source/Task/AsyncLib.cpp [1044:1147]


STDAPI XAsyncGetResult(
    _Inout_ XAsyncBlock* asyncBlock,
    _In_opt_ const void* identity,
    _In_ size_t bufferSize,
    _Out_writes_bytes_to_opt_(bufferSize, *bufferUsed) void* buffer,
    _Out_opt_ size_t* bufferUsed
    ) noexcept
{
    HRESULT result = E_PENDING;
    AsyncStateRef state;
    bool resultsAlreadyReturned;

    {
        AsyncBlockInternalGuard internal{ asyncBlock };
        result = internal.GetStatus();
        state = internal.GetState();
        resultsAlreadyReturned = internal.GetResultsRetrieved();
    }

    if (SUCCEEDED(result))
    {
        // If the call was successful and we've already returned
        // results, fail now to prevent us interpreting a null
        // state object as a zero payload success.  Note if the
        // call had completed with zero payload it gets cleaned
        // up immediately and therefore a call to XAsyncGetResult
        // never extracts and sets the results as retrieved.  This
        // means you can safely call this multiple times for calls
        // that had no result payload, which is consistent with
        // how XAsyncGetStatus works.  We only want to guard against
        // the case where results were offered once, but can't be
        // offered again now that we've shut the call down.

        RETURN_HR_IF(E_ILLEGAL_METHOD_CALL, resultsAlreadyReturned);

        if (state == nullptr)
        {
            if (bufferUsed != nullptr)
            {
                *bufferUsed = 0;
            }
        }
        else if (identity != state->identity)
        {
            // Call/Result mismatch.  This XAsyncBlock was initiated by state->identityName
            char buf[100];
            int sprintfResult;
            if (state->identityName != nullptr)
            {
                sprintfResult = snprintf(
                    buf,
                    sizeof(buf),
                    "Call/Result mismatch.  This XAsyncBlock was initiated by '%s'.\r\n",
                    state->identityName);
            }
            else
            {
                sprintfResult = snprintf(buf, sizeof(buf), "Call/Result mismatch\r\n");
            }

            result = E_INVALIDARG;
            ASYNC_LIB_TRACE(result, buf);
            ASSERT(false);
            ASSERT(sprintfResult > 0);
        }
        else if (state->providerData.bufferSize == 0)
        {
            // Caller has not supplied a payload
            result = E_NOT_SUPPORTED;
        }
        else if (buffer == nullptr)
        {
            result = E_INVALIDARG;
        }
        else if (bufferSize < state->providerData.bufferSize)
        {
            result = E_NOT_SUFFICIENT_BUFFER;
        }
        else
        {
            if (bufferUsed != nullptr)
            {
                *bufferUsed = state->providerData.bufferSize;
            }

            state->providerData.bufferSize = bufferSize;
            state->providerData.buffer = buffer;
            result = state->provider(XAsyncOp::GetResult, &state->providerData);
        }
    }

    // Cleanup state if needed.
    if (result != E_PENDING && state != nullptr)
    {
        {
            AsyncBlockInternalGuard internal{ asyncBlock };
            internal.ExtractState(true);
        }

        CleanupState(std::move(state));
    }

    return result;
}