Figure 6   QuoteProviders.idl


 // Copyright (c) 1998, Microsoft Systems Journal
 // Author: Aaron Skonnard
 //
 // QuoteProviders.idl : IDL source for QuoteProviders.dll
 //
 
 // This file will be processed by the MIDL tool to
 // produce the type library (QuoteProviders.tlb) and marshalling code.
 
 import "oaidl.idl";
 import "ocidl.idl";
 
 [
     uuid(F02EAD3F-9F0C-11D1-B2B2-006008ACADF7),
     helpstring("IQuoteProvider Interface"),
     pointer_default(unique)
 ]
 interface IQuoteProvider : IUnknown
 {
     [helpstring("method GetHost")] HRESULT GetHost(LPSTR lpszHost, 
         DWORD dwLen);
     [helpstring("method GetPort")] HRESULT GetPort(USHORT* pnPort);
     [helpstring("method LoginIsRequired")] HRESULT LoginIsRequired(
         BOOL* pbResult);
     [helpstring("method GetMethod")] HRESULT GetMethod(LPSTR lpszMethod, 
         DWORD dwLen);
     [helpstring("method GetURL")] HRESULT GetURL(LPSTR lpszURL, DWORD dwLen);
     [helpstring("method GetAcceptTypes")] HRESULT GetAcceptTypes(
         LPSTR lpszAcceptTypes, DWORD dwLen);
     [helpstring("method GetHttpVersion")] HRESULT GetHttpVersion(
         LPSTR lpszHttpVersion, DWORD dwLen);
     [helpstring("method GetFlags")] HRESULT GetFlags(DWORD* pdwFlags);
     [helpstring("method GetHeaders")] HRESULT GetHeaders(LPSTR lpszHeaders, 
         DWORD dwLen);
     [helpstring("method GetData")] HRESULT GetData(LPSTR lpszData, 
         DWORD dwLen);
     [helpstring("method ParseResult")] HRESULT ParseResult(LPSTR lpszResult);
     [helpstring("method InitializeData")] HRESULT InitializeData(
         LPSTR lpszData);
 };
 [
     uuid(BFD86BC0-A004-11d1-9912-004033D06B6E),
     helpstring("IQuoteProviderEvent Interface"),
     pointer_default(unique)
 ]
 interface IQuoteProviderEvent : IUnknown //IDispatch
 {
     import "oaidl.idl";
     HRESULT UpdateSymbol(LPCTSTR lpszSymbol, LPCTSTR lpszPrice, 
                          LPCTSTR lpszChange, LPCTSTR lpszOpen, 
                          LPCTSTR lpszVolume);
 };
 [
     uuid(F02EAD31-9F0C-11D1-B2B2-006008ACADF7),
     version(1.0),
     helpstring("QuoteProviders 1.0 Type Library")
 ]
 library QUOTEPROVIDERSLib
 {
     importlib("stdole32.tlb");
     importlib("stdole2.tlb");
 
     [
         uuid(F02EAD40-9F0C-11D1-B2B2-006008ACADF7),
         helpstring("ProviderYahoo Class")
     ]
     coclass ProviderYahoo
     {
         [default] interface IQuoteProvider;
         [default, source] interface IQuoteProviderEvent;
     };
     [
         uuid(32FBDA43-A002-11D1-9912-004033D06B6E),
         helpstring("ProviderFastQuote Class")
     ]
     coclass ProviderFastQuote
     {
         [default] interface IQuoteProvider;
         [default, source] interface IQuoteProviderEvent;
     };
 };

Figure 7   MIDL-Generated Files

File Description
QuoteProviders.tlb The QuoteProvider type library
DllData.c Implements a DLL containing the proxy/stub code
QuoteProviders_i.c Defines the GUIDs used in the IDL file
QuoteProviders_p.c Implements the proxy/stub code
QuoteProviders.h A header file containing the declarations for all of the interfaces defined in the IDL file


Figure 8   QuoteProviders

ProviderYahoo.h


 // 1998, Microsoft Systems Journal
 // Author: Aaron Skonnard
 //
 // ProviderYahoo.h : Declaration of the CProviderYahoo
 //
 
 #ifndef __PROVIDERYAHOO_H_
 #define __PROVIDERYAHOO_H_
 
 #include "resource.h"       // main symbols
 #include "CPQuoteProviders.h"
 
 /////////////////////////////////////////////////////////////////////////////
 // CProviderYahoo
 class ATL_NO_VTABLE CProviderYahoo : 
     public CComObjectRootEx<CComSingleThreadModel>,
     public CComCoClass<CProviderYahoo, &CLSID_ProviderYahoo>,
     public IConnectionPointContainerImpl<CProviderYahoo>,
     public CProxyIQuoteProviderEvent<CProviderYahoo>,
     public IQuoteProvider
 {
 public:
     CProviderYahoo()
     {
     }
     ~CProviderYahoo()
     {
     }
     
 DECLARE_REGISTRY_RESOURCEID(IDR_PROVIDERYAHOO)
 
 BEGIN_COM_MAP(CProviderYahoo)
     COM_INTERFACE_ENTRY(IQuoteProvider)
     COM_INTERFACE_ENTRY_IMPL(IConnectionPointContainer)
 END_COM_MAP()
 
 BEGIN_CONNECTION_POINT_MAP(CProviderYahoo)
     CONNECTION_POINT_ENTRY(IID_IQuoteProviderEvent)
 END_CONNECTION_POINT_MAP()
 
 // IProviderYahoo
 public:
     STDMETHOD(InitializeData)(LPSTR lpszData);
     STDMETHOD(ParseResult)(LPSTR lpszResult);
     STDMETHOD(GetData)(LPSTR lpszData, DWORD dwLen);
     STDMETHOD(GetHeaders)(LPSTR lpszHeaders, DWORD dwLen);
     STDMETHOD(GetFlags)(DWORD* pdwFlags);
     STDMETHOD(GetHttpVersion)(LPSTR lpszHttpVersion, DWORD dwLen);
     STDMETHOD(GetAcceptTypes)(LPSTR lpszAcceptTypes, DWORD dwLen);
     STDMETHOD(GetURL)(LPSTR lpszURL, DWORD dwLen);
     STDMETHOD(GetMethod)(LPSTR lpszMethod, DWORD dwLen);
     STDMETHOD(LoginIsRequired)(BOOL* pbResult);
     STDMETHOD(GetPort)(USHORT* pnPort);
     STDMETHOD(GetHost)(LPSTR lpszHost, DWORD dwLen);
 };
 
 #endif //__PROVIDERYAHOO_H_
CPQuoteProviders.h

 // 1998, Microsoft Systems Journal
     // Author: Aaron Skonnard
     //
 
     #ifndef __CPROXYIQUOTEPROVIDEREVENT_H__
     #define __CPROXYIQUOTEPROVIDEREVENT_H__
 //////////////////////////////////////////////////////////////////////////////
     // CProxyIQuoteProviderEvent
     template <class T>
     class CProxyIQuoteProviderEvent : public IConnectionPointImpl<T,  
         &IID_IQuoteProviderEvent, CComDynamicUnkArray>
 {
 public:
 
 //IQuoteProviderEvent : IUnknown
 public:
     HRESULT Fire_UpdateSymbol(LPCTSTR lpszSymbol, LPCTSTR lpszPrice, 
         LPCTSTR lpszChange, LPCTSTR lpszOpen, LPCTSTR lpszVolume)
     {
         T* pT = (T*)this;
         pT->Lock();
         HRESULT ret;
         IUnknown** pp = m_vec.begin();
         while (pp < m_vec.end())
         {
             if (*pp != NULL)
             {
                 IQuoteProviderEvent* pIQuoteProviderEvent = 
                     reinterpret_cast<IQuoteProviderEvent*>(*pp);
                 ret = pIQuoteProviderEvent->UpdateSymbol(lpszSymbol, lpszPrice, 
                     lpszChange, lpszOpen, lpszVolume);
             }
             pp++;
         }
         pT->Unlock();
         return ret;
     }
 };
 
 #endif
QuoteProviders.cpp

 // 1998, Microsoft Systems Journal
 // Author: Aaron Skonnard
 //
 // QuoteProviders.cpp : Implementation of DLL Exports.
 //
 
 #include "stdafx.h"
 #include "resource.h"
 #include "CategoryGuid.h"
 #include "initguid.h"
 #include "QuoteProviders.h"
 #include "QuoteProviders_i.c"
 #include "ProviderYahoo.h"
 #include "ProviderFastQuote.h"
 #include "comcat.h"
 
 //Component Category Helper Functions
 HRESULT CreateComponentCategory(CATID catid, WCHAR* catDescription);
 HRESULT RegisterCLSIDInCategory(REFCLSID clsid, CATID catid);
 HRESULT UnRegisterCLSIDInCategory(REFCLSID clsid, CATID catid);   
 
 CRITICAL_SECTION g_CS;
 
 CComModule _Module;
 
 BEGIN_OBJECT_MAP(ObjectMap)
     OBJECT_ENTRY(CLSID_ProviderYahoo, CProviderYahoo)
     OBJECT_ENTRY(CLSID_ProviderFastQuote, CProviderFastQuote)
 END_OBJECT_MAP()
 
 class CQuoteProvidersApp : public CWinApp
 {
 public:
     virtual BOOL InitInstance();
     virtual int ExitInstance();
 };
 
 CQuoteProvidersApp theApp;
 
 BOOL CQuoteProvidersApp::InitInstance()
 {
     _Module.Init(ObjectMap, m_hInstance);
     return CWinApp::InitInstance();
 }
 
 int CQuoteProvidersApp::ExitInstance()
 {
     _Module.Term();
     return CWinApp::ExitInstance();
 }
 
 /////////////////////////////////////////////////////////////////////////////
 // Used to determine whether the DLL can be unloaded by OLE
 
 STDAPI DllCanUnloadNow(void)
 {
     AFX_MANAGE_STATE(AfxGetStaticModuleState());
     return (AfxDllCanUnloadNow()==S_OK && _Module.GetLockCount()==0) ? S_OK :    
         S_FALSE;
 }
 
 /////////////////////////////////////////////////////////////////////////////
 // Returns a class factory to create an object of the requested type
 
 STDAPI DllGetClassObject(REFCLSID rclsid, REFIID riid, LPVOID* ppv)
 {
     return _Module.GetClassObject(rclsid, riid, ppv);
 }
 
 /////////////////////////////////////////////////////////////////////////////
 // DllRegisterServer - Adds entries to the system registry
 
 STDAPI DllRegisterServer(void)
 {
     HRESULT hr;
     // registers object, typelib and all interfaces in typelib
     hr = _Module.RegisterServer(TRUE);
     if (FAILED(hr))
         return hr;
     hr = CreateComponentCategory(CATID_QuoteProviders, 
                                  L"Stock Watcher Quote Providers");    
     if (FAILED(hr))
         return hr;
     hr = RegisterCLSIDInCategory(CLSID_ProviderYahoo, CATID_QuoteProviders);
     if (FAILED(hr))        
         return hr;
     hr = RegisterCLSIDInCategory(CLSID_ProviderFastQuote, CATID_QuoteProviders);
     if (FAILED(hr))        
     return hr;
     return S_OK;
 }
 
 /////////////////////////////////////////////////////////////////////////////
 // DllUnregisterServer - Removes entries from the system registry
 
 STDAPI DllUnregisterServer(void)
 {
     HRESULT hr;
     _Module.UnregisterServer();
     hr=UnRegisterCLSIDInCategory(CLSID_ProviderYahoo, CATID_QuoteProviders);
     if (FAILED(hr))        
         return hr;
     hr=UnRegisterCLSIDInCategory(CLSID_ProviderFastQuote, CATID_QuoteProviders);
     if (FAILED(hr))        
     return hr;
     return S_OK;
 }
 
 HRESULT CreateComponentCategory(CATID catid, WCHAR* catDescription)
 {
 
     ICatRegister* pcr = NULL ;
     HRESULT hr = S_OK ;
 
     hr = CoCreateInstance(CLSID_StdComponentCategoriesMgr, NULL, 
                           CLSCTX_INPROC_SERVER, IID_ICatRegister, (void**)&pcr);
                           
     if (FAILED(hr))
         return hr;
 
     // Make sure the HKCR\Component Categories\{..catid...}
     // key is registered.
     CATEGORYINFO catinfo;
     catinfo.catid = catid;
     catinfo.lcid = 0x0409 ; // english
 
     // Make sure the provided description is not too long.
     // Only copy the first 127 characters if it is.
     int len = wcslen(catDescription);
     if (len>127)
         len = 127;
     wcsncpy(catinfo.szDescription, catDescription, len);
     // Make sure the description is null terminated.
     catinfo.szDescription[len] = '\0';
 
     hr = pcr->RegisterCategories(1, &catinfo);
         pcr->Release();
 
     return hr;
 }
 
 HRESULT RegisterCLSIDInCategory(REFCLSID clsid, CATID catid)
 {
     // Register your component categories information.
     ICatRegister* pcr = NULL ;
     HRESULT hr = S_OK ;
     hr = CoCreateInstance(CLSID_StdComponentCategoriesMgr, NULL,  
                           CLSCTX_INPROC_SERVER, IID_ICatRegister, (void**)&pcr);
                           
     if (SUCCEEDED(hr))
     {
        // Register this category as being "implemented" by the class.
        CATID rgcatid[1] ;
        rgcatid[0] = catid;
        hr = pcr->RegisterClassImplCategories(clsid, 1, rgcatid);
     }
     if (pcr != NULL)
         pcr->Release();
 
     return hr;
 }
 
 HRESULT UnRegisterCLSIDInCategory(REFCLSID clsid, CATID catid)    
 {
     ICatRegister* pcr = NULL ;    
     HRESULT hr = S_OK ;
     hr = CoCreateInstance(CLSID_StdComponentCategoriesMgr, NULL, 
                           CLSCTX_INPROC_SERVER, IID_ICatRegister, (void**)&pcr);
     if (SUCCEEDED(hr))    
     {
        // Unregister this category as being "implemented" by the class.
        CATID rgcatid[1] ;       
        rgcatid[0] = catid;
        hr = pcr->UnRegisterClassImplCategories(clsid, 1, rgcatid);    
     }
     if (pcr != NULL)        
     pcr->Release();        
     return hr;        
 }

Figure 10   IProviderYahoo Methods

Method Description
GetHost Provides the Yahoo! quote server's host name (quote.yahoo.com)
GetPort Provides the Yahoo! quote server's port number
GetMethod Provides the HTTP method for the quote request (GET, POST, and so on)
GetURL Provides the identifier for the quote request
GetHttpVersion Provides the HTTP version understood by the server (HTTP/1.0)
GetHeaders Provides additional HTTP headers that should be used in the quote request
GetAcceptTypes Provides additional HTTP accept types that should be used in the quote request
GetData Provides properly formatted input data for the quote request
GetFlags Provides additional WinInet specific flags that should be used in the quote request
InitializeData The client uses this method to pass in a string of stock symbols
LoginIsRequired Determines if this quote provider requires authentication
ParseResult Parses the HTML response returned by the Yahoo! server


Figure 11   HTTP ObjectServer

HTTPObjectServer.idl


 // 1998, Microsoft Systems Journal
 // Author: Aaron Skonnard
 //
 // HttpObjectServer.idl : IDL source for HttpObjectServer.dll
 
 // This file will be processed by the MIDL tool to
 // produce the type library (HttpObjectServer.tlb) and marshalling code.
 
 import "oaidl.idl";
 import "ocidl.idl";
 
 [
     uuid(10BE7FE8-9CC7-11D1-9912-004033D06B6E),
     helpstring("IHttpRequest Interface"),
     pointer_default(unique)
 ]
 interface IHttpRequest : IUnknown //IDispatch
 {
     [helpstring("method ProcessRequest")] HRESULT ProcessRequest(
         IUnknown* pQuoteProvider, long lMainHwnd);
     [helpstring("method GetProviderInterface")] HRESULT 
         GetProviderInterface(IUnknown** ppUnk);
 };
 [
     uuid(10BE7FDB-9CC7-11D1-9912-004033D06B6E),
     version(1.0),
     helpstring("HttpObjectServer 1.0 Type Library")
 ]
 library HTTPOBJECTSERVERLib
 {
     importlib("stdole32.tlb");
     importlib("stdole2.tlb");
 
     [
         uuid(10BE7FE9-9CC7-11D1-9912-004033D06B6E),
         helpstring("HttpRequest Class")
     ]
     coclass HttpRequest
     {
         [default] interface IHttpRequest;
     };
 };
HTTPRequest.h

 // 1998, Microsoft Systems Journal
 // Author: Aaron Skonnard
 //
 // HttpRequest.h : Declaration of the CHttpRequest
 
 #ifndef __HTTPREQUEST_H_
 #define __HTTPREQUEST_H_
 
 #include "resource.h"       // main symbols
 #include "..\QuoteProviders\QuoteProviders.h"
 
 /////////////////////////////////////////////////////////////////////////////
 // CHttpRequest
 class ATL_NO_VTABLE CHttpRequest : 
     public CComObjectRootEx<CComSingleThreadModel>,
     public CComCoClass<CHttpRequest, &CLSID_HttpRequest>,
     public IHttpRequest
 {
 public:
     CHttpRequest()
     {
         m_pQuoteProvider = NULL;
     }
     ~CHttpRequest()
     {
         //release the IQuoteProvider interface
         if (m_pQuoteProvider)
             m_pQuoteProvider->Release();
     }
 
     //pointer to current quote provider interface
     IQuoteProvider* m_pQuoteProvider;
 
     //main application HWND
     HWND m_hwndMain;
 
 DECLARE_REGISTRY_RESOURCEID(IDR_HTTPREQUEST)
 
 BEGIN_COM_MAP(CHttpRequest)
     COM_INTERFACE_ENTRY(IHttpRequest)
 END_COM_MAP()
 
 // IHttpRequest
 public:
     STDMETHOD(GetProviderInterface)(IUnknown** ppUnk);
     STDMETHOD(ProcessRequest)(IUnknown* pQuoteProvider, long lMainHwnd);
 };
 
 #endif //__HTTPREQUEST_H_
MyInternetSession.h

 // 1998, Microsoft Systems Journal
 // Author: Aaron Skonnard
 //
 // MyInternetSession.h: Declaration of CMyInternetSession
 //
 
 class CMyInternetSession : public CInternetSession
 {
 public:
     CMyInternetSession(LPCTSTR pstrAgent = NULL, DWORD dwContext = 1, 
                        DWORD dwAccessType = INTERNET_OPEN_TYPE_PRECONFIG, 
                        LPCTSTR pstrProxyName = NULL, 
                        LPCTSTR pstrProxyBypass = NULL, 
                        DWORD dwFlags = 0 ) 
         : CInternetSession(pstrAgent, dwContext, dwAccessType, pstrProxyName, 
                            pstrProxyBypass, dwFlags) { m_pRequest=NULL; };
     
     //override OnStatusCallback
     virtual void OnStatusCallback(DWORD dwContext, DWORD dwInternetStatus, 
                    LPVOID lpvStatusInformation, DWORD dwStatusInformationLength );
 
     //pointer to the CHttpRequest object controlling this Internet session
     CHttpRequest* m_pRequest;
 };
HTTPRequest.cpp

 // 1998, Microsoft Systems Journal
 // Author: Aaron Skonnard
 //
 // HttpRequest.cpp : Implementation of CHttpRequest
 
 #include "stdafx.h"
 #include "HttpObjectServer.h"
 #include "HttpRequest.h"
 #include "MyInternetSession.h"
 #include "LoginDlg.h"
 #include "profile.h"
 #include "..\QuoteProviders\QuoteProviders.h"
 #include "..\Stock Watcher\globals.h"
 
 #define HTTPFILE_BUFFLEN    4096
 #define BUFFLEN         255
 
 extern CRITICAL_SECTION g_CS;
 
 UINT HttpWorkerThread(LPVOID lpVoid);
 
 /////////////////////////////////////////////////////////////////////////////
 // CMyInternetSession
 
 void CMyInternetSession::OnStatusCallback(DWORD dwContext, DWORD dwInternetStatus, 
                     LPVOID lpvStatusInformation, DWORD dwStatusInformationLength )
 {
     if (m_pRequest)
     {
         if (m_pRequest->m_hwndMain)
             ::PostMessage(m_pRequest->m_hwndMain, WM_HTTP_THREAD_MESSAGE, 
                           UPDATE_STATUS, dwInternetStatus);
     }
 }
 
 /////////////////////////////////////////////////////////////////////////////
 // CHttpRequest
 
 STDMETHODIMP CHttpRequest::ProcessRequest(IUnknown* pQuoteProvider, 
                                           long lMainHwnd)
 {
     AFX_MANAGE_STATE(AfxGetStaticModuleState())
     m_pQuoteProvider = static_cast<IQuoteProvider*>(pQuoteProvider);
     m_pQuoteProvider->AddRef();
     m_hwndMain = reinterpret_cast<HWND>(lMainHwnd);
     AfxBeginThread(HttpWorkerThread, this);
     return S_OK;
 }
 
 STDMETHODIMP CHttpRequest::GetProviderInterface(IUnknown * * ppUnk)
 {
     AFX_MANAGE_STATE(AfxGetStaticModuleState())
     *ppUnk = m_pQuoteProvider;
     return S_OK;
 }
 
 /////////////////////////////////////////////////////////////////////////////
 // HttpWorkerThread
 
 UINT HttpWorkerThread(LPVOID lpVoid)
 {
     CHttpRequest* pRequest = reinterpret_cast<CHttpRequest*>(lpVoid);
     IQuoteProvider* pProvider = pRequest->m_pQuoteProvider;
     CHttpConnection* pHttpConnection = NULL;
     CHttpFile* pHttpFile = NULL;
     LPVOID lpvoid = NULL;
     char lpszHost[BUFFLEN], lpszUserName[BUFFLEN], lpszPassword[BUFFLEN], 
         lpszMethod[BUFFLEN];
     char lpszURL[BUFFLEN], lpszAcceptTypes[BUFFLEN], lpszHttpVersion[BUFFLEN], 
         lpszHeaders[BUFFLEN];
     char lpszData[BUFFLEN];
     DWORD dwFlags;
     LPCTSTR pstrAcceptTypes[2];
     INTERNET_PORT nPort;
     BOOL bResult;
 
     //declare CMyInternetSession object
     CMyInternetSession InternetSession(NULL, 1, INTERNET_OPEN_TYPE_PRECONFIG);
     try
     {
         //enable the status callback
         InternetSession.EnableStatusCallback(TRUE);
         InternetSession.m_pRequest = pRequest;
         //get connection information
         pProvider->GetHost(lpszHost, BUFFLEN);
         pProvider->GetPort(&nPort);
         pProvider->LoginIsRequired(&bResult);
         if (bResult)
         {
             CString strUserName = GetMyProfileString("Settings", "username", "");
             CString strPassword = GetMyProfileString("Settings", "password", "");
             if (strUserName.IsEmpty() || strPassword.IsEmpty())
             {
                 // get user name & password information
                 CLoginDlg dlg;
                 if (IDCANCEL == dlg.DoModal())
                     return 0;
                 WriteMyProfileString("Settings", "username", dlg.m_strUserName);
                 WriteMyProfileString("Settings", "password", dlg.m_strPassword);
                 strcpy(lpszUserName, dlg.m_strUserName);
                 strcpy(lpszPassword, dlg.m_strPassword);
             }
             else
             {
                 strcpy(lpszUserName, strUserName);
                 strcpy(lpszPassword, strPassword);
             }
         }
         //establish the HTTP connection
         pHttpConnection = InternetSession.GetHttpConnection( lpszHost, nPort,    
                                                     lpszUserName, lpszPassword);
 
         //get request specific information
         pProvider->GetMethod(lpszMethod, BUFFLEN);
         pProvider->GetURL(lpszURL, BUFFLEN);
         pProvider->GetAcceptTypes(lpszAcceptTypes, BUFFLEN);
         pstrAcceptTypes[0] = lpszAcceptTypes;
         pstrAcceptTypes[1] = NULL;
         pProvider->GetHttpVersion(lpszHttpVersion, BUFFLEN);
         pProvider->GetFlags(&dwFlags);    
 
         //open the HTTP request
         pHttpFile = pHttpConnection->OpenRequest(lpszMethod, lpszURL, "", 1,  
             pstrAcceptTypes, lpszHttpVersion, dwFlags);
         //get the request data
         pProvider->GetHeaders(lpszHeaders, BUFFLEN);
         pProvider->GetData(lpszData, BUFFLEN);
 
         if (pHttpFile->SendRequest(lpszHeaders, strlen(lpszHeaders), 
             static_cast<void*>(lpszData), strlen(lpszData)))
         {
             DWORD dwRet;
             //query the HTTP status code
             pHttpFile->QueryInfoStatusCode(dwRet);
             //authentication techniques
             while (dwRet == HTTP_STATUS_DENIED)
             {
                CLoginDlg dlg;
                dlg.m_strUserName = GetMyProfileString("Settings", 
                                                       "username", "");
                dlg.m_strPassword = GetMyProfileString("Settings", 
                                                       "password", "");
                if (IDCANCEL == dlg.DoModal())
                {
                     if (pHttpConnection)
                     {
                         pHttpConnection->Close();
                         delete pHttpConnection;
                     }
                     if (pHttpFile)
                     {
                         pHttpFile->Close();
                         delete pHttpFile;
                     }
                     return 0;
                 }
                 WriteMyProfileString("Settings", "username", dlg.m_strUserName);
                 WriteMyProfileString("Settings", "password", dlg.m_strPassword);
                 strcpy(lpszUserName, dlg.m_strUserName);
                 strcpy(lpszPassword, dlg.m_strPassword);
                 InternetSetOption((HINTERNET)(*pHttpFile),   
                     INTERNET_OPTION_USERNAME, lpszUserName, strlen(lpszUserName));
                 InternetSetOption((HINTERNET)(*pHttpFile), 
                     INTERNET_OPTION_PASSWORD, lpszPassword, strlen(lpszPassword));    
                 pHttpFile->SendRequest(lpszHeaders, strlen(lpszHeaders), 
                     static_cast<void*>(lpszData), strlen(lpszData));
                 pHttpFile->QueryInfoStatusCode(dwRet);
             }
             if (dwRet == HTTP_STATUS_OK)
             {
                 CString strContentLen;
                 LPSTR lpszResult=NULL;
                 UINT nRead=0, nTotalRead=0;
 
                 lpvoid = malloc(HTTPFILE_BUFFLEN);
                 //memory error
                 if (!lpvoid)
                     return 0;
                 //read the HTTP response
                 nRead = pHttpFile->Read(lpvoid, HTTPFILE_BUFFLEN);
                 nTotalRead += nRead;
                 while (nRead == HTTPFILE_BUFFLEN)
                 {
                     lpvoid = realloc(lpvoid, nTotalRead+HTTPFILE_BUFFLEN);
                     nRead = pHttpFile->Read((byte*)lpvoid+nTotalRead, 
                                             HTTPFILE_BUFFLEN);
                     nTotalRead += nRead;
                 }
                 lpszResult = (LPSTR)lpvoid;
                 *(lpszResult + nTotalRead) = NULL;
                 
                 //parse the HTTP response
                 EnterCriticalSection(&g_CS);
                 HRESULT hr = pProvider->ParseResult(lpszResult);
                 if (FAILED(hr))
                     ::MessageBox(pRequest->m_hwndMain, 
                                  "Error parsing quote format", 
                                  "ParseResult Error", MB_ICONEXCLAMATION | MB_OK);
                 LeaveCriticalSection(&g_CS);
 
                 //notify the main application window
                 if (pRequest->m_hwndMain)
                     ::PostMessage(pRequest->m_hwndMain, WM_HTTP_THREAD_MESSAGE,
                                   UPDATE_ALL_VIEWS, 0);
             }
         }
     }
     catch(CInternetException *e)
     {
         e->ReportError();
         e->Delete();
     }
     // cleanup
     if (lpvoid)
         free(lpvoid);
 
     if (pHttpFile)
     {
         pHttpFile->Close();
         delete pHttpFile;
     }
     if (pHttpConnection)
     {
         pHttpConnection->Close();
         delete pHttpConnection;
     }
     return 0;
}

Figure 13   StockWatcherDoc.cpp


 // 1998, Microsoft Systems Journal
 // Author: Aaron Skonnard
 //
 // Stock WatcherDoc.cpp : implementation of the CStockWatcherDoc class
 
 #include "stdafx.h"
 #include "Stock Watcher.h"
 #include "Stock WatcherDoc.h"
 #include "Mainfrm.h"
 #include "AddSymbolDlg.h"
 #include "QuoteProviderDlg.h"
 #include "..\QuoteProviders\QuoteProviders.h"
 
 #ifdef _DEBUG
 #define new DEBUG_NEW
 #undef THIS_FILE
 static char THIS_FILE[] = __FILE__;
 #endif
 
 IMPLEMENT_SERIAL(CStockSymbol, CObject, 1);
 
 /////////////////////////////////////////////////////////////////////////////
 // CStockWatcherDoc
 
 IMPLEMENT_DYNCREATE(CStockWatcherDoc, CDocument)
 
 BEGIN_MESSAGE_MAP(CStockWatcherDoc, CDocument)
     //{{AFX_MSG_MAP(CStockWatcherDoc)
     ON_COMMAND(ID_SYMBOL_NEW, OnSymbolNew)
     ON_COMMAND(ID_REFRESH_ALL_SYMBOLS, OnRefreshAllSymbols)
     ON_COMMAND(ID_QUOTE_PROVIDER, OnQuoteProvider)
     //}}AFX_MSG_MAP
 END_MESSAGE_MAP()
 
 /////////////////////////////////////////////////////////////////////////////
 // CStockWatcherDoc construction/destruction
 
 CStockWatcherDoc::CStockWatcherDoc()
 {
     //create and initialized the CQuoteProviderEventSink object
     CComObject<CQuoteProviderEventSink>::CreateInstance(&m_pQuoteProviderEventSink);
     m_pQuoteProviderEventSink->AddRef();
     m_pQuoteProviderEventSink->m_pDoc = this;
 }
 
 CStockWatcherDoc::~CStockWatcherDoc()
 {
     EmptySymbolList();
 
     //release all IHttpRequest interfaces
     long lSize = m_arrConnections.GetSize(), i;
     for (i=0; i<lSize; i++)
     {
         IHttpRequest* pRequest =  
             static_cast<IHttpRequest*>(m_arrIHttpRequests.GetAt(i));
         IQuoteProvider* pProvider = NULL;
         pRequest->GetProviderInterface(reinterpret_cast<IUnknown**>(&pProvider));
         if (pProvider)
             AtlUnadvise(pProvider, IID_IQuoteProviderEvent, 
                         m_arrConnections.GetAt(i));
         pRequest->Release();
     }
     //release the CQuoteProviderEventSink
     m_pQuoteProviderEventSink->Release();
 }
 
 void CStockWatcherDoc::OnRefreshAllSymbols() 
 {
     long lSize = m_Objects.GetSize(), i;
     CStockSymbol* pSymbol;
     CString strSymbols, strCLSID;
     DWORD dwConnection;
 
     if (lSize == 0)
         return;
 
     //get the currently selected quote provider's CLSID
     strCLSID = AfxGetApp()->GetProfileString("Settings", "ProviderCLSID", "");
     if (strCLSID.IsEmpty())
         return;
     
     LPOLESTR lpszCLSID;
     USES_CONVERSION;
     lpszCLSID = A2W(strCLSID.GetBuffer(255));
     CLSID clsid;
 
     //convert from string to CLSID
     CLSIDFromString(lpszCLSID, &clsid);
 
     for (i=0; i<lSize; i++)
     {
         pSymbol = static_cast<CStockSymbol*>(m_Objects.GetAt(i));
         if (i == 0)
             strSymbols += pSymbol->m_strSymbol;    
         else strSymbols += ' ' + pSymbol->m_strSymbol;
     }
 
     //create the IHttpRequest component
     IHttpRequest* pRequest = NULL;
     CoCreateInstance(CLSID_HttpRequest, NULL, CLSCTX_ALL, IID_IHttpRequest, 
                      (void**)&pRequest);
     _ASSERTE(pRequest != NULL);
     m_arrIHttpRequests.Add(pRequest);
 
     //create the IQuoteProvider component
     IQuoteProvider *pQuoteProvider = NULL;
     CoCreateInstance(clsid, NULL, CLSCTX_ALL, IID_IQuoteProvider, 
                      (void**)&pQuoteProvider);
     _ASSERTE(pQuoteProvider != NULL);
 
     //establish the connection point
     AtlAdvise(pQuoteProvider, m_pQuoteProviderEventSink->GetUnknown(), 
               IID_IQuoteProviderEvent, &dwConnection);
     m_arrConnections.Add(dwConnection);
 
     //initialize the data (strSymbols)
     pQuoteProvider->InitializeData(strSymbols.GetBuffer(255));
 
     //call ProcessRequest
     pRequest->ProcessRequest(pQuoteProvider,
         reinterpret_cast<long>(AfxGetMainWnd()->GetSafeHwnd()));
     
     //release IQuoteProvider, let the IHttpRequest object manage lifetime
     pQuoteProvider->Release();
 }
 
 void CStockWatcherDoc::UpdateSymbol(LPCTSTR lpszSymbol, LPCTSTR lpszPrice, 
     LPCTSTR lpszChange, LPCTSTR lpszOpen, LPCTSTR lpszVolume)
 {
     CStockSymbol* pSymbol = FindObjectBySymbol(lpszSymbol);
     pSymbol->m_strPrice = lpszPrice;
     pSymbol->m_strChange = lpszChange;
     pSymbol->m_strOpen = lpszOpen;
     pSymbol->m_strVolume = lpszVolume;
     pSymbol->m_dtLastUpdate = COleDateTime::GetCurrentTime();
     SetModifiedFlag();
 }
 
 CStockSymbol* CStockWatcherDoc::FindObjectBySymbol(LPCTSTR lpszSymbol)
 {
     long lSize = m_Objects.GetSize(), i;
     CStockSymbol* pSymbol=NULL;
 
     for (i=0; i<lSize; i++)
     {
         pSymbol = static_cast<CStockSymbol*>(m_Objects.GetAt(i));
         if (pSymbol->m_strSymbol.CompareNoCase(lpszSymbol) == 0)
             return pSymbol;
     }
     return pSymbol;
 }
 
 STDMETHODIMP CQuoteProviderEventSink::UpdateSymbol(LPCTSTR lpszSymbol, 
     LPCTSTR lpszPrice, LPCTSTR lpszChange, LPCTSTR lpszOpen, LPCTSTR lpszVolume)
 {
     m_pDoc->UpdateSymbol(lpszSymbol, lpszPrice, lpszChange, lpszOpen, lpszVolume);
     return S_OK;
 }