好吧,我遇上了windows api的bug...
一开始程序是采用了CInternetSession来打开一个Session,然后再用OpenUrl来打开一个CHttpFile文件. 这个程序一直工作得很好,只要ie能上网,它就能下载。如果用代理服务器,只需要在ie中设置好即可。如果代理服务器需要口令,只需要先在ie中访问页面,输入口令,并选择保存口令,这个程序就也能正常透过代理连接了。
直到有一天,它被安装在了一台ie6的windows xp机器上,它不能工作了。
因为 CInternetSession::OpenUrl方法调用了InternetOperUrl api函数,而InternetOperUrl函数,有个BUG.
InternetOperUrl在IE6的环境下,除非代理服务器的用户名与口令与当前用户的用户名与口令一致,否则他不能透过代理服务器连接http文件。
为什么我知道这是个BUG? 因为安装了ie8之后, InternetOperUrl就能正常工作了——只要在ie通过代理上网时,输入代理服务器口令时选择一下“保存我的口令”,InternetOperUrl就也能正常连接了。
在ie6下, InternetOperUrl的似乎是总是用当前登录电脑的用户名与口令向代理服务器验证。这明显是一个错误。后来他们修好了。
这个问题让我折腾了三天,安装了n台不同版本的windows和ie环境,测试了各种各样的程序,我都几乎准备要动用我的msdn技术支持时, 终于确认了问题原因。
知道了原因就好办了。只需要绕开mfc的这个问题,直接用几个底层api就可以正常工作了。下面是可以正常工作的代码,一共支持三种不同的代理服务器设置:0 用ie的设置(包括ie保存了的密码),1,坚决不用代理,一定要直连。2,用本程序指定的代理。
iProxyMode 、bProxyNeedPassword、proxyinfo、sProxyUserName、sProxyPassword等是全局变量,你可以在调用这个函数之前准备好这些变量。
{
BOOL b;
BOOL bOK = TRUE;
DWORD dwServiceType;
CString strServer;
CString strObject;
INTERNET_PORT nPort;
BOOL bParsed = AfxParseURL(psUrl, dwServiceType, strServer, strObject, nPort);
if (!bParsed)
{
sErrMg = "远程文件地址格式不对!";
return FALSE;
}
HINTERNET m_hInternet = InternetOpen(
"Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; InfoPath.1)",
INTERNET_OPEN_TYPE_PRECONFIG,
NULL,
NULL, 0);
if (FALSE == m_hInternet)
{
InternetCloseHandle(m_hInternet);
sErrMg.Format("建立网络会话 %s 失败", psUrl);
return FALSE;
}
if (iProxyMode != 0)
{
b = InternetSetOption (NULL, INTERNET_OPTION_PROXY, (LPVOID) &proxyinfo, sizeof (proxyinfo));
}
HINTERNET m_hConnection = InternetConnect(
m_hInternet,
strServer,
nPort,
NULL,
NULL,
INTERNET_SERVICE_HTTP,
INTERNET_FLAG_NO_UI,
NULL);
if (FALSE == m_hConnection)
{
InternetCloseHandle(m_hConnection);
InternetCloseHandle(m_hInternet);
sErrMg.Format("建立网络连接 %s 失败", psUrl);
return FALSE;
}
if (iProxyMode == 2 && bProxyNeedPassword)
{
DWORD dwUserNameLen = sProxyUserName.GetLength() + 1;
DWORD dwUserPassLen = sProxyPassword.GetLength() + 1;
b = InternetSetOption (m_hConnection, INTERNET_OPTION_PROXY_USERNAME, (LPVOID)(LPCTSTR)sProxyUserName, dwUserNameLen);
b = InternetSetOption (m_hConnection, INTERNET_OPTION_PROXY_PASSWORD, (LPVOID)(LPCTSTR)sProxyPassword, dwUserPassLen);
}
static LPCTSTR s_szAcceptTypes[] = { _T("*/*"), NULL };
HINTERNET m_hRequest = HttpOpenRequest(
m_hConnection, _T("GET"),
strObject,
_T("HTTP/1.0"), NULL,
s_szAcceptTypes,
INTERNET_FLAG_NO_UI | INTERNET_FLAG_KEEP_CONNECTION, // | ((m_url.GetScheme() == ATL_URL_SCHEME_HTTPS) ? INTERNET_FLAG_SECURE : 0)
NULL);
if (FALSE == m_hRequest)
{
InternetCloseHandle(m_hRequest);
InternetCloseHandle(m_hConnection);
InternetCloseHandle(m_hInternet);
sErrMg.Format("打开网络连接 %s 失败", psUrl);
return FALSE;
}
CString strHeaders;
//strHeaders.Append(_T("Content-Type: text/xml; charset=utf-8\r\n"));
b = HttpSendRequest(m_hRequest, strHeaders, (DWORD) strHeaders.GetLength(),
NULL, 0);
if (FALSE == b)
{
InternetCloseHandle(m_hRequest);
InternetCloseHandle(m_hConnection);
InternetCloseHandle(m_hInternet);
sErrMg.Format("向服务器发送请求 %s 失败", psUrl);
return FALSE;
}
int iStatus = GetInternetRequestStatusCode(m_hRequest);
if(!(iStatus>= 200&& iStatus<300 ))
{
InternetCloseHandle(m_hRequest);
InternetCloseHandle(m_hConnection);
InternetCloseHandle(m_hInternet);
sErrMg.Format("打开远程文件出错,错误码:%d", iStatus);
return FALSE;
}
byte pData[65535];
DWORD dwReadedLen;
DWORD dwWrittenLen;
HANDLE hfile = CreateFile(psLocalFile,GENERIC_WRITE,0,0,CREATE_ALWAYS,FILE_ATTRIBUTE_NORMAL,0);
if (hfile == INVALID_HANDLE_VALUE)
{
InternetCloseHandle(m_hRequest);
InternetCloseHandle(m_hConnection);
InternetCloseHandle(m_hInternet);
sErrMg.Format("创建本地文件 %s 失败", psLocalFile);
return FALSE;
}
while(1)
{
b = InternetReadFile(m_hRequest, (LPVOID)pData, sizeof(pData), &dwReadedLen);
if (b == FALSE)
{
CloseHandle(hfile);
InternetCloseHandle(m_hRequest);
InternetCloseHandle(m_hConnection);
InternetCloseHandle(m_hInternet);
sErrMg.Format("读取文件 %s 失败", psUrl);
return FALSE;
}
if(dwReadedLen == 0)
{
break;
}
b = WriteFile(hfile, pData, dwReadedLen, &dwWrittenLen,NULL);
if (b == FALSE)
{
CloseHandle(hfile);
InternetCloseHandle(m_hRequest);
InternetCloseHandle(m_hConnection);
InternetCloseHandle(m_hInternet);
sErrMg.Format("写入本地文件 %s 失败", psLocalFile);
return FALSE;
}
}
CloseHandle(hfile);
InternetCloseHandle(m_hRequest);
InternetCloseHandle(m_hConnection);
InternetCloseHandle(m_hInternet);
sErrMg.Format("下载成功");
return TRUE;
}