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

WTL调用ActiveX 

以调用Flash控件为例:
1.包含相关文件:
#include <atlcom.h>
#include <atlhost.h>
2.导入ShockwaveFlash类型库:
#import "C:/WINDOWS/SYSTEM32/MACROMED/FLASH/Flash.ocx"
using namespace ShockwaveFlashObjects;

3.WinMain中进行初始化:
AtlAxWinInit();
4.添加一个ActiveX Control,选择ShockwaveFlash Object
5.对其进行操作:
CAxWindow wndFlash = GetDlgItem(IDC_FLASH);
CComPtr<IShockwaveFlash> p;
HRESULT hr = wndFlash.QueryControl(__uuidof(IShockwaveFlash), (LPVOID *)&p);
if (SUCCEEDED(hr))
{
  p->put_Movie(CComBSTR(_T("C://1.swf")));
  p->Play();
}

 

 

很久没用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, 000L);

    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
 
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
在MFC中实现 EventSink 。 (1) 在MFC中,添加ATL简单对象 CFileMonitorSink (2) 添加继承父类 IDispEventImpl public IDispEventImpl (1) 0 唯一标识符, 用于区别 连接到 事件源的多个客户端 CFileMonitorSink, 当前类名 _IFun1Events, COM 中的事件源接口, 包含各种事件 __ATLEventLib, COM 中Lib类 具体查 MSDN --IDispEventImpl (2) 添加映射项 BEGIN_SINK_MAP(CFileMonitorSink) SINK_ENTRY_EX( 0, __uuidof(_IFun1Events), 1, OnNotify) //0 唯一标识符,用于区别 连接到 事件源的多个客户端 同上 , 1, 事件号 , 发生1号事件 由OnNotify来处理 SINK_ENTRY_EX( 0, __uuidof(_IFun1Events), 2, OnNotify2) //发生2号事件 由OnNotify2来处理 END_SINK_MAP() 并添加方法 STDMETHOD(OnNotify)(void); //事件处理类 STDMETHOD(OnNotify2)(CHAR* lszContent); (3) 连接到COM中的事件容器 添加变量 CComPtr m_Object; //COM 中的事件源对象 添加方法 STDMETHOD(Start)(IUnknown* pSinkThisObject, VARIANT_BOOL* succeeded) { AFX_MANAGE_STATE(AfxGetAppModuleState()); // TODO: 在此添加实现代码 if ( DispEventAdvise(pSinkThisObject) == S_OK ) { m_Object = pSinkThisObject; *succeeded = VARIANT_TRUE; } else { *succeeded = VARIANT_FALSE; } return S_OK; } STDMETHOD(Stop)(void) //解除连接 { AFX_MANAGE_STATE(AfxGetAppModuleState()); DispEventUnadvise(m_Object); return S_OK; } 在其他类中的 使用方法: CComPtr m_FileMonitorSink; CComPtr m_FileMonitor; //COM中导出接口 CoInitialize(0); HRESULT lRt = m_FileMonitorSink.CoCreateInstance( __uuidof(FileMonitorSink) ); lRt = m_FileMonitor.CoCreateInstance(__uuidof(Fun1)); //创建COM接口实例 VARIANT_BOOL succeeded; lRt = m_FileMonitorSink->Start(m_FileMonitor, &succeeded); //把 m_FileMonitorSink 连接到COM中的事件容器上 m_FileMonitor->HelloWorld(); //调用COM接口,接口中触发事件s m_FileMonitorSink->stop(); //从COM接口中解除连接 CoUninitialize(); // ################# CFileMonitorSink 类代码 ################# class ATL_NO_VTABLE CFileMonitorSink : public CComObjectRootEx, public CComCoClass, public IDispatchImpl, public IDispEventImpl { public: CFileMonitorSink() { } DECLARE_REGISTRY_RESOURCEID(IDR_FILEMONITORSINK) BEGIN_COM_MAP(CFileMonitorSink) COM_INTERFACE_ENTRY(IFileMonitorSink) COM_INTERFACE_ENTRY(IDispatch) END_COM_MAP() BEGIN_SINK_MAP(CFileMonitorSink) SINK_ENTRY_EX( 0, __uuidof(_IFun1Events), 1, OnNotify) SINK_ENTRY_EX( 0, __uuidof(_IFun1Events), 2, OnNotify2) END_SINK_MAP() DECLARE_PROTECT_FINAL_CONSTRUCT() HRESULT FinalConstruct() { return S_OK; } void FinalRelease() { } CComPtr m_Object; //COM 事件源对象 public: STDMETHOD(OnNotify)(void); STDMETHOD(Stop)(void); STDMETHOD(Start)(IUnknown* pSinkThisObject, VARIANT_BOOL* succeeded); STDMETHOD(OnNotify2)(CHAR* lszContent); };
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值