65.9K
CodeProject 正在变化。 阅读更多。
Home

纯 C++ 中的透明 Flash 控件

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.10/5 (38投票s)

2006年7月5日

CPOL

4分钟阅读

viewsIcon

322292

downloadIcon

2383

使用纯 C++ 实现 OLE 容器,用于托管透明 Flash 播放器控件。未使用 ATL、MFC 或其他库。

引言

本文档演示了一个简单的 OLE 容器的实现,该容器用于托管透明或窗口化的 Flash 播放器对象。

第一部分。OLE 接口

ActiveX 对象最简单的 OLE 控件实现应包含几个接口:IOleClientSiteIOleInPlaceSiteWindowlessIOleInPlaceFrameIStorage

我的 OLE 容器实现称为 COleContainerWnd。其声明如下:

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

其中 TObj 是 ActiveX 对象所需的接口。

第二部分。Flash 对象

使用 #import 命令将 Flash 对象导入 VC++

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

named_guids 指令用于生成 CLSID_ShockwaveFlash 类 ID。如果您在 Windows 2000 下运行,请将系统文件夹更改为“winnt”。

作为 #import 命令的结果,编译器将生成包含 Flash 播放器接口声明的 flash.tliflash.tlh 文件。

第三部分。CFlashWnd 派生类

此类基于 COleContainer,并使用 ShockwaveFlashObjects::IShockwaveFlash 作为模板参数

class CFlashWnd :
public COleContainerWnd,
public ShockwaveFlashObjects::_IShockwaveFlashEvents

它还实现了 _IShockwaveFlashEvents 接口,以接收来自 Flash 电影的 fscommand() 事件。

创建 CFlashWnd 对象

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

第一个参数是 Flash 播放器对象的类 ID。第二个参数是扩展窗口样式,对于透明 Flash 控件应设置为 WS_EX_LAYERED,对于非透明则为 0。第三个是窗口样式,然后是所有者窗口和应用程序实例。

可以通过使用 GetHWND() 函数来检索 OLE 容器的 HWND 句柄。

第四部分。CFlashWnd::Create() 内部

首先,注册窗口类。然后,创建具有指定样式的窗口。

OleCreate 函数用于创建 IOleObject 对象实例。它将 COleContainerIOleClientSiteIStorage 传递给 IOleObject 对象。

然后,调用 OleSetContainedObject 以告知对象其嵌入状态。

使用 QueryInterfaceIOleObject 获取 IShockwaveFlash 接口。同样的方式获取 IViewObjectEx

如果创建的是无窗口控件,则容器需要 IOleInPlaceSiteWindowless 接口来将消息分派给对象,因为对象没有自己的窗口。换句话说,需要 IOleInPlaceObject 接口来绘制对象。

IOleObject::DoVerb() 用于显示对象并将其切换到运行状态。

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

现在 Flash 播放器对象已完全创建。

第五部分。透明窗口绘制

绘制半透明窗口并非易事。算法如下:

  1. 创建具有 WS_EX_LAYERED 样式的 WS_POPUP 窗口。
  2. 使用 CreateDIBSection() 函数创建一个 32 位 DIB Section,并将其选择到任何兼容的 DC。这将是用于渲染窗口内容的离屏平面。
  3. 渲染窗口内容,保留 alpha 通道。
  4. 调用 UpdateLayeredWindow() 函数将窗口绘制到屏幕。

为了渲染 Flash 播放器内容,我使用了 OleDraw 辅助函数,该函数内部调用 IViewObject::Draw() 方法

hr = OleDraw(lpV, DVASPECT_TRANSPARENT, hdcDraw, &rTotal);
  • lpV - Flash 播放器控件的 IViewObject 接口
  • hdcDraw - 离屏平面
  • rTotal - 容器窗口的客户端矩形

DVASPECT_TRANSPARENT 绘制方面告诉对象使用 alpha 混合绘制其内容。

在实现此功能时,我遇到了 Flash Player Control 8 的一个严重错误。此错误仅存在于此版本中。7 和 9 版本没有此问题。该错误在于 Flash Control 填充 32 位设备上下文的 alpha 通道的方式。如果将至少一个 255 的 alpha 值应用于像素,则颜色会正确混合,但结果的 alpha 通道被设置为 255,即使它最初为零。这使得无法创建半透明窗口。因此,我不得不开发一个解决方案来修复此错误。该解决方案非常简单:

Flash 播放器控件使用以下方程进行 alpha 混合:

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

如果我在黑色表面上绘制 Flash 的内容,我会得到:

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

如果我在白色表面上绘制 Flash 的内容,我会得到:

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

这是方程组:

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

其中 alphaRsrc 是未知的。求解它们后,您将得到:

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

因此,找到了解决方案。现在,我们可以绘制 Flash 播放器的内容两次,然后校正损坏的 alpha 通道。

第六部分。事件

Flash 控件事件使用 IDispatch 处理。创建 Flash 控件后,我们检索 IConnectionPointContainer 并尝试查找 DIID__IShockwaveFlashEvents 连接点

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;

成功 Advise() 后,我们将在 IDispatch::Invoke 方法中收到事件。

第七部分。DirectDraw (更新)

Flash 控件可以通过使用 DirectDraw3 接口来稍微提高绘图性能。Flash 对象通过 ShockwaveFlashObjects::IServiceProvider::raw_RemoteQueryService 方法查询它。它将 IID_IDirectDraw3 GUID 作为 guidService 和 RIID 参数传递。实现如下:

//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; 
}
© . All rights reserved.