2012-02-07 72 views
0

我前段時間創建了一個ATL COM服務器組件(exe)。它暴露了一些正常的COM API(派生自IDispatch),並且還觸發了一些COM事件。事件機制是使用ATL IConnectionPointContainer實現的。這個COM服務器最初由一個簡單的C#應用​​程序使用,該應用程序直接向COM服務器添加一個引用。所有的API和事件都可以在C#應用程序中正常工作。javascript的ATL COM事件

然後來到COM服務器的要求能夠在網頁(IE)中與JavaScript一起使用。因此,我將IProvideClassInfo2,IObjectSafety實現添加到原始COM類。但是,COM事件從未奏效。請參考下面的IDL,COM類頭文件和事件觸發代碼。

IDL:

import "oaidl.idl"; 
import "ocidl.idl"; 

[ 
    object, 
    // uuid replaced with dummy 
    uuid(00000000-0000-0000-0000-000000000000), 
    dual, 
    nonextensible, 
    helpstring("ICtrl Interface"), 
    pointer_default(unique) 
] 
interface ICtrl : IDispatch{ 
    [id(1), helpstring("method CtrlMethod1")] 
    HRESULT CtrlMethod1(void); 
    [id(2), helpstring("method CtrlMethod2")] 
    HRESULT CtrlMethod2([in] ULONG Reason); 
}; 


[ 
    // uuid replaced with dummy 
    uuid(00000000-0000-0000-0000-000000000001), 
    version(1.0), 
] 
library MyControlLib 
{ 
    importlib("stdole32.tlb"); 
    importlib("stdole2.tlb"); 
    [ 
     // uuid replaced with dummy 
     uuid(00000000-0000-0000-0000-000000000002) 
    ] 
    dispinterface _ICtrlEvents 
    { 
     properties: 
     methods: 
      [id(1), helpstring("method OnCtrlEvent1")] 
      HRESULT OnCtrlEvent1([in] LONG ErrorCode); 
      [id(2), helpstring("method OnCtrlEvent2")] 
      HRESULT OnCtrlEvent2([in] LONG ErrorCode); 
    }; 


    [ 
     // uuid replaced with dummy 
     uuid(00000000-0000-0000-0000-000000000003)  
    ] 
    coclass Ctrl 
    { 
     [default] interface ICtrl; 
     [default, source] dispinterface _ICtrlEvents; 
    }; 
}; 

COM類的頭:

// CCtrl 

class ATL_NO_VTABLE CCtrl : 
    public CComObjectRootEx<CComSingleThreadModel>, 
    public CComCoClass<CCtrl, &CLSID_Ctrl>, 
    public IConnectionPointContainerImpl<CCtrl>, 
    public CProxy_ICtrlEvents<CCtrl>, 
    public IDispatchImpl<ICtrl, &IID_ICtrl, &LIBID_MyControlLib, /*wMajor =*/ 1, /*wMinor =*/ 0>, 
    public IDispatchImpl<_ICtrlEvents, &__uuidof(_ICtrlEvents), &LIBID_MyControlLib, /* wMajor = */ 1, /* wMinor = */ 0>, 
    public IObjectSafetyImpl<CCtrl, INTERFACESAFE_FOR_UNTRUSTED_CALLER>, 
    public IProvideClassInfo2Impl<&CLSID_Ctrl, NULL, &LIBID_MyControlLib> 
{ 
public: 
    DECLARE_CLASSFACTORY_SINGLETON(CCtrl) 
    CCtrl(); 


    DECLARE_REGISTRY_RESOURCEID(IDR_CTRL) 


    BEGIN_COM_MAP(CCtrl) 
     COM_INTERFACE_ENTRY(ICtrl) 
     COM_INTERFACE_ENTRY2(IDispatch, ICtrl) 
     COM_INTERFACE_ENTRY2(IDispatch, _ICtrlEvents) 
     COM_INTERFACE_ENTRY(IConnectionPointContainer) 
     COM_INTERFACE_ENTRY(_ICtrlEvents) 
     COM_INTERFACE_ENTRY(IObjectSafety) 
     COM_INTERFACE_ENTRY(IProvideClassInfo) 
     COM_INTERFACE_ENTRY(IProvideClassInfo2) 
    END_COM_MAP() 

    BEGIN_CONNECTION_POINT_MAP(CCtrl) 
     CONNECTION_POINT_ENTRY(__uuidof(_ICtrlEvents)) 
    END_CONNECTION_POINT_MAP() 


    DECLARE_PROTECT_FINAL_CONSTRUCT() 

    HRESULT FinalConstruct(); 
    void FinalRelease(); 

public: 

    STDMETHOD(CtrlMethod1)(void); 
    STDMETHOD(CtrlMethod2)(ULONG Reason); 

}; 

OBJECT_ENTRY_AUTO(__uuidof(Ctrl), CCtrl) 

ATL生成的事件觸發代碼:

#pragma once 

template<class T> 
class CProxy_ICtrlEvents : 
    public ATL::IConnectionPointImpl<T, &__uuidof(_ICtrlEvents)> 
{ 
public: 

    HRESULT OnCtrlEvent1(LONG ErrorCode) 
    { 
     HRESULT hr = S_OK; 
     T * pThis = static_cast<T *>(this); 
     int cConnections = m_vec.GetSize(); 

     for (int iConnection = 0; iConnection < cConnections; iConnection++) 
     { 
      pThis->Lock(); 
      CComPtr<IUnknown> punkConnection = m_vec.GetAt(iConnection); 
      pThis->Unlock(); 

      IDispatch * pConnection = static_cast<IDispatch *>(punkConnection.p); 

      if (pConnection) 
      { 
       CComVariant avarParams[1]; 
       avarParams[0] = ErrorCode; 
       avarParams[0].vt = VT_I4; 
       CComVariant varResult; 

       DISPPARAMS params = { avarParams, NULL, 1, 0 }; 
       hr = pConnection->Invoke(1, IID_NULL, LOCALE_USER_DEFAULT, DISPATCH_METHOD, &params, &varResult, NULL, NULL); 
      } 
     } 
     return hr; 
    } 
    HRESULT Fire_OnCtrlEvent2(LONG ErrorCode) 
    { 
     HRESULT hr = S_OK; 
     T * pThis = static_cast<T *>(this); 
     int cConnections = m_vec.GetSize(); 

     for (int iConnection = 0; iConnection < cConnections; iConnection++) 
     { 
      pThis->Lock(); 
      CComPtr<IUnknown> punkConnection = m_vec.GetAt(iConnection); 
      pThis->Unlock(); 

      IDispatch * pConnection = static_cast<IDispatch *>(punkConnection.p); 

      if (pConnection) 
      { 
       CComVariant avarParams[1]; 
       avarParams[0] = ErrorCode; 
       avarParams[0].vt = VT_I4; 
       CComVariant varResult; 

       DISPPARAMS params = { avarParams, NULL, 1, 0 }; 
       hr = pConnection->Invoke(2, IID_NULL, LOCALE_USER_DEFAULT, DISPATCH_METHOD, &params, &varResult, NULL, NULL); 
      } 
     } 
     return hr; 
    } 
}; 

在JavaScript代碼,是使用

創建COM對象
var CtrlObj = new ActiveXObject('ProgID_of_Ctrl') 

'ProgID_of_Ctrl'映射到__uuidof(Ctrl)。在IE調試器中,創建的對象是ICtrl類型的。 COM API是可見的,但COM事件不是。任何嘗試使用CtrlObj.attachEvent()都會導致javascript錯誤。我希望CtrlObj應該是coclass(Ctrl)類型,就像C#應用程序的情況一樣。 COM_MAP部分中是否有錯誤?任何意見和幫助表示讚賞。

-CodeFarmer

回答

1

從我讀過的,你應該使用OBJECT標籤和標籤SCRIPT for掛鉤在HTML ATL/COM事件。事情是這樣的:

<object 
    id="myCtrlObj" 
    classid="CLSID:00000000-0000-0000-0000-000000000003" 
    height="32" 
    width="32"/> 

<script language="javascript" 
    id="myCtrlHandler1" 
    event="OnCtrlEvent1()" 
    for="myCtrlObj"> 
    alert("OnCtrlEvent1 fired"); 
</script> 

<script language="javascript" 
    id="myCtrlHandler2" 
    event="OnCtrlEvent2(reason)" 
    for="myCtrlObj"> 
    alert("OnCtrlEvent2 fired with parameter: " + reason.toString()); 
</script> 

因爲你使用JScript中,我有時喜歡欺騙,使一個IDispatch VARIANT財產模擬事件的行爲。在下面的JScript代碼片段音符如何OnCtrlEvent1和OnCtrlEvent2被分配給功能:

function tst() 
{ 
    var ctrl = new ActiveXObject("MyControl.Ctrl"); 
    ctrl.OnCtrlEvent1 = myevent1; 
    ctrl.OnCtrlEvent2 = myevent2; 
    ctrl.CtrlMethod1(); 
    ctrl.CtrlMethod2(); 
} 

function myevent1() 
{ 
    alert("Event1"); 
} 

function myevent2(reason) 
{ 
    alert("Event2 " + reason.toString()); 
} 

的詭計被處理它作爲IDL的屬性來完成。那些JScript函數作爲包含可調用IDispatch接口的VARIANT傳遞給我們。這裏是我的MyControl.idl:

import "oaidl.idl"; 
import "ocidl.idl"; 

[ 
    object, 
    // uuid replaced with dummy 
    uuid(00000000-0000-0000-0000-000000000000) 
    dual, 
    nonextensible, 
    helpstring("ICtrl Interface"), 
    pointer_default(unique) 
] 
interface ICtrl : IDispatch{ 
    [id(1), helpstring("method CtrlMethod1")] HRESULT CtrlMethod1(void); 
    [id(2), helpstring("method CtrlMethod2")] HRESULT CtrlMethod2(void); 
    [propget, id(3), helpstring("property OnCtrlEvent1")] HRESULT OnCtrlEvent1([out, retval] VARIANT* pVal); 
    [propput, id(3), helpstring("property OnCtrlEvent1")] HRESULT OnCtrlEvent1([in] VARIANT newVal); 
    [propget, id(4), helpstring("property OnCtrlEvent2")] HRESULT OnCtrlEvent2([out, retval] VARIANT* pVal); 
    [propput, id(4), helpstring("property OnCtrlEvent2")] HRESULT OnCtrlEvent2([in] VARIANT newVal); 
}; 
[ 
    // uuid replaced with dummy 
    uuid(00000000-0000-0000-0000-000000000001), 
    version(1.0), 
    helpstring("MyControl 1.0 Type Library") 
] 
library MyControlLib 
{ 
    importlib("stdole2.tlb"); 
    [ 
     // uuid replaced with dummy 
     uuid(00000000-0000-0000-0000-000000000003) 
     helpstring("Ctrl Class") 
    ] 
    coclass Ctrl 
    { 
     [default] interface ICtrl; 
    }; 
}; 

這裏是我的Ctrl.h在那裏你看到的JScript函數將被保存在VARIANT成員:

class ATL_NO_VTABLE CCtrl : 
    public CComObjectRootEx<CComSingleThreadModel>, 
    public CComCoClass<CCtrl, &CLSID_Ctrl>, 
    public IDispatchImpl<ICtrl, &IID_ICtrl, &LIBID_MyControlLib, /*wMajor =*/ 1, /*wMinor =*/ 0> 
{ 
public: 
DECLARE_REGISTRY_RESOURCEID(IDR_CTRL) 

BEGIN_COM_MAP(CCtrl) 
    COM_INTERFACE_ENTRY(ICtrl) 
    COM_INTERFACE_ENTRY(IDispatch) 
END_COM_MAP() 

DECLARE_PROTECT_FINAL_CONSTRUCT() 

    HRESULT FinalConstruct() 
    { 
     return S_OK; 
    } 

    void FinalRelease() 
    { 
    } 

public: 
    STDMETHOD(CtrlMethod1)(void); 
    STDMETHOD(CtrlMethod2)(void); 
    STDMETHOD(get_OnCtrlEvent1)(VARIANT* pVal); 
    STDMETHOD(put_OnCtrlEvent1)(VARIANT newVal); 
    STDMETHOD(get_OnCtrlEvent2)(VARIANT* pVal); 
    STDMETHOD(put_OnCtrlEvent2)(VARIANT newVal); 

private: 
    CComVariant m_ctrlEvent1; 
    CComVariant m_ctrlEvent2; 

    STDMETHOD(Invoke_CtrlEvent1)(); 
    STDMETHOD(Invoke_CtrlEvent2)(LONG nReason); 
}; 

OBJECT_ENTRY_AUTO(__uuidof(Ctrl), CCtrl) 

在Ctrl.cpp掛羊頭賣狗肉是我們要尋找的JScript的功能的IDispatch接口在這些VARIANTs和我們的「知識」的參數,我們援引每個事件與正確的參數:

#include "stdafx.h" 
#include "Ctrl.h" 

STDMETHODIMP CCtrl::CtrlMethod1(void) 
{ 
    Invoke_CtrlEvent1(); 
    return S_OK; 
} 

STDMETHODIMP CCtrl::CtrlMethod2(void) 
{ 
    Invoke_CtrlEvent2(12345); 
    return S_OK; 
} 

STDMETHODIMP CCtrl::get_OnCtrlEvent1(VARIANT* pVal) 
{ 
    VariantInit(pVal); 
    return VariantCopy(pVal, &m_ctrlEvent1); 
    return S_OK; 
} 

STDMETHODIMP CCtrl::put_OnCtrlEvent1(VARIANT newVal) 
{ 
    m_ctrlEvent1 = newVal; 
    return S_OK; 
} 

STDMETHODIMP CCtrl::get_OnCtrlEvent2(VARIANT* pVal) 
{ 
    VariantInit(pVal); 
    return VariantCopy(pVal, &m_ctrlEvent2); 
} 

STDMETHODIMP CCtrl::put_OnCtrlEvent2(VARIANT newVal) 
{ 
    m_ctrlEvent2 = newVal; 
    return S_OK; 
} 

STDMETHODIMP CCtrl::Invoke_CtrlEvent1() 
{ 
    if (m_ctrlEvent1.vt != VT_DISPATCH) 
    { 
     return S_OK; 
    } 
    DISPPARAMS DispParams = { 0, 0, 0, 0 }; 
    VARIANT Var = { 0 }; 
    return V_DISPATCH(&m_ctrlEvent1)->Invoke((DISPID) 0, IID_NULL, 0, DISPATCH_METHOD, &DispParams, &Var, NULL, NULL); 
} 

STDMETHODIMP CCtrl::Invoke_CtrlEvent2(LONG nReason) 
{ 
    if (m_ctrlEvent1.vt != VT_DISPATCH) 
    { 
     return S_OK; 
    } 
    VARIANTARG Arg = {0}; 
    Arg.vt = VT_I4; 
    Arg.lVal = nReason; 
    DISPPARAMS DispParams = { &Arg, 0, 1, 0 }; 
    VARIANT Var = { 0 }; 
    return V_DISPATCH(&m_ctrlEvent2)->Invoke((DISPID) 0, IID_NULL, 0, DISPATCH_METHOD, &DispParams, &Var, NULL, NULL); 
} 
+0

謝謝。你的解決方案有效我並不認爲它是一個騙子。主界面不是提供單獨的事件界面,而是爲JavaScript提供了一種註冊回調的方法。 – CodeFarmer 2012-02-07 05:06:20