mfc 的wininet库, 异步io模式, 弄了两天还没搞定:
首先debug版本中不让用异步模式, 在CInternetSession的构造函数中有一句
ASSERT(dwFlags & INTERNET_FLAG_ASYNC == 0);不知道为什么要这么做, 只好在release配置中也加入调试信息, 一直用release版本开发可以代码都写的差不多了, 又碰到奇怪的问题:
只要一调用CHttpFile::SendRequest或者SendRequestEx, 就会报kernel32.dll出错, 程序根本没办法进行下去:主要代码: try
{
m_pSession = new CMyInternetSession(_T("Internet Exploror"), (DWORD)this);
m_pSession->SetCallback(StatusCallback);
m_pConnection = m_pSession->GetHttpConnection(m_strServer, (INTERNET_PORT)m_nPort);
}
catch (CInternetException *e)
{
e->Delete();
ErrMsg(0);
Exit();
return -1;
}...
m_pHttpFile = m_pConnection->OpenRequest(_T("POST"),
strProgram,
NULL,
(DWORD)this); if (m_pHttpFile == NULL)
{
if (GetLastError() == ERROR_IO_PENDING)
{
if (WaitExitEvent())
{
Exit();
return -1;
}
}
else
{
TryTime++;
continue;
}
} CString strHeader = _T("multipart/form-data");
if (!m_pHttpFile->SendRequest(strHeader, m_pBuf, dwReaded))
{
if (GetLastError() == ERROR_IO_PENDING)
{
if (WaitExitEvent())
{
Exit();
return -1;
}
}
else
{
TryTime++;
m_pHttpFile->Close();
continue;
}
}每次一到m_pHttpFile->SendRequest, 就出错了刚才一怒之下, 直接用wininet api来做, 
if (!HttpSendRequest(m_hHttpFile, 
NULL, 0,
m_pBuf, dwReaded))
{
if (GetLastError() == ERROR_IO_PENDING)
{
if (WaitExitEvent())
{
Exit();
return -1;
}
}
else
{
DWORD dwErr = GetLastError();
TryTime++;
InternetCloseHandle(m_hHttpFile);
m_hHttpFile = NULL;
continue;
}
}倒是不报错了, 但是又出来个怪问题, 回调函数中每次收到的dwInternetStatus都是
INTERNET_STATUS_HANDLE_CREATED, 这个问题出的比较怪, 本来回调函数都工作正常了, 可程序调着调着, 发现收到的所有消息都成了
INTERNET_STATUS_HANDLE_CREATED, (HttpSendRequest之后回调函数也收到INTERNET_STATUS_HANDLE_CREATED消息), 其他应该会收到的消息, 象INTERNET_STATUS_NAME_RESOLVED, INTERNET_STATUS_RECEIVING_RESPONSE等等消息统统都没有了!?wininet库的资料好像还很少, msdn上也简略到不能再简略, 请熟悉这方面技术的朋友来帮帮忙。

解决方案 »

  1.   

    另外还有关于回调函数的问题, 比如当HttpOpenRequest返回NULL, 并且
    GetLastError() == ERROR_IO_PENDING, 也就是需要等待异步io完成, 那么我在回调函数中会收到INTERNET_STATUS_HANDLE_CREATED消息, 这时候在回调函数中, HINTERNET handle = (HINTERNET)(((LPINTERNET_ASYNC_RESULT)(lpvStatusInformation))->dwResult);可以得到创建的句柄, 但是由于其他函数也可能导致接受到INTERNET_STATUS_HANDLE_CREATED消息(比如InternetConnect函数, 虽然能够直接返回有效的连接句柄, 但回调函数同样会收到一条INTERNET_STATUS_HANDLE_CREATED消息, 那在回调函数中, 就需要方法来区分, 创建的是连接句柄还是文件句柄? 我想应该有标准的方法吧?另外, 如果用mfc wininet库, 在回调函数中, 能直接得到一个CHttpFile指针吗?
    还是同样得到HINTERNET句柄? 而CHttpFile类构造函数是protected的, 那这样根本无法从一个HINTERNET句柄构造出CHttpFile对象来,  这可是大问题了请高手来指点一下吧
      

  2.   

    另外如果在HttpSendRequest函数中, Header参数不等于NULL, 比如:
    CString strHeader = _T("multipart/form-data");
    if (!HttpSendRequest(m_hHttpFile, 
    strHeader.GetBuffer(0), strHeader.GetLength(),
    m_pBuf, dwReaded))那么总是会收到一条12150 错误, 在msdn网站上查到该错误表示"The requested header cannot be located. "这又是什么原因呢
      

  3.   

    终于弄明白了, 关键在于回调函数的参数dwContext, 这个参数一定不能简单的用通常包装回调函数的方法, 设置成this对象的指针, 因为在回调函数中, 某个消息可能由多个操作引发, 而dwContext就是用来区分是由哪一种操作引发的该消息, 就像前面所说的HttpOpenRequest和
    InternetConnect, HttpSendRequest都会引发一个INTERNET_STATUS_HANDLE_CREATED消息,那这时就要考dwContext来区分这三种情况, 只有当消息由HttpSendRequest引发时, 才能够设置request句柄; case INTERNET_STATUS_HANDLE_CREATED:
    if (dwContext == 2)
    {
    m_hHttpFile = (HINTERNET)(((LPINTERNET_ASYNC_RESULT)(lpvStatusInformation))->dwResult);
    SetEvent(m_hOperation);
    }
    break;在我的程序中, 其他两种情况似乎可以忽略。这样先前的奇怪现象: 回调函数只能收到INTERNET_STATUS_HANDLE_CREATED消息, 就可以解释了, 因为由HttpSendRequest引发INTERNET_STATUS_HANDLE_CREATED消息时, 我也设置了连接句柄:
    m_hHttpFile = (HINTERNET)(((LPINTERNET_ASYNC_RESULT)(lpvStatusInformation))->dwResult);  此时连接句柄已经被改变, 之后的操作肯定是会出问题的。目前我的解决如下:首先包装一个Context结构:
    struct Context
    {
       void *pObj;         // 对象句柄
       DWORD dwContext;    // context标识变量
    };在函数InternetConnect, HttpOpenRequest, HttpSendRequestEx调用之前, 分别把dwContext设置为1, 2, 3, 用来区分这三种调用, 在回调函数中做一判断: case INTERNET_STATUS_HANDLE_CREATED:
    if (dwContext == 2)
    {
    m_hHttpFile = (HINTERNET)(((LPINTERNET_ASYNC_RESULT)(lpvStatusInformation))->dwResult);
    SetEvent(m_hOperation);
    }
    break;如此以来, 一切都恢复正常了。期待以久的INTERNET_STATUS_REQUEST_COMPLETE消息也如约而至了。另外还有一点就是要用HttpSendRequestEx来替代HttpSendRequest函数, 因为HttpSendRequestEx函数可以设置自己的dwContext, 而不是直接使用先前HttpOpenRequest函数中设置的dwContext(我试过直接通过指针改变dwContext数值, 但不起作用, 该值在在内部应该是有一份拷贝), 否则在回调函数中将无法把SendRequest和OpenRequest区分开来.目前还是直接用的WinInet API, 至于mfc WinInet库的那两个问题:
    1. 调用SendRequest或者SendRequesetEx导致kernel32.dll出错
    2. 在回调函数中如何获取一个CHttpFile对象指针还是没有找到解决方法, 有知道的希望来跟贴啊