纯 C++ 中的透明 Flash 控件






4.10/5 (38投票s)
使用纯 C++ 实现 OLE 容器,用于托管透明 Flash 播放器控件。未使用 ATL、MFC 或其他库。
引言
本文档演示了一个简单的 OLE 容器的实现,该容器用于托管透明或窗口化的 Flash 播放器对象。
第一部分。OLE 接口
ActiveX 对象最简单的 OLE 控件实现应包含几个接口:IOleClientSite
、IOleInPlaceSiteWindowless
、IOleInPlaceFrame
和 IStorage
。
我的 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.tli 和 flash.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
对象实例。它将 COleContainer
的 IOleClientSite
和 IStorage
传递给 IOleObject
对象。
然后,调用 OleSetContainedObject
以告知对象其嵌入状态。
使用 QueryInterface
从 IOleObject
获取 IShockwaveFlash
接口。同样的方式获取 IViewObjectEx
。
如果创建的是无窗口控件,则容器需要 IOleInPlaceSiteWindowless
接口来将消息分派给对象,因为对象没有自己的窗口。换句话说,需要 IOleInPlaceObject
接口来绘制对象。
IOleObject::DoVerb()
用于显示对象并将其切换到运行状态。
hr = m_lpO->DoVerb(OLEIVERB_SHOW, NULL, (IOleClientSite *)this, 0, NULL, NULL);
现在 Flash 播放器对象已完全创建。
第五部分。透明窗口绘制
绘制半透明窗口并非易事。算法如下:
- 创建具有
WS_EX_LAYERED
样式的WS_POPUP
窗口。 - 使用
CreateDIBSection()
函数创建一个 32 位 DIB Section,并将其选择到任何兼容的 DC。这将是用于渲染窗口内容的离屏平面。 - 渲染窗口内容,保留 alpha 通道。
- 调用
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
其中 alpha
和 Rsrc
是未知的。求解它们后,您将得到:
(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;
}