// 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;
     };
 };
| 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 | 
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;        
 }
| 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 | 
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;
}
 // 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;
 }