Is there a working example of COM event handling i

2019-06-01 05:09发布

I am trying to connect a C++ Builder app with a .NET service app, and would like the .NET service app to be able to send events back to the C++ Builder app.

Is there a working example of a C++ Builder app which can handle COM events?

1条回答
Bombasti
2楼-- · 2019-06-01 05:31

There was an error in the DocWiki regarding handling COM Events. I was able to collect some examples from various places, and put them together here for reference.

The .NET application has the following:

using System;
using System.IO;
using System.Runtime.InteropServices;

namespace IPractLib01
{
    // 
    // GUID's to use for the COM objects.
    [Guid("ACD03FE3-E506-4D87-BF8B-CC1F52E1FF0C")]
    public interface IManagedInterface
    {
        int SendMessage(
            string message
            );
    }


    // Source interface with "event handlers" for COM objects to implement
    [Guid("1ACAB463-55A3-4B3F-BE10-6252CDD93CE8")]
    [InterfaceType(ComInterfaceType.InterfaceIsIDispatch)] // use InterfaceIsDual to get callbacks by other than InvokeEvent()
    public interface IIntelliPractEvents
    {
        [DispId(1)] void MessageReceived();

        [DispId(2)] void MessageTextReceived(string message);
    }

    // Delegates for the events
    public delegate void MessageReceivedEventHandler();
    public delegate void MessageTextReceivedEventHandler(string message);

    [Guid("1F4A7EDA-EE2A-4EA3-B213-A1911C5F766E")]
    [ComSourceInterfaces(typeof(IIntelliPractEvents))]
    public class IPractLib01Class : IManagedInterface
    {
        public event MessageReceivedEventHandler MessageReceived;
        public event MessageTextReceivedEventHandler MessageTextReceived;

        public int SendMessage(string message)
        {
            if (MessageReceived != null)
            {
                MessageReceived();
            }
            if (MessageTextReceived != null)
            {
                int len = message.Length;
                string newMessage = "The message is '" + message + "', and the length of the message is " + len;
                MessageTextReceived(newMessage);
            }
            return 0;
        }    
    }
}

The C++ Builder application contains:

#include "Unit1.h"
#include "IPractLib01_TLB.h" // created with Component/Import Component/Import a Type Library

//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;
//---------------------------------------------------------------------------
//---------------------------------------------------------------------------

// to register the assembly dll:
// regasm IPractLib01.dll /tlb /verbose
// Need to run regasm as administrator, due to need to modify registry.  If a regular user login was used to map drives, the mappings are not normally seen by the administrator login.  Use "net use" in the administrator login to map them for the administrator

static TCOM_IPractLib01Class iplClass;

// The DocWiki does not take the address of the DIID_, but that does not compile
class MyEventSinkClass : public TEventDispatcher<MyEventSinkClass, &DIID_IIntelliPractEvents>
{
public:
  MyEventSinkClass(IUnknown* sourceClass);
  ~MyEventSinkClass();
// declare the methods of DIID_IIntelliPractEvents here
  void __fastcall MessageReceived();
  void __fastcall MessageTextReceived(BSTR message/*[in]*/);
  virtual HRESULT InvokeEvent(DISPID id, TVariant* params = 0);

  IUnknown* theSource_;
};

static MyEventSinkClass* theEventSink = NULL;

static void ConnectNetHandler(void)
{
    if (!iplClass)
        iplClass = CoIPractLib01Class::Create();

    if (!theEventSink) {
        theEventSink = new MyEventSinkClass(iplClass);
    }

}

// All of the events come through InvokeEvent -- change the interface to InterfaceIsDual if you want events through the other routines
HRESULT MyEventSinkClass::InvokeEvent(DISPID id, TVariant* params)
{
    ShowMessage("got InvokeEvent with DISPID " + String(id));
    // params would need better handling in a real app
    if (params) {
        String st = params->operator WideString();
        ShowMessage("String is " + st);
    }
    return 0;
}

MyEventSinkClass::MyEventSinkClass(IUnknown* sourceClass)
{
    theSource_ = sourceClass;
    ConnectEvents(sourceClass);
}

MyEventSinkClass::~MyEventSinkClass()
{
    DisconnectEvents(theSource_);
}


// These two routines do not get called with InterfaceIsDispatch; change that to InterfaceIsDual for these routines to be called.
void __fastcall MyEventSinkClass::MessageReceived()
{
    ShowMessage("Message handler received");
}

void __fastcall MyEventSinkClass::MessageTextReceived(BSTR message/*[in]*/)
{
    ShowMessage(String("Message handler received with message: ") + message);
}    

void __fastcall TForm1::Button1Click(TObject *Sender)
{
    ConnectNetHandler();

    long result = -1;
    TCOMIManagedInterface mif = iplClass;
    if (!mif) {
        ShowMessage("Unable to connect to interface");
        return;
    }

    String theMessage = "the message";
    mif->SendMessage(WideString(theMessage).c_bstr(), &result);

    // check the IErrorInfo
    // (Microsoft: Making a COM call that goes through a proxy-stub will clear any existing error object for the calling thread)

    IErrorInfo *pperrinfo = NULL;
    HRESULT hr = GetErrorInfo(0, &pperrinfo);
    if (SUCCEEDED(hr) && pperrinfo) {

    WideString wideStringMessage, wideStringDescription; // WideString is a wrapper for BSTR
    pperrinfo->GetSource(&wideStringMessage);
    pperrinfo->GetDescription(&wideStringDescription);
    ShowMessage("Got error from " + String(wideStringMessage) + "; error description: " + String(wideStringDescription));
}

The DLL created by the .NET code must be registered with regasm /tlb to generate the type library, to allow C++ Builder to create the unit to implement COM. However, once the application has been created, regasm does not need to be called on the systems where this is deployed.

查看更多
登录 后发表回答