Transparent Flash Control in Plain C++

This article illustrates the implementation of a simple OLE container that is used to host a transparent or windowed Flash Player Object.

Part 1. OLE Interfaces

The simplest OLE control implementation for an ActiveX object should consist of several interfaces:

  • IOleClientSite
  • IOleInPlaceSiteWindowless
  • IOleInPlaceFrame
  • IStorage

My implementation of the OLE container is called COleContainerWnd. It is declared as in the following:

template<class TObj> class COleContainerWnd :
   virtual public IOleClientSite,
   virtual public IOleInPlaceSiteWindowless,
   virtual public IOleInPlaceFrame,
   virtual public IStorage

where TObj is the desired interface of an ActiveX object.

Part 2. Flash Object

Importing the Flash Object into VC++ is done by using the #import command:

#import "c:\\windows\\system32\\macromed\\flash\\flash.ocx" named_guids

The named_guids directive is used to generate a CLSID_ShockwaveFlash class ID.

If you are running under Windows 2000, change the system folder to "winnt".

As a result of the #import command, the compiler will generate the flash.tli and flash.tlh files. They contain your Flash Player interface declarations.

Part 3. CFlashWnd Derivative Class

This class is based on COleContainer and uses ShockwaveFlashObjects:IShockwaveFlash as the template parameter:

class CFlashWnd :
   public COleContainerWnd<ShockwaveFlashObjects::IShockwaveFlash>,
   public ShockwaveFlashObjects::_IShockwaveFlashEvents

It also implements the _IShockwaveFlashEvents interface to receive fscommand() events from the Flash movie.

The creation of CFlashWnd object:

g_flashWnd = new CFlashWnd;
g_flashWnd->Create(ShockwaveFlashObjects::CLSID_ShockwaveFlash,
                   WS_EX_LAYERED, WS_POPUP | WS_VISIBLE |
                   WS_CLIPSIBLINGS, g_hWnd, g_hInst);

The first parameter is the class ID of the Flash Player Object. The second parameter is the extended window style; it should be set to WS_EX_LAYERED for transparent Flash control and 0 for non-transparent. The third is the window style, followed by the owner window and application instance.

The HWND handle for the OLE container can be retrieved by using the GetHWND() function.

Part 4. Inside CFlashWnd::Create()

First, the window class is registered. Then, the window with your specified styles is created.

The OleCreate function creates an instance of an IOleObject object. It passes COleContainer's IOleClientSite and IStorage to the IOleObject object. Then, the OleSetContainedObject is called to inform the object of its embedded state. TheIShockwaveFlash interface is obtained from IOleObject by using QueryInterface. IViewObjectEx is obtained in the same way.

If a windowless control is created, the container needs the IOleInPlaceObjectWindowless interface to dispatch messages to the object because the object does not have its own window. In another case, the IOleInPlaceObject interface is required to draw the object.

IOleObject::DoVerb() is used to show the object and switch it to its running state.

hr = m_lpO->DoVerb(OLEIVERB_SHOW, NULL, (IOleClientSite *)this,
                   0, NULL, NULL);

Now, the Flash Player object is fully created.

Part 5. Transparent Window Drawing

It is not quite trivial to draw semitransparent translucent windows. The algorithm is the following:

  1. Create a WS_POPUP window with WS_EX_LAYERED style.
  2. Create a 32-bit DIB Section using the CreateDIBSection() function and select it to any compatible DC. It will be an offscreen plain to render window contents to.
  3. Render window contents, preserving the alpha channel.
  4. Call the UpdateLayeredWindow() function to draw the window to the screen.

To render Flash player contents, I use the OleDraw helper function. It internally calls the IViewObject::Draw() method:

hr = OleDraw(lpV, DVASPECT_TRANSPARENT, hdcDraw, &rTotal);
  • lpV: IViewObject interface of flash player control
  • hdcDraw: Offscreen plain
  • rTotal: Client rectangle of the container window

The DVASPECT_TRANSPARENT drawing aspect tells the object to draw its content using alpha blending.

When implementing this, I met a serious bug in Flash Player Control 8. This bug is only in this version. Players 7 and 9 are free of it. The bug is in the way Flash Control fills the alpha channel of a 32-bit device context. If at least 1 of 255 alpha values is applied to a pixel, the colors are mixed correctly, but the resulting alpha channel is set to 255, even if it was initially zero. This makes it impossible to create semitransparent windows. So, I had to develop a solution to fix this bug. The solution is quite simple. These equations are used by the Flash Player Control for alpha blending:

  • R' = Rdst * (1 - alpha) + Rsrc * alpha
  • G' = Gdst * (1 - alpha) + Gsrc * alpha
  • B' = Bdst * (1 - alpha) + Bsrc * alpha

If I draw the contents of Flash onto a black surface, I get the following:

  • R'black = Rsrc * alpha
  • G'black = Gsrc * alpha
  • B'black = Bsrc * alpha

If I draw the contents of Flash onto a white surface, I get this:

  • R'white = 255 * (1 - alpha) + Rsrc * alpha
  • G'white = 255 * (1 - alpha) + Rsrc * alpha
  • B'white = 255 * (1 - alpha) + Rsrc * alpha

Here is the system of equations:

  • R'black = Rsrc * alpha
  • R'white = 255 * (1 - alpha) + Rsrc * alpha

where alpha and Rsrc are unknown. After solving it, you will get:

  • (255-Alpha) = R'white - R'black
  • Alpha = 255 - (R'white - R'black)

So, the solution is found. Now, you can draw the contents of the Flash player twice and then correct the spoiled alpha channel.

Part 6. Events

Flash Control Events are handled by using IDispatch. After the Flash control is created, you retrieve a IConnectionPointContainer and try to find DIID__IShockwaveFlashEvents' connection point:

hr = m_lpControl->QueryInterface(IID_IConnectionPointContainer,
                                 (void**)&m_lpConCont);
if (FAILED(hr))
   return FALSE;
hr = m_lpConCont->FindConnectionPoint(
   ShockwaveFlashObjects::DIID__IShockwaveFlashEvents, &m_lpConPoint);
if (FAILED(hr))
   return FALSE;
hr = m_lpConPoint->Advise((ShockwaveFlashObjects::
                           _IShockwaveFlashEvents *)this,
                           &m_dwConPointID);
if (FAILED(hr))
   return FALSE;

After a successful Advise(), you will receive events in the IDispatch::Invoke method.



About the Author

Igor Makarov

Senior Visual C++ developer.

Downloads

Comments

  • Access Violation

    Posted by YanTayga on 08/24/2012 05:16am

    There is error running your example on flash 11 - access violation. This line: hr = m_lpO-DoVerb(OLEIVERB_SHOW, NULL, (IOleClientSite *)this, 0, NULL, NULL); Wrong this pointer is passed to OLECONTAINER(void)::Draw

    • How to fixed the AV

      Posted by Kend on 09/13/2014 11:50pm

      Modify this function add 2 lines. OLECONTAINER(HRESULT STDMETHODCALLTYPE)::QueryInterface(REFIID riid, void ** ppvObject) { if (IsEqualGUID(riid, IID_IUnknown)) *ppvObject = (void*)(this); // Add this 2 lines return IOleClientSite interface to flash. else if (IsEqualGUID(riid, IID_IOleClientSite)) *ppvObject = (void*)dynamic_cast(this);

      Reply
    • Resolution to Access Violation

      Posted by Arvind Lakra on 12/10/2012 03:50am

      Hi , Did u got any valid resolution for the above issue ?

      Reply
    Reply
  • Broken

    Posted by DJ on 05/01/2012 05:27pm

    The download link is broken

    Reply
  • Greate job!

    Posted by hailongxl on 04/22/2011 09:48pm

    Thanks a lot!

    Reply
  • .Net?

    Posted by jberenguer on 04/05/2009 09:22pm

    could you point me towards a C# version - I really could use transparent control in .net - can you convert this to C# dll? pretty please? Jimmy Berenguer

    Reply
  • I am impressed by your container.

    Posted by Wirt on 02/08/2008 05:08pm

    You did a great job. You are really senior. How old are you by the way? Wirt From Canada.

    Reply
  • #import problem

    Posted by Ali Imran on 01/10/2008 04:36am

    #import not working with mingw compiler (dev c++ ide) any other solution ? #import "c:\\windows\\system32\\macromed\\flash\\flash.ocx" named_guids regards

    Reply
  • great

    Posted by elliot115 on 04/13/2007 02:06am

    great

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

Top White Papers and Webcasts

  • On-demand Event Event Date: December 18, 2014 The Internet of Things (IoT) incorporates physical devices into business processes using predictive analytics. While it relies heavily on existing Internet technologies, it differs by including physical devices, specialized protocols, physical analytics, and a unique partner network. To capture the real business value of IoT, the industry must move beyond customized projects to general patterns and platforms. Check out this webcast and join industry experts as …

  • 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