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