S.l.e!ep.¢%

像打了激速一样,以四倍的速度运转,开心的工作
简单、开放、平等的公司文化;尊重个性、自由与个人价值;
posts - 1098, comments - 335, trackbacks - 0, articles - 1
  C++博客 :: 首页 :: 新随笔 :: 联系 :: 聚合  :: 管理

COM callback notify HTML 的两种方法

Posted on 2009-09-11 14:25 S.l.e!ep.¢% 阅读(679) 评论(0)  编辑 收藏 引用 所属分类: COM

COM callback notify HTML 的两种方法

方法1:
1. Create ATL Project
2. New ATL Object...
3. Simple Object  -> Next
4. Attributes --> choose  'Support ISupportErrorInfo'   == you can throw some exception in COM
    Attributes --> choose 'Support Connection Points'
5. RightClick  VC6 - IDE  Class Name  --> Implement Connection Point
    Interfaces will display '_xxxxEvents'  <--- Choose it , Click OK
6. VC6 IDE Will Create A New Class named 'CProxy_IxxxxxEvents'
7. Add Your Methods

// CSDN.idl : IDL source for CSDN.dll
//

// This file will be processed by the MIDL tool to
// produce the type library (CSDN.tlb) and marshalling code.

import "oaidl.idl";
import "ocidl.idl";
 [
  object,
  uuid(2EE8F461-7200-4C13-A2FC-2552F8773089),
  dual,
  helpstring("IA Interface"),
  pointer_default(unique)
 ]
 interface IA : IDispatch
 {

 [id(1), helpstring("method Init")] HRESULT Init();
 
};

 [
  object,
  uuid(C5C88155-7CAB-4109-9610-234A6AD529DC),
  dual,
  helpstring("IEvent Interface"),
  pointer_default(unique)
 ]

 interface IEvent :  IUnknown
 {
  [id(1), helpstring("method OnDataChanged")] HRESULT OnDataChanged();
 };

[
 uuid(477B6435-238C-43AF-95DA-2F890256DF43),
 version(1.0),
 helpstring("CSDN 1.0 Type Library")
]
library CSDNLib
{
 importlib("stdole32.tlb");
 importlib("stdole2.tlb");

 [
  uuid(1FBB2F1E-E12E-4CE6-88EA-704E1CAE1091),
  helpstring("A Class")
 ]
 coclass A
 {
  [default] interface IA;
  [source]  interface IEvent;
 
};
};//end


------

其中红色代码为手工添加.IEvent UUID由GUIDGEN.EXE生成.IEvent 添加方法OnDataChanged.在客户端调用时添加功能代码,我们将在接口IEvent的Init方法中回调其方法。此处为Connection Point之精粹.在ActiveX中事件的实现方法也以此技术为基础.

3.编译程序后,点击COM对象Implement Connection Point...选项后,选中IEvent。则ATL向导将为我们生成新的实现联接点的COM对象CA。

class ATL_NO_VTABLE CA :
 public CComObjectRootEx<CComSingleThreadModel>,
 public CComCoClass<CA, &CLSID_A>,
 public IDispatchImpl<IA, &IID_IA, &LIBID_CSDNLib>,
 public CProxyIEvent< CA >,
 public IConnectionPointContainerImpl<CA>

4.实现IA接口的Init方法

STDMETHODIMP CA::Init()
{
 AFX_MANAGE_STATE(AfxGetStaticModuleState())

 Fire_OnDataChanged();

 return S_OK;
}

编译程序后,COM服务器的编码写成.

COM客户端实现:

1.实现接收器类

http://study.feloo.com/Event.h/default.htm

class CEvent  : public IEvent
{
public:
// IUnknown
 ULONG __stdcall AddRef();
 ULONG __stdcall Release();
 HRESULT __stdcall QueryInterface(REFIID iid, void** ppv);
//IEvent
public:
 STDMETHOD(OnDataChanged)();
public:
 CEvent():m_cRef(0){}
 virtual ~CEvent(){}
private:
 long m_cRef;
};//end

//Event.cpp

ULONG CEvent::AddRef()

{
 return InterlockedIncrement(&m_cRef);
}

ULONG CEvent::Release()
{
 if (InterlockedDecrement(&m_cRef) != 0)
  return m_cRef;
 delete this;
 return 0;
}

HRESULT CEvent::QueryInterface(REFIID riid, void** ppv)
{
 if (riid == IID_IUnknown)
 {
  *ppv = (IUnknown*)this;
 }
 else if (riid == IID_IEvent)
 {
  *ppv = (IEvent*)this;
 }
 else
 {
  *ppv = NULL;
  return E_NOINTERFACE;
 }
 AddRef();
 return S_OK;
}

STDMETHODIMP CEvent::OnDataChanged()
{
 AfxMessageBox(_T("OnDataChanged!"));
 return S_OK;
}

//end

在CEvent::OnDataChanged()中添加的实现将由接口IA的Init方法回调。

2.客户端实现代码

void CCSDNClientDlg::OnButton1()

{

 CoInitialize(NULL);

 IA* pIA = NULL;
 CEvent* pEvent = NULL;
 HRESULT hr = CoCreateInstance(CLSID_A,NULL,
         CLSCTX_INPROC_SERVER,
         IID_IA,
         (void**)&pIA);


 if ( FAILED(hr) )
 {
  AfxMessageBox("Initalize com failed!");
  return ;
 } // if     

 IConnectionPointContainer* pConnectionPointContainer = NULL;   
 IConnectionPoint* pConnectionPoint = NULL ;


 pEvent= new CEvent();
 pEvent->AddRef();

 DWORD dwCookie;

    hr = pIA->QueryInterface(IID_IConnectionPointContainer, (void**)   &pConnectionPointContainer);    
    //IRecord->Release();
 ASSERT(SUCCEEDED(hr));

  hr = pConnectionPointContainer->FindConnectionPoint(IID_IEvent, &pConnectionPoint);

 ASSERT(SUCCEEDED(hr));
 ConnectionPoint->Advise((IUnknown*)pEvent, &dwCookie);    
 pConnectionPoint->Release();    


  hr = pIA->Init();//此处将激发事件OnDataChanged()

 ASSERT(SUCCEEDED(hr));

      hr = pConnectionPointContainer->FindConnectionPoint(IID_IEvent, &pConnectionPoint); 

 ASSERT(SUCCEEDED(hr));
 pConnectionPoint->Unadvise(dwCookie);    
pConnectionPoint->Release();

 pConnectionPointContainer->Release();
 pIA->Release();

 pEvent->Release();
// delete pEvent;

 CoUninitialize();
}

//end

文笔不好,见谅了!

完成 com  之后,就可以在 html 加上

<SCRIPT ID=EventHandler FOR="xxxx" EVENT="OnTest(a, b)">
alert(a);  
</SCRIPT>
来响应了, 但这种方法只适用于

<OBJECT style="display:none;" TYPE="application/x-oleobject" classid=clsid:91443C71-B7DD-49F5-9F86-A2D305CED76A CODEBASE="abc.cab#Version=1,0,0,001"></OBJECT>  

如果在 HTML 是通过 new ActiveXObject 来创建 COM 的, 那么则需要用第二种方法
---------------------

方法二:

在htm中接受com控件发出的事件(VC/MFC ATL/ActiveX/COM


我为客户做了一个com控件有一些事件(如OnStateChange)发出,客户要求用htm调用。
一开始,一切正常htm中调用代码如下:

<OBJECT ID="DvdPlayCtl" CLASSID="CLSID:EE9626A3-976C-470C-8282-07AB2FE2F85F"></OBJECT>

<SCRIPT language="JavaScript">

DvdPlayCtl.
attachEvent("OnStateChange", MyOnStateChange);

function MyOnStateChange(state,info)
{
alert("state change to "+state+" ,"+info);
}
</script>
则一旦com的状态发生改变就发出OnStateChange事件,htm就可以正常接受并提示,但后来客户要求用
另一种方式声明com控件,代码如下:

<SCRIPT language="JavaScript">
var DvdPlayCtl = new ActiveXObject("DvdPlayCtl.DvdPlayCtl");
</script>
即动态生成此com控件,则运行htm时以前的代码DvdPlayCtl.attachEvent部分出错:“对象不支持此操作”


1、在com中增加一个属性OnStateChange,其类型为IDispatch *并为其添加put方法。

2、在put方法的实现中将传进的DISPATCH型指针赋给自己的成员变量IDispatch *m_pDispatch。

STDMETHODIMP CDvdPlayCtl::put_OnStateChange(IDispatch *newVal)
{
// TODO: Add your implementation code here
m_pDispatch = newVal;
return S_OK;
}

3、定义成员函数void Send_Event(int state, TCHAR * info);在发送事件的函数中添加以下代码:
if (m_pDispatch != NULL)
{
CComVariant* pvars = new CComVariant[2];

pvars[1] = state;//回调函数的第一个参数
pvars[0] = info;//回调函数的第二个参数

DISPPARAMS disp = { pvars, NULL, 2, 0 };
HRESULT hr = m_pDispatch->Invoke(0, IID_NULL, LOCALE_USER_DEFAULT, DISPATCH_METHOD, &disp, NULL, NULL, NULL);
delete[] pvars;
}
注意:
1、pvars的填充与函数参数顺序是相反的
4、在htm中如下调用
<script language="JavaScript">

var DvdPlayCtl = new ActiveXObject("DvdPlayCtl.DvdPlayCtl");

DvdPlayCtl.OnStateChange = OnStateChange;
DvdPlayCtl.OnError = OnError;

function OnStateChange(state,info)
{
alert("state change to "+state+" ,"+info);
}
</script>



 


只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理