WinInet异步处理

wininet不仅支持同步的处理方式,也支持异步的处理方式。具体方法如下

1. 调用InternetOpen时设置参数INTERNET_FLAG_ASYNC

hSession = InternetOpen(szAgent, INTERNET_OPEN_TYPE_PRECONFIG, NULL, NULL, INTERNET_FLAG_ASYNC);
2. 设置回调函数

INTERNET_STATUS_CALLBACK pOldStatusCallback = InternetSetStatusCallback(hSession, InternetStatusCallback);
3. 调用InternetConnect打开一个会话

hConnect = InternetConnect(hSession, _T("down.sandai.net"), 80, NULL, NULL, INTERNET_SERVICE_HTTP, 0, dwContext);
根据文档,若为异步操作且dwContext为0时,回调函数不会被调用,但是InternetConnect依然以异步模式执行。但我在win7 x64上的测试结果是当dwContext为0时,回调函数的确不会被调用,但是InternetConnect已同步方式执行。若dwContext不为0时,回调函数被调用,但InternetConnect依然是同步方式执行,回调函数就在InternetConnect上下文中执行。稳妥的方案还是设置非0的dwContext参数,并根据InternetConnect返回值及错误码进行下一步处理。

4. 打开请求

	hResource = HttpOpenRequest(hConnect, _T("GET"),
		_T("/thunder9/Thunder9.0.16.408.exe"),
		_T("HTTP/1.1"),
		NULL,
		szAcceptType,
		INTERNET_FLAG_KEEP_CONNECTION | INTERNET_FLAG_RELOAD, (DWORD_PTR)ptc);

请判断返回值及错误码。

5. 发送请求

	bResult = HttpSendRequest(hResource, NULL, 0, NULL, 0);
	if (!bResult)
	{
.............................
		WaitForSingleObject(ptc->hRequestCompleteEvent, INFINITE);
	}
6 读取Reponse Header

WCHAR ResponseHeader[2000];
	DWORD dwSize = 2000;
	bResult = HttpQueryInfo(hResource, HTTP_QUERY_RAW_HEADERS_CRLF,
		ResponseHeader, &dwSize, NULL);
函数HttpQueryInfo为同步执行函数。

7 读取数据

char buffer[1024];
	BOOL bVerbose = FALSE;
	BOOL bAllDone=FALSE;
	DWORD dwBytesTransfered;

	do
	{
	    INTERNET_BUFFERS InetBuff;
	    FillMemory(&InetBuff, sizeof(InetBuff), 0);
	    InetBuff.dwStructSize = sizeof(InetBuff);
	    InetBuff.lpvBuffer = buffer;
	    InetBuff.dwBufferLength = sizeof(buffer) - 1;

	    if (!InternetReadFileEx(hResource,
	                        &InetBuff,
	                        0, (DWORD_PTR)ptc))
	    {
	        if (GetLastError() == ERROR_IO_PENDING)
	        {
	            WaitForSingleObject(ptc->hRequestCompleteEvent, INFINITE);
	        }
	        else
	        {
	            MessageBox(NULL,_T("InternetReadFileEx Error"),_T("Pause"), MB_OK|MB_ICONHAND);;
	        }
	    }

		WriteFile(hFile, buffer, InetBuff.dwBufferLength, &dwBytesTransfered, NULL);

	    if (InetBuff.dwBufferLength == 0) 
	        bAllDone = TRUE;

	} while (bAllDone == FALSE);

根据我的测试,如果InternetReadFileEx设置了一个不同于HttpOpenRequest的上下文,回调函数接收的依然是HttpOpenRequest设置的上下文。请注意InternetReadFileEx完成读操作后,结构体INTERNET_BUFFERS成员dwBufferLength被设置成读取的字节长度大小。

根据文档说明,可用于异步操作函数列表中不包括HttpSendRequest和InternetReadFile。但是我在win7 上测试的结果是这两个函数是以异步方式执行的。所以稳妥的方案还是检查函数的返回值及错误码。

在异步操作中使用InternetReadFile是需要注意,参数lpdwNumberOfBytesRead不应该为NULL,否则产生参数错误。而不为NULL的时候,异步回调函数会设置读取的字节数值,所以此内存在调用回调函数时必须有效。

我已上写的处理流程只是异步的一种实现方案而已,文档中有一个样例async,我觉得实现更异步。特将其代码粘贴如下:

/*++
 Copyright (c) 2002 - 2006 Microsoft Corporation.  All Rights Reserved.

 THIS CODE AND INFORMATION IS PROVIDED "AS-IS" WITHOUT WARRANTY OF
 ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
 THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
 PARTICULAR PURPOSE.

 THIS CODE IS NOT SUPPORTED BY MICROSOFT. 

--*/
#include <windows.h>

#ifdef DBG
#define ASYNC_ASSERT(x)   \
            if(!x) \
                DebugBreak();
#else
#define ASYNC_ASSERT(x)
#endif

#define BUFFER_LEN  4096
#define ERR_MSG_LEN 512

#define METHOD_NONE 0
#define METHOD_GET  1
#define METHOD_POST 2

#define REQ_STATE_SEND_REQ             0
#define REQ_STATE_SEND_REQ_WITH_BODY   1
#define REQ_STATE_POST_GET_DATA        2
#define REQ_STATE_POST_SEND_DATA       3
#define REQ_STATE_POST_COMPLETE        4
#define REQ_STATE_RESPONSE_RECV_DATA   5
#define REQ_STATE_RESPONSE_WRITE_DATA  6
#define REQ_STATE_COMPLETE             7

#define DEFAULT_TIMEOUT 2 * 60 * 1000 // Two minutes

#define DEFAULT_HOSTNAME L"www.microsoft.com"
#define DEFAULT_RESOURCE L"/"

#define DEFAULT_OUTPUT_FILE_NAME L"response.htm"
#define SPIN_COUNT 4000

//
// Structure to store configuration in that was gathered from
// passed in arguments
//

typedef struct _CONFIGURATION
{
    DWORD Method;                 // Method, GET or POST
    LPWSTR HostName;              // Host to connect to
    LPWSTR ResourceOnServer;      // Resource to get from the server
    LPWSTR InputFileName;         // File containing data to post
    LPWSTR OutputFileName;        // File to write the data received from the server
    BOOL UseProxy;                // Flag to indicate the use of a proxy
    LPWSTR ProxyName;             // Name of the proxy to use
    BOOL IsSecureConnection;      // Flag to indicate the use of SSL
    DWORD UserTimeout;            // Timeout for the async operations
} CONFIGURATION, *PCONFIGURATION;

//
// Structure used for storing the context for the asynchronous calls
//

typedef struct _REQUEST_CONTEXT {
    HINTERNET RequestHandle;
    HINTERNET ConnectHandle;
    HANDLE CompletionEvent;
    HANDLE CleanUpEvent;
    LPSTR OutputBuffer;
    DWORD DownloadedBytes;
    DWORD WrittenBytes;
    DWORD ReadBytes;
    HANDLE UploadFile;
    DWORD FileSize;
    HANDLE DownloadFile;
    DWORD Method;
    DWORD State;
    
    CRITICAL_SECTION CriticalSection;
    BOOL CritSecInitialized;


    //
    // Synchronized by CriticalSection
    //

    DWORD HandleUsageCount; // Request object is in use(not safe to close handle)
    BOOL Closing;           // Request is closing(don't use handle)

} REQUEST_CONTEXT, *PREQUEST_CONTEXT;



// WinInet Callback function
VOID CALLBACK 
CallBack(
    __in HINTERNET hInternet,
    __in DWORD_PTR dwContext,
    __in DWORD dwInternetStatus,
    __in_bcount(dwStatusInformationLength) LPVOID lpvStatusInformation,
    __in DWORD dwStatusInformationLength
    );


//
// IO related functions
//

VOID ProcessRequest(
    __inout PREQUEST_CONTEXT ReqContext,
    __in DWORD Error
    );

DWORD SendRequest(
    __in PREQUEST_CONTEXT ReqContext
    );

DWORD SendRequestWithBody(
    __in PREQUEST_CONTEXT ReqContext
    );

DWORD GetDataToPost(
    __inout PREQUEST_CONTEXT ReqContext
    );

DWORD PostDataToServer(
    __inout PREQUEST_CONTEXT ReqContext,
    __out PBOOL Eof
    );

DWORD CompleteRequest(
    __inout PREQUEST_CONTEXT ReqContext
    );

DWORD RecvResponseData(
    __inout PREQUEST_CONTEXT ReqContext
    );

DWORD WriteResponseData(
    __in PREQUEST_CONTEXT ReqContext,
    __out PBOOL Eof
    );


//
// Initialization functions
//

DWORD AllocateAndInitializeRequestContext(
    __in HINTERNET SessionHandle,
    __in PCONFIGURATION Configuration,
    __deref_out PREQUEST_CONTEXT *ReqContext
    );

DWORD CreateWininetHandles(
    __inout PREQUEST_CONTEXT ReqContext,
    __in HINTERNET SessionHandle,
    __in LPWSTR HostName,
    __in LPWSTR Resource,
    __in BOOL IsSecureConnection
    );


//
// Cleanup functions
//

VOID CleanUpRequestContext(
    __inout_opt PREQUEST_CONTEXT ReqContext
    );

    
VOID CleanUpSessionHandle(
    __in HINTERNET SessionHandle
    );

//
// Cancellation support functions
//


VOID CloseRequestHandle(
    __inout PREQUEST_CONTEXT ReqContext
);

BOOL AcquireRequestHandle(
    __inout PREQUEST_CONTEXT ReqContext
);

VOID ReleaseRequestHandle(
    __inout PREQUEST_CONTEXT ReqContext
);


//
// Utility functions
//

VOID WaitForRequestCompletion(
    __in PREQUEST_CONTEXT ReqContext,
    __in DWORD Timeout
);

DWORD OpenFiles(
    __inout PREQUEST_CONTEXT ReqContext,
    __in DWORD Method,
    __in LPWSTR InputFileName,
    __in LPWSTR OutputFileName    
    );

DWORD ParseArguments(
    __in int argc,
    __in_ecount(argc) LPWSTR *argv,
    __inout PCONFIGURATION Configuration
    );


VOID ShowUsage(
    VOID
    );

VOID  LogInetError(
    __in DWORD Err,
    __in LPCWSTR Str
    );

VOID LogSysError(
    __in DWORD Err,
    __in LPCWSTR Str
    );


/*++
 Copyright (c) 2002 - 2006 Microsoft Corporation.  All Rights Reserved.

 THIS CODE AND INFORMATION IS PROVIDED "AS-IS" WITHOUT WARRANTY OF
 ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
 THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
 PARTICULAR PURPOSE.

 THIS CODE IS NOT SUPPORTED BY MICROSOFT. 

--*/

#include "precomp.h"
#include "async.h"


#pragma warning(disable:4127) // Conditional expression is constant


int __cdecl 
wmain(
    __in int argc,
    __in_ecount(argc) LPWSTR *argv
    )
{
    DWORD Error;
    DWORD OpenType = INTERNET_OPEN_TYPE_PRECONFIG; // Use pre-configured options as default

    CONFIGURATION Configuration = {0};

    PREQUEST_CONTEXT ReqContext = NULL;
    HINTERNET SessionHandle = NULL;

    // Callback function
    INTERNET_STATUS_CALLBACK CallbackPointer;


    // Parse the command line arguments
    Error = ParseArguments(argc, argv, &Configuration);
    if(Error != ERROR_SUCCESS)
    {
        ShowUsage();
        goto Exit; 
    }

    if(Configuration.UseProxy)
    {
        OpenType = INTERNET_OPEN_TYPE_PROXY;
    }
    
    // Create Session handle and specify async Mode
    SessionHandle = InternetOpen(L"WinInet HTTP Async Sample",  // User Agent
                                 OpenType,                      // Preconfig or Proxy
                                 Configuration.ProxyName,       // Proxy name
                                 NULL,                          // Proxy bypass, do not bypass any address
                                 INTERNET_FLAG_ASYNC);          // 0 for Synchronous
    
    if (SessionHandle == NULL)
    {
        LogInetError(GetLastError(), L"InternetOpen");
        goto Exit;
    }


    // Set the status callback for the handle to the Callback function
    CallbackPointer = InternetSetStatusCallback(SessionHandle, 
                                                (INTERNET_STATUS_CALLBACK)CallBack);

    if (CallbackPointer == INTERNET_INVALID_STATUS_CALLBACK)
    {
        fprintf(stderr, "InternetSetStatusCallback failed with INTERNET_INVALID_STATUS_CALLBACK\n");
        goto Exit;
    }


    
    // Initialize the ReqContext to be used in the asynchronous calls
    Error = AllocateAndInitializeRequestContext(SessionHandle,
                                                &Configuration,
                                                &ReqContext);
    if (Error != ERROR_SUCCESS)
    {
        fprintf(stderr, "AllocateAndInitializeRequestContext failed with error %d\n", Error);
        goto Exit;
    }

    //
    // Send out request and receive response
    //
    
    ProcessRequest(ReqContext, ERROR_SUCCESS);


    //
    // Wait for request completion or timeout
    //

    WaitForRequestCompletion(ReqContext, Configuration.UserTimeout);
    

Exit:

    // Clean up the allocated resources
    CleanUpRequestContext(ReqContext);

    CleanUpSessionHandle(SessionHandle);

    return (Error != ERROR_SUCCESS) ? 1 : 0;
}

VOID CALLBACK 
CallBack(
    __in HINTERNET hInternet,
    __in DWORD_PTR dwContext,
    __in DWORD dwInternetStatus,
    __in_bcount(dwStatusInformationLength) LPVOID lpvStatusInformation,
    __in DWORD dwStatusInformationLength
    )
/*++

Routine Description:
    Callback routine for asynchronous WinInet operations

Arguments:
     hInternet - The handle for which the callback function is called.
     dwContext - Pointer to the application defined context.
     dwInternetStatus - Status code indicating why the callback is called.
     lpvStatusInformation - Pointer to a buffer holding callback specific data.
     dwStatusInformationLength - Specifies size of lpvStatusInformation buffer.

Return Value:
    None.

--*/
{
    InternetCookieHistory cookieHistory;
    PREQUEST_CONTEXT ReqContext = (PREQUEST_CONTEXT)dwContext;
 
    UNREFERENCED_PARAMETER(dwStatusInformationLength);

    fprintf(stderr, "Callback Received for Handle %p \t", hInternet);
    
    switch(dwInternetStatus)
    {
        case INTERNET_STATUS_COOKIE_SENT:
            fprintf(stderr, "Status: Cookie found and will be sent with request\n");
            break;
            
        case INTERNET_STATUS_COOKIE_RECEIVED:
            fprintf(stderr, "Status: Cookie Received\n");
            break;
            
        case INTERNET_STATUS_COOKIE_HISTORY:

            fprintf(stderr, "Status: Cookie History\n");

            ASYNC_ASSERT(lpvStatusInformation);
            ASYNC_ASSERT(dwStatusInformationLength == sizeof(InternetCookieHistory));

            cookieHistory = *((InternetCookieHistory*)lpvStatusInformation);
            
            if(cookieHistory.fAccepted)
            {
                fprintf(stderr, "Cookie Accepted\n");
            }
            if(cookieHistory.fLeashed)
            {
                fprintf(stderr, "Cookie Leashed\n");
            }        
            if(cookieHistory.fDowngraded)
            {
                fprintf(stderr, "Cookie Downgraded\n");
            }        
            if(cookieHistory.fRejected)
            {
                fprintf(stderr, "Cookie Rejected\n");
            }

 
            break;
            
        case INTERNET_STATUS_CLOSING_CONNECTION:
            fprintf(stderr, "Status: Closing Connection\n");
            break;
            
        case INTERNET_STATUS_CONNECTED_TO_SERVER:
            fprintf(stderr, "Status: Connected to Server\n");
            break;
            
        case INTERNET_STATUS_CONNECTING_TO_SERVER:
            fprintf(stderr, "Status: Connecting to Server\n");
            break;
            
        case INTERNET_STATUS_CONNECTION_CLOSED:
            fprintf(stderr, "Status: Connection Closed\n");
            break;
            
        case INTERNET_STATUS_HANDLE_CLOSING:
            fprintf(stderr, "Status: Handle Closing\n");

            //
            // Signal the cleanup routine that it is
            // safe to cleanup the request context
            //
            
            ASYNC_ASSERT(ReqContext);
            SetEvent(ReqContext->CleanUpEvent);
            
            break;
            
        case INTERNET_STATUS_HANDLE_CREATED:
            ASYNC_ASSERT(lpvStatusInformation);
            fprintf(stderr,
                    "Handle %x created\n", 
                    ((LPINTERNET_ASYNC_RESULT)lpvStatusInformation)->dwResult);

            break;
            
        case INTERNET_STATUS_INTERMEDIATE_RESPONSE:
            fprintf(stderr, "Status: Intermediate response\n");
            break;
            
        case INTERNET_STATUS_RECEIVING_RESPONSE:
            fprintf(stderr, "Status: Receiving Response\n");    
            break;
            
        case INTERNET_STATUS_RESPONSE_RECEIVED:
            ASYNC_ASSERT(lpvStatusInformation);
            ASYNC_ASSERT(dwStatusInformationLength == sizeof(DWORD));
            
            fprintf(stderr, "Status: Response Received (%d Bytes)\n", *((LPDWORD)lpvStatusInformation));
            
            break;
            
        case INTERNET_STATUS_REDIRECT:
            fprintf(stderr, "Status: Redirect\n");
            break;

        case INTERNET_STATUS_REQUEST_COMPLETE:
            fprintf(stderr, "Status: Request complete\n");

            ASYNC_ASSERT(lpvStatusInformation);
            
            ProcessRequest(ReqContext, ((LPINTERNET_ASYNC_RESULT)lpvStatusInformation)->dwError);

            break;
            
        case INTERNET_STATUS_REQUEST_SENT:
            ASYNC_ASSERT(lpvStatusInformation);
            ASYNC_ASSERT(dwStatusInformationLength == sizeof(DWORD));

            fprintf(stderr,"Status: Request sent (%d Bytes)\n", *((LPDWORD)lpvStatusInformation));
            break;
            
        case INTERNET_STATUS_DETECTING_PROXY:
            fprintf(stderr, "Status: Detecting Proxy\n");
            break;            
            
        case INTERNET_STATUS_RESOLVING_NAME:
            fprintf(stderr, "Status: Resolving Name\n");
            break;
            
        case INTERNET_STATUS_NAME_RESOLVED:
            fprintf(stderr, "Status: Name Resolved\n");
            break;
            
        case INTERNET_STATUS_SENDING_REQUEST:
            fprintf(stderr, "Status: Sending request\n");
            break;
            
        case INTERNET_STATUS_STATE_CHANGE:
            fprintf(stderr, "Status: State Change\n");
            break;
            
        case INTERNET_STATUS_P3P_HEADER:
            fprintf(stderr, "Status: Received P3P header\n");
            break;
            
        default:
            fprintf(stderr, "Status: Unknown (%d)\n", dwInternetStatus);
            break;
    }

    return;
}


VOID
ProcessRequest(
    __inout PREQUEST_CONTEXT ReqContext,
    __in DWORD Error
    )
/*++

Routine Description:
    Process the request context - Sending the request and
    receiving the response

Arguments:
    ReqContext - Pointer to request context structure
    Error - error returned from last asynchronous call

Return Value:
    None.

--*/
{
    BOOL Eof = FALSE;
    
    ASYNC_ASSERT(ReqContext);

    
    while(Error == ERROR_SUCCESS && ReqContext->State != REQ_STATE_COMPLETE)
    {
        
        switch(ReqContext->State)
        {
            case REQ_STATE_SEND_REQ:

                ReqContext->State = REQ_STATE_RESPONSE_RECV_DATA;
                Error = SendRequest(ReqContext);
                
                break;

            case REQ_STATE_SEND_REQ_WITH_BODY: 
                
                ReqContext->State = REQ_STATE_POST_GET_DATA;
                Error = SendRequestWithBody(ReqContext);                

                break;
                
            case REQ_STATE_POST_GET_DATA:
                
                ReqContext->State = REQ_STATE_POST_SEND_DATA;
                Error = GetDataToPost(ReqContext);
                
                break;
                
            case REQ_STATE_POST_SEND_DATA:
                
                ReqContext->State = REQ_STATE_POST_GET_DATA;
                Error = PostDataToServer(ReqContext, &Eof);
                
                if(Eof)
                {
                    ASYNC_ASSERT(Error == ERROR_SUCCESS);
                    ReqContext->State = REQ_STATE_POST_COMPLETE;  
                }
                
                break;
                
            case REQ_STATE_POST_COMPLETE:     
                
                ReqContext->State = REQ_STATE_RESPONSE_RECV_DATA;
                Error = CompleteRequest(ReqContext);
                
                break;                
                
            case REQ_STATE_RESPONSE_RECV_DATA: 
                
                ReqContext->State = REQ_STATE_RESPONSE_WRITE_DATA;
                Error = RecvResponseData(ReqContext);
                
                break;
                
            case REQ_STATE_RESPONSE_WRITE_DATA:

                ReqContext->State = REQ_STATE_RESPONSE_RECV_DATA;
                Error = WriteResponseData(ReqContext, &Eof);

                if(Eof)
                {
                    ASYNC_ASSERT(Error == ERROR_SUCCESS);
                    ReqContext->State = REQ_STATE_COMPLETE;
                }
                
                break;
                
            default:
                
                ASYNC_ASSERT(FALSE);
                
                break;
        }
    }

    if(Error != ERROR_IO_PENDING)
    {
        //
        // Everything has been procesed or has failed. 
        // In either case, the signal processing has
        // completed
        //
        
        SetEvent(ReqContext->CompletionEvent);
    }

    return;
}

DWORD
SendRequest(
    __in PREQUEST_CONTEXT ReqContext
    )
/*++

Routine Description:
    Send the request using HttpSendRequest

Arguments:
    ReqContext - Pointer to request context structure

Return Value:
    Error code for the operation.

--*/
{
    BOOL Success;
    DWORD Error = ERROR_SUCCESS;
    

    ASYNC_ASSERT(ReqContext->Method == METHOD_GET);

    Success = AcquireRequestHandle(ReqContext);
    if(!Success)
    {
        Error = ERROR_OPERATION_ABORTED;
        goto Exit;
    }
    
    Success = HttpSendRequest(ReqContext->RequestHandle,
                              NULL,                   // do not provide additional Headers
                              0,                      // dwHeadersLength 
                              NULL,                   // Do not send any data 
                              0);                     // dwOptionalLength 

    ReleaseRequestHandle(ReqContext);

    if (!Success)
    {
        Error = GetLastError();

        if(Error != ERROR_IO_PENDING)
        {
            LogInetError(Error, L"HttpSendRequest");
        }
        goto Exit;
    }

Exit:

    return Error;
}


DWORD
SendRequestWithBody(
    __in PREQUEST_CONTEXT ReqContext
    )
/*++

Routine Description:
    Send the request with entity-body using HttpSendRequestEx

Arguments:
    ReqContext - Pointer to request context structure

Return Value:
    Error code for the operation.

--*/
{
    BOOL Success;
    INTERNET_BUFFERS BuffersIn;    
    DWORD Error = ERROR_SUCCESS;
    
    //
    // HttpSendRequest can also be used also to post data to a server, 
    // to do so, the data should be provided using the lpOptional
    // parameter and it's size on dwOptionalLength.
    // Here we decided to depict the use of HttpSendRequestEx function.
    //
    

    ASYNC_ASSERT(ReqContext->Method == METHOD_POST);
    
    //Prepare the Buffers to be passed to HttpSendRequestEx
    ZeroMemory(&BuffersIn, sizeof(INTERNET_BUFFERS));
    BuffersIn.dwStructSize = sizeof(INTERNET_BUFFERS);
    BuffersIn.lpvBuffer = NULL;
    BuffersIn.dwBufferLength = 0;
    BuffersIn.dwBufferTotal = ReqContext->FileSize; // content-length of data to post


    Success = AcquireRequestHandle(ReqContext);
    if(!Success)
    {
        Error = ERROR_OPERATION_ABORTED;
        goto Exit;
    }
    
    Success = HttpSendRequestEx(ReqContext->RequestHandle,
                                &BuffersIn,
                                NULL,                 // Do not use output buffers
                                0,                    // dwFlags reserved
                                (DWORD_PTR)ReqContext);

    ReleaseRequestHandle(ReqContext);

    if (!Success)
    {
        Error = GetLastError();

        if(Error != ERROR_IO_PENDING)
        {
            LogInetError(Error, L"HttpSendRequestEx");
        }
        
        goto Exit;
    }
    
Exit:    

    return Error;
}

DWORD 
GetDataToPost(
    __inout PREQUEST_CONTEXT ReqContext
    )
/*++

Routine Description:
    Reads data from a file

Arguments:
    ReqContext - Pointer to request context structure

Return Value:
    Error code for the operation.

--*/
{
    DWORD Error = ERROR_SUCCESS;
    BOOL Success;


    //
    //
    // ReadFile is done inline here assuming that it will return quickly
    // I.E. the file is on disk
    //
    // If you plan to do blocking/intensive operations they should be
    // queued to another thread and not block the callback thread
    //
    //

    Success = ReadFile(ReqContext->UploadFile,
                       ReqContext->OutputBuffer,
                       BUFFER_LEN,
                       &ReqContext->ReadBytes,
                       NULL);
    if(!Success)
    {
        Error = GetLastError();
        LogSysError(Error, L"ReadFile");
        goto Exit;
    }

    
Exit:    
 
    return Error;
}

DWORD 
PostDataToServer(
    __inout PREQUEST_CONTEXT ReqContext,
    __out PBOOL Eof
    )
/*++

Routine Description:
    Post data in the http request

Arguments:
    ReqContext - Pointer to request context structure
    Eof - Done posting data to server

Return Value:
    Error code for the operation.

--*/
{
    DWORD Error = ERROR_SUCCESS;
    BOOL Success;

    *Eof = FALSE;

    if(ReqContext->ReadBytes == 0)
    {
        *Eof = TRUE;
        goto Exit;
    }

        
    Success = AcquireRequestHandle(ReqContext);
    if(!Success)
    {
        Error = ERROR_OPERATION_ABORTED;
        goto Exit;
    }


    //
    // The lpdwNumberOfBytesWritten parameter will be
    // populated on async completion, so it must exist
    // until INTERNET_STATUS_REQUEST_COMPLETE.
    // The same is true of lpBuffer parameter.
    //

    Success = InternetWriteFile(ReqContext->RequestHandle,
                                ReqContext->OutputBuffer,
                                ReqContext->ReadBytes,
                                &ReqContext->WrittenBytes);
    

    ReleaseRequestHandle(ReqContext);
    
    if(!Success)
    {
        Error = GetLastError();
        
        if(Error == ERROR_IO_PENDING)
        {
            fprintf(stderr, "Waiting for InternetWriteFile to complete\n");                
        }
        else
        {
            LogInetError(Error, L"InternetWriteFile");
        }
        
        goto Exit;
        
    }
 
    

Exit:
    
 
    return Error;
}


DWORD 
CompleteRequest(
    __inout PREQUEST_CONTEXT ReqContext
    )
/*++

Routine Description:
    Perform completion of asynchronous post.

Arguments:
    ReqContext - Pointer to request context structure

Return Value:
    Error Code for the operation.

--*/
{

    DWORD Error = ERROR_SUCCESS;
    BOOL Success;

    fprintf(stderr, "Finished posting file\n");
    
    Success = AcquireRequestHandle(ReqContext);
    if(!Success)
    {
        Error = ERROR_OPERATION_ABORTED;
        goto Exit;
    }   

    Success = HttpEndRequest(ReqContext->RequestHandle, NULL, 0, 0);

    ReleaseRequestHandle(ReqContext);
    
    if(!Success)
    {
        Error = GetLastError();
        if(Error == ERROR_IO_PENDING)
        {
            fprintf(stderr, "Waiting for HttpEndRequest to complete \n");
        }
        else
        {
            LogInetError(Error, L"HttpEndRequest");
            goto Exit;
        }
    }

Exit:
    
    return Error;
}



DWORD
RecvResponseData(
    __inout PREQUEST_CONTEXT ReqContext
    )
/*++

Routine Description:
     Receive response

Arguments:
     ReqContext - Pointer to request context structure

Return Value:
     Error Code for the operation.

--*/
{
    DWORD Error = ERROR_SUCCESS;
    BOOL Success;

 

            
    Success = AcquireRequestHandle(ReqContext);
    if(!Success)
    {
        Error = ERROR_OPERATION_ABORTED;
        goto Exit;
    } 

    //
    // The lpdwNumberOfBytesRead parameter will be
    // populated on async completion, so it must exist
    // until INTERNET_STATUS_REQUEST_COMPLETE.
    // The same is true of lpBuffer parameter.
    //
    // InternetReadFile will block until the buffer
    // is completely filled or the response is exhausted.
    //

    
    Success = InternetReadFile(ReqContext->RequestHandle,
                               ReqContext->OutputBuffer,
                               BUFFER_LEN,
                               &ReqContext->DownloadedBytes);
                               
    ReleaseRequestHandle(ReqContext);
    
    if(!Success)
    {
        Error = GetLastError();
        if(Error == ERROR_IO_PENDING)
        {
            fprintf(stderr, "Waiting for InternetReadFile to complete\n");
        }
        else
        {
            LogInetError(Error, L"InternetReadFile");
        }
        
        goto Exit;
    }

    
Exit:

    return Error;
}


DWORD
WriteResponseData(
    __in PREQUEST_CONTEXT ReqContext,
    __out PBOOL Eof
    )
/*++

Routine Description:
     Write response to a file

Arguments:
     ReqContext - Pointer to request context structure
     Eof - Done with response

Return Value:
     Error Code for the operation.

--*/
{
    DWORD Error = ERROR_SUCCESS;
    DWORD BytesWritten = 0;
    
    BOOL Success;

     *Eof = FALSE;

    //
    // Finished receiving response
    //
    
    if (ReqContext->DownloadedBytes == 0)
    {
        *Eof = TRUE;
        goto Exit;
        
    }    
    
    //
    //
    // WriteFile is done inline here assuming that it will return quickly
    // I.E. the file is on disk
    //
    // If you plan to do blocking/intensive operations they should be
    // queued to another thread and not block the callback thread
    //
    //
    
    Success = WriteFile(ReqContext->DownloadFile,
                        ReqContext->OutputBuffer,
                        ReqContext->DownloadedBytes,
                        &BytesWritten,
                        NULL);
    
    if (!Success)
    {
        Error = GetLastError();

        LogSysError(Error, L"WriteFile");
        goto Exit;;
    }

    
Exit:

    return Error;
}





VOID
CloseRequestHandle(
    __inout PREQUEST_CONTEXT ReqContext
)
/*++

Routine Description:
    Safely  close the request handle by synchronizing
    with all threads using the handle.

    When this function returns no more calls can be made with the
    handle.
    
Arguments:
    ReqContext - Pointer to Request context structure
Return Value:
    None.

--*/
{
    BOOL Close = FALSE;
    
    EnterCriticalSection(&ReqContext->CriticalSection);

    //
    // Current implementation only supports the main thread
    // kicking off the request handle close
    //
    // To support multiple threads the lifetime 
    // of the request context must be carefully controlled
    // (most likely guarded by refcount/critsec)
    // so that they are not trying to abort a request
    // where the context has already been freed.
    //
    
    ASYNC_ASSERT(ReqContext->Closing == FALSE);
    ReqContext->Closing = TRUE;
    
    if(ReqContext->HandleUsageCount == 0)
    {
        Close = TRUE;
    }

    LeaveCriticalSection(&ReqContext->CriticalSection);



    if(Close)
    {
        //
        // At this point there must be the guarantee that all calls
        // to wininet with this handle have returned with some value
        // including ERROR_IO_PENDING, and none will be made after
        // InternetCloseHandle.
        //        
        (VOID)InternetCloseHandle(ReqContext->RequestHandle);
    }

    return;
}

BOOL
AcquireRequestHandle(
    __inout PREQUEST_CONTEXT ReqContext
)
/*++

Routine Description:
    Acquire use of the request handle to make a wininet call
Arguments:
    ReqContext - Pointer to Request context structure
Return Value:
    TRUE - Success
    FALSE - Failure
--*/
{
    BOOL Success = TRUE;

    EnterCriticalSection(&ReqContext->CriticalSection);

    if(ReqContext->Closing == TRUE)
    {
        Success = FALSE;        
    }
    else
    {
        ReqContext->HandleUsageCount++;
    }

    LeaveCriticalSection(&ReqContext->CriticalSection);
    
    return Success;
}


VOID
ReleaseRequestHandle(
    __inout PREQUEST_CONTEXT ReqContext
)
/*++

Routine Description:
    release use of the request handle
Arguments:
    ReqContext - Pointer to Request context structure
Return Value:
    None.

--*/
{
    BOOL Close = FALSE;

    EnterCriticalSection(&ReqContext->CriticalSection);

    ASYNC_ASSERT(ReqContext->HandleUsageCount > 0);
    
    ReqContext->HandleUsageCount--;
    
    if(ReqContext->Closing == TRUE && ReqContext->HandleUsageCount == 0)
    {
        Close = TRUE;

    }

    LeaveCriticalSection(&ReqContext->CriticalSection);


    if(Close)
    {
        //
        // At this point there must be the guarantee that all calls
        // to wininet with this handle have returned with some value
        // including ERROR_IO_PENDING, and none will be made after
        // InternetCloseHandle.
        //        
        (VOID)InternetCloseHandle(ReqContext->RequestHandle);
    }
    
    return;
}


DWORD 
AllocateAndInitializeRequestContext(
    __in HINTERNET SessionHandle,
    __in PCONFIGURATION Configuration,
    __deref_out PREQUEST_CONTEXT *ReqContext
    )
/*++

Routine Description:
    Allocate the request context and initialize it values

Arguments:
    ReqContext - Pointer to Request context structure
    Configuration - Pointer to configuration structure
    SessionHandle - Wininet session handle to use when creating
                    connect handle
    
Return Value:
    Error Code for the operation.

--*/
{
    DWORD Error = ERROR_SUCCESS;
    BOOL Success;
    PREQUEST_CONTEXT LocalReqContext;

    *ReqContext = NULL;

    LocalReqContext = malloc(sizeof(REQUEST_CONTEXT));

    if(LocalReqContext == NULL)
    {
        Error = ERROR_NOT_ENOUGH_MEMORY;
        goto Exit;
    }
    
    LocalReqContext->RequestHandle = NULL;
    LocalReqContext->ConnectHandle = NULL;
    LocalReqContext->DownloadedBytes = 0;
    LocalReqContext->WrittenBytes = 0;
    LocalReqContext->ReadBytes = 0;    
    LocalReqContext->UploadFile = INVALID_HANDLE_VALUE;
    LocalReqContext->DownloadFile = INVALID_HANDLE_VALUE;
    LocalReqContext->FileSize = 0;
    LocalReqContext->HandleUsageCount = 0;
    LocalReqContext->Closing = FALSE;
    LocalReqContext->Method = Configuration->Method;
    LocalReqContext->CompletionEvent = NULL;    
    LocalReqContext->CleanUpEvent = NULL;
    LocalReqContext->OutputBuffer = NULL;
    LocalReqContext->State = 
        (LocalReqContext->Method == METHOD_GET) ?  REQ_STATE_SEND_REQ : REQ_STATE_SEND_REQ_WITH_BODY;
    LocalReqContext->CritSecInitialized = FALSE;    
    
       
    // initialize critical section

    Success = InitializeCriticalSectionAndSpinCount(&LocalReqContext->CriticalSection, SPIN_COUNT);
    
    if(!Success)
    {
        Error = GetLastError();
        LogSysError(Error, L"InitializeCriticalSectionAndSpinCount");
        goto Exit;
    }

    LocalReqContext->CritSecInitialized = TRUE;

    LocalReqContext->OutputBuffer = malloc(BUFFER_LEN);
    
    if(LocalReqContext->OutputBuffer == NULL)
    {
        Error = ERROR_NOT_ENOUGH_MEMORY;
        goto Exit;
    }
        
    // create events
    LocalReqContext->CompletionEvent = CreateEvent(NULL,  // Sec attrib
                                                   FALSE, // Auto reset
                                                   FALSE, // Initial state unsignalled
                                                   NULL); // Name
    if(LocalReqContext->CompletionEvent == NULL)
    {
        Error = GetLastError();
        LogSysError(Error, L"CreateEvent CompletionEvent");
        goto Exit;
    }

    // create events
    LocalReqContext->CleanUpEvent = CreateEvent(NULL,  // Sec attrib
                                                FALSE, // Auto reset
                                                FALSE, // Initial state unsignalled
                                                NULL); // Name
    if(LocalReqContext->CleanUpEvent == NULL)
    {
        Error = GetLastError();
        LogSysError(Error, L"CreateEvent CleanUpEvent");
        goto Exit;
    }    
    
    // Open the file to dump the response entity body and
    // if required the file with the data to post
    Error = OpenFiles(LocalReqContext,
                      Configuration->Method, 
                      Configuration->InputFileName, 
                      Configuration->OutputFileName);
    
    if(Error != ERROR_SUCCESS)
    {
            fprintf(stderr, "OpenFiles failed with %d\n", Error);
            goto Exit;
    }

    // Verify if we've opened a file to post and get its size
    if (LocalReqContext->UploadFile != INVALID_HANDLE_VALUE)
    {
        LocalReqContext->FileSize = GetFileSize(LocalReqContext->UploadFile, NULL);
        if(LocalReqContext->FileSize == INVALID_FILE_SIZE)
        {
            Error = GetLastError();
            LogSysError(Error, L"GetFileSize");
            goto Exit;
        }
    }


    Error = CreateWininetHandles(LocalReqContext, 
                                 SessionHandle,
                                 Configuration->HostName,
                                 Configuration->ResourceOnServer,
                                 Configuration->IsSecureConnection);
    
    if(Error != ERROR_SUCCESS)
    {
        fprintf(stderr, "CreateWininetHandles failed with %d\n", Error);
        goto Exit;        
    }
    

    *ReqContext = LocalReqContext;
    
Exit:  
    
    if(Error != ERROR_SUCCESS)
    {
        CleanUpRequestContext(LocalReqContext);
    }
    
    return Error;
}


DWORD 
CreateWininetHandles(
    __inout PREQUEST_CONTEXT ReqContext,
    __in HINTERNET SessionHandle,
    __in LPWSTR HostName,
    __in LPWSTR Resource,
    __in BOOL IsSecureConnection
    )
/*++

Routine Description:
    Create connect and request handles

Arguments:
    ReqContext - Pointer to Request context structure
    SessionHandle - Wininet session handle used to create
                    connect handle
    HostName - Hostname to connect
    Resource - Resource to get/post
    IsSecureConnection - SSL?

Return Value:
    Error Code for the operation.

--*/
{
    DWORD Error = ERROR_SUCCESS;
    INTERNET_PORT ServerPort = INTERNET_DEFAULT_HTTP_PORT;
    DWORD RequestFlags = 0;
    LPWSTR Verb;


    //
    // Set the correct server port if using SSL
    // Also set the flag for HttpOpenRequest 
    //
    
    if(IsSecureConnection)
    {
        ServerPort = INTERNET_DEFAULT_HTTPS_PORT;
        RequestFlags = INTERNET_FLAG_SECURE;
    }    

    // Create Connection handle and provide context for async operations
    ReqContext->ConnectHandle = InternetConnect(SessionHandle,
                                                HostName,                  // Name of the server to connect to
                                                ServerPort,                // HTTP (80) or HTTPS (443)
                                                NULL,                      // Do not provide a user name for the server
                                                NULL,                      // Do not provide a password for the server
                                                INTERNET_SERVICE_HTTP,
                                                0,                         // Do not provide any special flag
                                                (DWORD_PTR)ReqContext);    // Provide the context to be
                                                                           // used during the callbacks
    //                                                                        
    // For HTTP InternetConnect returns synchronously because it does not
    // actually make the connection.
    //
    // For FTP InternetConnect connects the control channel, and therefore
    // can be completed asynchronously.  This sample would have to be
    // changed, so that the InternetConnect's asynchronous completion
    // is handled correctly to support FTP.
    //
    
    if (ReqContext->ConnectHandle == NULL)
    {
        Error = GetLastError();
        LogInetError(Error, L"InternetConnect");
        goto Exit;
    }


    // Set the Verb depending on the operation to perform
    if(ReqContext->Method == METHOD_GET)
    {
        Verb = L"GET";
    }
    else
    {
        ASYNC_ASSERT(ReqContext->Method == METHOD_POST);
        
        Verb = L"POST";
    }

    //
    // We're overriding WinInet's default behavior.
    // Setting these flags, we make sure we get the response from the server and not the cache.
    // Also ask WinInet not to store the response in the cache.
    //
    // These flags are NOT performant and are only used to show case WinInet's Async I/O.
    // A real WinInet application would not want to use this flags.
    //
    
    RequestFlags |= INTERNET_FLAG_RELOAD | INTERNET_FLAG_NO_CACHE_WRITE;

    // Create a Request handle
    ReqContext->RequestHandle = HttpOpenRequest(ReqContext->ConnectHandle,
                                                Verb,                     // GET or POST
                                                Resource,                 // root "/" by default
                                                NULL,                     // Use default HTTP/1.1 as the version
                                                NULL,                     // Do not provide any referrer
                                                NULL,                     // Do not provide Accept types
                                                RequestFlags,
                                                (DWORD_PTR)ReqContext);
    
    if (ReqContext->RequestHandle == NULL)
    {
        Error = GetLastError();
        LogInetError(Error, L"HttpOpenRequest");
        
        goto Exit;
    }
    
    
Exit:
    
    return Error;
}

VOID
WaitForRequestCompletion(
    __in PREQUEST_CONTEXT ReqContext,
    __in DWORD Timeout
)
/*++

Routine Description:
    Wait for the request to complete or timeout to occur

Arguments:
    ReqContext - Pointer to request context structure

Return Value:
    None.

--*/
{
    DWORD SyncResult;

    //
    // The preferred method of doing timeouts is to
    // use the timeout options through InternetSetOption,
    // but this overall timeout is being used to show 
    // the correct way to abort and close a request.
    //
    
    SyncResult = WaitForSingleObject(ReqContext->CompletionEvent,
                                     Timeout);              // Wait until we receive the completion
    
    switch(SyncResult)
    {
        case WAIT_OBJECT_0:
            
            printf("Done!\n");
            break;
            
        case WAIT_TIMEOUT:

            fprintf(stderr,
                    "Timeout while waiting for completion event (request will be cancelled)\n");
            break;
            
        case WAIT_FAILED:
            
            fprintf(stderr,
                    "Wait failed with Error %d while waiting for completion event (request will be cancelled)\n", 
                    GetLastError());
            break;
            
        default:
            // Not expecting any other error codes
            ASYNC_ASSERT(FALSE);
            break;
            
 
    }

    return;
}
    
VOID 
CleanUpRequestContext(
    __inout_opt PREQUEST_CONTEXT ReqContext
    )
/*++

Routine Description:
    Used to cleanup the request context before exiting.

Arguments:
    ReqContext - Pointer to request context structure

Return Value:
    None.

--*/
{
    if(ReqContext == NULL)
    {
        goto Exit;
    }

    if(ReqContext->RequestHandle) 
    {
        CloseRequestHandle(ReqContext);
        
        //
        // Wait for the closing of the handle to complete
        // (waiting for all async operations to complete)
        //
        // This is the only safe way to get rid of the context
        //
        
        (VOID)WaitForSingleObject(ReqContext->CleanUpEvent, INFINITE);           
    }
    
    if(ReqContext->ConnectHandle)
    {
        //
        // Remove the callback from the ConnectHandle since
        // we don't want the closing notification
        // The callback was inherited from the session handle
        //
        (VOID)InternetSetStatusCallback(ReqContext->ConnectHandle, 
                                        NULL);

        (VOID)InternetCloseHandle(ReqContext->ConnectHandle);
    }    

    if(ReqContext->UploadFile != INVALID_HANDLE_VALUE) 
    {
        CloseHandle(ReqContext->UploadFile);
    }
    
    if(ReqContext->DownloadFile != INVALID_HANDLE_VALUE) 
    {
        CloseHandle(ReqContext->DownloadFile);
    }

    if(ReqContext->CompletionEvent) 
    {
        CloseHandle(ReqContext->CompletionEvent);
    }

    if(ReqContext->CleanUpEvent) 
    {
        CloseHandle(ReqContext->CleanUpEvent);
    }
    
    if(ReqContext->CritSecInitialized)
    {
        DeleteCriticalSection(&ReqContext->CriticalSection);
    }
    
    if(ReqContext->OutputBuffer)
    {
        free(ReqContext->OutputBuffer);
    }
    

    
    free(ReqContext);
    
    
Exit:
    
    return;
}



VOID 
CleanUpSessionHandle(
    __in HINTERNET SessionHandle
    )
/*++

Routine Description:
    Used to cleanup session before exiting.

Arguments:
    SessionHandle - Wininet session handle

Return Value:
    None.

--*/
{

    if(SessionHandle) 
    {
        //
        // Remove the callback from the SessionHandle since
        // we don't want the closing notification
        //
        (VOID)InternetSetStatusCallback(SessionHandle, 
                                        NULL);
        //
        // Call InternetCloseHandle and do not wait for the closing notification 
        // in the callback function
        //
        (VOID)InternetCloseHandle(SessionHandle);

    }

    return;
}


DWORD 
OpenFiles(
    __inout PREQUEST_CONTEXT ReqContext,
    __in DWORD Method,
    __in LPWSTR InputFileName,
    __in LPWSTR OutputFileName    
    )
/*++

Routine Description:
    This routine opens files, one to post data from, and
    one to write response into

Arguments:
    ReqContext - Pointer to request context structure
    Method - GET or POST - do we need to open the input file
    InputFileName - Input file name
    OutputFileName - output file name

Return Value:
    Error Code for the operation.

--*/
{
    DWORD Error = ERROR_SUCCESS;
    
    if (Method == METHOD_POST)
    {
        // Open input file
        ReqContext->UploadFile = CreateFile(InputFileName,
                                            GENERIC_READ,
                                            FILE_SHARE_READ, 
                                            NULL,                   // handle cannot be inherited
                                            OPEN_ALWAYS,            // if file exists, open it
                                            FILE_ATTRIBUTE_NORMAL,
                                            NULL);                  // No template file

        if (ReqContext->UploadFile == INVALID_HANDLE_VALUE)
        {
            Error = GetLastError();
            LogSysError(Error, L"CreateFile for input file");
            goto Exit;
        }
    }

    // Open output file
    ReqContext->DownloadFile = CreateFile(OutputFileName,
                                          GENERIC_WRITE,
                                          0,                        // Open exclusively
                                          NULL,                     // handle cannot be inherited
                                          CREATE_ALWAYS,            // if file exists, delete it
                                          FILE_ATTRIBUTE_NORMAL,
                                          NULL);                    // No template file
    
    if (ReqContext->DownloadFile == INVALID_HANDLE_VALUE)
    {
            Error = GetLastError();
            LogSysError(Error, L"CreateFile for output file");
            goto Exit;
    }
    
Exit:
    return Error;
}


DWORD 
ParseArguments(
    __in int argc,
    __in_ecount(argc) LPWSTR *argv,
    __inout PCONFIGURATION Configuration
    )
/*++

Routine Description:
     This routine is used to Parse command line arguments. Flags are
     case sensitive.

Arguments:
     argc - Number of arguments
     argv - Pointer to the argument vector
     Configuration - pointer to configuration struct to write configuration

Return Value:
    Error Code for the operation.

--*/
{ 
    int i;
    DWORD Error = ERROR_SUCCESS;

    for (i = 1; i < argc; ++i)
    {        
        if (wcsncmp(argv[i], L"-", 1))
        {
            printf("Invalid switch %ws\n", argv[i]);
            i++;
            continue;
        }
        
        switch(argv[i][1])
        {            
            case L'p':
                
                Configuration->UseProxy = 1;
                if (i < argc - 1)
                {
                    Configuration->ProxyName = argv[++i];
                }
                break;
                
            case L'h':
                
                if (i < argc - 1)
                {
                    Configuration->HostName = argv[++i];
                }
                
                break;
                
            case L'o':
                
                if (i < argc - 1)
                {
                    Configuration->ResourceOnServer = argv[++i];
                }
                
                break;
                
            case L'r':
            
                if (i < argc - 1)
                {
                    Configuration->InputFileName = argv[++i];
                }
                
                break;

            case L'w':
                
                if (i < argc - 1)
                {
                    Configuration->OutputFileName = argv[++i];
                }
                
                break;
            
            case L'm':
                
                if (i < argc - 1)
                {
                    if (!_wcsnicmp(argv[i + 1], L"get", 3))
                    {
                        Configuration->Method = METHOD_GET;
                    }
                    else if (!_wcsnicmp(argv[i + 1], L"post", 4))
                    {
                        Configuration->Method = METHOD_POST;
                    }
                }
                ++i;
                break;

            case L's':
                Configuration->IsSecureConnection = TRUE;
                break;

            case L't':
                if (i < argc - 1)
                {
                    Configuration->UserTimeout = _wtoi(argv[++i]);
                }
                break;      

            default:
                Error = ERROR_INVALID_PARAMETER;
                break;
        }
    }

    if(Error == ERROR_SUCCESS)
    {
        if(Configuration->UseProxy && Configuration->ProxyName == NULL)
        {
            printf("No proxy server name provided!\n\n");
            Error = ERROR_INVALID_PARAMETER;
            goto Exit;
        }

        if(Configuration->HostName == NULL)
        {
            printf("Defaulting hostname to: %ws\n", DEFAULT_HOSTNAME);
            Configuration->HostName = DEFAULT_HOSTNAME;
        }

        if(Configuration->Method == METHOD_NONE)
        {
            printf("Defaulting method to: GET\n");
            Configuration->Method = METHOD_GET;
        }

        if(Configuration->ResourceOnServer == NULL)
        {
            printf("Defaulting resource to: %ws\n", DEFAULT_RESOURCE);
            Configuration->ResourceOnServer = DEFAULT_RESOURCE; 
        }

        if(Configuration->UserTimeout == 0)
        {
            printf("Defaulting timeout to: %d\n", DEFAULT_TIMEOUT);
            Configuration->UserTimeout = DEFAULT_TIMEOUT;         
        }

        if(Configuration->InputFileName == NULL && Configuration->Method == METHOD_POST)
        {
            printf("Error: File to post not specified\n");
            Error = ERROR_INVALID_PARAMETER;
            goto Exit;
        }
        
        if(Configuration->OutputFileName == NULL)
        {
            printf("Defaulting output file to: %ws\n", DEFAULT_OUTPUT_FILE_NAME);
            
            Configuration->OutputFileName = DEFAULT_OUTPUT_FILE_NAME;
        }

    }

Exit:    
    return Error;
}


VOID
ShowUsage(
    VOID
    )
/*++

Routine Description:
      Shows the usage of the application.

Arguments:
      None.

Return Value:
      None.

--*/
{
    printf("Usage: async [-m {GET|POST}] [-h <hostname>] [-o <resourcename>] [-s] ");
    printf("[-p <proxyname>] [-w <output filename>] [-r <file to post>] [-t <userTimeout>]\n");
    printf("Option Semantics: \n");
    printf("-m : Specify method (Default: \"GET\")\n");
    printf("-h : Specify hostname (Default: \"%ws\"\n", DEFAULT_HOSTNAME);
    printf("-o : Specify resource name on the server (Default: \"%ws\")\n", DEFAULT_RESOURCE);
    printf("-s : Use secure connection - https\n");
    printf("-p : Specify proxy\n");
    printf("-w : Specify file to write output to (Default: \"%ws\")\n", DEFAULT_OUTPUT_FILE_NAME);
    printf("-r : Specify file to post data from\n");
    printf("-t : Specify time to wait in ms for operation completion (Default: %d)\n", DEFAULT_TIMEOUT);
    
    return;
}

VOID 
LogInetError(
    __in DWORD Err,
    __in LPCWSTR Str
    )
/*++

Routine Description:
     This routine is used to log WinInet errors in human readable form.

Arguments:
     Err - Error number obtained from GetLastError()
     Str - String pointer holding caller-context information 

Return Value:
    None.

--*/
{
    DWORD Result;
    PWSTR MsgBuffer = NULL;
    
    Result = FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
                           FORMAT_MESSAGE_FROM_HMODULE,
                           GetModuleHandle(L"wininet.dll"),
                           Err,
                           MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
                           (LPWSTR)&MsgBuffer,
                           ERR_MSG_LEN,
                           NULL);
    
    if (Result)
    {
        fprintf(stderr, "%ws: %ws\n", Str, MsgBuffer);
        LocalFree(MsgBuffer);
    }
    else
    {
        fprintf(stderr,
                "Error %d while formatting message for %d in %ws\n",
                GetLastError(),
                Err,
                Str);
    }

    return;
}

VOID
LogSysError(
    __in DWORD Err,
    __in LPCWSTR Str
    )
/*++

Routine Description:
     This routine is used to log System Errors in human readable form.

Arguments:
     Err - Error number obtained from GetLastError()
     Str - String pointer holding caller-context information 

Return Value:
    None.

--*/
{
    DWORD Result;
    PWSTR MsgBuffer = NULL;

    Result = FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
                           FORMAT_MESSAGE_FROM_SYSTEM,
                           NULL,
                           Err,
                           MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
                           (LPWSTR)&MsgBuffer,
                           ERR_MSG_LEN,
                           NULL);
    
    if (Result)
    {
        fprintf(stderr,
                "%ws: %ws\n",
                Str,
                MsgBuffer);
       LocalFree(MsgBuffer);
    }
    else
    {
        fprintf(stderr,
                "Error %d while formatting message for %d in %ws\n",
                GetLastError(),
                Err,
                Str);    
    }

    return;
}







发布了106 篇原创文章 · 获赞 10 · 访问量 31万+
展开阅读全文

关于HttpSendRequestA函数发送请求成功后,保存网页返回内容的问题

08-22

下边的代码是点击按钮事件的,HttpSendRequest明显已经执行成功,得到的recv.html中依然是登陆页面,不是我想要的登陆后跳过去的页面 ``` #include <afxinet.h> #include <WinInet.h> #include <cstring> #pragma comment(lib,"wininet.lib") void CLOGINDlg::OnOK() { // TODO: Add extra validation here //CDialog::OnOK(); HINTERNET hSession = NULL; HINTERNET hConnect = NULL; HINTERNET hRequest = NULL; //InternerOpen LPCSTR lpszAgent = "Application"; DWORD dwAccessType = INTERNET_OPEN_TYPE_PRECONFIG; /* HINTERNET InternetOpen( _In_ LPCTSTR lpszAgent, //指定调用Wininet函数的应用程序或入口,该入口作HTTP协议中用户代理项 _In_ DWORD dwAccessType, //访问要求类型 _In_ LPCTSTR lpszProxyName, //代理服务器的名字 _In_ LPCTSTR lpszProxyBypass,//指向一个字符串,指定一个可选的主机名列表或IP地址 _In_ DWORD dwFlags ); */ hSession = InternetOpenA(lpszAgent, dwAccessType, NULL, NULL, 0); if (!hSession) { printf("网络打开失败!"); return ; } //InternetConnectA LPCSTR lpszServerName = "202.200.144.63"; INTERNET_PORT nServerPort = INTERNET_DEFAULT_HTTP_PORT; DWORD dwService = INTERNET_SERVICE_HTTP; hConnect = InternetConnectA(hSession, lpszServerName, nServerPort, NULL, NULL, dwService, 0, 1); if (!hConnect) { printf("网络连接失败!"); return ; } //HttpOpenRequestA LPCSTR lpszVerb = "GET"; LPCSTR lpszObjectName = "xs_main.aspx?xh=120620223";//网站的对象名,网址在IntetnetConnectA第二个参数指定 LPCSTR lpszVersion = "HTTP/1.1"; LPCSTR lpszReferer = "Referer:http://202.200.144.63/xs_main.aspx?xh=120620223 "; LPCSTR lpszAcceptTypes = "Accept:text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8"; DWORD dwFlags = INTERNET_FLAG_RELOAD; hRequest = HttpOpenRequestA(hConnect, lpszVerb, lpszObjectName, lpszVersion, lpszReferer, &lpszAcceptTypes, dwFlags, 1); if (!hRequest) { printf("打开请求失败!"); return ; } //HttpSendRequestA LPCSTR lpszHeaders = "Content-Type: application/x-www-form-urlencoded;Cookie: safedog-flow-item=F727C5A572FD29201828136C14E22568; ASP.NET_SessionId=uxizon55w1ua4djitykxu3vl\r\n"; DWORD dwHeadersLength = -1L; char szFormData[1024] = { 0 }; strcpy(szFormData, "__VIEWSTATE=dDwyODE2NTM0OTg7Oz4h2wC4w12c8RCRF3jDvJBjVLPUHQ%3D%3D&txtUserName=120620223&TextBox2=******&txtSecretCode="+m_YZM+"&RadioButtonList1=%D1%A7%C9%FA&Button1=&lbLanguage=&hidPdrs=&hidsc=\r\n");//保存用户名,密码,用户类别及验证码 LPVOID pBuf = (LPVOID)szFormData; BOOL bReturn = HttpSendRequestA(hRequest, lpszHeaders, dwHeadersLength, NULL, 0); if (!bReturn) { printf("返回请求失败!"); return ; }else AfxMessageBox("登陆成功!"); char szRecvBuf[1024 + 1]; // 接受数据缓冲区 DWORD dwNumberOfBytesRead; // 服务器返回大小 DWORD dwRecvTotalSize = 0; // 接受数据总大小 DWORD dwRecvBuffSize = 0; // 接受数据buf的大小 memset(szRecvBuf, 0, 1024 + 1); LPCSTR lpFileName = "recv.html"; DWORD dwNumberOfBytesWritten; HANDLE hOpenFile = (HANDLE)CreateFileA(lpFileName, GENERIC_WRITE, 0, NULL, OPEN_ALWAYS, NULL, NULL); if (hOpenFile == INVALID_HANDLE_VALUE) { printf("打开文件失败!"); hOpenFile = NULL; return ; } do { bReturn = InternetReadFile(hRequest, szRecvBuf, 1024, &dwNumberOfBytesRead); if (!bReturn) { printf("读取文件失败!"); break; } // 统计接受数据的大小 szRecvBuf[dwNumberOfBytesRead] = '\0'; dwRecvTotalSize += dwNumberOfBytesRead; dwRecvBuffSize += strlen(szRecvBuf); WriteFile(hOpenFile, szRecvBuf, dwNumberOfBytesRead, &dwNumberOfBytesWritten, NULL); } while (dwNumberOfBytesRead != 0); CloseHandle(hOpenFile); InternetCloseHandle(hSession); InternetCloseHandle(hConnect); InternetCloseHandle(hRequest); } ``` 问答

没有更多推荐了,返回首页

©️2019 CSDN 皮肤主题: 大白 设计师: CSDN官方博客

分享到微信朋友圈

×

扫一扫,手机浏览