<p>Suppose I have a COM object which users can access via a call such as:</p>

<pre><code>Set s = CreateObject("Server")
</code></pre>

<p>What I'd like to be able to do is allow the user to specify an event handler for the object, like so:</p>

<pre><code>Function ServerEvent

   MsgBox "Event handled"

End Function

s.OnDoSomething = ServerEvent
</code></pre>

<p>Is this possible and, if so, how do I expose this in my type library in C++ (specifically BCB 2007)?</p>

[{'answer_id': 61723, 'author': '1800 INFORMATION', 'author_id': 3146, 'author_profile': 'https://Stackoverflow.com/users/3146', 'pm_score': 2, 'selected': False, 'text': '<p>I\'m a little hazy on the details, but maybe the link below might help:</p>\n\n<p><a href="http://msdn.microsoft.com/en-us/library/ms974564.aspx" rel="nofollow noreferrer">http://msdn.microsoft.com/en-us/library/ms974564.aspx</a></p>\n\n<p>It looks like your server object needs to implement <code>IProvideClassInfo</code> and then you call <code>ConnectObject</code> in your VBScript code. See also:</p>\n\n<p><a href="http://blogs.msdn.com/ericlippert/archive/2005/02/15/373330.aspx" rel="nofollow noreferrer">http://blogs.msdn.com/ericlippert/archive/2005/02/15/373330.aspx</a></p>\n'}, {'answer_id': 61762, 'author': 'Jeff Hillman', 'author_id': 3950, 'author_profile': 'https://Stackoverflow.com/users/3950', 'pm_score': 3, 'selected': False, 'text': '<p>This is how I did it just recently.  Add an interface that implements IDispatch and a coclass for that interface to your IDL:</p>\n\n<pre><code>[\n    object,\n    uuid(6EDA5438-0915-4183-841D-D3F0AEDFA466),\n    nonextensible,\n    oleautomation,\n    pointer_default(unique)\n]\ninterface IServerEvents : IDispatch\n{\n    [id(1)]\n    HRESULT OnServerEvent();\n}\n\n//...\n\n[\n    uuid(FA8F24B3-1751-4D44-8258-D649B6529494),\n]\ncoclass ServerEvents\n{\n    [default] interface IServerEvents;\n    [default, source] dispinterface IServerEvents;\n};\n</code></pre>\n\n<p>This is the declaration of the CServerEvents class:</p>\n\n<pre><code>class ATL_NO_VTABLE CServerEvents :\n    public CComObjectRootEx&lt;CComSingleThreadModel&gt;,\n    public CComCoClass&lt;CServerEvents, &amp;CLSID_ServerEvents&gt;,\n    public IDispatchImpl&lt;IServerEvents, &amp;IID_IServerEvents , &amp;LIBID_YourLibrary, -1, -1&gt;,\n    public IConnectionPointContainerImpl&lt;CServerEvents&gt;,\n    public IConnectionPointImpl&lt;CServerEvents,&amp;__uuidof(IServerEvents)&gt;\n{\npublic:\n    CServerEvents()\n    {\n    }\n\n    // ...\n\nBEGIN_COM_MAP(CServerEvents)\n    COM_INTERFACE_ENTRY(IServerEvents)\n    COM_INTERFACE_ENTRY(IDispatch)\n    COM_INTERFACE_ENTRY(IConnectionPointContainer)\nEND_COM_MAP()\n\nBEGIN_CONNECTION_POINT_MAP(CServerEvents)\n    CONNECTION_POINT_ENTRY(__uuidof(IServerEvents))\nEND_CONNECTION_POINT_MAP()\n\n    // ..\n\n    // IServerEvents\n    STDMETHOD(OnServerEvent)();\n\nprivate:\n    CRITICAL_SECTION m_csLock;        \n};\n</code></pre>\n\n<p>The key here is the implementation of the IConnectionPointImpl and IConnectionPointContainerImpl interfaces and the connection point map.  The definition of the OnServerEvent method looks like this:</p>\n\n<pre><code>STDMETHODIMP CServerEvents::OnServerEvent()\n{\n    ::EnterCriticalSection( &amp;m_csLock );\n\n    IUnknown* pUnknown;\n\n    for ( unsigned i = 0; ( pUnknown = m_vec.GetAt( i ) ) != NULL; ++i )\n    {       \n        CComPtr&lt;IDispatch&gt; spDisp;\n        pUnknown-&gt;QueryInterface( &amp;spDisp );\n\n        if ( spDisp )\n        {\n            spDisp.Invoke0( CComBSTR( L"OnServerEvent" ) );\n        }\n    }\n\n    ::LeaveCriticalSection( &amp;m_csLock );\n\n    return S_OK;\n}\n</code></pre>\n\n<p>You need to provide a way for your client to specify their handler for your events.  You can do this with a dedicated method like "SetHandler" or something, but I prefer to make the handler an argument to the method that is called asynchronously.  This way, the user only has to call one method:</p>\n\n<pre><code>STDMETHOD(DoSomethingAsynchronous)( IServerEvents *pCallback );\n</code></pre>\n\n<p>Store the pointer to the IServerEvents, and then when you want to fire your event, just call the method:</p>\n\n<pre><code>m_pCallback-&gt;OnServerEvent();\n</code></pre>\n\n<p>As for the VB code, the syntax for dealing with events is a little different than what you suggested:</p>\n\n<pre><code>Private m_server As Server\nPrivate WithEvents m_serverEvents As ServerEvents\n\nPrivate Sub MainMethod()\n    Set s = CreateObject("Server")\n    Set m_serverEvents = New ServerEvents\n\n    Call m_searchService.DoSomethingAsynchronous(m_serverEvents)\nEnd Sub\n\nPrivate Sub m_serverEvents_OnServerEvent()\n    MsgBox "Event handled"\nEnd Sub\n</code></pre>\n\n<p>I hope this helps.</p>\n'}, {'answer_id': 67506, 'author': 'Carl', 'author_id': 5449, 'author_profile': 'https://Stackoverflow.com/users/5449', 'pm_score': 1, 'selected': False, 'text': '<p>I ended up following the technique described <a href="http://www.mp3car.com/vbulletin/digitalmods-scripts-api/81425-how-create-c-com-object-streetdeck.html" rel="nofollow noreferrer">here</a>.</p>\n'}]
['https://Stackoverflow.com/questions/61677', 'https://Stackoverflow.com', 'https://Stackoverflow.com/users/5449/']