viksoe.dk

Hosting a FLASH Charts control

Hosting a FLASH Charts control


This article was submitted .


In one of my very old projects I used a simple Graph control I made myself. At that time I took pride in developing everything, including all the visual controls, of my applications myself. But eventually one has to realize that there exists much better options with some of the really fine 3rd party controls available. So when I stumbled over a really nice charts control, I decided that it was time to replace my old Graph control.

The control that I found was this, the XML/SWF Charts control.
It is a FLASH control, which makes it a little difficult to integrate in a native Win32 application - so this is a small tutorial on how to add a control designed for the Internet to a WTL application.

A FLASH control, when shown in Internet Explorer, is created through a traditional COM ActiveX control. WTL already has the ability to create and display ActiveX controls through the CAxWindow ATL windowing classes. We could simply spawn the FLASH control this way, however, I decided to take another approach. To emulate the web environment a little closer, I decided to embed the control on a regular web-page and load this web-page in an embedded Internet Explorer ActiveX control. As explained, this might not be strictly necessary for the FLASH control, but if you're going to host other web-only controls, this approach can be very useful.

The CAxWindow classes are also needed to create the Internet Explorer ActiveX control.

When you create your WTL project, make sure to enable the "Enable ActiveX Control Hosting" option in the project wizard. This will properly initialize the CAxWindow stub.


The ATL library has numerous ways to create an ActiveX control. So many that it can become a little confusing what to use when. If you want to stick a regular ActiveX on a dialog, using something like the CAxDialogImpl and the Resource Editor is the easiest way. The Internet Explorer however is a different story since we not only wish to create the ActiveX but also immediately load a HTML page, and so we need to do a few extra initialization steps.
I usually then prefer to manually create the needed controls. I place a STATIC (label) control in place where the ActiveX will be, and then during dialog initialization replace this with the IE host.
CAxWindow m_wndHTML;
...
CStatic wndStatic = GetDlgItem(IDC_WEBPAGE);
CRect rcClient;
wndStatic.GetWindowRect(&rcClient);
::MapWindowPoints(NULL, m_hWnd, (LPPOINT) static_cast<LPRECT>(&rcClient), 2);
m_wndHTML.Create(m_hWnd, rcClient, 0, WS_CHILD | WS_VISIBLE | WS_TABSTOP, 
   WS_EX_CLIENTEDGE, IDC_WEBPAGE);
CComPtr<IAxWinHostWindow> spAxWindow;
HRESULT Hr = m_wndHTML.QueryHost(&spAxWindow);
if( FAILED(Hr) ) return 0;
CString sURL = _T("file:///xyz/basic.html"));
Hr = spAxWindow->CreateControl(CComBSTR(sURL), m_wndHTML.m_hWnd, NULL);
if( FAILED(Hr) ) return 0;
This snippet of code finds the label control on the dialog (IDC_WEBPAGE) and replaces it with an Internet Explorer ActiveX host. It also loads the HTML page. This page must be loaded with full path or alternatively through navigating with the res: protocol. After creation, it is prudent to configure the security settings and behaviour of the IE host.

There are multiple ways to create a FLASH control on a web-page. My example uses the official Flash Javascript initialization routine. That is only possible because I decided to host the FLASH control in a web-page. The XML/SWF Charts control needs to be initialized through several FLASH parameters but this can easily be configured from the HTML page loaded.
Here is a snippet of JavaScript on the basic.html web-page that creates the page control:
if(hasRightVersion) { 
  AC_FL_RunContent(
    'codebase', 'http://download.macromedia.com/..../swflash.cab#version=9,0,45,0',
    'width', '100%',
    'height', '100%',
    ...
It continues with several more setup parameters. What this eventually does, is to generate the following HTML:
  <object classid="{FLASH9-GUID}" ...>
tag on the page that loads the actual FLASH Chart control.

A mini webserver

Further, the XML/SWF Charts control loads its definition and style from an XML file. In the basic setup, this file can be an external file placed in the same folder as the HTML file. This solution works well - even with the tightened security settings of Internet Explorer.
The Charts control also allows for sending the XML definition through JavaScript or from a web-server request. I opted for the slightly more advanced setup of serving the XML file from a web-server. I still wanted my application to serve the XML file, so I'm implementing a mini web-server into the application.

Here is the code for the web-server:
::CoInitialize(NULL);
WSADATA wsaData = { 0 };
::WSAStartup(MAKEWORD(1,1), &wsaData);
SOCKET sockfd = NULL;
if( (sockfd = ::socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0 ) return 0;
SOCKADDR_IN service = { 0 };
service.sin_family = AF_INET;
service.sin_addr.s_addr = htonl(INADDR_ANY);
service.sin_port = htons(CHART_WEBSERVER_PORT);
if( ::bind(sockfd, (SOCKADDR*) &service, sizeof(service)) < 0 ) return 0;
::listen(sockfd, SOMAXCONN);
while( true )
{
   SOCKET newsockfd = NULL;
   if( (newsockfd = ::accept(sockfd, NULL, NULL)) < 0 ) break;
   TIMEVAL timeout = { 3, 0 };
   FD_SET readfds; FD_ZERO(&readfds); FD_SET(newsockfd, &readfds);
   FD_SET writefds; FD_ZERO(&writefds); FD_SET(newsockfd, &writefds);
   CString sInput;
   while( ::select(32, &readfds, NULL, NULL, &timeout) == 1 ) {
      char buffer[1000] = { 0 };
      if( ::recv(newsockfd, buffer, sizeof(buffer) - 10, 0) < 0 ) break;
      sInput += buffer;
      if( sInput.Find("\r\n\r\n") > 0 ) break;
   }
   CString sOutput = _GenerateChartXml();
   CString sTemp, sResponse = "HTTP/1.0 200 OK\r\n";
   sResponse += "Content-type: text/xml\r\n";
   sResponse += "Cache-Control: no-cache\r\n";
   sResponse += "Expires: Fri, 30 Oct 1998 14:19:41 GMT\r\n";
   sTemp.Format("Content-length: %d\r\n", sOutput.GetLength());
   sResponse += sTemp;
   sResponse += "\r\n";
   sResponse += sOutput;
   if( ::select(32, NULL, &writefds, NULL, &timeout) == 1 ) {
      ::send(newsockfd, sResponse, sResponse.GetLength(), 0);
   }
   ::closesocket(newsockfd);
}
::closesocket(sockfd);
::WSACleanup();
::CoUninitialize();

This implements an ultra-simple web-server that will run in a separate thread. It serves any kind of HTTP request and responds solely with the XML definition for the Charts control. You can review the documentation of the XML/SWF Charts XML file on the homepage of the control. A link is provided at the bottom of this article.

To make the FLASH control load its XML definition from the web-server, I had to change the basic.html loading page:
   'movie', 'charts',
   'src', 'charts',
   'FlashVars', 'library_path=charts_library&xml_source=sample.xml', 
    ...
Simply point the "XML source" to the localhost URL including the TCP/IP port.
   'FlashVars', 'library_path=charts_library&xml_source=http://127.0.0.1:11801',

There is one slight problem with the web-server, and that is its shutdown procedure. To properly handle shutting down our service while it is busy waiting for new incoming connections, we actually have to replace the WinSOCK accept() call with an abortable version, which takes an event that will abrupt the blocking accept loop. I will not show the code of the implementation, but you can inspect the code in the download for more details.
Running a TCP/IP server will cause a Security Prompt in Windows Vista firewall at the first run. Hopefully users will not choose to block the service connection. Also, the web-server uses IPv4 strictly because the FLASH control seems to have problems with IPv6, and Vista bumps localhost to IPv6 adding to the confusion.

Alternatives

One of the reasons for choosing the Internet Explorer hosting design was to be able to use other web-centric controls. Specifically I wanted to try out other Charts controls. And so should you. You can search the Internet for alternatives, this one seem interesting as well.
Since these controls' primary audience is web/ASP/PHP developers, the tutorials on how to integrate these controls are using heavily on JavaScript and other web technologies, so being able to cut'n'paste sample code to your locally hosted webpage make things a little easier.

Chart licensing

The XML/SWF Charts control is free to use in its nag-screen version. It is fully functional, except that it displays the author's webpage when you click on the control. Support the author by buying a proper license if you use it in your own product.

You will need to install Adobe Flash 9.0 or better to view the sample.

Source Code Dependencies

Microsoft Visual C++ 6.0
Microsoft ATL Library
Microsoft WTL 7.5 Library
Adobe Flash 9.0

Useful Links

XML/SWF Charts

Download Files

DownloadSource Code and sample (377 Kb)

To the top