SUMMARY
To properly simulate a Form submission using WinInet, you need to send a header that indicates the proper Content-Type. For Forms, the proper Content-Type header is: Content-Type: application/x-www-form-urlencoded
MORE INFORMATION
In many cases, the server does not respond appropriately if a Content-Type is not specified. For example, the Active Server Pages component of IIS 3.0 actually checks this header specifically for 'application/x-www-form- urlencoded' before adding form variables to the "Request.Form" object. This MIME/Content-Type indicates that the data of the request is a list of URL- encoded form variables. URL-encoding means that space character (ASCII 32) is encoded as ' ', special character such '!' encoded in hexadecemal form as '!'.
Here is a snippet of code that uses the MFC WinInet classes to simulate a Form POST request:
CString strHeaders = _T("Content-Type: application/x-www-form-urlencoded"); // URL-encoded form variables - // name = "John Doe", userid = "hithere", other = "P&Q" CString strFormData = _T("name=John Doe&userid=hithere&other=P&Q"); CInternetSession session; CHttpConnection* pConnection = session.GetHttpConnection(_T("ServerNameHere")); CHttpFile* pFile = pConnection->OpenRequest(CHttpConnection::HTTP_VERB_POST,_T("FormActionHere")); BOOL result = pFile->SendRequest(strHeaders,(LPVOID)(LPCTSTR)strFormData, strFormData.GetLength());
Without MFC, the same code translates to straight SDK calls as follows:
static
TCHAR frmdata[] = _T("name=John Doe&userid=hithere&other=P&Q"); statuc TCHAR accept[] = _T("Accept: */*"); // for clarity, error-checking has been removed HINTERNET hSession = InternetOpen("MyAgent",INTERNET_OPEN_TYPE_PRECONFIG, NULL, NULL, 0); HINTERNET hConnect = InternetConnect(hSession, _T("ServerNameHere"), INTERNET_DEFAULT_HTTP_PORT, NULL, NULL, INTERNET_SERVICE_HTTP, , ); HINTERNET hRequest = HttpOpenRequest(hConnect, "POST", _T("FormActionHere"), NULL, NULL, accept, , ); HttpSendRequest(hRequest, hdrs, strlen(hdrs), frmdata, strlen(frmdata));
我这里有一段程序,用来在一个对话框里显示出一次http request的原始信息,不过使用Inet API做的,希望能有帮助。
void CHTTPRequestDlg::OnButtonRequest() { UpdateData(TRUE); HINTERNET hInternet = InternetOpen("Mozilla/4.0 (compatible; MSIE 5.0; Windows NT 5.0)", INTERNET_OPEN_TYPE_DIRECT, NULL, NULL, NULL); HINTERNET hSession = InternetConnect(hInternet, m_strHost, m_nPort, "username", "password", INTERNET_SERVICE_HTTP, , ); char* szAccept[] = {"*/*",NULL}; CString strVerb; m_comboVerb.GetWindowText(strVerb); HINTERNET hRequest = HttpOpenRequest(hSession, strVerb, m_strObject, NULL, NULL, (LPCSTR*)szAccept, , ); struct { char* Language; char* Encoding; char* ContentType; }Headers = {"Accept-Language:zh-cn\r\n", "Accept-Encoding:gzip,deflate\r\n", "Content-Type:application/x-www-form-urlencoded\r\n"}; if(m_bLanguage) { HttpAddRequestHeaders(hRequest, Headers.Language, -1, HTTP_ADDREQ_FLAG_ADD|HTTP_ADDREQ_FLAG_REPLACE); } if(m_bEncoding) { HttpAddRequestHeaders(hRequest, Headers.Encoding, -1, HTTP_ADDREQ_FLAG_ADD|HTTP_ADDREQ_FLAG_REPLACE); } if(m_bContentType) { HttpAddRequestHeaders(hRequest, Headers.ContentType, -1, HTTP_ADDREQ_FLAG_ADD|HTTP_ADDREQ_FLAG_REPLACE); } LPCSTR lpAddHeader = NULL; LPCSTR lpContent = NULL; if(m_strHeaders.GetLength()) { if(m_strHeaders.Right(2) != "\r\n") { m_strHeaders+="\r\n"; } lpAddHeader = (LPCSTR)m_strHeaders; } if(m_strContent.GetLength() && (strVerb == "POST" || strVerb == "PUT")) { lpContent = (LPCSTR)m_strContent; } HttpSendRequest(hRequest, lpAddHeader, -1, (LPVOID)lpContent, m_strContent.GetLength()); m_editContentGot.SetSel(0,-1); m_editContentGot.ReplaceSel(""); LPSTR lpszData; //buffer for the data DWORD dwSize; //size of the data available DWORD dwDownloaded; //size of the downloaded data //Set the cursor to an hourglass. SetCursor(LoadCursor(NULL,IDC_WAIT)); // This loop handles reading the data. while(1) { // The call to InternetQueryDataAvailable determines the amount of // data available to download. if (!InternetQueryDataAvailable(hRequest,&dwSize,0,0)) { SetCursor(LoadCursor(NULL,IDC_ARROW)); break; } else { // Allocates a buffer of the size returned by InternetQueryDataAvailable lpszData = new char[dwSize+1]; // Reads the data from the HINTERNET handle. if(!InternetReadFile(hRequest,(LPVOID)lpszData,dwSize,&dwDownloaded)) { delete[] lpszData; break; } else { // Adds a null terminator to the end of the data buffer lpszData[dwDownloaded]='\0'; int nLen = m_editContentGot.GetWindowTextLength(); m_editContentGot.SetSel(nLen-1, nLen-1); m_editContentGot.ReplaceSel(lpszData); // Delete the two buffers delete[] lpszData; // Check the size of the remaining data. If it is zero, break. if (dwDownloaded == 0) { break; } } } } // Close the HINTERNET handle InternetCloseHandle(hRequest); InternetCloseHandle(hSession); InternetCloseHandle(hInternet); // Set the cursor back to an arrow SetCursor(LoadCursor(NULL,IDC_ARROW)); }
使用MFC示例如下:
首先设置m_strRequest请求字符串 eg."name=aaa&pass=bbb";
m_strServerName 服务器名称或者IP eg."www.yahoo.com"
m_strObjectName 请求文件位置 eg. "pub/aaa.asp"
请求的结果存放在m_strHtml中
void func() { CInternetSession m_InetSession("session"); CHttpConnection *pServer = NULL; CHttpFile* pFile = NULL; try { INTERNET_PORT nPort; nPort=80; pServer = m_InetSession.GetHttpConnection(m_strServerName, nPort); pFile = pServer->OpenRequest(CHttpConnection::HTTP_VERB_POST,m_strObjectName); char szHeaders[100]; strcpy(szHeaders,"Accept: text*/*\r\nContent-Type: application/x-www-form-urlencoded"); pFile->AddRequestHeaders(szHeaders); pFile->SendRequestEx(m_strRequest.GetLength()); pFile->WriteString(m_strRequest);//重要-->m_Request 中有"name=aaa&name2=BBB&" pFile->EndRequest(); DWORD dwRet; pFile->QueryInfoStatusCode(dwRet); CString str; m_Mutex.Lock(); m_strHtml=""; char szBuff[1024]; if (dwRet == HTTP_STATUS_OK) { UINT nRead; while ((nRead = pFile->Read(szBuff,1023))>0) { m_strHtml += CString(szBuff,nRead); } } m_Mutex.Unlock(); delete pFile; delete pServer; } catch (CInternetException* e) { CString s; s.Format("Internet Exception\r\nm_dwError%u,m_dwContextError%u",e->m_dwError,e->m_dwContext); AfxMessageBox(s); //catch errors from WinInet } }
1、获得WebBrowser Control的DWebBrowserEvents2::DocumentComplete事件
2、在DWebBrowserEvents2::DocumentComplete事件中根据IWebBrowser2::Document获得IHTMLDocument2
3、IHTMLDocument2::forms得到IHTMLElementCollection
4、在IHTMLElementCollection中根据name、tagName、ID得到指定的IHTMLElement
5、从IHTMLElement得到IHTMLFormElement
6、执行IHTMLFormElement::submit