2015-02-24 299 views
0

我最近一直在研究一个需要与服务器建立FTP连接并从中下载/上传文件的应用程序。出于性能原因,我想一次下载多个文件。出于这个原因,我试图在使用InternetOpen功能与INTERNET_FLAG_ASYNC标志,还有InternetSetStatusCallback功能沿Wininet API实现异步操作。这里是我的代码示例,在这里我想列出的递归远程服务器的主目录下的所有文件:FTP异步操作ERROR_IO_PENDING [Wininet C++]

 /*Global variables*/ 
      HANDLE MayContinue=0; 
      DWORD LatestResult=1; 
     /*Prototypes*/ 
     void CALLBACK CallbackFunction(HINTERNET,DWORD_PTR,DWORD,LPVOID,DWORD); 

    //Iteration function called by main() 

     void FTPIterate() 
      { 
       WIN32_FIND_DATAA data; 
       HINTERNET Internet; 
       INTERNET_STATUS_CALLBACK call; 
       HINTERNET h_file; 

       MayContinue = ::CreateEvent (NULL, FALSE, FALSE, NULL); 
       iconnect=InternetOpen(NULL,INTERNET_OPEN_TYPE_PROXY,proxy_url,NULL,INTERNET_FLAG_ASYNC); 
       call=InternetSetStatusCallback(iconnect,(INTERNET_STATUS_CALLBACK)CallbackFunction); 

       while(f[FLAG_FTP_ITERATE]) 
       { 
        MayContinue = ::CreateEvent(NULL,FALSE,FALSE,NULL); 
        InternetConnect(iconnect,ftp_url,INTERNET_DEFAULT_FTP_PORT,ftp_user,ftp_pass,INTERNET_SERVICE_FTP,NULL,LatestResult); 
         WaitForSingleObject (MayContinue, INFINITE); 
         server=(HINTERNET)LatestResult; 
          printf("Server handle: %i\n",(int)server); 
          printf("Server Error: %i\n",GetLastError()); 
          SetLastError(0); 
         MayContinue = ::CreateEvent(NULL,FALSE,FALSE,NULL); 
         FtpFindFirstFile(server,ftp_base,&data,INTERNET_FLAG_NO_CACHE_WRITE,LatestResult); 
         WaitForSingleObject(MayContinue,INFINITE); 
         h_file=(HINTERNET)LatestResult; 
          //do stuff 
          printf("FindFirstFile handle: %i\n",(int)h_File); 
         while((MayContinue = ::CreateEvent(NULL,FALSE,FALSE,NULL)) && InternetFindNextFileA(h_file,&data)) 
         { 
          WaitForSingleObject(MayContinue,INFINITE); 
          //do stuff 
         } 
           printf("FindNextFile Error: %i\n",GetLastError()); //loop is never executed 
        InternetCloseHandle(server); 
       } 

      } 

     void CALLBACK CallbackFunction(HINTERNET hInternet,DWORD_PTR dwContext,DWORD dwInternetStatus,LPVOID lpvStatusInformation,DWORD dwStatusInformationLength) 
     { 
     if (dwInternetStatus == INTERNET_STATUS_REQUEST_COMPLETE) 
      { 
      LatestResult = ((LPINTERNET_ASYNC_RESULT)lpvStatusInformation)->dwResult; 
      SetEvent (MayContinue); 
      } 
     } 

我的代码是基于堆栈溢出this岗位。当我运行它时,首先在拨打InternetConnect,即ERROR_IO_PENDING之后出现错误。根据WinAPI参考,这意味着还有一些操作正在执行。不应该致电WaitForSingleObject防止这种情况发生? (实际上,由InternetConnect返回的HINTERNET句柄似乎是有效的)。 当我打电话FtpFindFirstFile功能它正确检索的第一个文件,但是当我用它返回的句柄HINTERNET(这又似乎是有效的)在InternetFindNextFile功能失败与错误INVALID_HANDLE_VALUE

编辑:我用雷米的代码时EGT这些错误:

Connect Handle 00CC0004 
Waiting for server handle 
    Unable to find first file. OS Error: 6 //aren't those calls to FindFirstFile weird if InternetConnect hasn't returned yet? 
Waiting for server handle 
    Unable to find first file. OS Error: 6 
Waiting for server handle 
    Unable to find first file. OS Error: 6 
Waiting for server handle 
    Unable to find first file. OS Error: 6 
Waiting for server handle 
    Unable to find first file. OS Error: 6 
Waiting for server handle 
    Unable to find first file. OS Error: 6 
Waiting for server handle. 
Unable to connect to Server. Inet Error: 12015 Waiting for server handle 

有人可以帮助我找到错误了吗? 预先感谢您。

+1

“我调用'InternetConnect',这是'ERROR_IO_PENDING'后,首先是得到一个错误” - 你知道这* *如何*?你似乎没有检查'InternetConnect'的结果是否有成功的HANDLE返回值,也没有任何代码在NULL返回句柄的情况下检查子路径到'GetLastError()'。 – WhozCraig 2015-02-24 21:11:26

+0

@WhozCraig正如我在帖子中所说,错误我已经省略错误检查的清晰度。事实上,在我的源代码中,我检查了由'InternetConnect'返回的HANDLE(在调用WaitForSingleObject之后),将其转换为一个“not”(非空),然后调用GetLastError(),它返回'997'。在'FtpFindFirstFile'和'InternetFindNextFile'之后执行类似的检查。我正在编辑该片段以显示它。 – JuanGM 2015-02-24 21:17:10

+1

ERROR_IO_PENDING实际上并不是一个错误。这仅表示I/O操作已成功启动,但尚未完成。由于您请求异步操作,因此ERROR_IO_PENDING是正常的。 – 2015-02-24 23:02:02

回答

2

ERROR_IO_PENDING误差从InternetOpen()本身到来。由于WaitForSingleObject()成功,因此它不会覆盖GetLastError()(因为大多数API都会出错,所以错误会从InternetOpen()的结果继续进行)。这是不是正确的使用方法GetLastError()。假设所有的API覆盖GetLastError()(如果记录使用GetLastError()在所有),并确保你怎么称呼它立即只有当API失败(除非记录为在成功的条件下使用)。

什么你的代码做的是异步!您正在发出异步API调用,但是您正在等待其结果,这打破了目的。您的代码是同步作用,一样的,如果你省略INTERNAL_FLAG_ASYNC标志和WaitForSingleObject()电话(别说你是通过调用CreateEvent()不必要的泄漏事件资源),例如:

void LogError(const char *Op) 
{ 
    DWORD err = GetLastError(); 

    if (err == ERROR_INTERNET_EXTENDED_ERROR) 
    { 
     LPSTR szBuffer; 
     DWORD dwLength = 0; 

     InternetGetLastResponseInfoA(&err, NULL, &dwLength); 
     if (GetLastError() != INSUFFICIENT_BUFFER) 
     { 
      printf("%s. Unknown Inet Error. OS Error: %u", Op, GetLastError()); 
      return; 
     } 

     szBuffer = new char[dwLength+1]; 
     InternetGetLastResponseInfoA(&err, szBuffer, &dwLength); 
     szBuffer[dwLength] = 0; 

     printf("%s. Inet Error: %u %s", Op, err, szBuffer); 
     delete[] szBuffer; 
    } 
    else 
    { 
     LPSTR lpBuffer = NULL; 
     DWORD dwLen = FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_ARGUMENT_ARRAY | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, err, 0, (LPSTR)&lpBuffer, 0, NULL); 

     if (lpBuffer) 
     { 
      printf("%s. OS Error: %u %s", Op, err, lpBuffer); 
      LocalFree(lpBuffer); 
     } 
     else 
      printf("%s. OS Error: %u", Op, err); 
    } 

    printf("\n"); 
} 

void FTPIterate() 
{ 
    WIN32_FIND_DATAA data; 
    HINTERNET hConnect; 
    HINTERNET hServer; 
    HINTERNET hFile; 

    hConnect = InternetOpen(NULL, INTERNET_OPEN_TYPE_PROXY, proxy_url, NULL, 0); 
    if (hConnect == NULL) 
    { 
     LogError("Unable to Open Internet"); 
     return; 
    } 

    printf("Connect handle: %p\n", hConnect); 

    while (f[FLAG_FTP_ITERATE]) 
    { 
     printf("Connecting to Server\n"); 

     hServer = InternetConnect(hConnect, ftp_url, INTERNET_DEFAULT_FTP_PORT, ftp_user, ftp_pass, INTERNET_SERVICE_FTP, NULL, 0); 
     if (hServer == NULL) 
     { 
      LogError("Unable to connect to Server"); 
      continue; 
     } 

    printf("Connected to Server. Server handle: %p\n", hServer); 
     printf("Finding first file\n"); 

     hFile = FtpFindFirstFileA(hServer, ftp_base, &data, INTERNET_FLAG_NO_CACHE_WRITE, 0); 
     if (hFile == NULL) 
     { 
      if (GetLastError() == ERROR_NO_MORE_FILES) 
       printf("No files were found\n"); 
      else 
       LogError("Unable to find first file"); 
     } 
     else 
     { 
      printf("Find handle: %p\n", hFile); 

      do 
      { 
       //do stuff 

       printf("Finding next file\n"); 

       if (!InternetFindNextFileA(hFile, &data)) 
       { 
        if (GetLastError() == ERROR_NO_MORE_FILES) 
         printf("No more files were found\n"); 
        else 
         LogError("Unable to find next file") 

        break; 
       } 
      } 
      while (true); 

      InternetCloseHandle(hFile); 
     } 

     InternetCloseHandle(hServer); 
    } 

    InternetCloseHandle(hConnect); 
} 

为了使该代码运行异步,摆脱所有的关闭等待和实施需要回调的进步状态机,如:

enum FTPState {ftpConnect, ftpWaitingForConnect, ftpFindFirstFile, ftpWaitingForFirstFind, ftpFindNextFile, ftpWaitingForNextFind, ftpProcessFile, ftpDisconnect}; 

struct REQ_CONTEXT 
{ 
    FTPState State; 
    WIN32_FIND_DATAA data; 
    HINTERNET hConnect; 
    HINTERNET hServer; 
    HINTERNET hFile; 
    HANDLE hDoneEvent; 
}; 

void LogError(const char *Op, DWORD err) 
{ 
    if (err == ERROR_INTERNET_EXTENDED_ERROR) 
    { 
     LPSTR szBuffer; 
     DWORD dwLength = 0; 

     InternetGetLastResponseInfoA(&err, NULL, &dwLength); 
     if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) 
     { 
      printf("%s. Unknown Inet Error. OS Error: %u", Op, GetLastError()); 
      return; 
     } 

     szBuffer = new char[dwLength+1]; 
     InternetGetLastResponseInfoA(&err, szBuffer, &dwLength); 
     szBuffer[dwLength] = 0; 

     printf("%s. Inet Error: %u %s", Op, err, szBuffer); 
     delete[] szBuffer; 
    } 
    else 
    { 
     LPSTR lpBuffer = NULL; 
     DWORD dwLen = FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_ARGUMENT_ARRAY | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, err, 0, (LPSTR)&lpBuffer, 0, NULL); 

     if (lpBuffer) 
     { 
      printf("%s. OS Error: %u %s", Op, err, lpBuffer); 
      LocalFree(lpBuffer); 
     } 
     else 
      printf("%s. OS Error: %u", Op, err); 
    } 

    printf("\n"); 
} 

void LogError(const char *Op) 
{ 
    LogError(Op, GetLastError()); 
} 

void DoNextStep(REQ_CONTEXT *ctx) 
{ 
    do 
    { 
     if ((ctx->State == ftpConnect) && (!f[FLAG_FTP_ITERATE])) 
     { 
      printf("Done!\n"); 
      SetEvent(ctx->hDoneEvent); 
      return; 
     } 

     switch (ctx->State) 
     { 
      case ftpConnect: 
      { 
       printf("Connecting to Server\n"); 

       HINTERNET hServer = InternetConnect(ctx->hConnect, ftp_url, INTERNET_DEFAULT_FTP_PORT, ftp_user, ftp_pass, INTERNET_SERVICE_FTP, NULL, (DWORD_PTR)ctx); 
       if (hServer != NULL) 
       { 
        if (ctx->hServer == NULL) 
        { 
         ctx->hServer = hServer; 
         printf("Server handle: %p\n", ctx->hServer); 
        } 

        printf("Connected to Server\n"); 
        ctx->State = ftpFindFirstFile; 
       } 
       else if (GetLastError() == ERROR_IO_PENDING) 
       { 
        if (ctx->hServer == NULL) 
         printf("Waiting for Server handle\n"); 

        printf("Waiting for Server connect to complete\n"); 
        ctx->State = ftpWaitingForConnect; 
       } 
       else 
        LogError("Unable to connect to Server"); 

       break; 
      } 

      case ftpWaitingForConnect: 
       return; 

      case ftpFindFirstFile: 
      { 
       printf("Finding first file\n"); 

       HINTERNET hFile = FtpFindFirstFileA(ctx->hServer, ftp_base, &ctx->data, INTERNET_FLAG_NO_CACHE_WRITE, (DWORD_PTR)ctx); 
       if (hFile != NULL) 
       { 
        if (ctx->hFile == NULL) 
        { 
         ctx->hFile = hFile; 
         printf("Find handle: %p\n", ctx->hFile); 
        } 

        ctx->State = ftpProcessFile; 
       } 
       else if (GetLastError() == ERROR_IO_PENDING) 
       { 
        if (ctx->hFile == NULL) 
         printf("Waiting for Find handle\n"); 

        printf("Waiting for Find to complete\n"); 
        ctx->State = ftpWaitingForFirstFind; 
       } 
       else 
       { 
        if (GetLastError() == ERROR_NO_MORE_FILES) 
         printf("No files were found\n"); 
        else 
         LogError("Unable to find first file"); 

        ctx->State = ftpDisconnect; 
       } 

       break; 
      } 

      case ftpWaitingForFirstFind: 
      case ftpWaitingForNextFind: 
       return; 

      case ftpProcessFile: 
      { 
       //do stuff 

       printf("Finding next file\n"); 
       if (!InternetFindNextFileA(ctx->hFile, &ctx->data)) 
       { 
        if (GetLastError() == ERROR_IO_PENDING) 
        { 
         printf("Waiting for next file to complete\n"); 

         ctx->State = ftpWaitingForNextFind; 
        } 
        else 
        { 
         if (GetLastError() == ERROR_NO_MORE_FILES) 
          printf("No more files were found\n"); 
         else 
          LogError("Unable to find next file"); 

         ctx->State = ftpDisconnect; 
        } 
       } 

       break; 
      } 

      case ftpDisconnect: 
      { 
       printf("Disconnecting\n"); 

       if (ctx->hFile != NULL) 
       { 
        InternetCloseHandle(ctx->hFile); 
        ctx->hFile = NULL; 
       } 

       if (ctx->hServer != NULL) 
       { 
        InternetCloseHandle(ctx->hServer); 
        ctx->hServer = NULL; 
       } 

       ctx->State = ftpConnect; 
       break; 
      } 
     } 
    } 
    while (true); 
} 

void CALLBACK CallbackFunction(HINTERNET hInternet, DWORD_PTR dwContext, DWORD dwInternetStatus, LPVOID lpvStatusInformation, DWORD dwStatusInformationLength) 
{ 
    REQ_CONTEXT *ctx = (REQ_CONTEXT*) dwContext; 

    switch (dwInternetStatus) 
    { 
     case INTERNET_STATUS_HANDLE_CREATED: 
     { 
      LPINTERNET_ASYNC_RESULT Result = (LPINTERNET_ASYNC_RESULT) lpvStatusInformation; 

      switch (ctx->State) 
      { 
       case ftpConnect: 
       case ftpWaitingForConnect: 
        ctx->hServer = (HINTERNET) Result->dwResult; 
        printf("Server handle: %p\n", ctx->hServer); 
        break; 

       case ftpFindFirstFile: 
       case ftpWaitingForFirstFind: 
        ctx->hFile = (HINTERNET) Result->dwResult; 
        printf("Find handle: %p\n", ctx->hFile); 
        break; 
      } 

      break; 
     } 

     case INTERNET_STATUS_REQUEST_COMPLETE: 
     { 
      LPINTERNET_ASYNC_RESULT Result = (LPINTERNET_ASYNC_RESULT) lpvStatusInformation; 

      switch (ctx->State) 
      { 
       case ftpWaitingForConnect: 
       { 
        if (!Result->dwResult) 
        { 
         LogError("Unable to connect to Server", Result->dwError); 
         ctx->State = ftpDisconnect; 
        } 
        else 
        { 
         printf("Connected to Server\n"); 
         ctx->State = ftpFindFirstFile; 
        } 

        break; 
       } 

       case ftpWaitingForFirstFind: 
       case ftpWaitingForNextFind: 
       { 
        if (!Result->dwResult) 
        { 
         if (Result->dwError == ERROR_NO_MORE_FILES) 
          printf("No %sfiles were found\n", (ctx->State == ftpWaitingForNextFind) ? "more " : ""); 
         else if (ctx->State == ftpWaitingForFirstFind) 
          LogError("Unable to find first file", Result->dwError); 
         else 
          LogError("Unable to find next file", Result->dwError); 

         ctx->State = ftpDisconnect; 
        } 
        else 
         ctx->State = ftpProcessFile; 

        break; 
       } 
      } 

      DoNextStep(ctx); 
      break; 
     } 
    } 
} 

void FTPIterate() 
{ 
    REQ_CONTEXT ctx = {0}; 
    ctx.State = ftpConnect; 

    ctx.hDoneEvent = CreateEvent(NULL, TRUE, FALSE, NULL); 
    if (ctx.hDoneEvent == NULL) 
    { 
     LogError("Unable to Create Done Event"); 
     return; 
    } 

    ctx.hConnect = InternetOpen(NULL, INTERNET_OPEN_TYPE_PROXY, proxy_url, NULL, INTERNET_FLAG_ASYNC); 
    if (ctx.hConnect == NULL) 
    { 
     LogError("Unable to Open Internet"); 
     CloseHandle(ctx.hDoneEvent); 
     return; 
    } 

    printf("Connect handle: %p\n", ctx.hConnect); 
    InternetSetStatusCallback(ctx.hConnect, &CallbackFunction); 

    DoNextStep(&ctx); 
    WaitForSingleObject(ctx.hDoneEvent, INFINITE); 

    InternetCloseHandle(ctx.hConnect); 
    CloseHandle(ctx.hDoneEvent); 
} 
+0

感谢您的回复。至于错误代码,不是SetLastError将代码重置为SUCCES?关于异步操作,我的目标不是在索引上执行它们,而是在下载后执行它们,尽管这部分代码没有显示。谢谢你的帮助! – JuanGM 2015-02-24 23:04:45

+0

在循环的第一次迭代中,直到记录了从InternetConnect()传出的错误之后,才调用SetLastError()。 – 2015-02-24 23:31:46

+0

抱歉。我以为你在谈论'InternetConnect()'而不是'InternetOpen'。事实上,我没有正确处理该功能的错误。再次感谢。 – JuanGM 2015-02-25 18:13:34