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

  • IBM Worklight is a mobile application development platform that lets you extend your business to mobile devices. It is designed to provide an open, comprehensive platform to build, run and manage HTML5, hybrid and native mobile apps.

  • On-demand Event Event Date: October 23, 2014 Despite the current "virtualize everything" mentality, there are advantages to utilizing physical hardware for certain tasks. This is especially true for backups. In many cases, it is clearly in an organization's best interest to make use of physical, purpose-built backup appliances rather than relying on virtual backup software (VBA - Virtual Backup Appliances). Join us for this webcast to learn why physical appliances are preferable to virtual backup appliances, …

Most Popular Programming Stories

More for Developers

Latest Developer Headlines

RSS Feeds