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>
?