问题
I have managed to create a C# COM object with events. Please find code below,
[Guid("1212674-38748-45434")]
public interface ICalculator
{
int Add(int Num1, int Num2);
}
[InterfaceType(ComInterfaceType.InterfaceIsIDispatch)]
[Guid("3453674234-84444-84784")]
public interface ICalculatorEvents
{
[DispId(1)]
void Completed(int Result);
}
[ClassInterface(ClassInterfaceType.None)]
[ComSourceInterfaces(typeof(ICalculatorEvents))]
[Guid("87457845-945u48-4954")]
public class Calculator : ICalculator
{
public delegate void CompletedDelegate(int result);
public event CompletedDelegate Completed;
public Add(int Num1, int Num2)
{
int Result = Num1 + Num2;
if(Completed != null)
Completed(Result);
}
}
I have imported this COM object in a C++ console application and able to call the 'Add()' method. I am not sure how to handle 'Completed' event in my C++ application. Can you please advise on this? I am looking to display the result value in console whenever this event occurs.
Please find C++ application's code below. The Event 'Completed' never gets handled here. This goes into an infinite loop.
#import "Calculator.tlb"
using namespace Calculator;
int Flag = 0;
class HandleEvent : public ICalculatorEvent
{
public:
HandleEvent(void);
~HandleEvent(void);
HRESULT __stdcall QueryInterface(const IID &, void **);
ULONG __stdcall AddRef(void) { return 1; }
ULONG __stdcall Release(void) { return 1; }
HRESULT __stdcall Completed(int Result);
};
HandleEvent::HandleEvent(void)
{
}
HRESULT HandleEvent::Completed(int Result)
{
printf("Addition Completed, Result: %d", Result);
Flag = 1;
}
HRESULT HandleEvent::QueryInterface(const IID & iid,void ** pp)
{
if (iid == __uuidof(ICalculatorEvent) || iid == __uuidof(IUnknown))
{
*pp = this;
AddRef();
return S_OK;
}
return E_NOINTERFACE;
}
int _tmain(int argc, _TCHAR* argv[])
{
CoInitialize(NULL);
Flag = 0;
ICalculatorPtr pCalc(__uuidof(Calculator));
pCalc->Add(5, 6);
do
{
}while(Flag == 0);
CoUninitialize ();
return 0;
}
Thanks in advance.
回答1:
If you want to use delegates you don't need to declare an interface. Change _tmain() function like this :
int _tmain(int argc, _TCHAR* argv[])
{
CoInitialize(NULL);
Flag = 0;
EventHandler evh ;
ICalculatorPtr pCalc(__uuidof(Calculator));
pCalc->Completed = &evh.Completed() ;
pCalc->Add(5, 6);
do
{
}while(Flag == 0);
CoUninitialize ();
return 0;
}
If you want to use an interface try this.
[ClassInterface(ClassInterfaceType.None)]
[ComSourceInterfaces(typeof(ICalculatorEvents))]
[Guid("87457845-945u48-4954")]
public class Calculator : ICalculator
{
public ICalculatorEvents callbackObject ;
public Add(int Num1, int Num2)
{
int Result = Num1 + Num2;
if(callbackObject != null)
callbackObject.Completed(Result);
}
}
and change _tmain() method to this.
int _tmain(int argc, _TCHAR* argv[])
{
CoInitialize(NULL);
Flag = 0;
EventHandler evh ;
ICalculatorPtr pCalc(__uuidof(Calculator));
pCalc->callbackObject = &evh ;
pCalc->Add(5, 6);
do
{
}while(Flag == 0);
CoUninitialize ();
return 0;
}
回答2:
I found that COM initialization in C++ client should be done using
CoInitializeEx(NULL, COINIT_MULTITHREADED);
for asynchronous event processing from C# (.NET) COM server, otherwise C++ client receives events only after CoUninitialize() call.
Events handling class:
class EventWrapper : public IDispEventSimpleImpl<1, EventWrapper, &DIID_RumCardCOMEvents >
{
public:
// now you need to declare a sink map - a map of methods handling the events
BEGIN_SINK_MAP(EventWrapper)
SINK_ENTRY_INFO(1, DIID_RumCardCOMEvents, 0x1, isCardInserted, &cardInserted)
SINK_ENTRY_INFO(1, DIID_RumCardCOMEvents, 0x2, isCardRemoved, &cardRemoved)
// event interface id (can be more than 1)---+ | | |
// must match dispid of your event -----------------+ | |
// method which handles the event ------------------------+ |
// type information for event, see below --------------------------------------+
END_SINK_MAP()
// declare the type info object. You will need one for each method with different signature.
// it will be defined in the .cpp file, as it is a static member
static _ATL_FUNC_INFO cardInserted; // 'placeholder' object to carry event information (see below)
static _ATL_FUNC_INFO cardRemoved; // 'placeholder' object to carry event information (see below)
// method which handles the event
STDMETHOD (isCardInserted)(unsigned char type)
{
// usually it is defined it in the .cpp file
cout << "isCardInserted: " << (int)type << endl;
return 0;
}
STDMETHOD (isCardRemoved)()
{
// usually it is defined it in the .cpp file
cout << "isCardRemoved" << endl;
return 0;
}
};
Main:
int main()
{
CoInitializeEx(NULL, COINIT_MULTITHREADED);
try
{
EventWrapper ev;
ev.DispEventAdvise(/*COM interface*/);
// receiving events
ev.DispEventUnadvise(/*COM interface*/);
}
catch (_com_error& e)
{
cout << "Exception: " << e.ErrorMessage() << endl;
}
CoUninitialize();
return 0;
}
来源:https://stackoverflow.com/questions/10923717/handle-c-sharp-com-events-in-c