Hosting Windowed ActiveX Controls in ASP Scripts (MSChart Example).

Environment: VC6.0 SP3+, NT4.0 SP3+, Win98 SE, Windows 2000/Me/XP, IIS 3.0+ or PWS

Have you ever wondered how to display charts or any other visual ActiveX controls in a web environment without having the need for the client (the browser in this case) to download these conrols in order to display the data. The best way of doing that is by creating an image from the control's client area and saving it on disk and then having that image as a link reference to display the image on the client page like the image shown above.

Well I found a lot of controls that did that, but none of them was giving away the source code for free, so I decided to write my own because I wanted to learn and on the other hand I wanted it to be custom made to suit my needs. In this Chart Example we will create an ATL project with a single COM object to host our MSChart Control.

The assumptions for the Chart Example are:

  1. Running in a MS-Windows environment
  2. Have a web server running on the machine (IIS or PWS)
  3. Have MSChart ActiveX control registered on the system (It should come with the OS or VB or MS-Office or download it from Microsoft's web site)

There are 10 easy steps to the process:

  1. Create a new ATL COM project and name it AXHostLib



  2. Create an ActiveX Server Component Object and name it Host



  3. Create a property and call it CreateObject with only the get attribute.



  4. Create a method and call it SavePicture



  5. Create a "dummy" dialog in the resource with id IDD_DIALOG1 that will be the parent window for our MSChart control



  6. In the Host.h file add the include and the private section shown:
    #include "afxtempl.h"
     //////////////////////////////////////////////////////////////
    // CHost
    
    class ATL_NO_VTABLE CHost :
    
    private:
        CDialog* pDlg;
        CWnd* pControlWnd;
    
        CArray<IDISPATCH*, IDispatch*> m_DispatchArray;
        CArray<CDIALOG*, CDialog*> m_DialogArray;
        CArray<CWND*, CWnd*> m_ControlWndArray;
    
  7. In the Host.cpp file the includes and the two methods (CreateObject and SavePicture) look like this:
    Note:
    Notice that the SavePicture method grabs the image from the clipboard and then saves it on the disk as a GIF, using a third-party library. You can use any other code you want at that point. You can find other functions for saving the image to disk in the http://www.codeguru.com/bitmap/ or you can download the CXImage library from http://www.codeproject.com/bitmap/cximage.asp and place the source in the CXImage directory of this project's source code. Using the CXImage library the image can be saved in one of the following formats PNG, BMP, JPG, TIFF or GIF. There are also other third party libraries that can be used under the GNU aggreement like the PaintLib found at http://www.paintlib.de/paintlib/.

    #include "comdef.h"
    #include "CXImage/CXImage/ximagif.h"
    //-------------------------------------------------------
    STDMETHODIMP CHost::get_CreateObject( BSTR ObjectID,
                                          long Width, 
                                          long Height, 
                                          IDispatch **pVal)
    {
        AFX_MANAGE_STATE(AfxGetStaticModuleState())
    
        _bstr_t tmpbstr = _bstr_t(ObjectID, TRUE);
        CString ObjID = (char*) tmpbstr;
    
        HRESULT hr;
        IUnknown* pUnknown = NULL;
    
        //Create rect structure according to the size we want
        RECT rect;
        rect.left = 0;
        rect.top = 0;
        rect.bottom = Height;
        rect.right = Width;
    
        Create the dialog as a modeless one
        pDlg =  new CDialog();
        pDlg->Create(IDD_DIALOG1, NULL);
    
        //Create the window from an ActiveX Control with 
        // parent the dialog
        pControlWnd =  new CWnd();
        pControlWnd->CreateControl( ObjID,
                                       NULL,
                                       WS_VISIBLE,
                                       rect,
                                       pDlg,
                                       1);
    
        // Get the IUnknown in order to query the 
        // IDispatch and return it to the ASP page
        pUnknown = pControlWnd->GetControlUnknown();
    
        if (m_bOnStartPageCalled)
            hr = pUnknown->QueryInterface( IID_IDispatch,
                                              (void**) pVal);
        else
            *pVal = 0;
    
        //Save all the pointers for later clean up
        m_DispatchArray.Add(*pVal);
        m_DialogArray.Add(pDlg);
        m_ControlWndArray.Add(pControlWnd);
    
        return hr;
    }
    //------------------------------------------------------
    STDMETHODIMP CHost::SavePicture(BSTR filename)
    {
        AFX_MANAGE_STATE(AfxGetStaticModuleState())
    
        _bstr_t tmpbstr = _bstr_t(filename, TRUE);
        CString name = (char*) tmpbstr;
    
        //paste
        HANDLE hBitmap = NULL;
        if (OpenClipboard(NULL))
            hBitmap = GetClipboardData(CF_DIB);
    
        if (hBitmap)
        {
            CxImage image(hBitmap);
            image.DecreaseBpp(8);
            image.SaveFile(name, CXIMAGE_FORMAT_GIF);
        }
        CloseClipboard();
    
        return S_OK;
    }
  8. In the InitInstance of the Application in the AXHostLib.cpp add the the following line:
    AfxEnableControlContainer();
  9. Create the ASP page now and add the following:
    Note:
    Make sure you change the line that says l_conn.Open to point to the correct directory where the database file is.
      dim Host
      dim Chart
      dim l_conn
      'Create our COM Object
      set Host = Server.CreateObject("AXHostLib.Host")
      Set Chart = Host.CreateObject("MSChart20Lib.MSChart", 600, 350)
    
      'Create the data connection for our chart
      set l_conn = Server.CreateObject("ADODB.Connection")
      l_conn.Open "PROVIDER=Microsoft.Jet.OLEDB.3.51;Data
       Source=c:/inetpub/wwwroot/Stocks/data/MSFT.mdb;"
    
      dim l_rec
      dim l_sqlStatement
      set l_rec = Server.CreateObject("ADODB.Recordset")
      l_sqlstatement = "SELECT close FROM Quotes order by date asc"
      l_rec.Open l_sqlstatement, l_conn, 2, 3
    
      'Set the Chart's properties and DataSource
      set Chart.DataSource = l_rec
      Chart.ChartType = 3
      Chart.AllowSelections = 0
      Chart.ShowLegend = 0
      dim serX
      For Each serX In Chart.Plot.SeriesCollection
        serX.Pen.Width = 1
        serX.Pen.VtColor.Automatic = 0
        serX.Pen.VtColor.Red = 0
        serX.Pen.VtColor.Green = 0
        serX.Pen.VtColor.Blue = 255
      Next
      Set XAxis = Chart.Plot.Axis(0)
      'Scale of the ticks
      XAxis.CategoryScale.Auto = 0
      XAxis.CategoryScale.DivisionsPerLabel = 
                                   Chart.RowCount / 10
      XAxis.CategoryScale.DivisionsPerTick = 
                                   Chart.RowCount / 10
    
      'Close connection and recordset
      l_rec.Close
      l_conn.Close
    
      'Save the image
      Chart.EditCopy
      Host.SavePicture "c:/inetpub/wwwroot/Stocks/data/image.gif"
    

Links

CXImage Library with documentation and examples of how to use it.
PaintLib Library with documentation and examples of how to use it.
(The MSChart Example here uses only the GIF format from the CXImage Library to save the image on disk)

Downloads

Download demo project - 261 Kb
Download source - 283 Kb


Comments

  • There are no comments yet. Be the first to comment!

Leave a Comment
  • Your email address will not be published. All fields are required.

Top White Papers and Webcasts

  • Live Event Date: December 11, 2014 @ 1:00 p.m. ET / 10:00 a.m. PT Market pressures to move more quickly and develop innovative applications are forcing organizations to rethink how they develop and release applications. The combination of public clouds and physical back-end infrastructures are a means to get applications out faster. However, these hybrid solutions complicate DevOps adoption, with application delivery pipelines that span across complex hybrid cloud and non-cloud environments. Check out this …

  • On-demand Event Event Date: October 29, 2014 It's well understood how critical version control is for code. However, its importance to DevOps isn't always recognized. The 2014 DevOps Survey of Practice shows that one of the key predictors of DevOps success is putting all production environment artifacts into version control. In this webcast, Gene Kim discusses these survey findings and shares woeful tales of artifact management gone wrong! Gene also shares examples of how high-performing DevOps …

Most Popular Programming Stories

More for Developers

RSS Feeds