使用ATL开发AJAX控件即XMLHttpRequest控件的经验体会

来源:互联网 发布:92game源码破解 编辑:程序博客网 时间:2024/06/12 19:52

由于最近一段时间要使用ATL开发相关的Ajax的组件,在开发过程中遇到的一些难点与大家分享一下

1)在COM中使用线程,要注意线程内与COM是否同步,使用散列集的方法可以保证
   在创建线程前使用散列,参考代码:
   HRESULT hr = CoMarshalInterThreadInterfaceInStream(IID_IReadXML, m_RXML, &m_pStr);//IID_IReadXML为接口IID,m_RXML为接口的智能指针CComQIPtr m_RXML,m_pStr为流IStream *m_pStr;
   if(FAILED(hr))
   {
    return E_NOTIMPL;
   }
   DWORD thread;
   ::CreateThread(NULL, 0, BackSendThread, (void *)this, 0, &thread);
   在线程中通过流来获取同步
   参考代码:
   DWORD WINAPI CXMLHttp::BackSendThread(void *pThis)
{
 CoInitialize(NULL);
 CXMLHttp *This = (CXMLHttp *)pThis;
 HRESULT hRes;
 CComQIPtr<IReadXML, &IID_IReadXML> m_spT;
 m_spT.Release();

 //接口散集
 if (This->m_pStr)
 {
  hRes =::CoGetInterfaceAndReleaseStream(This->m_pStr, IID_IReadXML, (void**)&m_spT);
 }
 //m_spT->backSend(This->m_varBody);
 m_spT->CallBackSend();
 m_spT.Release();

 CoUninitialize();
 return 0;
}

如果需要消息循环:可以在主线程中使用AtlWaitWithMessageLoop(handle);这样会保证线程的同步运行,毕竟运行STA环境里的

2)与网络实名等软件使用全局HOOK技术的软件的冲突,这些软件通常会截取软件发出的系统信息,造成资源被占用,解决方法是自定义用户消息
   WM_USER+1616等
   参考代码:
   发送消息
 HWND Wnd= ::FindWindow(NULL,"XMLDlg");
 IDispatch *temp = this;
 if(Wnd)
 {
  ::SendMessage(Wnd, WM_USER+1616, 0, (LPARAM)temp);
 }
   接收消息
   1、先映射消息
   BEGIN_MESSAGE_MAP(CXMLDlgDlg, CDHtmlDialog)
 ON_WM_SYSCOMMAND()
 //}}AFX_MSG_MAP
 ON_MESSAGE(WM_USER+1616, OnIDispatcMsg)
   END_MESSAGE_MAP()
   2、实现代码
   LRESULT CXMLDlgDlg::OnIDispatcMsg(WPARAM wParam, LPARAM lParam)
{
 m_spObj = (IDispatch *)lParam;
 const IID IID_IXMLHttpEvents = {0x030AB05F, 0x2898, 0x4042, 0x88, 0xC1, 0x3D, 0x9D, 0x4F, 0x1F, 0xE9, 0xE7};
 if(m_onCopyData)
 { 
  m_sink.SetDispPatch( m_spObj );//用于回调函数访问控件接口(IReadXML)
  CComQIPtr<IConnectionPointContainer> spContainer( m_spObj );
  if( !spContainer )
  {
   AfxMessageBox( _T("组件没有提供连接点功能") );
   return FALSE;
  }
  // 得到连接点接口
  spContainer->FindConnectionPoint(
   IID_IXMLHttpEvents,
   &m_spCP );
  if( !m_spCP )
  {
   AfxMessageBox( _T("没有找到连接点接口") );
   return FALSE;
  }
  HRESULT hr = m_spCP->Advise( &m_sink, &m_dwCookie );
  if( FAILED( hr ) )
  {
   AfxMessageBox( _T("连接失败") );
  }
  m_onCopyData = FALSE;
 }
 else
 {
  if( m_spCP )
  {
   m_spCP->Unadvise( m_dwCookie );
   m_spCP.Release();
   
   m_sink.SetDispPatch( m_spObj );
   CComQIPtr<IConnectionPointContainer> spContainer( m_spObj );
   if( !spContainer )
   {
    AfxMessageBox( _T("组件没有提供连接点功能") );
    return FALSE;
   }
   // 得到连接点接口
   spContainer->FindConnectionPoint(
    IID_IXMLHttpEvents,
    &m_spCP );

   if( !m_spCP )
   {
    AfxMessageBox( _T("没有找到连接点接口") );
    return FALSE;
   }
   HRESULT hr = m_spCP->Advise( &m_sink, &m_dwCookie );
   if( FAILED( hr ) )
   {
    AfxMessageBox( _T("连接失败") );
   }
   m_onCopyData = FALSE;
  }
 }
 return 0;
}

3)对于获取其它容器所创建的控件的接口(比如JS创建的控件),主动获取有可能会比较困难,可以逆向思维,让创建的控件将接口自已以消息的方式发送过来,这样要相对简单一些。

4)增加MFC的浏览器滚动条
   在OnInitDialog()中 CDHtmlDialog::OnInitDialog();之前使用:SetHostFlags(DOCHOSTUIFLAG_NO3DBORDER   |   DOCHOSTUIFLAG_FLAT_SCROLLBAR );
   参考代码:
   BOOL CXMLDlgDlg::OnInitDialog()
{
 SetHostFlags(DOCHOSTUIFLAG_NO3DBORDER   |   DOCHOSTUIFLAG_FLAT_SCROLLBAR );//增加浏览器滚动条
 CDHtmlDialog::OnInitDialog();

 // 将/“关于.../”菜单项添加到系统菜单中。

 // IDM_ABOUTBOX 必须在系统命令范围内。
 ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);
 ASSERT(IDM_ABOUTBOX < 0xF000);

 CMenu* pSysMenu = GetSystemMenu(FALSE);
 if (pSysMenu != NULL)
 {
  CString strAboutMenu;
  strAboutMenu.LoadString(IDS_ABOUTBOX);
  if (!strAboutMenu.IsEmpty())
  {
   pSysMenu->AppendMenu(MF_SEPARATOR);
   pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu);
  }
 }

 // 设置此对话框的图标。当应用程序主窗口不是对话框时,框架将自动
 //  执行此操作
 SetIcon(m_hIcon, TRUE);   // 设置大图标
 SetIcon(m_hIcon, FALSE);  // 设置小图标

 // TODO: 在此添加额外的初始化代码
 return TRUE;  // 除非设置了控件的焦点,否则返回 TRUE
}

5)直接调用JS函数
HRESULT CXMLHttp::CallJsFun(IDispatch *Fun)
{

//Fun为你的JS函数地址

 try
 {
  HRESULT hr;
  DISPPARAMS dispparams;
  memset(&dispparams, 0, sizeof dispparams);
  CComVariant vResult;
  EXCEPINFO excepInfo;
  memset(&excepInfo, 0, sizeof excepInfo);
  UINT nArgErr = (UINT)-1;
  hr = Fun->Invoke(0, IID_NULL, 0, DISPATCH_METHOD, &dispparams, &vResult, &excepInfo, &nArgErr);
 }
 return S_OK;
 }
 catch(...)
 {
   return E_FAIL;
 }

6)接收回车键退出,且其它控件可以响应回车键
1:重载PreTranslateMessage函数:virtual BOOL PreTranslateMessage(MSG* pMsg);
2: 参考代码如下
BOOL CXXXDlg::PreTranslateMessage(MSG* pMsg)
{
if(pMsg->message == WM_KEYDOWN)
{
if (pMsg->wParam == VK_ESCAPE)//Esc键不退出程序
{
return TRUE;
}
else if(pMsg->wParam == VK_RETURN)
{
return FALSE;//对话框内部控件可以接收到回车消息!!如果你想将回车安全交给前台页面,请用DispatchMessage(pMsg);
//return TRUE;//对话框内部控件不可以接收到回车消息!!
//原因原来在这里,难为我困惑了那么久!!!
}
}

return CDialog::PreTranslateMessage(pMsg);
}

原创粉丝点击