I've been working on how to handle DccManSink (RAPI class and interface) in C++/CLI. I worked up this simple test code, which I hope will translate into my production RAPI code.
codewiz51, 2008/07/10 10:22Oops! I discovered the code I had posted did not keep the delegates alive. Here is the marked up code.
codewiz51, 2008/07/10 14:32 // DelegateTesting.cpp : main project file.
#include <iostream>
#include <fstream>
#include <msclr\gcroot.h>
#include <msclr\auto_gcroot.h>
#include <msclr\event.h>
using namespace System;
delegate void DelegateDefinition(UInt32 x);
// The equivalent function pointer (not used)
typedef void (* ptrDelegateDefinition) (unsigned int x);
// This is more or less a native class
// Reflection will not disassemble it
public class CallDelegateFromNative
{
private:
// I was not able to get this to work with auto_gcroot
// Examining the auto_gcroot.h code indicates that I am
// not smart enough to understand how to use auto_gcroot
// with a delegate type. The truth hurts.
msclr::gcroot<DelegateDefinition^> _MyDelegate;
public:
// This is the method that simulates raising an event in native code
// I intend to use this code in my work with DccManSink. I hope it works
// with RAPI
int DoWork()
{
unsigned int i = 0;
std::wcout << L"Entering DoWork" << std::endl;
for each(DelegateDefinition^ dd in _MyDelegate->GetInvocationList())
{
std::wcout << L"Calling delegate:" << std::endl;
System::Threading::Thread::Sleep(1500);
dd->Invoke(++i);
}
std::wcout << L"Exiting DoWork" << std::endl;
return 1;
}
// Subscribe and Unsubscribe
// These only exist because I couln't figure out how to utilize
// the += operator with the gcrooted pointer. Plus, I have a
// deeply seated feeling _MyDelegate should be private.
// With DccMan sink, I will need to use names like
// SubscribeActive, SubscribeAnswer, etc.
void Subscribe(DelegateDefinition^ pDel)
{
_MyDelegate = (DelegateDefinition^)System::Delegate::Combine(_MyDelegate, pDel);
}
// With DccMan sink, I will need to use names like
// UnsubscribeActive, UnsubscribeAnswer, etc.
void Unsubscribe(DelegateDefinition^ pDel)
{
_MyDelegate = (DelegateDefinition^)System::Delegate::Remove(_MyDelegate, pDel);
}
// dtor
~CallDelegateFromNative()
{
// Remove the delegates from the list
System::Delegate::RemoveAll(_MyDelegate, _MyDelegate);
// Delete the gcrooted pointer
delete _MyDelegate;
}
};
#pragma managed
public ref class ReceiveDelegateCall
{
private:
// The neat thing is, this does not have to be
// private. We could create the class somewhere
// else and pass the pointer in via assignment.
// (And then pray real hard the class instance
// doesn't disappear. "It could happen" - Judy Tenuta)
CallDelegateFromNative* embeddedNativeClassPointer;
static DelegateDefinition^ pfn1;
static DelegateDefinition^ pfn2;
public:
// The delegates that get called from native code - sort of
void TheDelegateMethod(UInt32 x)
{
String^ strTemp = String::Format("The Delegate has been called. Value: {0}", x);
Console::WriteLine(strTemp);
}
void TheSecondDelegateMethod(UInt32 x)
{
String^ strTemp = String::Format("The Second Delegate has been called. Value: {0}", x);
Console::WriteLine(strTemp);
}
// ctor
ReceiveDelegateCall()
{
pfn1 = gcnew DelegateDefinition(this, &ReceiveDelegateCall::TheDelegateMethod);
pfn2 = gcnew DelegateDefinition(this, &ReceiveDelegateCall::TheSecondDelegateMethod);
// Create the native class (more or less native)
embeddedNativeClassPointer = new CallDelegateFromNative();
// I had to define this method to get around the += operator problem with _MyDelegate
embeddedNativeClassPointer->Subscribe(pfn1);
embeddedNativeClassPointer->Subscribe(pfn2);
}
// dtor
~ReceiveDelegateCall()
{
delete embeddedNativeClassPointer;
}
// This method kicks off the whole thing
// If the juju works, the native[sic] class
// will invoke the managed delegates.
void StartMeUp()
{
embeddedNativeClassPointer->DoWork();
}
// Remove the first subscriber
void RemoveSubscription()
{
embeddedNativeClassPointer->Unsubscribe(pfn1);
}
};
int main(array<System::String ^> ^args)
{
// Create an instance of the managed class that will receive the
// callbacks
ReceiveDelegateCall theInstance;
// Progress
Console::WriteLine(L"First Call to StartMeUp");
// Start up the process
theInstance.StartMeUp();
// Remove a delegate and call StartMeUp again to make sure
// the unsubscribe function works
Console::WriteLine(L"Remove First Subscription");
theInstance.RemoveSubscription();
Console::WriteLine(L"Second Call to StartMeUp");
theInstance.StartMeUp();
// If we got here, then things worked. Now the question is,
// will they work in the real world with different assemblies,
// <insert other failure modes here>...
Console::WriteLine(L"Finished without a blue screen");
return 0;
}