Program examples compiled using Visual C++ 6.0 compiler on Windows XP Pro machine with Service Pack 2 and some Figure screen snapshots have been taken on Windows 2000 server. Topics and sub topics for this tutorial are listed below. Don’t forget to read Tenouk’s small disclaimer. Supplementary item is WEBSITE.
Add the following client threads source files for Winsock, WinInet and OpenURL(): ClientinetThread.cpp, ClientsockThread.cpp and ClienturlThread.cpp.
Figure 77: Adding another new source file to project.
Add the following codes.
// clientinetthread.cpp (uses MFC Wininet calls)
#include <stdafx.h> #include "utility.h" #define MAXBUF 80000
HWND g_hMainWnd = 0; char g_pchStatus[25] = ""; CCriticalSection g_csStatus;
UINT ClientWinInetThreadProc(LPVOID pParam) { CCallbackInternetSession session; CHttpConnection* pConnection = NULL; CHttpFile* pFile1 = NULL; char* buffer = new char[MAXBUF]; UINT nBytesRead = 0; DWORD dwStatus; try { // username/password doesn't work yet if(!g_strServerName.IsEmpty()) { pConnection = session.GetHttpConnection(g_strServerName, (INTERNET_PORT) g_nPort); } else { pConnection = session.GetHttpConnection(g_strServerIP, (INTERNET_PORT) g_nPort); } pFile1 = pConnection->OpenRequest(1, g_strFile, NULL, 1, NULL, NULL,// GET request INTERNET_FLAG_KEEP_CONNECTION); // needed for NT Challenge/Response authentication // INTERNET_FLAG_RELOAD forces reload from the server (bypasses client's cache) pFile1->SendRequest(); pFile1->QueryInfoStatusCode(dwStatus); TRACE("QueryInfoStatusCode = %d\n", dwStatus); nBytesRead = pFile1->Read(buffer, MAXBUF - 1); buffer[nBytesRead] = '\0'; // necessary for message box char temp[100]; if(pFile1->Read(temp, 100) != 0) { // makes caching work if read complete AfxMessageBox("File overran buffer -- not cached"); } ::MessageBox(::GetTopWindow(::GetDesktopWindow()), buffer, "WININET CLIENT", MB_OK); // could use existing pFile1 to SendRequest again if we wanted to } catch(CInternetException* e) { LogInternetException(pParam, e); e->Delete(); } // could call OpenRequest again on same connection if we wanted to if(pFile1) delete pFile1; // does the close -- prints a warning // why does it print a warning? if(pConnection) delete pConnection; delete [ ] buffer; g_csStatus.Lock(); // problem with empty string. bug #9897 strcpy(g_pchStatus, ""); g_csStatus.Unlock(); return 0; }
|
Listing 21.
// clientsockthread.cpp (uses Winsock calls only)
#include <stdafx.h> #include "blocksock.h" #include "utility.h" #define MAXBUF 80000
CString g_strIPClient; CString g_strProxy = "ITGPROXY"; BOOL g_bUseProxy = FALSE;
UINT ClientSocketThreadProc(LPVOID pParam) { // sends a blind request, followed by a request for a specific URL CHttpBlockingSocket sClient; char* buffer = new char[MAXBUF]; int nBytesReceived = 0; // We're doing a blind GET, but we must provide server name if we're using a proxy. // A blind GET is supposed to retrieve the server's default HTML document. // Some servers don't have a default document but return a document name in the Location header. char request[ ] = "GET %s%s%s HTTP/1.0\r\n"; char headers[ ] = "User-Agent: Mozilla/1.22 (Windows; U; 32bit)\r\n" "Accept: */*\r\n" "Accept: image/gif\r\n" "Accept: image/x-xbitmap\r\n" "Accept: image/jpeg\r\n" // following line tests server's ability to not send the URL // "If-Modified-Since: Wed, 11 Sep 1996 20:23:04 GMT\r\n" "\r\n"; // need this CSockAddr saServer, saPeer, saTest, saClient; try { sClient.Create(); if(!g_strIPClient.IsEmpty()) { // won't work if network is assigning us our IP address // good only for intranets where client computer has several IP addresses saClient = CSockAddr(g_strIPClient); sClient.Bind(saClient); } if(g_bUseProxy) { saServer = CBlockingSocket::GetHostByName(g_strProxy, 80); } else { if(g_strServerIP.IsEmpty()) { saServer = CBlockingSocket::GetHostByName(g_strServerName, g_nPort); } else { saServer = CSockAddr(g_strServerIP, g_nPort); } } sClient.Connect(saServer); sClient.GetSockAddr(saTest); TRACE("SOCK CLIENT: GetSockAddr = %s, %d\n", saTest.DottedDecimal(), saTest.Port()); if(g_bUseProxy) { wsprintf(buffer, request, "https://" , (const char*) g_strServerName, g_strFile); } else { wsprintf(buffer, request, "", "", g_strFile); } sClient.Write(buffer, strlen(buffer), 10); sClient.Write(headers, strlen(headers), 10); // read all the server's response headers do { nBytesReceived = sClient.ReadHttpHeaderLine(buffer, MAXBUF, 10); TRACE("SOCK CLIENT: %s", buffer); } while(strcmp(buffer, "\r\n")); // read the server's file nBytesReceived = sClient.ReadHttpResponse(buffer, MAXBUF, 10); TRACE("SOCK CLIENT: bytes received = %d\n", nBytesReceived); if(nBytesReceived == 0) { AfxMessageBox("No response received. Bad URL?"); } else { buffer[nBytesReceived] = '\0'; ::MessageBox(::GetTopWindow(::GetDesktopWindow()), buffer, "WINSOCK CLIENT", MB_OK); } // could do another request on sClient by calling Close, then Create, etc. } catch(CBlockingSocketException* e) { LogBlockingSocketException(pParam, "CLIENT:", e); e->Delete(); } sClient.Close(); delete [ ] buffer; return 0; }
|
Listing 22.
// clienturlthread.cpp (uses CInternetSession::OpenURL)
#include <stdafx.h> #include "utility.h" #define MAXBUF 80000
CString g_strURL = "https://";
UINT ClientUrlThreadProc(LPVOID pParam) { char* buffer = new char[MAXBUF]; UINT nBytesRead = 0;
CInternetSession session; // can't get status callbacks for OpenURL CStdioFile* pFile1 = NULL; // could call ReadString to get 1 line try { pFile1 = session.OpenURL(g_strURL, 0, INTERNET_FLAG_TRANSFER_BINARY | INTERNET_FLAG_KEEP_CONNECTION); // if OpenURL fails, we won't get past here nBytesRead = pFile1->Read(buffer, MAXBUF - 1); buffer[nBytesRead] = '\0'; // necessary for message box char temp[100]; if(pFile1->Read(temp, 100) != 0) { // makes caching work if read complete AfxMessageBox("File overran buffer -- not cached"); } ::MessageBox(::GetTopWindow(::GetDesktopWindow()), buffer, "URL CLIENT", MB_OK); } catch(CInternetException* e) { LogInternetException(pParam, e); e->Delete(); } if(pFile1)delete pFile1; delete [ ] buffer; return 0; }
|
Listing 23.
Using ClassWizard, add SaveModified() virtual function to CMyex33aDoc class.
Figure 78: Adding SaveModified() function to CMyex33aDoc class.
Click the Edit Code button and edit the SaveModified() as shown below.
BOOL CMyex33aDoc::SaveModified()
{
// TODO: Add your specialized code here and/or call the base class
return TRUE; // eliminates "save doc" message on exit
}
Using ClassWizard, add the following message map to CMyex33aView class. Before that, open the ResourceView, select the IDD_DIALOGBAR under the dialog folder and launch ClassWizard. When the Adding a class prompt displayed, chooseSelect an existing class radio button and select CMyex33aView class. Just click Yes button if warning/prompt dialog displayed.
|
Figure 79: Adding new class prompt dialog.
Figure 80: Selecting the existing class. |
Next, add the following message handlers.
ID |
Message |
Function |
IDR_REQUEST |
BN_CLICK |
OnRequest() |
Table 28. |
Figure 81: Adding message handler to CMyex33aView class.
Next, add the following command, command updates and Windows message handler to the CMyex33aView class.
ID |
Message map |
Function |
ID_INTERNET_START_SERVER |
Command |
OnInternetStartServer() |
- |
Command Update |
OnUpdateInternetStartServer(CCmdUI* pCmdUI) |
ID_INTERNET_REQUEST_SOCK |
Command |
OnInternetRequestSocket() |
ID_INTERNET_REQUEST_INET |
Command |
OnInternetRequestWininet() |
ID_INTERNET_STOP_SERVER |
Command |
OnInternetStopServer() |
- |
Command Update |
OnUpdateInternetStopServer(CCmdUI* pCmdUI) |
ID_INTERNET_CONFIGURATION |
Command |
OnInternetConfiguration() |
- |
Command Update |
OnUpdateInternetConfiguration(CCmdUI* pCmdUI) |
ID_EDIT_CLEAR_ALL |
Command |
OnEditClearAll() |
IDR_CONTEXT_MENU |
WM_CONTEXTMENU |
OnContextMenu(CWnd* pWnd, CPoint point) |
Table 29. |
Figure 82: Adding command, command update and Windows message handler.
You can verify the result in Myex33aView.h as shown below.
Listing 24.
And in Myex33aView.cpp as shown below.
Listing 25.
Next, add the implementation codes to CMyex33aView.cpp. Don’t forget the related header files that need to be included as shown below.
// myex33aView.cpp : implementation of the CMyex33aView class //
#include "stdafx.h" #include "myex33a.h" #include "MainFrm.h"
#include "myex33aDoc.h" #include "myex33aView.h" #include "Utility.h" #include "Sheetconfig.h" #include "Blocksock.h"
#ifdef _DEBUG #define new DEBUG_NEW #undef THIS_FILE static char THIS_FILE[ ] = __FILE__; #endif
extern CBlockingSocket g_sListen;
///////////////////////////////////////////////////////////////////////////// // CMyex33aView
IMPLEMENT_DYNCREATE(CMyex33aView, CEditView)
BEGIN_MESSAGE_MAP(CMyex33aView, CEditView) //{{AFX_MSG_MAP(CMyex33aView) ON_BN_CLICKED(IDC_REQUEST, OnRequest) ON_COMMAND(ID_INTERNET_START_SERVER, OnInternetStartServer) ON_UPDATE_COMMAND_UI(ID_INTERNET_START_SERVER, OnUpdateInternetStartServer) ON_COMMAND(ID_INTERNET_REQUEST_SOCK, OnInternetRequestSocket) ON_COMMAND(ID_INTERNET_REQUEST_INET, OnInternetRequestWininet) ON_COMMAND(ID_INTERNET_STOP_SERVER, OnInternetStopServer) ON_UPDATE_COMMAND_UI(ID_INTERNET_STOP_SERVER, OnUpdateInternetStopServer) ON_COMMAND(ID_INTERNET_CONFIGURATION, OnInternetConfiguration) ON_UPDATE_COMMAND_UI(ID_INTERNET_CONFIGURATION, OnUpdateInternetConfiguration) ON_COMMAND(ID_EDIT_CLEAR_ALL, OnEditClearAll) //}}AFX_MSG_MAP ON_WM_CONTEXTMENU() // Standard printing commands ON_COMMAND(ID_FILE_PRINT, CEditView::OnFilePrint) ON_COMMAND(ID_FILE_PRINT_DIRECT, CEditView::OnFilePrint) ON_COMMAND(ID_FILE_PRINT_PREVIEW, CEditView::OnFilePrintPreview) END_MESSAGE_MAP()
///////////////////////////////////////////////////////////////////////////// // CMyex33aView construction/destruction
CMyex33aView::CMyex33aView() { // TODO: add construction code here }
CMyex33aView::~CMyex33aView() { }
BOOL CMyex33aView::PreCreateWindow(CREATESTRUCT& cs) { BOOL bPreCreated = CEditView::PreCreateWindow(cs); cs.style &= ~(ES_AUTOHSCROLL|WS_HSCROLL); // Enable word-wrapping cs.style |= ES_READONLY; return bPreCreated; }
///////////////////////////////////////////////////////////////////////////// // CMyex33aView drawing
void CMyex33aView::OnDraw(CDC* pDC) { CMyex33aDoc* pDoc = GetDocument(); ASSERT_VALID(pDoc); // TODO: add draw code for native data here }
///////////////////////////////////////////////////////////////////////////// // CMyex33aView printing
BOOL CMyex33aView::OnPreparePrinting(CPrintInfo* pInfo) { // default CEditView preparation return CEditView::OnPreparePrinting(pInfo); }
void CMyex33aView::OnBeginPrinting(CDC* pDC, CPrintInfo* pInfo) { // Default CEditView begin printing. CEditView::OnBeginPrinting(pDC, pInfo); }
void CMyex33aView::OnEndPrinting(CDC* pDC, CPrintInfo* pInfo) { // Default CEditView end printing CEditView::OnEndPrinting(pDC, pInfo); }
///////////////////////////////////////////////////////////////////////////// // CMyex33aView diagnostics
#ifdef _DEBUG void CMyex33aView::AssertValid() const { CEditView::AssertValid(); }
void CMyex33aView::Dump(CDumpContext& dc) const { CEditView::Dump(dc); }
CMyex33aDoc* CMyex33aView::GetDocument() // non-debug version is inline { ASSERT(m_pDocument->IsKindOf(RUNTIME_CLASS(CMyex33aDoc))); return (CMyex33aDoc*)m_pDocument; } #endif //_DEBUG
///////////////////////////////////////////////////////////////////////////// // CMyex33aView message handlers
void CMyex33aView::OnInternetStartServer() { try { CSockAddr saServer; if(g_strIPServer.IsEmpty()) { // first or only IP saServer = CSockAddr(INADDR_ANY, (USHORT) g_nPortServer); } else { // if our computer has multiple IP addresses... saServer = CSockAddr(g_strIPServer, (USHORT) g_nPortServer); } g_sListen.Create(); g_sListen.Bind(saServer); g_sListen.Listen();// start listening g_bListening = TRUE; g_nConnection = 0; AfxBeginThread(ServerThreadProc, GetSafeHwnd(), THREAD_PRIORITY_NORMAL); } catch(CBlockingSocketException* e) { g_sListen.Cleanup(); LogBlockingSocketException(GetSafeHwnd(), "VIEW:", e); e->Delete(); } }
void CMyex33aView::OnUpdateInternetStartServer(CCmdUI* pCmdUI) { pCmdUI->Enable(!g_bListening); }
void CMyex33aView::OnInternetRequestSocket() { AfxBeginThread(ClientSocketThreadProc, GetSafeHwnd(), THREAD_PRIORITY_NORMAL); }
void CMyex33aView::OnInternetRequestWininet() { AfxBeginThread(ClientWinInetThreadProc, GetSafeHwnd(), THREAD_PRIORITY_NORMAL); }
void CMyex33aView::OnInternetStopServer() { try { if(g_bListening) { g_sListen.Close(); } } catch(CBlockingSocketException* e) { LogBlockingSocketException(GetSafeHwnd(), "VIEW:", e); e->Delete(); } }
void CMyex33aView::OnUpdateInternetStopServer(CCmdUI* pCmdUI) { pCmdUI->Enable(g_bListening); }
void CMyex33aView::OnInternetConfiguration() { CSheetConfig sh("Configuration"); sh.m_pageServer.m_strDirect = g_strDirect; sh.m_pageServer.m_strDefault = g_strDefault; sh.m_pageServer.m_nPortServer = g_nPortServer; sh.m_pageClient.m_strServerIP = g_strServerIP; sh.m_pageClient.m_nPort = g_nPort; sh.m_pageClient.m_strServerName = g_strServerName; sh.m_pageClient.m_strFile = g_strFile; sh.m_pageClient.m_strProxy = g_strProxy; sh.m_pageClient.m_bUseProxy = g_bUseProxy; sh.m_pageAdv.m_strIPClient = g_strIPClient; sh.m_pageAdv.m_strIPServer = g_strIPServer; if(sh.DoModal() == IDOK) { g_strDirect = sh.m_pageServer.m_strDirect; g_strDefault = sh.m_pageServer.m_strDefault; g_nPortServer = sh.m_pageServer.m_nPortServer; g_strServerIP = sh.m_pageClient.m_strServerIP; g_nPort = sh.m_pageClient.m_nPort; g_strServerName = sh.m_pageClient.m_strServerName; if(sh.m_pageClient.m_strFile.IsEmpty()) { g_strFile = "/"; } else { g_strFile = sh.m_pageClient.m_strFile; } g_strProxy = sh.m_pageClient.m_strProxy; g_bUseProxy = sh.m_pageClient.m_bUseProxy; g_strIPClient = sh.m_pageAdv.m_strIPClient; g_strIPServer = sh.m_pageAdv.m_strIPServer; if(!g_strIPClient.IsEmpty() && g_bUseProxy) { AfxMessageBox("Warning: you can't assign a client IP address if you are using a proxy server"); } if(!g_strServerIP.IsEmpty() && g_bUseProxy) { AfxMessageBox("Warning: you must specify the server by name if you are using a proxy server"); } if(g_strServerIP.IsEmpty() && g_strServerName.IsEmpty()) { AfxMessageBox("Warning: you must specify either a server name or a server IP address"); } if(!g_strServerIP.IsEmpty() && !g_strServerName.IsEmpty()) { AfxMessageBox("Warning: you cannot specify both a server name and a server IP address"); } ::SetCurrentDirectory(g_strDirect); } }
void CMyex33aView::OnUpdateInternetConfiguration(CCmdUI* pCmdUI) { pCmdUI->Enable(!g_bListening); }
void CMyex33aView::OnEditClearAll() { SetWindowText(""); }
void CMyex33aView::OnRequest() { CWnd& rBar = ((CMainFrame*) AfxGetApp()->m_pMainWnd)->m_wndDialogBar; // g_strURL: thread sync? rBar.GetDlgItemText(IDC_URL, g_strURL); TRACE("CMyex33aView::OnRequest -- URL = %s\n", (const char*) g_strURL); AfxBeginThread(ClientUrlThreadProc, GetSafeHwnd(), THREAD_PRIORITY_NORMAL); }
void CMyex33aView::OnContextMenu(CWnd* pWnd, CPoint point) { // clear-all menu activated on right button CMenu menu; menu.LoadMenu(IDR_CONTEXT_MENU); menu.GetSubMenu(0)->TrackPopupMenu(TPM_LEFTALIGN | TPM_RIGHTBUTTON, point.x, point.y, this); }
|
Listing 26.
Continue on next Module...
Further reading and digging:
Win32 process, thread and synchronization story can be found starting from Module R.
DCOM at MSDN.
COM+ at MSDN.
COM at MSDN.
Unicode and Multi-byte character set: Story and program examples.