Passing a Delegate^ to a Native Function Expecting a Function Pointer
From a managed component you can call a native function with function pointer parameters where the native function then can call the member function of the managed component's delegate. The following program sample created the .dll (mydll.dll) that exports the native function.
1. Create a new CLR Class Library named mydll.
|
2. Add the following code to the mydll.h file.
// mydll.h
// For windows.h and uuid.lib, make sure
// you install the Windows SDK and set the include
// and lib directories properly through the
// Tools->Options...->VC++ Directories folder
// Compile with /clr
#pragma once
#include <windows.h>
using namespace System;
namespace mydll {
// TODO: Add your methods for this class here.
extern "C"{
__declspec(dllexport)
void nativeFunction(void (CALLBACK *mgdFunc)(const char* str))
{
mgdFunc("Call to the Managed Function...this string is in xxxx.dll lor!");
}
}
}
3. Make sure you have installed the Windows SDK. Then make sure you set the SDK’s include and lib (library) path in your project setting. The following are the steps to set the path for those directories provided that you already installed the SDK. Click Tools menu and select Options… sub menu.
4. Expand the Projects and Solutions folder and select VC++ Directories. Under the Show directories for: select the Include files.
5. Click the New Line icon and then click the three dots at the end of the empty new line.
6. Browse and find the Windows SDK’s Include directory and click Open. The path for the Include will be added.
7. The following Figure shows the added Windows SDK’s Include path has been added to the project’s VC++ Directories.
8. Next, repeat the same step for the Libraries files. Browse and find the Windows SDK’s Lib directory shown below.
9. The following Figure shows the added Lib path to the project. Click OK button to close the project options page.
10. Build your project. The DLL file will be generated as shown below under the debug folder. You can’t run this program component. It will be used in the next program example.
11. The following example will consumes the previous mydll.dll and passes a delegate handle to the native function expecting a function pointer. Next, create a new CLR Console Application and add the following code. The next section shows the steps.
// native.cpp : main project file.
// delegate to native function
// Calling the Dll (mydll.dll)
// compile with: /clr
#include "stdafx.h"
using namespace System;
using namespace System::Runtime::InteropServices;
delegate void Del(String ^s);
public ref class Test
{
public:
void delMember(String ^s)
{
Console::WriteLine(s);
}
};
// mydll.dll is the previous DLL file, change accordingly if yours
// is different and it is copied to this project’s debug folder
[DllImportAttribute("mydll.dll", CharSet=CharSet::Ansi)]
extern "C" void nativeFunction(Del ^d);
int main(array<System::String ^> ^args)
{
Test ^MyTest = gcnew Test;
Del ^d = gcnew Del(MyTest, &Test::delMember);
nativeFunction(d); // Call to native function
return 0;
}
Output:
12. Create a new CLR Console Application named native. Open the main file, native.cpp and add the previous code.
13. Before you can call the native function, the DLL file, mydll.dll must be found by this native program. Copy mydll.dll file and put it into the native debug’s folder. You can put it in the %System32% folder so that all the program can use it.
-------------------------------------------------
14. Build and run your native program. The following output should be expected.
The Generic Delegates (C++)
You can use generic type parameters with delegates. The following is the syntax.
[attributes]
generic < [class | typename] type-parameter-identifiers >
[type-parameter-constraints-clauses]
[accessibility-modifiers] delegate result-type identifier ([formal-parameters]);
Parameters | |
attributes (Optional) | Additional declarative information. |
type-parameter-identifier(s) | Comma-separated list of identifiers for the type parameters. |
type-parameter-constraints-clauses | Takes the form specified in Constraints. |
accessibility-modifiers (Optional) | Accessibility modifiers (e.g. public, private). |
result-type | The return type of the delegate. |
identifier | The name of the delegate. |
formal-parameters (Optional) | The parameter list of the delegate. |
Table 4 |
The delegate type parameters are specified at the point where a delegate object is created. Both the delegate and method associated with it must have the same signature. The following is an example of a generic delegate declaration.
// generics delegate
// compile with: /clr /c
generic < class ItemType>
delegate ItemType GenDelegate(ItemType p1, ItemType% p2);
The following sample shows that:
You cannot use the same delegate object with different constructed types. Create different delegate objects for different types.
A generic delegate can be associated with a generic method.
When a generic method is called without specifying type arguments, the compiler tries to infer the type arguments for the call.
// generics delegate
// compile with: /clr
#include "stdafx.h"
using namespace System;
generic < class ItemType>
delegate ItemType GenDelegate(ItemType p1, ItemType% p2);
generic <class ItemType>
ref struct MyGenClass
{
ItemType MyMethod(ItemType i, ItemType % j)
{
return ItemType();
}
};
ref struct MyClass
{
generic <class ItemType>
static ItemType MyStaticMethod(ItemType i, ItemType % j)
{
return ItemType();
}
};
int main(array<System::String ^> ^args)
{
MyGenClass<int> ^ myObj1 = gcnew MyGenClass<int>();
MyGenClass<double> ^ myObj2 = gcnew MyGenClass<double>();
GenDelegate<int>^ myDelegate1 = gcnew GenDelegate<int>(myObj1, &MyGenClass<int>::MyMethod);
GenDelegate<double>^ myDelegate2 = gcnew GenDelegate<double>(myObj2, &MyGenClass<double>::MyMethod);
GenDelegate<int>^ myDelegate = gcnew GenDelegate<int>(&MyClass::MyStaticMethod<int>);
return 0;
}
// No output
The following example declares a generic delegate GenDelegate<ItemType>, and then instantiates it by associating it to the method MyMethod that uses the type parameter ItemType. Two instances of the delegate (an integer and a double) are created and invoked.
// generics delegate in action
// compile with: /clr
#include "stdafx.h"
using namespace System;
// declare generic delegate
generic <typename ItemType>
delegate ItemType GenDelegate(ItemType p1, ItemType% p2);
// Declare a generic class
generic <typename ItemType>
ref class MyGenClass
{
public:
ItemType MyMethod(ItemType p1, ItemType% p2)
{
p2 = p1;
return p1;
}
};
int main(array<System::String ^> ^args)
{
int i = 0, j = 0;
double m = 0.0, n = 0.0;
MyGenClass<int>^ myObj1 = gcnew MyGenClass<int>();
MyGenClass<double>^ myObj2 = gcnew MyGenClass<double>();
// Instantiate a delegate using int.
GenDelegate<int>^ MyDelegate1 = gcnew GenDelegate<int>(myObj1, &MyGenClass<int>::MyMethod);
// Invoke the integer delegate using MyMethod.
i = MyDelegate1(123, j);
Console::WriteLine("Invoking the integer delegate: i = {0}, j = {1}", i, j);
// Instantiate a delegate using double.
GenDelegate<double>^ MyDelegate2 = gcnew GenDelegate<double>(myObj2, &MyGenClass<double>::MyMethod);
// Invoke the integer delegate using MyMethod.
m = MyDelegate2(0.123, n);
Console::WriteLine("Invoking the double delegate: m = {0}, n = {1}", m, n);
return 0;
}
Output: