HRESULT CALLBACK AuthorizationCallback(
    __in const WS_OPERATION_CONTEXT* context, 
    __out BOOL* authorized, 
    __in_opt WS_ERROR* error)
{
    HRESULT hr = S_OK;
    const WS_STRING fixedUsername = WS_STRING_VALUE(L"usr1");
    WS_MESSAGE* message = NULL;
    WS_STRING usernameIdentity = {};
    *authorized = FALSE;
    
    hr = WsGetOperationContextProperty(context, WS_OPERATION_CONTEXT_PROPERTY_INPUT_MESSAGE, &message, sizeof(message), error);
    if (FAILED(hr))
    {
        return hr;
    }
    
    hr = WsGetMessageProperty(message, WS_MESSAGE_PROPERTY_USERNAME, &usernameIdentity, sizeof(usernameIdentity), error);
    if (FAILED(hr))
    {
        return hr;
    }
        
    *authorized = CompareWsString(&usernameIdentity, &fixedUsername);
    return S_OK;
}
HRESULT CALLBACK GetOrderStatusImpl(
    __in const WS_OPERATION_CONTEXT* context,
    __inout unsigned int* orderID,
    __out_opt __deref __nullterminated WCHAR** status,
    __in_opt const WS_ASYNC_CONTEXT* asyncContext,
    __in_opt WS_ERROR* error)
{
    UNREFERENCED_PARAMETER(asyncContext);

    WS_HEAP* heap = NULL;
    HRESULT hr = S_OK;
    
    *orderID = *orderID;
    
    hr = WsGetOperationContextProperty(context, WS_OPERATION_CONTEXT_PROPERTY_HEAP, &heap, sizeof(heap), error);
    if (FAILED(hr))
    {
        return hr;
    }
    
    hr = WsAlloc(heap, sizeof(OrderStatusString), (void**)status, error);
    if (FAILED(hr))
    {
        return hr;
    }
    
    hr = StringCbCopyW(*status, sizeof(OrderStatusString), OrderStatusString);
    if (FAILED(hr))
    {
        return hr;
    }
    
    return S_OK;
}
// In this sample, wsutil is used with the /string:WS_STRING command line option
// to compile the schema files. When /string:WS_STRING is used, wsutil generates stubs
// using WS_STRING (instead of WCHAR*) type for strings.
HRESULT CALLBACK PurchaseOrderImpl(
    _In_ const WS_OPERATION_CONTEXT* context,
    _In_ int quantity,
    _In_ WS_STRING productName,
    _Out_ unsigned int* orderID,
    _In_ WS_STRING* expectedShipDate,
    _In_ const WS_ASYNC_CONTEXT* asyncContext,
    _In_ WS_ERROR* error)
{
    UNREFERENCED_PARAMETER(asyncContext);

    WS_HEAP* heap = NULL;
    HRESULT hr = S_OK;

    wprintf(L"%ld, %.*s\n",
        quantity,
        productName.length,
        productName.chars);
    fflush(stdout);

    hr = WsGetOperationContextProperty(
        context,
        WS_OPERATION_CONTEXT_PROPERTY_HEAP,
        &heap,
        sizeof(heap),
        error);
    if (FAILED(hr))
    {
        goto Exit;
    }

    hr = WsAlloc(
        heap,
        sizeof(ExpectedShipDate),
        (void**)&expectedShipDate->chars,
        error);
    if (FAILED(hr))
    {
        goto Exit;
    }

    hr = StringCbCopyW(
        expectedShipDate->chars,
        sizeof(ExpectedShipDate),
        ExpectedShipDate);
    if (FAILED(hr))
    {
        goto Exit;
    }

    *orderID = 123;
    expectedShipDate->length = (ULONG)wcslen(ExpectedShipDate);

Exit:
    return hr;
}
HRESULT CALLBACK PurchaseOrderImpl(
    __in const WS_OPERATION_CONTEXT* context,
    __in int quantity,
    __in_opt __nullterminated WCHAR* productName,
    __out unsigned int* orderID,
    __out_opt __deref __nullterminated WCHAR** expectedShipDate,
    __in_opt const WS_ASYNC_CONTEXT* asyncContext,
    __in_opt WS_ERROR* error)
{
    UNREFERENCED_PARAMETER(asyncContext);

    WS_HEAP* heap = NULL;
    HRESULT hr = S_OK;
    
    wprintf(L"%ld, %s\n", quantity, productName);
    fflush(stdout);
    
    hr = WsGetOperationContextProperty(
        context, 
        WS_OPERATION_CONTEXT_PROPERTY_HEAP, 
        &heap, 
        sizeof(heap), 
        error);
if (FAILED(hr))
{
    goto Exit;
}
    
    hr = WsAlloc(
        heap, 
        sizeof(ExpectedShipDate), 
        (void**)expectedShipDate, 
        error);
if (FAILED(hr))
{
    goto Exit;
}
    
    hr = StringCbCopyW(
        *expectedShipDate, 
        sizeof(ExpectedShipDate), 
        ExpectedShipDate);
if (FAILED(hr))
{
    goto Exit;
}
    
    *orderID = 123;
    
Exit:
    return hr;
}
HRESULT CALLBACK FreeSessionCalculator(
    __in const WS_OPERATION_CONTEXT* context,
    __in const WS_ASYNC_CONTEXT* asyncContext)
{
    UNREFERENCED_PARAMETER(asyncContext);

    SessionfulCalculator* calculator = NULL;
    WsGetOperationContextProperty(
        context,
        WS_OPERATION_CONTEXT_PROPERTY_CHANNEL_USER_STATE,
        &calculator,
        sizeof(SessionfulCalculator*),
        NULL);
    if (calculator != NULL)
    {
        wprintf(L"Deleting Calculator instance\n");
        delete calculator;
    }

    SetEvent(closeServer);
    return S_OK;
}
    static HRESULT CALLBACK Clear(
        __in const WS_OPERATION_CONTEXT* context,
        __in const WS_ASYNC_CONTEXT* asyncContext,
        __in_opt WS_ERROR* error)
    {
        UNREFERENCED_PARAMETER(asyncContext);

        HRESULT hr = S_OK;
        SessionfulCalculator* calculator = NULL;
        hr = WsGetOperationContextProperty(
                 context,
                 WS_OPERATION_CONTEXT_PROPERTY_CHANNEL_USER_STATE,
                 &calculator,
                 sizeof(SessionfulCalculator*),
                 error);
        if (FAILED(hr))
        {
            goto Exit;
        }
        hr = calculator->Clear();
Exit:
        return hr;
    }
// In this sample, wsutil is used with the /string:WS_STRING command line option
// to compile the schema files. When /string:WS_STRING is used, wsutil generates stubs
// using WS_STRING (instead of WCHAR*) type for strings.
HRESULT CALLBACK GetOrderStatusImpl(
    _In_ const WS_OPERATION_CONTEXT* context,
    _Inout_ unsigned int* orderID,
    _Inout_ WS_STRING* status,
    _In_ const WS_ASYNC_CONTEXT* asyncContext,
    _In_ WS_ERROR* error)
{
    UNREFERENCED_PARAMETER(asyncContext);

    WS_HEAP* heap = NULL;
    HRESULT hr = S_OK;

    // Generate a fault if we don't recognize the order ID
    if (*orderID != 123)
    {
        // Fill out details about the fault
        _OrderNotFoundFaultType orderNotFound;
        orderNotFound.orderID = *orderID;
        
        static const WS_XML_STRING _faultDetailName = WS_XML_STRING_VALUE("OrderNotFound");
        static const WS_XML_STRING _faultDetailNs = WS_XML_STRING_VALUE("http://example.com");
        static const WS_XML_STRING _faultAction = WS_XML_STRING_VALUE("http://example.com/fault");
        static const WS_ELEMENT_DESCRIPTION _faultElementDescription = 
        { 
            (WS_XML_STRING*)&_faultDetailName, 
            (WS_XML_STRING*)&_faultDetailNs, 
            WS_UINT32_TYPE, 
            NULL 
        };
        static const WS_FAULT_DETAIL_DESCRIPTION orderNotFoundFaultTypeDescription = 
        { 
            (WS_XML_STRING*)&_faultAction, 
            (WS_ELEMENT_DESCRIPTION*)&_faultElementDescription 
        };
        
        // Set fault detail information in the error object
        hr = WsSetFaultErrorDetail(
            error,
            &orderNotFoundFaultTypeDescription,
            WS_WRITE_REQUIRED_VALUE,
            &orderNotFound,
            sizeof(orderNotFound));
        
        if (FAILED(hr))
        {
            goto Exit;
        }
        
        // Add an error string to the error object.  This string will
        // be included in the fault that is sent.
        static const WS_STRING errorMessage = WS_STRING_VALUE(L"Invalid order ID");
        hr = WsAddErrorString(error, &errorMessage);
        
        if (FAILED(hr))
        {
            goto Exit;
        }

        hr = E_FAIL;
        goto Exit;
    }

    *orderID = *orderID;

    hr = WsGetOperationContextProperty(
        context,
        WS_OPERATION_CONTEXT_PROPERTY_HEAP,
        &heap,
        sizeof(heap),
        error);
    if (FAILED(hr))
    {
        goto Exit;
    }

    hr = WsAlloc(
        heap,
        sizeof(OrderStatusString),
        (void**)&status->chars,
        error);
    if (FAILED(hr))
    {
        goto Exit;
    }

    hr = StringCbCopyW(
        status->chars,
        sizeof(OrderStatusString),
        OrderStatusString);
    if (FAILED(hr))
    {
        goto Exit;
    }

    status->length = (ULONG)wcslen(OrderStatusString);
Exit:
    return hr;
}
HRESULT CALLBACK ProcessMessage(
    __in const WS_OPERATION_CONTEXT* context, 
    __in_opt const WS_ASYNC_CONTEXT* asyncContext, 
    __in_opt WS_ERROR* error)
{
    UNREFERENCED_PARAMETER(asyncContext);
    
    WS_CHANNEL* channel = NULL;
    HRESULT hr = S_OK;
    WS_MESSAGE* requestMessage = NULL;
    WS_MESSAGE* replyMessage = NULL;
    WS_HEAP* heap;
    
    // Get the request mesasge
    hr = WsGetOperationContextProperty(
        context, 
        WS_OPERATION_CONTEXT_PROPERTY_INPUT_MESSAGE, 
        &requestMessage, 
        sizeof(requestMessage), 
        error);
    if (FAILED(hr))
    {
        return hr;
    }
    
    // Get the channel 
    hr = WsGetOperationContextProperty(
        context, 
        WS_OPERATION_CONTEXT_PROPERTY_CHANNEL, 
        &channel, 
        sizeof(channel), 
        error);
    if (FAILED(hr))
    {
        return hr;
    }
    
    // Get the heap 
    hr = WsGetOperationContextProperty(
        context, 
        WS_OPERATION_CONTEXT_PROPERTY_HEAP, 
        &heap, 
        sizeof(heap), 
        error);
    if (FAILED(hr))
    {
        return hr;
    }
    
    // Create a reply message
    hr = WsCreateMessageForChannel(
        channel,
        NULL, 
        0, 
        &replyMessage, 
        error);
    if (FAILED(hr))
    {
        goto Exit;
    }
    
    // Initialize reply message
    hr = WsInitializeMessage(replyMessage, WS_REPLY_MESSAGE, requestMessage, error);
    if (FAILED(hr))
    {
        goto Exit;
    }
        
    // Get the HTTP Verb
    WS_STRING verb;
    hr = WsGetMappedHeader(
        requestMessage, 
        &verbHeaderName, 
        WS_SINGLETON_HEADER,
        0,
        WS_STRING_TYPE,
        WS_READ_REQUIRED_VALUE, 
        NULL, 
        &verb, 
        sizeof(verb), 
        error);
    if (FAILED(hr))
    {
        goto Exit;
    }
    
    wprintf(L"Verb: %.*s\n", verb.length, verb.chars);
    
    if (CompareString(LOCALE_INVARIANT, NORM_IGNORECASE, verb.chars, verb.length, L"GET", 3) != CSTR_EQUAL)
    {
        // Unrecognized verb
        hr = SendFailureMessage(channel, replyMessage, 405, L"Method Not Allowed", error);
        goto Exit;
    }
    
    // Read end of request message
    hr = WsReadMessageEnd(channel, requestMessage, NULL, error);
    if (FAILED(hr))
    {
        goto Exit;
    }
    
    // Set content type header of reply
    hr = WsAddMappedHeader(
        replyMessage,
        &contentTypeHeaderName, 
        WS_STRING_TYPE,
        WS_WRITE_REQUIRED_VALUE,
        &replyContentType,
        sizeof(replyContentType),
        error);
    if (FAILED(hr))
    {
        goto Exit;
    }
    
    // Set status of reply
    hr = SetStatus(replyMessage, 200, L"OK", error);
    if (FAILED(hr))
    {
        goto Exit;
    }
    
    // Set up the bytes to return in the HTTP body
    WS_BYTES body;
    body.bytes = const_cast<BYTE*>(replyBodyBytes);
    body.length = sizeof(replyBodyBytes);
    
    // Send the reply message
    hr = WsSendMessage(
        channel, 
        replyMessage, 
        &bytesMessageDescription, 
        WS_WRITE_REQUIRED_VALUE,
        &body,
        sizeof(body),
        NULL, 
        error);
    if (FAILED(hr))
    {
        goto Exit;
    }
    
Exit:
    if (replyMessage != NULL)
    {
        WsFreeMessage(replyMessage);
    }
    return hr;    
}
HRESULT CALLBACK ProcessMessage(
    __in const WS_OPERATION_CONTEXT* context, 
    __in_opt const WS_ASYNC_CONTEXT* asyncContext, 
    __in_opt WS_ERROR* error)
{
    UNREFERENCED_PARAMETER(asyncContext);

    WS_CHANNEL* channel = NULL;
    HRESULT hr = NOERROR;
    WS_HEAP* heap = NULL;
    WS_MESSAGE* replyMessage = NULL;

    hr = WsGetOperationContextProperty(
        context, 
        WS_OPERATION_CONTEXT_PROPERTY_HEAP, 
        &heap, 
        sizeof(heap), 
        error);
if (FAILED(hr))
{
    goto Exit;
}
    
    WS_MESSAGE* requestMessage = NULL;
    hr = WsGetOperationContextProperty(
        context, 
        WS_OPERATION_CONTEXT_PROPERTY_INPUT_MESSAGE, 
        &requestMessage, 
        sizeof(requestMessage), 
        error);
if (FAILED(hr))
{
    goto Exit;
}
    
    hr = WsGetOperationContextProperty(
    context, 
    WS_OPERATION_CONTEXT_PROPERTY_CHANNEL, 
    &channel, 
    sizeof(channel), 
    error);
if (FAILED(hr))
{
    goto Exit;
}
    
hr = WsCreateMessageForChannel(
    channel,
    NULL, 
    0, 
    &replyMessage, 
    error);
if (FAILED(hr))
{
    goto Exit;
}
    
    // Get action value
    WS_XML_STRING receivedAction;
    hr = WsGetHeader(
        requestMessage, 
        WS_ACTION_HEADER, 
        WS_XML_STRING_TYPE,
        WS_READ_REQUIRED_VALUE, 
        NULL, 
        &receivedAction, 
        sizeof(receivedAction), 
        error);
if (FAILED(hr))
{
    goto Exit;
}

    // Make sure action is what we expect
    if (WsXmlStringEquals(
        &receivedAction, 
        PurchaseOrder_wsdl.messages.PurchaseOrder.action, 
        error) != S_OK)
    {
        hr = WS_E_ENDPOINT_ACTION_NOT_SUPPORTED;
        goto Exit;
    }

    // Read purchase order
    _PurchaseOrderType purchaseOrder;
    hr = WsReadBody(
        requestMessage, 
        &PurchaseOrder_wsdl.globalElements.PurchaseOrderType, 
        WS_READ_REQUIRED_VALUE, 
        heap, &purchaseOrder, 
        sizeof(purchaseOrder), 
        error);
if (FAILED(hr))
{
    goto Exit;
}
    
    // Read end of message
    hr = WsReadMessageEnd(
        channel, 
        requestMessage, 
        NULL, 
        error);
if (FAILED(hr))
{
    goto Exit;
}
    
    // Print out purchase order contents
    wprintf(L"%ld, %s\n", 
        purchaseOrder.quantity, 
        purchaseOrder.productName);
    
    // Initialize order confirmation data
    _OrderConfirmationType orderConfirmation;
    orderConfirmation.expectedShipDate = L"1/1/2006";
    orderConfirmation.orderID = 123;

    // Send a reply message
    hr = WsSendReplyMessage(
        channel, 
        replyMessage, 
        &PurchaseOrder_wsdl.messages.OrderConfirmation, 
        WS_WRITE_REQUIRED_VALUE,
        &orderConfirmation, 
        sizeof(orderConfirmation),
        requestMessage, 
        NULL, 
        error);
if (FAILED(hr))
{
    goto Exit;
}
    
    hr = WS_S_END;
    
Exit:
    fflush(stdout);
    if (replyMessage != NULL)
    {
        WsFreeMessage(
            replyMessage);
    }
    return hr;    
}