Experiments in calling managed delegates

Modified on 2011/04/19 07:35 by codewiz51 — Categorized as: CLI

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 12:22

Oops! I discovered the code I had posted did not keep the delegates alive. Here is the marked up code. codewiz51, 2008/07/10 16: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
	// 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;

	// 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;
		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
		// Remove the delegates from the list
		System::Delegate::RemoveAll(_MyDelegate, _MyDelegate);
		// Delete the gcrooted pointer
		delete _MyDelegate;


#pragma managed
public ref class ReceiveDelegateCall
	// 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;

	// 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);

	void TheSecondDelegateMethod(UInt32 x)
		String^ strTemp = String::Format("The Second Delegate has been called. Value: {0}", x);

	// ctor
		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

	// dtor
		delete embeddedNativeClassPointer;

	// This method kicks off the whole thing
	// If the juju works, the native&#0091;sic&#0093; class
	// will invoke the managed delegates.
	void StartMeUp()

	// Remove the first subscriber
	void RemoveSubscription()

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

	// Remove a delegate and call StartMeUp again to make sure
	// the unsubscribe function works
	Console::WriteLine(L"Remove First Subscription");
	Console::WriteLine(L"Second Call to 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;