2012-02-07 5 views
0

Я создал серверный компонент ATL COM (exe) некоторое время назад. Он раскрыл несколько нормальных COM-интерфейсов (полученных из IDispatch), а также выпустил несколько COM-событий. Механизм событий был реализован с использованием ATL IConnectionPointContainer. Этот COM-сервер изначально использовался простым приложением C#, которое напрямую добавляло ссылку на COM-сервер. Все, API и события отлично работают в приложении C#.ATL COM-события для javascript

Затем появилось требование, чтобы сервер COM мог использовать с javascript на веб-странице (IE). Поэтому я добавил реализацию 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) 

АТЛ-генерироваться событие стрельбы Код:

#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; 
    } 
}; 

В яваскрипта коде, СОМ-объект создается с помощью

var CtrlObj = new ActiveXObject('ProgID_of_Ctrl') 

'ProgID_of_Ctrl' сопоставляется с __uuidof (Ctrl). В отладчике IE созданный объект имеет тип ICtrl. COM-API видны, но COM-события не являются. Любая попытка использования CtrlObj.attachEvent() приведет к ошибке javascript. Я бы ожидал, что CtrlObj должен иметь тип coclass (Ctrl), как это было в случае с C# app. Есть ли какие-либо ошибки в разделе COM_MAP? Любые комментарии и помощь приветствуются.

-CodeFarmer

ответ

1

Из того, что я читал, вы должны использовать OBJECT тег и SCRIPT for тег для подключения ATL/COM событий в HTML. Что-то вроде этого:

<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 ВАРИАНТ имитировать поведение события. В следующем фрагменте кода 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 передаются нам как VARIANT, содержащие invokable интерфейсы IDispatch. Вот мой 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 в этих VARIANT и с нашими «знаниями» параметров, мы вызываем каждое событие с правильными параметрами:

#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

Смежные вопросы