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>