将COM事件暴露给VBScript(ATL) [英] Exposing COM events to VBScript (ATL)

查看:224
本文介绍了将COM事件暴露给VBScript(ATL)的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我已经使用ATL简单对象向导通过ATL在C ++中构建了一个COM服务器DLL。我遵循微软的 ATLDLLCOMServer 示例。一切都很好,除了一件事:我没有在VBScript 中收到COM事件。我收到C#中的事件。我在早期基于MFC的实施中将VBScript的事件用作ActiveX控件。



我的控件定义如下:

  class ATL_NO_VTABLE CSetACLCOMServer:
public CComObjectRootEx< CComSingleThreadModel>
public CComCoClass< CSetACLCOMServer& CLSID_SetACLCOMServer>
public IConnectionPointContainerImpl< CSetACLCOMServer> ,
public CProxy_ISetACLCOMServerEvents< CSetACLCOMServer>
public IDispatchImpl< ISetACLCOMServer& IID_ISetACLCOMServer& LIBID_SetACLCOMLibrary / * wMajor = * / 1,/ * wMinor = * / 0>
public IProvideClassInfo2Impl& CLSID_SetACLCOMServer,& DIID__ISetACLCOMServerEvents& LIBID_SetACLCOMLibrary> / * VBS中的事件支持* /
{
public:

BEGIN_COM_MAP(CSetACLCOMServer)
COM_INTERFACE_ENTRY(ISetACLCOMServer)
COM_INTERFACE_ENTRY(IDispatch)
COM_INTERFACE_ENTRY(IConnectionPointContainer)
COM_INTERFACE_ENTRY(IProvideClassInfo)
COM_INTERFACE_ENTRY(IProvideClassInfo2)
END_COM_MAP()

BEGIN_CONNECTION_POINT_MAP(CSetACLCOMServer)
CONNECTION_POINT_ENTRY(__ uuidof _ISetACLCOMServerEvents))
END_CONNECTION_POINT_MAP()

[...]

在VBScript中,我使用COM对象:

  set objSetACL = WScript.CreateObject(SetACL.SetACL, SetACL_)

'从SetACL COM服务器捕获并打印作为事件传递(触发)的消息。
'此函数的名称后缀(MessageEvent)必须与SetACL定义的事件名称
'相同。
'前缀(SetACL_)可以在调用WScript.CreateObject
时自由设置。SetCallateEvent(Message)
WScript.Echo消息
end sub

IDL的相关部分如下所示:

  [
对象,
uuid(E1B57CA5-FD7F-4304-BC0A-8BEBDE231D53),
双,
不可扩展,
pointer_default(唯一)
helpstring(SetACL COM Interface)
]
接口ISetACLCOMServer:IDispatch
{
};

[
uuid(00D4DCD3-02B9-4A71-AB61-2283504620C8),
版本(1.0),
helpstring(SetACL类型库)
]
库SetACLCOMLibrary
{
importlib(stdole2.tlb);

[
uuid(35F76182-7F52-4D6A-BD6E-1317345F98FB),
helpstring(SetACL Event Interface)
]
dispinterface _ISetACLCOMServerEvents
{
属性:
方法:
[id(1),helpstring(接收将在命令行版本的屏幕上显示的字符串消息)
void MessageEvent([in] BSTR message);
};

[
uuid(13379563-8F21-4579-8AC7-CBCD488735DB),
helpstring(SetACL COM Server),
]
coclass SetACLCOMServer
{
[default] interface ISetACLCOMServer;
[default,source] dispinterface _ISetACLCOMServerEvents;
};
};

问题:SetACL_MessageEvent从未被调用。



我尝试过:




  • 阅读这个知识库文章我添加了IProvideClassInfo2的实现,但没有帮助。

  • 常见问题解答提示出站接口不应定义为双接口。我从我的界面定义中删除了dual,但没有帮助。

  • 我发现这个SO问题似乎描述了一个类似的问题。没有给出答案,只是一个解决方法。



更新1:



我现在知道它在哪里失败,但不是为什么:在_ISetACLCOMServerEvents_CP.h中的fire event方法中,调用Invoke失败,并带有E_UNEXPECTED。这是行:

  hr = pConnection-> Invoke(1,IID_NULL,LOCALE_USER_DEFAULT,DISPATCH_METHOD,& params,NULL ,NULL,NULL); 

解决方案:



问题是我从COM对象的后台线程中调用了事件触发函数,换句话说是从错误的公寓调用。

解决方案

实现 IProvideClassInfo2 应该是足够的(虽然我的印象是它应该可以工作,即使没有它)。我从模板创建了一个简约的ATL项目作为样本。这是ATL DLL + COM类+一个方法和事件+ IProvideClassInfo2



源代码[ SVN 浏览器友好] + Win32二进制



测试 .vbs 是以下:

 功能Test_Event(文本)
WScript.Echo事件:+文本
结束函数

Dim Test
Set Test = WScript.CreateObject(AlaxInfo.VbsEvents.Foo,Test_)
Test.Method(Event Fun)

方法本身是:

 code> // IFoo 
STDMETHOD(方法)(BSTR sText)throw()
{
ATLTRACE(atlTraceCOM,4,_T(sText \%s\ \\\
),CString(sText));
ATLVERIFY(SUCCEEDED(Fire_Event(sText)));
返回S_OK;
}

执行此操作时,输出为:





如果您从后台线程中触发,我以为您可能在示例项目中遇到事件问题。您还可以使用调试器进行项目,并检查 Fire _ 呼叫中是否有任何错误。


I have built a COM server DLL in C++ with ATL by using the "ATL simple object" wizard. I followed Microsoft's ATLDLLCOMServer example. Everything works well except for one thing: I do not receive COM events in VBScript. I do receive the events in C#. I had events working in VBScript in an earlier MFC-based implementation as ActiveX control.

My control is defined like this:

class ATL_NO_VTABLE CSetACLCOMServer :
    public CComObjectRootEx<CComSingleThreadModel>,
    public CComCoClass<CSetACLCOMServer, &CLSID_SetACLCOMServer>,
    public IConnectionPointContainerImpl<CSetACLCOMServer>,
    public CProxy_ISetACLCOMServerEvents<CSetACLCOMServer>,
    public IDispatchImpl<ISetACLCOMServer, &IID_ISetACLCOMServer, &LIBID_SetACLCOMLibrary, /*wMajor =*/ 1, /*wMinor =*/ 0>,
    public IProvideClassInfo2Impl<&CLSID_SetACLCOMServer, &DIID__ISetACLCOMServerEvents, &LIBID_SetACLCOMLibrary>   /* Required for event support in VBS */
{
public:

BEGIN_COM_MAP(CSetACLCOMServer)
    COM_INTERFACE_ENTRY(ISetACLCOMServer)
    COM_INTERFACE_ENTRY(IDispatch)
    COM_INTERFACE_ENTRY(IConnectionPointContainer)
   COM_INTERFACE_ENTRY(IProvideClassInfo)
   COM_INTERFACE_ENTRY(IProvideClassInfo2)
END_COM_MAP()

BEGIN_CONNECTION_POINT_MAP(CSetACLCOMServer)
    CONNECTION_POINT_ENTRY(__uuidof(_ISetACLCOMServerEvents))
END_CONNECTION_POINT_MAP()

[...]

In VBScript I use the COM object like this:

set objSetACL = WScript.CreateObject("SetACL.SetACL", "SetACL_")

' Catch and print messages from the SetACL COM server which are passed (fired) as events.
' The name postfix of this function (MessageEvent) must be identical to the event name 
' as defined by SetACL.
' The prefix (SetACL_) can be set freely in the call to WScript.CreateObject
sub SetACL_MessageEvent (Message)
    WScript.Echo Message
end sub

The relevant parts of the IDL look like this:

[
    object,
    uuid(E1B57CA5-FD7F-4304-BC0A-8BEBDE231D53),
    dual,
    nonextensible,
    pointer_default(unique),
   helpstring("SetACL COM Interface")
]
interface ISetACLCOMServer : IDispatch
{
};

[
    uuid(00D4DCD3-02B9-4A71-AB61-2283504620C8),
    version(1.0),
   helpstring ("SetACL Type Library")
]
library SetACLCOMLibrary
{
    importlib("stdole2.tlb");

   [
        uuid(35F76182-7F52-4D6A-BD6E-1317345F98FB),
      helpstring ("SetACL Event Interface")
    ]
    dispinterface _ISetACLCOMServerEvents
    {
        properties:
        methods:
         [id(1), helpstring("Receives string messages that would appear on the screen in the command line version")]
         void MessageEvent([in] BSTR message);
   };

   [
        uuid(13379563-8F21-4579-8AC7-CBCD488735DB),
      helpstring ("SetACL COM Server"),
    ]
    coclass SetACLCOMServer
    {
        [default] interface ISetACLCOMServer;
        [default, source] dispinterface _ISetACLCOMServerEvents;
    };
};

The problem: SetACL_MessageEvent never gets called.

What I have tried:

  • After reading this KB article I added the implementation of IProvideClassInfo2, but that did not help.
  • This FAQ mentions that outgoing interface should not defined as a dual interfaces. I removed the "dual" from my interface definition, but that did not help.
  • I found this SO question that seems to describe a similar problem. An answer was never given, only a workaround.

UPDATE 1:

I know now where it is failing, but not why: in the fire event method in _ISetACLCOMServerEvents_CP.h the call to Invoke fails with E_UNEXPECTED. This is the line:

hr = pConnection->Invoke(1, IID_NULL, LOCALE_USER_DEFAULT, DISPATCH_METHOD, &params, NULL, NULL, NULL);

Solution:

The problem was that I called the event firing function from a background thread in the COM object, in other words from the wrong apartment.

解决方案

Implementation of IProvideClassInfo2 should be sufficient (though I was under impression that it should work even without it). I created a minimalistic ATL project from template as a sample. This is ATL DLL + COM Class + a method and an event + IProvideClassInfo2:

Source code [SVN, Browser Friendly] + Win32 Binary.

Test .vbs is the following:

Function Test_Event(Text)
  WScript.Echo "Event: " + Text
End Function

Dim Test
Set Test = WScript.CreateObject("AlaxInfo.VbsEvents.Foo", "Test_")
Test.Method("Event Fun")

And the method itself is:

// IFoo
    STDMETHOD(Method)(BSTR sText) throw()
    {
        ATLTRACE(atlTraceCOM, 4, _T("sText \"%s\"\n"), CString(sText));
        ATLVERIFY(SUCCEEDED(Fire_Event(sText)));
        return S_OK;
    }

And having executed this, the output is:

I thought you might be having issues with events in your sample project if you are firing them from a background thread. You can also step your project with debugger and check if you have any errors in the Fire_ call.

这篇关于将COM事件暴露给VBScript(ATL)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆