您的位置:首页 > 其它

WTL8.0 调用 ActiveX 控件 - (调用 Flash 控件,响应 Flash 控件的事件)

2012-11-27 18:29 281 查看


很久没用WTL了,WTL都升级到8.0了,这两天做了个小例子,WTL调用Flash控件。

目标:使用WTL创建对话框的工程,调用Flash控件播放Flash,并响应Flash控件的事件。

环境:WindowsXP, VC++ 2005, WTL8.0, Flash9

1. 首先用WTL Wizard创建对话框工程,如图:



注意要选中 Enable ActiveX Control Hosting,我习惯于 Generate .CPP Files 这样可以使H文件和CPP文件分开。

工程创建好后,Wizard会为我们在 tWinMain 函数中添加 AtlAxWinInit() 函数,如下:


int WINAPI _tWinMain(HINSTANCE hInstance, HINSTANCE /**//*hPrevInstance*/, LPTSTR lpstrCmdLine, int nCmdShow)


{


HRESULT hRes = ::CoInitialize(NULL);


// If you are running on NT 4.0 or higher you can use the following call instead to


// make the EXE free threaded. This means that calls come in on a random RPC thread.


// HRESULT hRes = ::CoInitializeEx(NULL, COINIT_MULTITHREADED);


ATLASSERT(SUCCEEDED(hRes));




// this resolves ATL window thunking problem when Microsoft Layer for Unicode (MSLU) is used


::DefWindowProc(NULL, 0, 0, 0L);




AtlInitCommonControls(ICC_BAR_CLASSES); // add flags to support other controls




hRes = _Module.Init(NULL, hInstance);


ATLASSERT(SUCCEEDED(hRes));




AtlAxWinInit();




int nRet = Run(lpstrCmdLine, nCmdShow);




_Module.Term();


::CoUninitialize();




return nRet;


}

2. 接着在编辑对话框资源,单击右键添加ActiveX控件,这里选择 ShockwaveFlash 1.0控件。如图:





添加好以后,我们需要为这个控件定义一个变量,以便使用控件的方法。我们在CMainDlg类里手工增加ActiveX控件的窗口变量:CAxWindow
m_wndFlashPlayer。我们还需要增加ActiveX控件对象的COM接口 CComPtr<IShockwaveFlash> m_FlashPtr,为了增加这个接口,我们需要导入Flash的控件类型库,在 stdafx.h文件中增加如下行:

#import "c:/windows/system32/flash9c.ocx" raw_interfaces_only, raw_native_types, no_namespace, named_guids
raw_interfaces_only 表示以原始接口方式调用Flash类型库里的方法。

no_namespace 表示没有名字空间。

named_guids 表示生成命名的guid变量,如DIID__IShockwaveFlashEvents等变量。

3. 在对话框的初始化函数 OnInitDialog 里将ActiveX控件与变量绑定,如下:


m_wndFlashPlayer = GetDlgItem(IDC_SHOCKWAVEFLASH1);

// HRESULT hResult = m_wndFlashPlayer.QueryControl(__uuidof(IShockwaveFlash), reinterpret_cast<void**>(&m_FlashPtr));


HRESULT hResult = m_wndFlashPlayer.QueryControl(&m_FlashPtr);


ATLASSERT(hResult == S_OK);



IDC_SHOCKWAVEFLASH1 是ActiveX控件的资源ID, GetDlgItem 根据资源 ID 得到ActiveX控件的窗口对象,然后窗口对象 m_wndFlashPlayer 使用QueryControl方法得到ActiveX控件的COM对象指针。上面代码中,注释掉的方法也是可用的,但没有注释的使用比较简单。

接着装载一个Flash Movie,调用下面的方法,装载一个swf文件,并让它处于停止状态:


hResult = m_FlashPtr->put_Movie(_bstr_t("f:\\flashC.swf"));


ATLASSERT(hResult == S_OK);




hResult = m_FlashPtr->Stop();


ATLASSERT(hResult == S_OK);



这时候,我们可以在对话框上增加一个按钮,在Click事件里添加播放的代码,如下:


LRESULT CMainDlg::OnBnPlayClicked(WORD /**//*wNotifyCode*/, WORD /**//*wID*/, HWND /**//*hWndCtl*/, BOOL& /**//*bHandled*/)


{


HRESULT hResult = m_FlashPtr->Play();


ATLASSERT(hResult == S_OK);




return 0;


}



到此,我们可以编译一下工程,如果没有意外,程序可以正常运行,点击Play按钮,可以播放Flash文件。

4. 下面我们关注如何响应Flash的事件,我们以FSCommand事件为例。

首先编辑对话框资源,右键单击前面添加的Flash控件,选择Add Event Handler,如图:



我们选择添加FSCommand事件的响应处理,响应函数为OnFSCommand,响应的处理放在CMainDlg类中,如图:



添加好后,Wizard会为我们生成事件响应的代码,主要在CMainDlg类中,我们看代码:

// MainDlg.h : interface of the CMainDlg class

//

/////////////////////////////////////////////////////////////////////////////

#pragma once

class CMainDlg : public CAxDialogImpl<CMainDlg>, public CUpdateUI<CMainDlg>,

public CMessageFilter, public CIdleHandler,

public IDispEventImpl<IDC_SHOCKWAVEFLASH1,CMainDlg>

{

public:

enum { IDD = IDD_MAINDLG };

virtual BOOL PreTranslateMessage(MSG* pMsg);

virtual BOOL OnIdle();

BEGIN_UPDATE_UI_MAP(CMainDlg)

END_UPDATE_UI_MAP()

BEGIN_MSG_MAP(CMainDlg)

MESSAGE_HANDLER(WM_INITDIALOG, OnInitDialog)

MESSAGE_HANDLER(WM_DESTROY, OnDestroy)

COMMAND_ID_HANDLER(ID_APP_ABOUT, OnAppAbout)

COMMAND_ID_HANDLER(IDOK, OnOK)

COMMAND_ID_HANDLER(IDCANCEL, OnCancel)

END_MSG_MAP()

// Handler prototypes (uncomment arguments if needed):

// LRESULT MessageHandler(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)

// LRESULT CommandHandler(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/)

// LRESULT NotifyHandler(int /*idCtrl*/, LPNMHDR /*pnmh*/, BOOL& /*bHandled*/)

LRESULT OnInitDialog(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/);

LRESULT OnDestroy(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/);

LRESULT OnAppAbout(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/);

LRESULT OnOK(WORD /*wNotifyCode*/, WORD wID, HWND /*hWndCtl*/, BOOL& /*bHandled*/);

LRESULT OnCancel(WORD /*wNotifyCode*/, WORD wID, HWND /*hWndCtl*/, BOOL& /*bHandled*/);

void CloseDialog(int nVal);

public:

CAxWindow m_wndFlashPlayer;

CComPtr<IShockwaveFlash> m_FlashPtr;

BEGIN_SINK_MAP(CMainDlg)

SINK_ENTRY(IDC_SHOCKWAVEFLASH1, 150, OnFSCommand)

END_SINK_MAP()

void __stdcall OnFSCommand(BSTR command, BSTR args);

};

红色部分的代码是Wizard为我们生成的,注意如下几点:

(1)CMainDlg增加了父类 IDispEventImpl<IDC_SHOCKWAVEFLASH1, CMainDlg>, 其中IDC_SHOCKWAVEFLASH1是ActiveX控件的资源ID。

(2)增加了BEGIN_SINK_MAP(事件接受器映射),SINK_ENTRY(IDC_SHOCKWAVEFLASH1, 150, OnFSCommand)是事件接收的进入点,IDC_SHOKEWAVEFLASH1是控件的资源ID,通常这里定义的是事件源的ID,需要和继承的IDispEventImpl模版的第一个参数一致,表示同一个事件源。150 是Flash控件的FSCommand事件的ID,这是固定的,在Flash控件的idl文件已经写死了,我们可以用 OleView 工具察看 Flash 的事件接口里的方法
FSCommand 的ID, 0x96 即为 150。OnFSCommand是我们前面数据的事件响应的函数名,Wizard也为我们生成了函数的声明和框架,void __stdcall OnFSCommand(BSTR command, BSTR args)。

我们在该函数里添加我们的处理代码。

是否这样就可以了吗?答案是否定的,虽然程序编译没有问题,但事件的响应没有触发,我们忘了 DispEventAdvice 了。在 CMainDlg 的初始化函数 OnInitDialog 里加入:DispEventAdvice,如下:

LRESULT CMainDlg::OnInitDialog(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)

{

// center the dialog on the screen

CenterWindow();

// set icons

HICON hIcon = (HICON)::LoadImage(_Module.GetResourceInstance(), MAKEINTRESOURCE(IDR_MAINFRAME),

IMAGE_ICON, ::GetSystemMetrics(SM_CXICON), ::GetSystemMetrics(SM_CYICON), LR_DEFAULTCOLOR);

SetIcon(hIcon, TRUE);

HICON hIconSmall = (HICON)::LoadImage(_Module.GetResourceInstance(), MAKEINTRESOURCE(IDR_MAINFRAME),

IMAGE_ICON, ::GetSystemMetrics(SM_CXSMICON), ::GetSystemMetrics(SM_CYSMICON), LR_DEFAULTCOLOR);

SetIcon(hIconSmall, FALSE);

// register object for message filtering and idle updates

CMessageLoop* pLoop = _Module.GetMessageLoop();

ATLASSERT(pLoop != NULL);

pLoop->AddMessageFilter(this);

pLoop->AddIdleHandler(this);

UIAddChildWindowContainer(m_hWnd);

m_wndFlashPlayer= GetDlgItem(IDC_SHOCKWAVEFLASH1);

// HRESULT hResult = m_wndFlashPlayer.QueryControl(__uuidof(IShockwaveFlash), reinterpret_cast<void**>(&m_FlashPtr));

HRESULT hResult = m_wndFlashPlayer.QueryControl(&m_FlashPtr);

ATLASSERT(hResult == S_OK);

// AtlAdviseSinkMap(this, true);

DispEventAdvise(m_FlashPtr);

hResult = m_FlashPtr->put_Movie(_bstr_t("f:\\flashC.swf"));

ATLASSERT(hResult == S_OK);

hResult = m_FlashPtr->Stop();

ATLASSERT(hResult == S_OK);

return TRUE;

}

注释掉的代码 AtlAdviseSinkMap(this, true)也是可以用的。

到此,我们的目标算是实现了。

WTL8.0对 ActiveX的调用可算是比较简单的,回顾一下之前的做法,特别是接受事件的代码,相对还是比较不同的。不过原理都一样。

之前,事件接受的类要继承 IDispEventImpl<SOURCEID,CMainDlg,&DIID__IShockwaveFlashEvents,&LIBID_ShockwaveFlashObjects,1,0>

如下:

// MainDlg.h : interface of the CMainDlg class

//

/////////////////////////////////////////////////////////////////////////////

#pragma once

#define SOURCEID 1

class CMainDlg;

typedef IDispEventImpl<SOURCEID,CMainDlg,&DIID__IShockwaveFlashEvents,&LIBID_ShockwaveFlashObjects,1,0> CFlashEventSink;

class CMainDlg :

public CAxDialogImpl<CMainDlg>,

public CUpdateUI<CMainDlg>,

public CMessageFilter, public CIdleHandler,

public CComObjectRoot,

public CFlashEventSink

{

public:

enum { IDD = IDD_MAINDLG };

virtual BOOL PreTranslateMessage(MSG* pMsg);

virtual BOOL OnIdle();

BEGIN_UPDATE_UI_MAP(CMainDlg)

END_UPDATE_UI_MAP()

BEGIN_MSG_MAP(CMainDlg)

MESSAGE_HANDLER(WM_INITDIALOG, OnInitDialog)

MESSAGE_HANDLER(WM_DESTROY, OnDestroy)

COMMAND_ID_HANDLER(ID_APP_ABOUT, OnAppAbout)

COMMAND_ID_HANDLER(IDOK, OnOK)

COMMAND_ID_HANDLER(IDCANCEL, OnCancel)

COMMAND_HANDLER(IDC_BUTTON1, BN_CLICKED, OnBnClickedButton1)

END_MSG_MAP()

// Handler prototypes (uncomment arguments if needed):

// LRESULT MessageHandler(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)

// LRESULT CommandHandler(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/)

// LRESULT NotifyHandler(int /*idCtrl*/, LPNMHDR /*pnmh*/, BOOL& /*bHandled*/)

LRESULT OnInitDialog(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/);

LRESULT OnDestroy(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/);

LRESULT OnAppAbout(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/);

LRESULT OnOK(WORD /*wNotifyCode*/, WORD wID, HWND /*hWndCtl*/, BOOL& /*bHandled*/);

LRESULT OnCancel(WORD /*wNotifyCode*/, WORD wID, HWND /*hWndCtl*/, BOOL& /*bHandled*/);

void CloseDialog(int nVal);

LRESULT OnBnClickedButton1(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/);

public:

CAxWindow m_wndFlashPlayer;

CComPtr<IShockwaveFlash> m_FlashPtr;

BEGIN_SINK_MAP(CMainDlg)

SINK_ENTRY_EX(SOURCEID, DIID__IShockwaveFlashEvents, 150, OnFSCommand)

END_SINK_MAP()

void __stdcall OnFSCommand(BSTR command, BSTR args);

};

在ATL3.0时,还需要增加COM的映射宏。


BEGIN_COM_MAP(CMainDlg)


END_COM_MAP()



更早些的方法,SINK_MAP 使用 SINK_ENTRY_INFO 的方式映射事件接受函数


BEGIN_SINK_MAP(CMainDlg)


SINK_ENTRY_INFO(SOURCEID, DIID__IShockwaveFlashEvents, 150, OnFSCommand, &FSCommandInfo)


END_SINK_MAP()

其中 FSCommandInfo 的定义如下:

__declspec(selectany) _ATL_FUNC_INFO FSCommandInfo =

{ CC_STDCALL, VT_EMPTY, 2, { VT_BSTR, VT_BSTR } };

这样 CMainDlg 需要这样派生:

class CMainDlg : public CAxDialogImpl<CMainDlg>, public CUpdateUI<CMainDlg>,

public CMessageFilter, public CIdleHandler,

public CComObjectRootEx<CComSingleThreadModel>,

public CComCoClass<CMainDlg>,

public IDispEventSimpleImpl<SOURCEID, CMainDlg, &DIID__IShockwaveFlashEvents>

最后说说事件的订阅的方法:

有这么几种:

1. 写一个Sink类,继承 IDispEventImpl,如下:


#pragma once




#define DISPID_ONSTARTADD 1


#define DISPID_ONSTOPADD 2




#define SOURCEID 1




class CTestSink;




typedef IDispEventImpl<SOURCEID,CTestSink,&DIID__ITestOBJEvents,&LIBID_TestCOMLib,1,0> CTestEventSink;




//typedef IDispatchImpl<_ITestOBJEvents, &__uuidof(_ITestOBJEvents), &LIBID_TestCOMLib, /* wMajor = */ 1, /* wMinor = */ 0> CEventSink;




class ATL_NO_VTABLE CTestSink :


public CComObjectRoot,


public CTestEventSink


{


public:


CTestSink(void);


~CTestSink(void);




BEGIN_COM_MAP(CTestSink)


END_COM_MAP()




BEGIN_SINK_MAP(CTestSink)


SINK_ENTRY_EX(SOURCEID,DIID__ITestOBJEvents,DISPID_ONSTARTADD,OnStartAdd)


SINK_ENTRY_EX(SOURCEID,DIID__ITestOBJEvents,DISPID_ONSTOPADD,OnStopAdd)


END_SINK_MAP()




void __stdcall OnStartAdd();


void __stdcall OnStopAdd(LONG result);


};



事件的订阅可以使用如下代码:

ITestOBJPtr m_TestOBJPtr;

HRESULT hResult = m_TestOBJPtr.CreateInstance("TestCOM.TestOBJ");

CTestSink * m_pSink = NULL;

m_pSink = new CComObject<CTestSink>;

m_pSink->AddRef();

m_pSink->DispEventAdvise(m_TestOBJPtr);

// hResult = AtlAdvise(m_TestOBJPtr, (IUnknown *)m_pSink, DIID__ITestOBJEvents, &m_dwCookie);

上面注释的代码也是可以使用的。在最开始的例子里,还有比较简单的事件订阅的方法:


AtlAdviseSinkMap(this, true);

最后记住在合适的时候取消事件订阅:DispEventUnadvise 或者 AtlUnadvise 或者 AtlAdviseSinkMap(this, false)。

以上简单总结了 WTL 使用 ActiveX 控件的相关方法,欢迎拍砖,:-P

分类:
C/C++

绿色通道: 好文要顶
关注我 收藏该文与我联系






kylin

关注 - 1

粉丝 - 3

+加关注

0

0

(请您对文章做出评价)

«
博主前一篇:OpenLayers 学习笔记 (2)

»
博主后一篇:SubVersion 安装为 Windows 服务 (Service)

posted on 2007-11-28 01:02
kylin 阅读(4594) 评论(3)

编辑 收藏



评论:

#1楼
2007-11-28 09:20 |
网魂小兵

不错啊,最近我也在搞C++。
支持(0)反对(0)

#2楼
2008-11-12 12:44 |
coolstar1204[未注册用户]

很高兴能得到你的例子,基本我按照你的例子做了一下,都实现了.只不过

AtlAdviseSinkMap(this, true);在我这好用,而DispEventAdvice却提示

未识别的标示符......此函数是在哪里呢

#3楼23767332012/5/14 15:36:55
2012-05-14 15:36 |
zhchch

非常感谢你的这篇文章,我有个问题:

1.如何在Linux下如何实现类似的功能?由于在Linux下不使用COM技术,因此我们无法import “flash10c.ocx”。我想围绕Flash的非IE插件版本libflashplayer.so来实现类似功能。

2. 如果仍在Windows环境下实现类似功能,但不使用ActiveX插件,而使用Flash的非IE插件版本NPSWF32.dll来实现类似功能。

这两个方面都不太清楚怎样下手。希望您能给一些建议,谢谢。
支持(0)反对(0)

刷新评论刷新页面返回顶部

注册用户登录后才能发表评论,请
登录 或
注册,访问网站首页。

沪江网诚聘中级/高级.NET程序员(社区方向)

园豆兑换阿里云代金券,1元享用阿里云云服务器

博客园首页博问新闻闪存程序员招聘知识库

最新IT新闻:

· Zillow耗资1600万美元收购租房搜索HotPads

· 陈淑宁解密文思海辉合并案:前前后后谈了9年

· 传三星拟在硅谷开设创业公司孵化中心

· 谷歌宣布云计算将性能升级 租赁价格下降5%

· 谁能求解 Android 悖论

» 更多新闻...
最新知识库文章:

·
走出浮躁的泥沼

· HTTP服务七层架构技术探讨

· HTTP协议之基本认证

· 17家中国初创公司的失败史

· 高斯模糊的算法

» 更多知识库文章...
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: