Click here to Skip to main content
16,011,647 members
Articles / Programming Languages / C++

Transparent Flash Control in Plain C++

Rate me:
Please Sign up or sign in to vote.
4.10/5 (38 votes)
17 Jul 2006CPOL4 min read 316.6K   115   63
OLE container implementation for hosting a Transparent Flash Player control using plain C++. No ATL, MFC or other libraries are used.

Introduction

This article illustrates the implementation of a simple OLE container, used for hosting 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, and IStorage.

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

C++
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 using the #import command:

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

The named_guids directive is used to generate the 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 containing the Flash Player interface declarations.

Part 3. CFlashWnd derivative class

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

C++
class CFlashWnd :
public COleContainerWnd,
public ShockwaveFlashObjects::_IShockwaveFlashEvents

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

The creation of the CFlashWnd object:

C++
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, which should be set to WS_EX_LAYERED for a transparent Flash control and 0 for non-transparent. The third is the window style, then goes the owner window and the 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, a window with the specified styles is created.

The OleCreate function is used to create an instance of the IOleObject object. It passes COleContainer's IOleClientSite and IStorage to the IOleObject object.

Then, OleSetContainedObject is called to inform the object of its embedded state.

The IShockwaveFlash interface is obtained from IOleObject using QueryInterface. IViewObjectEx is obtained in the same way.

If a windowless control is created, then the container needs the IOleInPlaceObjectWindowless interface to dispatch messages to the object since the object does not have its own window. In other words, the IOleInPlaceObject interface is required to draw the object.

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

C++
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 follows:

  1. Create a WS_POPUP window with the 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 off-screen plain to render window contents to.
  3. Render the window contents, preserving the alpha channel.
  4. Call the UpdateLayeredWindow() function to draw the window to the screen.

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

C++
hr = OleDraw(lpV, DVASPECT_TRANSPARENT, hdcDraw, &rTotal);
  • lpV - the IViewObject interface of the Flash Player control
  • hdcDraw - off-screen plain
  • rTotal - client rectangle of the container window

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

While implementing this, I met with 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:

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:

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 them, you will get:

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

So, a solution was found. Now, we can draw the contents of a Flash Player twice and then correct the corrupted alpha channel.

Part 6. Events

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

C++
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 successful Advise(), we will receive the events in the IDispatch::Invoke method.

Part 7. DirectDraw (Updated)

The Flash control can slightly improve drawing performance by using a DirectDraw3 interface. The Flash object queries it through the ShockwaveFlashObjects::IServiceProvider::raw_RemoteQueryService method. It passes the IID_IDirectDraw3 GUID as guidService and RIID parameters. The implementation looks like this:

C++
//include direct draw header (do not link ddraw.lib) 
#include <ddraw.h> 

//declare required GUIDs 
#ifndef DEFINE_GUID2 
#define DEFINE_GUID2(name, l, w1, w2, b1, b2, b3, b4, b5, b6, b7, b8) \ 

const GUID name \ 
= { l, w1, w2, { b1, b2, b3, b4, b5, b6, b7, b8 } } 

#endif 

DEFINE_GUID2(CLSID_DirectDraw,0xD7B70EE0,0x4340,0x11CF,
             0xB0,0x63,0x00,0x20,0xAF,0xC2,0xCD,0x35); 
DEFINE_GUID2(CLSID_DirectDraw7,0x3c305196,0x50db,0x11d3,
             0x9c,0xfe,0x00,0xc0,0x4f,0xd9,0x30,0xc5); 
DEFINE_GUID2(IID_IDirectDraw,0x6C14DB80,0xA733,0x11CE,0xA5,
             0x21,0x00,0x20,0xAF,0x0B,0xE5,0x60); 
DEFINE_GUID2(IID_IDirectDraw3,0x618f8ad4,0x8b7a,0x11d0,
             0x8f,0xcc,0x0,0xc0,0x4f,0xd9,0x18,0x9d); 
DEFINE_GUID2(IID_IDirectDraw4,0x9c59509a,0x39bd,0x11d1,0x8c,
             0x4a,0x00,0xc0,0x4f,0xd9,0x30,0xc5); 
DEFINE_GUID2(IID_IDirectDraw7,0x15e65ec0,0x3b9c,0x11d2,0xb9,
             0x2f,0x00,0x60,0x97,0x97,0xea,0x5b); 

//declare smart pointer type for IDirectDraw4 interface 
_COM_SMARTPTR_TYPEDEF(IDirectDraw4, IID_IDirectDraw4); 

//the implementation of IServiceProvider interface 
HRESULT __stdcall CFlashWnd::raw_RemoteQueryService ( 
GUID * guidService, 
GUID * riid, 
IUnknown * * ppvObject ) 
{ 
    HRESULT hr; 

    if (IsEqualGUID(*guidService, IID_IDirectDraw3)) 
    { 
        if (!m_lpDD4) 
        { 
            m_lpDD4 = new IDirectDraw4Ptr; 
            hr = m_lpDD4->CreateInstance(CLSID_DirectDraw, NULL, 
                                         CLSCTX_INPROC_SERVER); 
            if (FAILED(hr)) 
            { 
                delete m_lpDD4; 
                m_lpDD4 = NULL; 
                return E_NOINTERFACE; 
            } 
        } 

        if (m_lpDD4 && m_lpDD4->GetInterfacePtr()) 
        { 
            *ppvObject = m_lpDD4->GetInterfacePtr(); 
            m_lpDD4->AddRef(); 
            return S_OK; 
        } 
    }

    return E_NOINTERFACE; 
}

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Web Developer
Russian Federation Russian Federation
Visual C++ developer, team leader (mailto: mak_test@mail.ru)

Comments and Discussions

 
GeneralWorks nice [modified] Pin
Attila F30-May-07 19:14
Attila F30-May-07 19:14 
QuestionHow to disable right-click menu Pin
vian2528-May-07 21:28
vian2528-May-07 21:28 
AnswerRe: How to disable right-click menu Pin
lakeman20003-Jun-07 23:33
lakeman20003-Jun-07 23:33 
GeneralRe: How to disable right-click menu [modified] Pin
vian256-Jun-07 22:38
vian256-Jun-07 22:38 
GeneralRe: How to disable right-click menu Pin
lakeman200017-Jun-07 20:52
lakeman200017-Jun-07 20:52 
QuestionHow to get flash dimensions Pin
vian2526-May-07 1:00
vian2526-May-07 1:00 
AnswerRe: How to get flash dimensions Pin
ClaytonW26-May-07 19:43
ClaytonW26-May-07 19:43 
AnswerRe: How to get flash dimensions Pin
lakeman200028-May-07 15:39
lakeman200028-May-07 15:39 
The Macromedia Flash (SWF) Header
All Macromedia Flash (SWF) files begin with the following header:
SWF File Header
Field Type* Comment
Signature UI8 Signature byte always ‘F’
Signature UI8 Signature byte always ‘W’
Signature UI8 Signature byte always ‘S’
Version UI8 Single byte file version (e.g. 0x04F for SWF 4)
FileLength UI32 Length of entire file in bytes
FrameSize RECT Frame size in twips
FrameRate UI16 Frame delay in 8.8 fixed number of frames per second
FrameCount UI16 Total number of frames in movie


The header begins with a three-byte Signature of either 0x46, 0x57, 0x53 (“FWS”) or 0x46,
0x57, 0x43 (“CWS”). An FWS signature indicates an uncompressed SWF file; CWS indicates
that the entire file after the first 8 bytes (that is, after the FileLength field) has been compressed using the open standard ZLIB. The data format used by the ZLIB library is described by Request for Comments (RFCs) documents 1950 to 1952. CWS file compression is only permitted in SWF version 6 or later.
A one-byte Version number follows the signature. The version number is not an ASCII character,but an 8-bit number. For example, for SWF 4 the version byte is 0x04, not the ASCII character‘4’ (0x35).
The FileLength field is the total length of the SWF file including the header. If this is an
uncompressed SWF (FWS signature), the FileLength field should exactly match the file size. If
this is a compressed SWF (CWS signature), the FileLength field indicates the total length of the
file after decompression, and thus will generally not match the file size. Having the uncompressed size available can make the decompression process more efficient.
The FrameSize field defines the width and height of the movie. This is stored as a RECT
structure, meaning that its size may vary according to the number of bits needed to encode the
coordinates. The FrameSize RECT always has Xmin and Ymin of 0; the Xmax and Ymax
members define the width and height (see Using Bit Values).
The FrameRate is the desired playback rate in frames per second. This rate is not guaranteed if the
SWF file contains streaming sound data, or Flash Player is running on a slow CPU.


For particular information about the format of swf files,please visit
http://www.half-serious.com/swf/format/

Existence is reasonable!

General16 bit background bug Pin
FeiluHang24-May-07 17:18
FeiluHang24-May-07 17:18 
GeneralRe: 16 bit background bug Pin
Makarov Igor27-May-07 20:56
Makarov Igor27-May-07 20:56 
GeneralBug when moving window [modified] Pin
vian2524-May-07 16:20
vian2524-May-07 16:20 
GeneralRe: Bug when moving window Pin
Makarov Igor27-May-07 21:00
Makarov Igor27-May-07 21:00 
QuestionWhy can't I get the paraent window to be transparent? Pin
FeiluHang24-May-07 3:36
FeiluHang24-May-07 3:36 
AnswerRe: Why can't I get the paraent window to be transparent? Pin
Makarov Igor27-May-07 21:01
Makarov Igor27-May-07 21:01 
GeneralAccess Violation in OleCreate() Pin
User 4864814-May-07 4:29
User 4864814-May-07 4:29 
GeneralRe: Access Violation in OleCreate() Pin
ClaytonW14-May-07 9:52
ClaytonW14-May-07 9:52 
GeneralRe: Access Violation in OleCreate() Pin
Makarov Igor27-May-07 21:03
Makarov Igor27-May-07 21:03 
GeneralAddRef needed in GetWindowContext Pin
ctdunn12-Apr-07 9:36
ctdunn12-Apr-07 9:36 
QuestionTransparent flash as IOleObject? Pin
Saar7411-Feb-07 22:58
Saar7411-Feb-07 22:58 
NewsFYI: solution for non 32-bit displays [modified] Pin
MehYam4-Feb-07 7:49
MehYam4-Feb-07 7:49 
GeneralRe: FYI: solution for non 32-bit displays Pin
Makarov Igor12-Feb-07 8:28
Makarov Igor12-Feb-07 8:28 
GeneralRe: FYI: solution for non 32-bit displays Pin
Makarov Igor27-Feb-07 8:27
Makarov Igor27-Feb-07 8:27 
QuestionWhat about these errors??? Pin
Shahbaz Hussain30-Jan-07 22:04
Shahbaz Hussain30-Jan-07 22:04 
AnswerRe: What about these errors??? Pin
MehYam4-Feb-07 9:39
MehYam4-Feb-07 9:39 
GeneralRe: What about these errors??? Pin
foreach(e in stein^2) e = m.c#25-Mar-07 10:21
foreach(e in stein^2) e = m.c#25-Mar-07 10:21 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.