Author E-mail: zhtrue@sina.com
Asynchronous mode is not a high-profile thing, Wininet API is even more familiar.
If you read the article about Wininet API, you will find that although it is used in many chapters, most of you only say that you can use it, but not say how to use. Despite this, there are still some articles to give us a lot of tips, I will list later.
Since network data transfer often consumes a certain time, we always put these possible operations to a separate sub-thread to avoid affecting the main thread. However, when the sub-thread occurs, the main thread needs to exit for some reason, and we usually hope that the subline can be properly exited before the main thread exits. At this time, the main thread has to wait for the WAIT sub-thread, so that the main thread is also blocked. Of course, the main thread can be exited from the WAIT sub-thread, and you can use TerminateThread to terminate the sub-thread, but such consequences are usually unpredictable, memory leaks may be the lightest harm.
Using asynchronous ways is to solve the correct means of such problems. Here we analyze the use of Wininet API asynchronous methods and precautions based on an instance.
Our example completes this function: give a URL (such as: http://www.sina.com.cn/), download the web page or file using the HTTP protocol. We have created three threads: the main thread is responsible for creating a sub-line thread and waits for a sub-thread to return messages; sub-threads use asynchronous Wininet API to complete the download task, and return messages to the main thread at each phase; Create a callback function thread, and the role is explained later.
API involves some threads, messages, events, and error handling, because it is not the content I discussed, I don't care.
1. Main thread workflow a. Create a sub-load thread m_hmainthread = :: Createthread (Null, 0, AsyncMainThread, this, null, & m_dwmainthreadid);
b. Waiting for the sub-thread return message MSG msg; while (1) {:: getMessage (& msg, m_hwnd, 0, 0); if (msg.MESSAGE == WM_ASYNCGETHTTTTTPFILE) {// Subline Release message Switch (Loword (MSG) .wparam) {copy agnf_fail: {MessageBox (_T ("Download Action Failure End!")); Return;} Case Aghf_suCcess: MessageBox (_T ("Download Action Successfully End!")); Return; Case Aghf_Process: // Download progress notification break; case AGHF_LENGTH: // get the download file size notification break;}} DispatchMessage (& msg);} 2. Download the child thread using a workflow mark INTERNET_FLAG_ASYNC initialization InternetOpen m_hInternet = :: InternetOpen (m_szAgent, INTERNET_OPEN_TYPE_PRECONFIG, NULL. , Null, internet_flag_async; starting is not difficult, it is not difficult to understand, after the MSDN says this setting, all API calls are asynchronous. Be alert ... It seems to be very simple, but there will be countless traps waiting for us to fall.
b. Set the status callback function InternetSetStatusCallback :: InternetSetStatusCallback (m_hinternet, askNCINTERNETCALLBACK); the first trap is waiting for you, the literature [2] mentioned using a separate thread to perform this setting, and explain if There is a potential impact, but there is no example in other documents. Despite it, we still write this method to discuss this method first. The sub-thread needs to create a callback function thread: // Reset the callback function Setting successful event:: resetEvent (m_hevent [0]); m_hcallbackthread = :: CreateThread (null, 0, asyncCallbackthread, this, null, & m_dwcallbackthread, this, null, & m_dwcallbackthread; // Waiting success event callback function :: WaitForSingleObject (m_hEvent [0], INFINITE); implemented callback thread follows: DWORD WINAPI CAsyncGetHttpFile :: AsyncCallbackThread (LPVOID lpParameter) {CAsyncGetHttpFile * pObj = (CAsyncGetHttpFile *) lpParameter; :: InternetSetStatusCallback (pObj -> m_hinternet, askNCINTERNETCALLBACK; // Notification Subline callback function setting success, the sub-thread can continue :: setEvent (pobj-> m_hevent [0]); // Wait for the user to terminate event or sub-end event // sub-thread Set the sub-thread end event before the end, and wait for the callback thread to end: WaitForsingleObject (pobj-> m_hevent [2], infinite); Return 0;} It is indeed a lot of complications, although the results I experiment have found two setup methods It can work correctly, but it does find some different effects generated by these two setup methods, and unfortunately I didn't figure out the specific reason. I recommend you to use the latter method.
c. all of a sudden interrupted thread process, due to the relationship between the part and the callback function so close, we take a look at its implementation void CALLBACK CAsyncGetHttpFile :: AsyncInternetCallback (HINTERNET hInternet, DWORD dwContext, DWORD dwInternetStatus, LPVOID lpvStatusInformation, DWORD dwStatusInformationLength) {CAsyncGetHttpFile * pObj = (CAsyncGetHttpFile *) dwContext; // in our application, we are only concerned with the following three state switch (dwInternetStatus) {// handle is created case INTERNET_STATUS_HANDLE_CREATED: pObj-> m_hFile = (HINTERNET) ((( LPINTERNET_ASYNC_RESULT) -> DWRESUSINFORMATION)) -> dwresult); Break; // Handle is turned off Case Internet_Status_Handle_Closing: :: setevent (pobj-> m_hevent [1]); Break; // A request is completed, such as a handle created, or Request case Internet_status_request_complete: if ((LPINTERNET_ASYNC_RESULT)) -> dwerror) {// set the handle to create an event or read data successfully Event :: setEvent (pobj-> m_hevent [0] } Else {// If an error occurs, set the sub-thread exits // here is also a trap, often ignored this error, :: setAvent (pobj-> m_hevent [2]);} Break;}}
d. Continue the process of the sub-thread, use Internetopenur to complete the connection and get the download file header information // Reset handle is created :: resetEvent (m_hevent [0]); m_hfile = :: Internetopenurl (m_hinternet, m_szurl, null, null, INTERNET_FLAG_DONT_CACHE | INTERNET_FLAG_RELOAD, (DWORD) this); if (NULL == m_hFile) {if (ERROR_IO_PENDING == :: GetLastError ()) {if (WaitExitEvent ()) {return FALSE;}} else {return FALSE;}} etc. We list the WaiteXiteVent function to all interpretation: BOOL CASYNCGETHTTPFILE :: WaiteXiteVent () {dWord dwit = :: WaitFormultiplebjects (3, m_hevent, false, infinite); switch (dwret) {// handle is created Event or read data request successfully completed the event case wait_Object_0: // Handle is closed Events Case Wait_Object_0 1: // User Requires Subriginal Events or Error Events Wait_Object_0 2: Break;} Return Wait_Object_0! = Dwret;} Here we finally see the huge advantage of asynchronous mode, the Internetopenurl function To complete the domain name resolution, server connection, send request, receive returns information, etc. In the asynchronous mode INTERNETOpenURL does not wait to successfully create m_hfile, we see M_Hfile It is possible to assign a value in the callback function. If the return value of Internetopenurl is null and getLastError returns ERROR_IO_PENDING, we use WaitFormultiPleObjects to wait for the successful completion of the request, so that the main thread will have the opportunity to terminate the operation of the child thread during this. I am really can't wait for the code that wants to force the main thread to force the sub-thread.: // Set the requirements of the required sub-thread Event :: setEvent (m_hevent [2]); // Waiting for the sub-thread security exit: WaitforsingleObject (m_hmainthread, Infinite; // Close thread handle :: CloseHandle (m_hmainthread); haha, do not need to use TerminateThread termination thread, everything is safe, expected. Let's consider a situation, this situation is better than your imagination, INTERNETOPENURL returns a non-empty M_HFILE? Oh, this shows that Internetopenurl has successfully created a m_hfile, and no blocking occurs, no need to wait any event, continue next step. Finally, it is necessary to say that the last parameter of Internetopenurl is used as the second parameter of the callback function. And even if this parameter is not needed in the callback function, you can't set it to 0, otherwise InternetopenURL will not work in an asynchronous manner.
Here, we have shown the key parts of the Wininet API's asynchronous way, you should be able to write your own application using the Wininet API asynchronous way. But let's continue to complete the other parts of this instance. . E header information analyzed using HttpQueryInfo DWORD dwStatusSize = sizeof (m_dwStatusCode); if (FALSE == :: HttpQueryInfo (m_hFile, HTTP_QUERY_STATUS_CODE | HTTP_QUERY_FLAG_NUMBER, & m_dwStatusCode, & dwStatusSize, NULL)) // Get Returns the status code {return FALSE;} // Analyzing the status code is not 200 if (HTTP_STATUS_OK = m_dwStatusCode!) {return FALSE;} DWORD dwLengthSize = sizeof (m_dwContentLength); if (FALSE == :: HttpQueryInfo (m_hFile, HTTP_QUERY_CONTENT_LENGTH | HTTP_QUERY_FLAG_NUMBER, & m_dwContentLength, & dwLengthSize, NULL)) // Gets the return Content-length {Return False;} ... // Notifies the main thread to get the file size The success needs to indicate that HTTPQueryInfo does not perform network operation, so it does not need to process the asynchronous operation.
f. Use the tag IRF_ASYNC read data InternetReadFileEx // In order to report the progress to the main thread, we set up to 1024 bytes for each read data (DWORD I = 0; I In the example, we build a complete HTTP download program and can fully monitor the download process in the main thread. We used the WinInet of these API functions: InternetOpen InternetSetStatusCallback InternetOpenUrl HttpQueryInfo InternetReadFileEx InternetCloseHandle wherein InternetOpenUrl InternetReadFileEx and functions work asynchronously according to the literature [4] are listed according to the API may operate asynchronously: FtpCreateDirectory FtpDeleteFile FtpFindFirstFile FtpGetCurrentDirectory FtpGetFile FtpOpenFile FtpPutFile FtpRemoveDirectory FtpRenameFile FtpSetCurrentDirectory GopherFindFirstFile GopherOpenFile HttpEndRequest HttpOpenRequest HttpSendRequestEx InternetConnect InternetOpenUrl InternetReadFileEx References: 1. http://www.codeproject.com/internet/asyncwininet.asp 2. MSDN: