好吧,我遇上了windows api的bug...
一開始程式是採用了CInternetSession來開啟一個Session,然後再用OpenUrl來開啟一個CHttpFile檔案. 這個程式一直工作得很好,只要ie能上網,它就能下載。如果用Proxy 伺服器,只需要在ie中設定好即可。如果Proxy 伺服器需要口令,只需要先在ie中訪問頁面,輸入口令,並選擇儲存口令,這個程式就也能正常透過代理串連了。
直到有一天,它被安裝在了一台ie6的windows xp機器上,它不能工作了。
因為 CInternetSession::OpenUrl方法調用了InternetOperUrl api函數,而InternetOperUrl函數,有個BUG.
InternetOperUrl在IE6的環境下,除非Proxy 伺服器的使用者名稱與口令與目前使用者的使用者名稱與口令一致,否則他不能透過Proxy 伺服器串連http檔案。
為什麼我知道這是個BUG? 因為安裝了ie8之後, InternetOperUrl就能正常工作了——只要在ie通過代理上網時,輸入Proxy 伺服器口令時選擇一下“儲存我的口令”,InternetOperUrl就也能正常串連了。
在ie6下, InternetOperUrl的似乎是總是用當前登入電腦的使用者名稱與口令向Proxy 伺服器驗證。這明顯是一個錯誤。後來他們修好了。
這個問題讓我折騰了三天,安裝了n台不同版本的windows和ie環境,測試了各種各樣的程式,我都幾乎準備要動用我的msdn支援人員時, 終於確認了問題原因。
知道了原因就好辦了。只需要繞開mfc的這個問題,直接用幾個底層api就可以正常工作了。下面是可以正常工作的代碼,一共支援三種不同的Proxy 伺服器設定:0 用ie的設定(包括ie儲存了的密碼),1,堅決不用代理,一定要直連。2,用本程式指定的代理。
iProxyMode 、bProxyNeedPassword、proxyinfo、sProxyUserName、sProxyPassword等是全域變數,你可以在調用這個函數之前準備好這些變數。
BOOL GetHttpFile(LPCTSTR psUrl, LPCTSTR psLocalFile, CString &sErrMg)
{
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;
}