Codwiz51's Wiki

RSS

Navigation







Quick Search
»
Advanced Search »

Experiments in calling managed delegates

RSS
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
{
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&#0091;sic&#0093; 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;
}

ScrewTurn Wiki version 3.0.5.600. Some of the icons created by FamFamFam.