通过近两个月的学习《Internet Explorer 5.0程序设计》,我终于知道该怎样来挂接一个网页中各元素的事件,现将我解决方法告知如下:
本例利用C++来捕获IE网页中的元素事件.
一、建立一个MFC单文档应用程序,选择视图类为CEditView.
二、新建一个C++类,该类派生于IDispatch接口。例如:(我的类为CIESpyEvent)
  #pragma once
#include "oaidl.h"    //包含了IDspatch接口定义
#include <Mshtml.h>    //事件IID的定义
class CCSpyEventView; 
class CIESpyEvent : public IDispatch
{
public:
CIESpyEvent(CCSpyEventView *pSpyEventView);
public:
~CIESpyEvent(void);
public:
//成员变量
CCSpyEventView *m_pSpyEventView;
CString str;
public:
HRESULT STDMETHODCALLTYPE QueryInterface(const struct _GUID &iid,void ** ppv)
{
//*ppv=this;
if(iid==IID_IUnknown)
*ppv=static_cast <IUnknown *>(this);
else

if(iid==IID_IDispatch)
*ppv=static_cast <IDispatch *>(this);
else
return E_NOINTERFACE;
return S_OK;
}

ULONG STDMETHODCALLTYPE AddRef(void)
{ return 1; } // 做个假的就可以,因为反正这个对象在程序结束前是不会退出的

ULONG STDMETHODCALLTYPE Release(void)
{ return 0; } // 做个假的就可以,因为反正这个对象在程序结束前是不会退出的

HRESULT  STDMETHODCALLTYPE GetTypeInfoCount(
            /* [out] */ UINT *pctinfo) ;
       
      HRESULT  STDMETHODCALLTYPE GetTypeInfo(
            /* [in] */ UINT iTInfo,
            /* [in] */ LCID lcid,
            /* [out] */ ITypeInfo **ppTInfo);
       
      HRESULT  STDMETHODCALLTYPE GetIDsOfNames(
            /* [in] */ REFIID riid,
            /* [size_is][in] */ LPOLESTR *rgszNames,
            /* [in] */ UINT cNames,
            /* [in] */ LCID lcid,
            /* [size_is][out] */ DISPID *rgDispId) ;
       
        HRESULT  STDMETHODCALLTYPE Invoke(
            /* [in] */ DISPID dispIdMember,
            /* [in] */ REFIID riid,
            /* [in] */ LCID lcid,
            /* [in] */ WORD wFlags,
            /* [out][in] */ DISPPARAMS *pDispParams,
            /* [out] */ VARIANT *pVarResult,
            /* [out] */ EXCEPINFO *pExcepInfo,
            /* [out] */ UINT *puArgErr) ;
       
public:
CComPtr <IConnectionPoint> m_pConnectionPoint;  //连接点
public:
void OnClickDoc(void);      //单击文档事件
public:
CComQIPtr <IHTMLDocument2,&IID_IHTMLDocument2> m_pDoc;  //保存文档的成员变量
};

//类实现

#include "StdAfx.h"
#include "IESpyEvent.h"
#include <afxconv.h>    //定义OLE2T宏
#include <ExDispID.h>  //
#include <mshtmdid.h>
#include <Mshtml.h>

#include "CSpyEventView.h"

CIESpyEvent::CIESpyEvent(CCSpyEventView *pSpyEventView)
{
m_pSpyEventView=NULL;
str=_T("");
if(pSpyEventView)
  m_pSpyEventView=pSpyEventView;
m_pConnectionPoint=NULL;
}

CIESpyEvent::~CIESpyEvent(void)
{
}

///实现IDispatch接口
HRESULT  CIESpyEvent::GetTypeInfoCount(  /* [out] */ UINT *pctinfo)
{
//反正没用
  return E_NOTIMPL;
}

HRESULT  CIESpyEvent::GetTypeInfo(
/* [in] */ UINT iTInfo,
/* [in] */ LCID lcid,
/* [out] */ ITypeInfo **ppTInfo)
{
    return E_NOTIMPL;
}

HRESULT CIESpyEvent::GetIDsOfNames(
  /* [in] */ REFIID riid,
  /* [size_is][in] */ LPOLESTR *rgszNames,
  /* [in] */ UINT cNames,
  /* [in] */ LCID lcid,
  /* [size_is][out] */ DISPID *rgDispId)
{
  return E_NOTIMPL;
}

HRESULT CIESpyEvent::Invoke(
/* [in] */ DISPID dispIdMember,
/* [in] */ REFIID riid,
/* [in] */ LCID lcid,
/* [in] */ WORD wFlags,
/* [out][in] */ DISPPARAMS *pDispParams,
/* [out] */ VARIANT *pVarResult,
/* [out] */ EXCEPINFO *pExcepInfo,
/* [out] */ UINT *puArgErr)
{
USES_CONVERSION;

switch(dispIdMember)
{
case DISPID_DOCUMENTCOMPLETE:
{
//获取文档指针
          LPDISPATCH lpDispatch=NULL;
  HRESULT hr=m_pSpyEventView->m_pIE->get_Document(&lpDispatch);
  ASSERT(lpDispatch);
  m_pSpyEventView->m_pHTMLDoc=lpDispatch;

m_pDoc=lpDispatch;
 
  //
  lpDispatch->Release();
  ASSERT(m_pSpyEventView->m_pHTMLDoc);
  //断开以前链接
  if(m_pConnectionPoint)
  {
  if(m_pSpyEventView->m_dwDocumentCookie)
  {
  m_pConnectionPoint->Unadvise(m_pSpyEventView->m_dwDocumentCookie);
  m_pSpyEventView->m_dwDocumentCookie=0;
  m_pConnectionPoint=NULL;

}
  }

//连接新的文档连接点
  //start
  CComPtr <IConnectionPointContainer> pContainer;
  hr=m_pSpyEventView->m_pHTMLDoc->QueryInterface(IID_IConnectionPointContainer,(void**)&pContainer);
          if(SUCCEEDED(hr))
  {
  hr=pContainer->FindConnectionPoint(DIID_HTMLDocumentEvents,&m_pConnectionPoint);
              ASSERT(SUCCEEDED(hr));
  if(SUCCEEDED(hr))
  {
  hr=m_pConnectionPoint->Advise(this,&m_pSpyEventView->m_dwDocumentCookie);  //接收器最好单独是一个类,尽量不要共用。
                  ASSERT(SUCCEEDED(hr));
  if(FAILED(hr))
  {
  ::MessageBox(NULL,_T("Failed to Advise"),_T("文档连接点"),MB_OK);
  }
  }
  }
         
      //end
         
}
break;
case DISPID_HTMLDOCUMENTEVENTS_ONCLICK:
OnClickDoc();
break;
case DISPID_NAVIGATECOMPLETE2:
if(pDispParams->rgvarg[0].vt==(VT_BYREF|VT_VARIANT))
{
CComVariant varURL(*pDispParams->rgvarg[0].pvarVal);
varURL.ChangeType(VT_BSTR);
//::MessageBox(NULL,OLE2T(varURL.bstrVal),_T("Invoke"),MB_OK);
str+=_T("IE事件DISPID_NAVIGATECOMPLETE2:");
str+=_T("/r/n");
str+=OLE2T(varURL.bstrVal);
str+=_T("/r/n");
m_pSpyEventView->SetWindowText(str);
}
break;
case DISPID_PROPERTYCHANGE:
if(pDispParams->cArgs>0&&pDispParams->rgvarg[0].vt==VT_BSTR)
{
str+=_T("IE事件DISPID_PROPERTYCHANGE:");
str+=_T("/r/n");
str+=OLE2T(pDispParams->rgvarg[0].bstrVal);
str+=_T("/r/n");
m_pSpyEventView->SetWindowText(str);
}
break;
case DISPID_HTMLDOCUMENTEVENTS_ONPROPERTYCHANGE:    //元素属性改变激发事件 */
{
str+=_T("IE事件DISPID_HTMLDOCUMENTEVENTS_ONPROPERTYCHANGE:");
            HRESULT hr;

if(!m_pSpyEventView->m_pHTMLDoc)
{
::MessageBox(NULL,_T("无法获取文档接口"),_T("出错"),MB_OK);
break;
}
CComPtr <IHTMLWindow2> pWin;  //声明
CComPtr <IHTMLEventObj> pEventObj;
CComPtr <IHTMLEventObj2> pEventObj2;
CComPtr <IHTMLElement>  pElement;
//hr=(m_pSpyEventView->m_pHTMLDoc)->get_parentWindow(&pWin);
if(m_pDoc)
{
hr=m_pDoc->get_parentWindow(&pWin);

if(FAILED(hr))
{
::MessageBox(NULL,_T("无法获取窗体接口"),_T("出错"),MB_OK);
break;
}
}

hr=pWin->get_event(&pEventObj);
           
            if(FAILED(hr))
{
              ::MessageBox(NULL,_T("无法获取事件接口"),_T("出错"),MB_OK);
break;
}

//获取事件对象2指针
            hr=pEventObj->QueryInterface(IID_IHTMLEventObj2,(void **)&pEventObj2);

//断言
ASSERT(SUCCEEDED(hr));

//获取发生事件的元素对象
hr=pEventObj->get_srcElement(&pElement);

ASSERT(SUCCEEDED(hr));

//获取事件属性名称
BSTR bstrName;    //#define OLECHAR *BSTR

hr=pEventObj2->get_propertyName(&bstrName);
if(SUCCEEDED(hr))
{
str+=_T("propertyname:");
str+=OLE2T(bstrName);
str+=_T("/r/n");
}

hr=pEventObj2->get_type(&bstrName);
if(SUCCEEDED(hr))
{
str+=_T("Type:");
str+=OLE2T(bstrName);
str+=_T("/r/n");
}

hr=pElement->get_tagName(&bstrName);
if(SUCCEEDED(hr))
{
str+=_T("tagName:");
str+=OLE2T(bstrName);
str+=_T("/r/n");
}

hr=pElement->get_id(&bstrName);
{
str+=_T("ID:");
str+=OLE2T(bstrName);
str+=_T("/r/n");
}

hr=pElement->toString(&bstrName);
if(SUCCEEDED(hr))
{
str+=_T("toString:");
str+=OLE2T(bstrName);
str+=_T("/r/n");
}

m_pSpyEventView->SetWindowText(str);
}
break;
default:
break;
}
return S_OK;
}

void CIESpyEvent::OnClickDoc(void)
{
::MessageBox(NULL,_T("单击了文档对象"),_T("文档连接点"),MB_OK);
}

 
  • 对我有用[0]
  • 丢个板砖[0]
  • 引用
  • 举报
  • 管理
  • TOP
  • wxinstudent
  • 等 级:
#7楼 得分:0回复于:2010-01-22 23:30:04
///
//
//CSpyEventView类为我建立应用程序所生存的类。

//头文件名称:CSpyEventView.h

// CSpyEventView.h

#include "IESpyEvent.h"      //接收器实现类(继承于IDispatch)
#include <Mshtml.h>
#pragma once

class CCSpyEventDoc;
class CCSpyEventView : public CEditView
{
protected: // 仅从序列化创建
CCSpyEventView();
DECLARE_DYNCREATE(CCSpyEventView)

// 属性
public:
CCSpyEventDoc* GetDocument() const;

// 操作
public:

// 重写
public:
virtual BOOL PreCreateWindow(CREATESTRUCT& cs);
protected:
virtual BOOL OnPreparePrinting(CPrintInfo* pInfo);
virtual void OnBeginPrinting(CDC* pDC, CPrintInfo* pInfo);
virtual void OnEndPrinting(CDC* pDC, CPrintInfo* pInfo);

// 实现
public:
virtual ~CCSpyEventView();
#ifdef _DEBUG
virtual void AssertValid() const;
virtual void Dump(CDumpContext& dc) const;
#endif

protected:

// 生成的消息映射函数
protected:
DECLARE_MESSAGE_MAP()
public:
afx_msg void OnStartSpyie();
public:
CComPtr <IWebBrowser2> m_pIE;
public:
virtual void OnInitialUpdate();
public:
IConnectionPoint *m_pConnectionPoint;
public:
DWORD m_dwBrowserCookie;
DWORD m_dwDocumentCookie;
public:
afx_msg void OnSttvHtml();
public:
CIESpyEvent *m_pIESpyEvent;
public:
afx_msg void OnClearTextbox();
public:
CComQIPtr <IHTMLDocument2,&IID_IHTMLDocument2> m_pHTMLDoc;
};

#ifndef _DEBUG  // CSpyEventView.cpp 中的调试版本
inline CCSpyEventDoc* CCSpyEventView::GetDocument() const
  { return reinterpret_cast <CCSpyEventDoc*>(m_pDocument); }
#endif

///
//
//源文件名称:CSpyEventView.CPP
//
///

#include "stdafx.h"
#include "CSpyEvent.h"

#include "CSpyEventDoc.h"
#include "CSpyEventView.h"
#include <mshtmdid.h>

#ifdef _DEBUG
#define new DEBUG_NEW
#endif

// CCSpyEventView

IMPLEMENT_DYNCREATE(CCSpyEventView, CEditView)

BEGIN_MESSAGE_MAP(CCSpyEventView, CEditView)
// 标准打印命令
ON_COMMAND(ID_FILE_PRINT, &CEditView::OnFilePrint)
ON_COMMAND(ID_FILE_PRINT_DIRECT, &CEditView::OnFilePrint)
ON_COMMAND(ID_FILE_PRINT_PREVIEW, &CEditView::OnFilePrintPreview)
ON_COMMAND(ID_START_SPYIE, &CCSpyEventView::OnStartSpyie)
ON_COMMAND(ID_STTV_HTML, &CCSpyEventView::OnSttvHtml)
ON_COMMAND(ID_CLEAR_TEXTBOX, &CCSpyEventView::OnClearTextbox)
END_MESSAGE_MAP()

// CCSpyEventView 构造/析构

CCSpyEventView::CCSpyEventView()
: m_pConnectionPoint(NULL)
, m_pIESpyEvent(NULL)
{
// TODO: 在此处添加构造代码
::CoInitialize(NULL);
    m_dwBrowserCookie=-1;
    m_dwDocumentCookie=-1;
}

CCSpyEventView::~CCSpyEventView()
{
    //析构中断开所有的连接
if(m_pConnectionPoint)
{
HRESULT hr=m_pConnectionPoint->Unadvise(m_dwBrowserCookie);
if(FAILED(hr))
{
::MessageBox(NULL,__T("Failed to Unadvise"),_T("C++ Event Sink"),MB_OK);
}
}

if(m_pIESpyEvent->m_pConnectionPoint)
{
HRESULT hr=m_pIESpyEvent->m_pConnectionPoint->Unadvise(m_dwDocumentCookie);
if(FAILED(hr))
{
::MessageBox(NULL,__T("Failed to Unadvise"),_T("C++ Event Sink"),MB_OK);
}
}
if(m_pIE)
m_pIE->Quit();
::CoUninitialize();
}

BOOL CCSpyEventView::PreCreateWindow(CREATESTRUCT& cs)
{
// TODO: 在此处通过修改
//  CREATESTRUCT cs 来修改窗口类或样式

BOOL bPreCreated = CEditView::PreCreateWindow(cs);
cs.style &= ~(ES_AUTOHSCROLL|WS_HSCROLL); // 启用换行

return bPreCreated;
}

// CCSpyEventView 打印

BOOL CCSpyEventView::OnPreparePrinting(CPrintInfo* pInfo)
{
// 默认 CEditView 准备
return CEditView::OnPreparePrinting(pInfo);
}

void CCSpyEventView::OnBeginPrinting(CDC* pDC, CPrintInfo* pInfo)
{
// 默认 CEditView 开始打印
CEditView::OnBeginPrinting(pDC, pInfo);
}

void CCSpyEventView::OnEndPrinting(CDC* pDC, CPrintInfo* pInfo)
{
// 默认 CEditView 结束打印
CEditView::OnEndPrinting(pDC, pInfo);
}

// CCSpyEventView 诊断

#ifdef _DEBUG
void CCSpyEventView::AssertValid() const
{
CEditView::AssertValid();
}

void CCSpyEventView::Dump(CDumpContext& dc) const
{
CEditView::Dump(dc);
}

CCSpyEventDoc* CCSpyEventView::GetDocument() const // 非调试版本是内联的
{
ASSERT(m_pDocument->IsKindOf(RUNTIME_CLASS(CCSpyEventDoc)));
return (CCSpyEventDoc*)m_pDocument;
}
#endif //_DEBUG

// CCSpyEventView 消息处理程序

void CCSpyEventView::OnStartSpyie()
{
// TODO: 在此添加命令处理程序代码
//SetWindowText(_T(""));
m_pIESpyEvent->str=_T("");
if(!m_pIE)
return;
BSTR bstrURL=::SysAllocString(L"http://www.ourgame.com");
CComVariant varEmpty;
m_pIE->Navigate(bstrURL,&varEmpty,&varEmpty,&varEmpty,&varEmpty);
::SysFreeString(bstrURL);

//为IE建立连接点
IConnectionPointContainer *pCPContainer;

HRESULT hr=m_pIE->QueryInterface(IID_IConnectionPointContainer,(void**)&pCPContainer);
ASSERT(pCPContainer);
hr=pCPContainer->FindConnectionPoint(DIID_DWebBrowserEvents2,&m_pConnectionPoint);
ASSERT(m_pConnectionPoint);
hr=m_pConnectionPoint->Advise(static_cast <IUnknown*>(this->m_pIESpyEvent),&m_dwBrowserCookie);
if(FAILED(hr))
return ;

pCPContainer->Release();
}

void CCSpyEventView::OnInitialUpdate()
{
CEditView::OnInitialUpdate();

// TODO: 在此添加专用代码和/或调用基类

m_pIESpyEvent=new CIESpyEvent(this);  //建立类

HRESULT hr=::CoCreateInstance(CLSID_InternetExplorer,NULL,
                          CLSCTX_SERVER,IID_IWebBrowser2,(void **)&m_pIE);
ASSERT(SUCCEEDED(hr));

m_pIE->put_Visible(VARIANT_TRUE);    //显示IE实例
}

void CCSpyEventView::OnSttvHtml()    //导航到站点
{
// TODO: 在此添加命令处理程序代码
if(m_pIE)
{
    BSTR bstrURL=::SysAllocString(L"http://www.abchina.com");//中国农业银行
CComVariant varEmpty;
m_pIE->Navigate(bstrURL,&varEmpty,&varEmpty,&varEmpty,&varEmpty);
::SysFreeString(bstrURL);
}
}

void CCSpyEventView::OnClearTextbox()    //清除文档内容
{
// TODO: 在此添加命令处理程序代码
    m_pIESpyEvent->str=_T("");
SetWindowText(m_pIESpyEvent->str);
}

//

//小结:
//本例是在VS2005中实现的。
/*******************************************************************************
注意的几个问题:
1。捕获连接点的问题。在这里我利用文档对象建立连接点DIID_HTMLDocumentEvents(也就是MSDN中说的dispinterface为HTMLDocumentEvents.
  例如:我为网页元素建立HTMLElementEvents连接点:
      CComPtr <IConnectionPointContainer> pCPContainer;
    CComPtr <IConnectionPoint> pConnectionPoint;
    CComPtr <IHTMLElement> pElem;
    CComPtr <IHTMLDocument2> pDoc;
      m_pIE->get_Document(&pDoc);  //m_pIE上面例子中
      HRESULT hr=pDOC->get_body(pElem);
      ASSERT(SUCCEEDED(hr));
      if(FAILED(hr))
      return hr;
      //获取连接点容器
      hr=pElem->QueryInterface(IID_IConectionPointContainer,&pCPContainer);
      if(FAILED(hr))
      return hr;
      //获取连接点
      hr=pCPContainer->FindConnectionPoint(DIID_HTMLElementEvents,&pConnectionPoint)
      if(FAILED(hr))
      return hr;
      //建立连接,CEventSink为实现事件处理的类(派生于IDispatch接口)
      hr=pConnectionPoint->Advise(CEventSink,&m_dwCookie);   
      //这里的CEventSink最好个连接点一个类,不要共用,因为连接点事件ID有可能相同。
      //处理事件在CEventSink::Invoke中进行,如DISPID_HTMLELEMENTEVENTS2_ONCLICK事件.
2。包容那些头文件
    mshtmdid.h  //定义了mshtml组件中连接点事件DISPID,如DISPID_HTMLELEMENTEVENTS2_ONCLICK
  mshtml.h    //定义了接口IID,如IID_IHTMLDocument2.
  exdisp.h    //定义了browser组件中的DISPID,如DISPID_DOCUMENTCOMPLETE
  afxconv.h  //BSTR到TCHAR的转换宏,如OLE2T
  exdispid.h
3.多多练习,实践出真知.

******************************************************************************/

VC 监视网页中的元素事件相关推荐

  1. VC6中用DOM遍历网页中的元素

    VC6中用DOM遍历网页中的元素 2005-4-21 6:56:47 作者:模板天下收集整理 来源:未知 网友评论 0 条 论坛286 一.摘要 在我们编写的程序中,如果想要实现对浏览器打开的网页进行 ...

  2. C#获取网页中某个元素的位置,并模拟点击

    我们在开发中,往往要得到网页中某个元素的位置,并且点击它.要模拟一次鼠标点击并不难,只要调用一个API就行了,关键就是怎么样得到这个元素的位置,还有判断是否要滚动滚动条,要滚动多少行能让元素显示出来. ...

  3. 精确定位网页中各个元素的位置

    精确定位网页中各个元素的位置有两种方法:使用表格或层.使用表格是目前比较通用的做法,具体方法是:先在网页中建立一个表格,注意表格的边框宽度应为0.然后再把各个元素按照你的要求放在各个表格单元之中.仔细 ...

  4. CSS定位网页中的元素

    relative相对定位 偏移设置:left.right.top.bottom 值单位:px 元素的规律: 相对定位元素的规律 设置相对定位的盒子会相对它原来的位置通过指定偏移,到达新的位置. 设置相 ...

  5. java 获取js html_JS获取网页中HTML元素的几种方法

    编写js程序的时候最常使用的就是获取网页中的html元素,并进行处理,我在网上发现了一篇获取html对象的几种方法进行整理的帖子,发上来大家一块学习~ getElementById getElemen ...

  6. 网页中审查元素(按F12)与查看网页源代码的区别

    在网页中右键,如图: 所谓查看源代码,就是别人服务器发送到浏览器的原封不动的代码. 审查元素时,你看到那些,在源代码中找不到的代码,是在浏览器执行js动态生成的. 通过审查元素看到的就是最终的html ...

  7. vc采集网页内所有元素(不指定具体table/form/frame)

    1.独立代码 //-----------开始---------------------// #include <atlbase.h> #include <mshtml.h> # ...

  8. 网页中嵌入JavaScript+事件触发程序

    嵌入方式: 1.JavaScript代码与HTML写在同一个文档中 JavaScript代码要放在<script>和</script>标签之间 而且整个JavaScript代码 ...

  9. python获取网页元素坐标_Python实战爬虫系统学习笔记一:解析网页中的元素

    学习python编写爬虫第一天,学会如何解析本地网页 第1步:使用用Beautiful Soup解析网页 Soup = BeautifulSoup(wb_data,'lxml') 第2步:描述爬取信息 ...

最新文章

  1. LeetCode简单题之相交链表
  2. Magento获取指定分类下的所有子分类信息
  3. CTFshow 命令执行 web39
  4. 如何判断LSTM模型中的过拟合和欠拟合 By 机器之心2017年10月02日 11:09 判断长短期记忆模型在序列预测问题上是否表现良好可能是一件困难的事。也许你会得到一个不错的模型技术得分,但了解
  5. Web安装项目创建桌面快捷方式
  6. ADO.NET 数据连接查询
  7. HTML5 学习笔记
  8. No module named ‘yaml‘错误解决办法
  9. 管理Exchange 2003注意事项
  10. Python的学习笔记案例4--52周存钱挑战1.0
  11. 和小松一起聆听遥唤根本上师
  12. Mstar 平台背光时序调试
  13. 用了python之后笔记本卡了_干货!如何用Python在笔记本电脑上分析100GB数据(上)...
  14. erase和remove
  15. GA002-186-11
  16. iOS Code Signing 学习笔记转写
  17. 2020.8.26丨Nanopore甲基化测序产品概述
  18. 访客模式 无痕模式 区别_访客设计模式示例
  19. SecureCRT安装配置使用
  20. MATLAB中nargin和nargout的妙用

热门文章

  1. docsify 使用说明
  2. Tangent Element调色台的设计与功能
  3. SQL之一种通用的连续性问题处理方法【重分组算法】--HiveSQL面试题33
  4. 发那科机器人零点找回_「发那科」FANUC机器人零点复归详解
  5. SQL(MySql)菜鸟教程知识
  6. MacOS Big Sur 11下安装运行EAS 8.2客户端
  7. WebRebuild第三届年会菩提树下介绍及相关资料下载
  8. 装逼必备代码上(专业的一定能看懂)
  9. RocketMQ的顺序消费
  10. Flutter 路由传参配置