用ATL实现无窗口(Windowless)的Flash播放器
首先,说明一点:我不会写文章,如果有表达不清晰的地方,还请各位包涵一二了,可以在评论中告诉我。
其次,看这篇文章的至少要对ATL有一定的了解。
OK,开始主题,一开始我也是不知道ATL已经有实现windowless容器接口的类,所以我找到了codeproject上一个无窗的实现(
http://www.codeproject.com/KB/atl/WindowlessMedia.aspx?msg=2948497#xx2948497xx)。他的标题是说用的ATL,其实不完全是这样,准确的说他是用了WTL。试着运行了他的例子,确实是无窗口显示Flash了。但是,因为我在我的应用里面加了个浏览器“刷新”一样的功能,用户可以按F5重新加载播放器。这时,我Release旧的播放器,再创建一个新的播放器,旧播放器并不能完全Release(也就是旧Flash还在内存中)。
后面在某个国外的帖子里面有人回复说可以用CAxHostWindow,于是看了下CAxHostWindow的代码,发现CAxHostWindow确实实现了Windowless容器所需要的接口,废话不多说了,直接上关键代码。
class ATL_NO_VTABLE CFlashWnd :
public CAxHostWindow,
public IFlashWindow
{
DECLARE_NOT_AGGREGATABLE(CFlashWnd);
DECLARE_WND_CLASS(_T( "FlashWnd"))
BEGIN_MSG_MAP(CFlashWnd)
// …………更多自己要处理的消息
if (uMsg == WM_NCHITTEST)
{
m_nHit = DefWindowProc(uMsg, wParam, lParam);
lResult = m_nHit;
return TRUE;
}
CHAIN_MSG_MAP(CAxHostWindow)
if (m_nHit == HTCLIENT)
{ // 应用Flash的鼠标光标
MESSAGE_HANDLER(WM_SETCURSOR, OnWindowMessage)
}
if (WM_MOUSEWHEEL == uMsg)
{ // 使Flash可以使用鼠标滚轮
MESSAGE_HANDLER(WM_MOUSEWHEEL, OnWindowlessMouseMessage)
}
MESSAGE_HANDLER(WM_DESTROY, OnDestroy)
END_MSG_MAP()
// create flash init stream,这个函数很关键,szFlashInit的数据是复制了设计器中的Flash控件的资源数据,里面已经设置好WMode为Opaque,这是Flash无窗口的基础。当然这里的做法是一个偷懒的办法,呵呵,因为我完全不了解ActiveX初始数据的组织格式;也没有去寻找其它方式提供初始数据
HRESULT STDMETHODCALLTYPE CreateStreamInit(IStream * * ppStream)
{
static WCHAR szFlashInit[] = { 0x0376, 0x011a, 0x0000,
0x0000, 0x0000, 0x5567, 0x5566, 0x0900, 0x0000, 0x6775, 0x0000, 0x420b,
0x0000, 0x0008, 0x0002, 0x0000, 0x0000, 0x0008, 0x0000, 0x0000, 0x0008,
0x0000, 0x0000, 0x0008, 0x000e, 0x0000, 0x004f, 0x0070, 0x0061, 0x0071,
0x0075, 0x0065, 0x0000, 0x0008, 0x0006, 0x0000, 0x002d, 0x0031, 0x0000,
0x0008, 0x0006, 0x0000, 0x002d, 0x0031, 0x0000, 0x0008, 0x000a, 0x0000,
0x0048, 0x0069, 0x0067, 0x0068, 0x0000, 0x0008, 0x0002, 0x0000, 0x0000,
0x0008, 0x0006, 0x0000, 0x002d, 0x0031, 0x0000, 0x0008, 0x0000, 0x0000,
0x0008, 0x0002, 0x0000, 0x0000, 0x0008, 0x0010, 0x0000, 0x0053, 0x0068,
0x006f, 0x0077, 0x0041, 0x006c, 0x006c, 0x0000, 0x0008, 0x0004, 0x0000,
0x0030, 0x0000, 0x0008, 0x0004, 0x0000, 0x0030, 0x0000, 0x0008, 0x0002,
0x0000, 0x0000, 0x0008, 0x0000, 0x0000, 0x0008, 0x0002, 0x0000, 0x0000,
0x000d, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
0x0008, 0x0004, 0x0000, 0x0031, 0x0000, 0x0008, 0x0004, 0x0000, 0x0030,
0x0000, 0x0008, 0x0000, 0x0000, 0x0008, 0x0004, 0x0000, 0x0030, 0x0000,
0x0008, 0x0008, 0x0000, 0x0061, 0x006c, 0x006c, 0x0000, 0x0008, 0x000a,
0x0000, 0x0074, 0x0072, 0x0075, 0x0065, 0x0000
};
HGLOBAL hMem = GlobalAlloc(GMEM_FIXED, sizeof(szFlashInit));
LPVOID pData = GlobalLock(hMem);
CopyMemory(pData, szFlashInit, sizeof(szFlashInit));
GlobalUnlock(hMem);
return CreateStreamOnHGlobal(hMem, TRUE, ppStream);
}
BOOL CreateFlash(HWND hWnd, RECT *pRect, DWORD ws =WS_OVERLAPPEDWINDOW |WS_VISIBLE, DWORD wsEx = 0)
{
m_sizeMin.cx = pRect - >right;
m_sizeMin.cy = pRect - >bottom;
HMENU hMenu = NULL;
Create(hWnd, pRect, NULL, ws, wsEx, hMenu);
// 修正最小窗口大小
LPSTREAM pStream = NULL;
HRESULT hr = CreateStreamInit( &pStream);
CComBSTR bstrLicKey;
hr = _DialogSplitHelper : :ParseInitData(pStream, &bstrLicKey.m_str);
BOOL bRet = CreateFlashInstance(pStream); // pStream不为NULL,且WMode的属性必须为Opaque或Transparent,才能实现无窗口的Flash播放器
pStream - >Release();
return bRet;
}
BOOL CreateFlashInstance(LPSTREAM pStreamInit =NULL)
{
HRESULT hr;
_bstr_t bStrClsid = L "ShockwaveFlash.ShockwaveFlash.1";
hr = CreateControl(bStrClsid, m_hWnd, pStreamInit);
if(FAILED(hr))
{
MessageBox(_T( "您没有安装Flash播放器或版本太低,请先安装最新版本Flash播放器"));
DestroyWindow();
return FALSE;
}
shwaveflash = NULL;
hr = QueryControl(__uuidof(IShockwaveFlash), ( void * *) &shwaveflash);
if(FAILED(hr))
return FALSE;
long lVer = shwaveflash - >FlashVersion();
if (HIWORD(lVer) < 10 || (HIWORD(lVer) == 10 && LOWORD(lVer) < 2))
{
MessageBox(_T( "您的Flash播放器版本太低,请升级到最新版本"));
DestroyWindow();
return FALSE;
}
IOleWindow * pOleWnd;
if (shwaveflash - >QueryInterface(IID_IOleWindow, (LPVOID *) &pOleWnd) == S_OK)
{
HWND hwnd;
pOleWnd - >GetWindow( &hwnd); // 这里是取不到窗口句柄的
pOleWnd - >Release();
}
return TRUE;
}
// 销毁Flash
BOOL DestroyFlash()
{
if (shwaveflash != NULL)
{
shwaveflash - >Release();
shwaveflash = NULL;
ReleaseAll();
}
return TRUE;
}
void LoadUrl(_bstr_t url, _bstr_t flashvars)
{
shwaveflash - >put_FlashVars(flashvars);
bstr_t fullscreen(_T( "true"));
shwaveflash - >put_AllowFullScreen(fullscreen);
bstr_t allownet(_T( "all"));
shwaveflash - >put_AllowNetworking(allownet);
bstr_t allowscript(_T( "always"));
shwaveflash - >put_AllowScriptAccess(allowscript);
shwaveflash - > put_Movie(url); // you have to change the path here
shwaveflash - > Play();
}
LRESULT OnDestroy(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL & bHandled)
{
DestroyFlash();
PostQuitMessage( 0);
bHandled = FALSE;
return 0;
}
// …………省略若干方法
public :
IShockwaveFlash * shwaveflash;
LRESULT m_nHit;
// …………省略若干变量
};
CFlashWnd类的使用方法:
CComObject<CFlashWnd> flashwnd;
flashwnd.CreateFlash(NULL, NULL);
flashwnd.LoadUrl(L"D:\\test.swf", L"");
以上代码因为是取的程序中的关键代码,所以不保证能够编译通过。