managed c++ calling c#: how to delegate?

A

Andreas Reiff

Hey!

I have a managed c++ app (actually, the app is mixed managed/unmanaged, but
this happens in the managed part) that dynamically generates some program in
c# (with CSharpCodeProvider).

The problem now is that somehow I get runtime errors when trying to pass a
function (via delegates) into the c# code in order to make c# call back that
function. The message box says: "An unhandled exeption of type
'System.ArgumentException' occurred in mscorlib.dll Additional information:
The object with type my_delegate cannot be converted to type my_delegate.'
(Actually, this is a translation of my message.)
I have given both delegate declarations the same name in case this was the
error.

Maybe someone can help?

Best regards + many thanks,
Andreas


A short code excerpt:

(managed c++)

public delegate void my_delegate();

void del_func_cpp(void)

{

Console::WriteLine("del_func_cpp called");

}

(later on..)

System::Reflection::Assembly^ a = cr->CompiledAssembly;

System::Object^ _Compiled;

_Compiled = a->CreateInstance("test_class");

System::Reflection::MethodInfo^ mi2 =

_Compiled->GetType()->GetMethod("test_ff");

array<my_delegate ^> ^ MyArray2 = gcnew array<my_delegate ^>(1);

MyArray2[0] = gcnew my_delegate( &del_func_cpp );

MyArray2[0](); <- this still works

mi2->Invoke(_Compiled, MyArray2); <- here it crashes



I create the c# program from the following string:

strncat_s(out, sizeof(out), "public delegate void my_delegate();\n",
sizeof(out));

strncat_s(out, sizeof(out), "public class test_class {\n", sizeof(out));

strncat_s(out, sizeof(out), " public void test_ff(my_delegate x)\n",
sizeof(out));

strncat_s(out, sizeof(out), " {\n", sizeof(out));

strncat_s(out, sizeof(out), " System.Console.WriteLine(\"at least the
function got called\");\n", sizeof(out));

strncat_s(out, sizeof(out), " x();\n", sizeof(out));

strncat_s(out, sizeof(out), " }\n", sizeof(out));

strncat_s(out, sizeof(out), "}", sizeof(out));



I don't get the "at least the function got called" message.
 
B

Ben Voigt

Andreas Reiff said:
Hey!

I have a managed c++ app (actually, the app is mixed managed/unmanaged,
but this happens in the managed part) that dynamically generates some
program in c# (with CSharpCodeProvider).

The problem now is that somehow I get runtime errors when trying to pass a
function (via delegates) into the c# code in order to make c# call back
that function. The message box says: "An unhandled exeption of type
'System.ArgumentException' occurred in mscorlib.dll Additional
information: The object with type my_delegate cannot be converted to type
my_delegate.' (Actually, this is a translation of my message.)
I have given both delegate declarations the same name in case this was the
error.

Maybe someone can help?

Best regards + many thanks,
Andreas


A short code excerpt:

(managed c++)

public delegate void my_delegate();

void del_func_cpp(void)

{

Console::WriteLine("del_func_cpp called");

}

(later on..)

System::Reflection::Assembly^ a = cr->CompiledAssembly;

System::Object^ _Compiled;

_Compiled = a->CreateInstance("test_class");

System::Reflection::MethodInfo^ mi2 =

_Compiled->GetType()->GetMethod("test_ff");

array<my_delegate ^> ^ MyArray2 = gcnew array<my_delegate ^>(1);

MyArray2[0] = gcnew my_delegate( &del_func_cpp );

MyArray2[0](); <- this still works

mi2->Invoke(_Compiled, MyArray2); <- here it crashes



I create the c# program from the following string:

strncat_s(out, sizeof(out), "public delegate void my_delegate();\n",
sizeof(out));

That's not going to work. You need to import the delegate type from the
C++/CLI assembly as a reference in the C# program, or else leave the
delegate declaration out of the C++/CLI assembly entirely, obtain the
delegate Type from the C# assembly via reflection, and use the static
methods of the Delegate to create an object of that delegate type, passing
the C++ function pointer.

Let's bring this discussion to the microsoft.public.dotnet.languages.vc
newsgroup -- almost all C++/CLI programmers know C# and are comfortable with
advanced topics like templates and reflection, while a very small fraction
of C# programmers could write even a short C++ program.
 
E

Egghead

hehe, Not all C# programmers are from Java side.

Anyway, assume no problem in complie the C# part.
First of all, you do not need that "public delegate void my_delegate();" in
the VC++.net. Unless I miss it. I do not see you create the instance of the
delegate.

cheers,
RL

Ben Voigt said:
Andreas Reiff said:
Hey!

I have a managed c++ app (actually, the app is mixed managed/unmanaged,
but this happens in the managed part) that dynamically generates some
program in c# (with CSharpCodeProvider).

The problem now is that somehow I get runtime errors when trying to pass
a function (via delegates) into the c# code in order to make c# call back
that function. The message box says: "An unhandled exeption of type
'System.ArgumentException' occurred in mscorlib.dll Additional
information: The object with type my_delegate cannot be converted to type
my_delegate.' (Actually, this is a translation of my message.)
I have given both delegate declarations the same name in case this was
the error.

Maybe someone can help?

Best regards + many thanks,
Andreas


A short code excerpt:

(managed c++)

public delegate void my_delegate();

void del_func_cpp(void)

{

Console::WriteLine("del_func_cpp called");

}

(later on..)

System::Reflection::Assembly^ a = cr->CompiledAssembly;

System::Object^ _Compiled;

_Compiled = a->CreateInstance("test_class");

System::Reflection::MethodInfo^ mi2 =

_Compiled->GetType()->GetMethod("test_ff");

array<my_delegate ^> ^ MyArray2 = gcnew array<my_delegate ^>(1);

MyArray2[0] = gcnew my_delegate( &del_func_cpp );

MyArray2[0](); <- this still works

mi2->Invoke(_Compiled, MyArray2); <- here it crashes



I create the c# program from the following string:

strncat_s(out, sizeof(out), "public delegate void my_delegate();\n",
sizeof(out));

That's not going to work. You need to import the delegate type from the
C++/CLI assembly as a reference in the C# program, or else leave the
delegate declaration out of the C++/CLI assembly entirely, obtain the
delegate Type from the C# assembly via reflection, and use the static
methods of the Delegate to create an object of that delegate type, passing
the C++ function pointer.

Let's bring this discussion to the microsoft.public.dotnet.languages.vc
newsgroup -- almost all C++/CLI programmers know C# and are comfortable
with advanced topics like templates and reflection, while a very small
fraction of C# programmers could write even a short C++ program.
 
W

Willy Denoyette [MVP]

Andreas Reiff said:
Hey!

I have a managed c++ app (actually, the app is mixed managed/unmanaged, but this happens
in the managed part) that dynamically generates some program in c# (with
CSharpCodeProvider).

The problem now is that somehow I get runtime errors when trying to pass a function (via
delegates) into the c# code in order to make c# call back that function. The message box
says: "An unhandled exeption of type 'System.ArgumentException' occurred in mscorlib.dll
Additional information: The object with type my_delegate cannot be converted to type
my_delegate.' (Actually, this is a translation of my message.)
I have given both delegate declarations the same name in case this was the error.


Your code ain't gonna work. Following is a small (Quick and dirty) sample that may help you
implement this in your code.
Compile and run, it should give you some idea's about what's wrong with your code.

// C++ file : useDynDel.cpp
using namespace System;
using namespace System::Text;
using namespace Microsoft::CSharp;
using namespace System::CodeDom;
using namespace System::CodeDom::Compiler;
using namespace System::Reflection;
using namespace System::IO;

namespace Tester
{
public delegate void SampleDelegate(String ^s);
public interface class Command
{
void Execute(SampleDelegate^ dl);
};

bool BuildAndCompileCS()
{
StringBuilder^ sb = gcnew StringBuilder();
sb->Append("using System;" + System::Environment::NewLine);
sb->Append(System::Environment::NewLine);
sb->Append("namespace Tester" + System::Environment::NewLine);
sb->Append("{" + System::Environment::NewLine);
sb->Append(" public class TestClass : Tester.Command" + System::Environment::NewLine);
sb->Append(" {" + System::Environment::NewLine);
sb->Append(" public TestClass()" + System::Environment::NewLine);
sb->Append(" {" + System::Environment::NewLine);
sb->Append(" }" + System::Environment::NewLine);
sb->Append(System::Environment::NewLine);
sb->Append(" public void Execute(SampleDelegate del) " + System::Environment::NewLine);
sb->Append(" {" + System::Environment::NewLine);
sb->Append(" del(\"Hello from Delegate - This is a testmessage\");" +
System::Environment::NewLine);
sb->Append(" }" + System::Environment::NewLine);
sb->Append(" }" + System::Environment::NewLine);
sb->Append("}" + System::Environment::NewLine);
CSharpCodeProvider^ csp = gcnew CSharpCodeProvider();
ICodeCompiler^ cc = csp->CreateCompiler();
CompilerParameters^ cp = gcnew CompilerParameters();
cp->OutputAssembly = ".\\TestClass.dll";
cp->ReferencedAssemblies->Add("useDynDel.exe");
cp->WarningLevel = 3;
cp->CompilerOptions = "/target:library /o+";
cp->GenerateExecutable = false;
cp->GenerateInMemory = false;
Compiler::TempFileCollection^ tfc = gcnew TempFileCollection(".\\", false);
CompilerResults^ cr = gcnew CompilerResults(tfc);

cr = cc->CompileAssemblyFromSource(cp, sb->ToString());

if (cr->Errors->Count > 0)
{
Console::WriteLine(cr->Errors[0]->ToString());
return false;
}
else

return true;
}

array<Byte>^ loadFile(String^ filename)
{
FileStream fs(filename, FileMode::Open);
array<Byte>^ buffer = gcnew array<Byte>((int) fs.Length);
fs.Read(buffer, 0, buffer->Length);
fs.Close();
return buffer;
}
void DelMethod(String^ s)
{
Console::WriteLine(s);
}

void Execute()
{

AppDomain^ newDomain = AppDomain::CurrentDomain;
array<Byte>^ rawAssembly = loadFile("TestClass.dll");
Assembly^ assembly = newDomain->Load(rawAssembly, nullptr);
SampleDelegate^ d1 = gcnew SampleDelegate(DelMethod);
Command^ testClass = dynamic_cast<Command^>(assembly->CreateInstance("Tester.TestClass"));
if(testClass != nullptr)
testClass->Execute(d1);
}
}
int main(array<System::String ^> ^args)
{
if(Tester::BuildAndCompileCS())
Tester::Execute();
return 0;
}

Willy.
 
A

Andreas Reiff

From the little managed c++ knowledge I have, I create it here
array<my_delegate ^> ^ MyArray2 = gcnew array<my_delegate ^>(1);

MyArray2[0] = gcnew my_delegate( &del_func_cpp );

Anyhow, the version of Willy Denoyette seems to work, so I will try to
understand it now. :)

Many many thx for your help though, to both of you!!

:)


Egghead said:
hehe, Not all C# programmers are from Java side.

Anyway, assume no problem in complie the C# part.
First of all, you do not need that "public delegate void my_delegate();"
in the VC++.net. Unless I miss it. I do not see you create the instance of
the delegate.

cheers,
RL

Ben Voigt said:
Andreas Reiff said:
Hey!

I have a managed c++ app (actually, the app is mixed managed/unmanaged,
but this happens in the managed part) that dynamically generates some
program in c# (with CSharpCodeProvider).

The problem now is that somehow I get runtime errors when trying to pass
a function (via delegates) into the c# code in order to make c# call
back that function. The message box says: "An unhandled exeption of type
'System.ArgumentException' occurred in mscorlib.dll Additional
information: The object with type my_delegate cannot be converted to
type my_delegate.' (Actually, this is a translation of my message.)
I have given both delegate declarations the same name in case this was
the error.

Maybe someone can help?

Best regards + many thanks,
Andreas


A short code excerpt:

(managed c++)

public delegate void my_delegate();

void del_func_cpp(void)

{

Console::WriteLine("del_func_cpp called");

}

(later on..)

System::Reflection::Assembly^ a = cr->CompiledAssembly;

System::Object^ _Compiled;

_Compiled = a->CreateInstance("test_class");

System::Reflection::MethodInfo^ mi2 =

_Compiled->GetType()->GetMethod("test_ff");

array<my_delegate ^> ^ MyArray2 = gcnew array<my_delegate ^>(1);

MyArray2[0] = gcnew my_delegate( &del_func_cpp );

MyArray2[0](); <- this still works

mi2->Invoke(_Compiled, MyArray2); <- here it crashes



I create the c# program from the following string:

strncat_s(out, sizeof(out), "public delegate void my_delegate();\n",
sizeof(out));

That's not going to work. You need to import the delegate type from the
C++/CLI assembly as a reference in the C# program, or else leave the
delegate declaration out of the C++/CLI assembly entirely, obtain the
delegate Type from the C# assembly via reflection, and use the static
methods of the Delegate to create an object of that delegate type,
passing the C++ function pointer.

Let's bring this discussion to the microsoft.public.dotnet.languages.vc
newsgroup -- almost all C++/CLI programmers know C# and are comfortable
with advanced topics like templates and reflection, while a very small
fraction of C# programmers could write even a short C++ program.
 
A

Andreas Reiff

I don't really understand it 100%ly right now, but I am working on it.

From what I think I understand, the difference is, that you use the calling
assembly as a reference and thus have the same delegate declaration in both
assemblies?!

Ohoh.. .net really is tricky..

Anyhow, many thx for your solution, I suppose that I will have understood it
probably tomorrow (and actually just with copy + paste it is working already
in my program).

Best regards!


Willy Denoyette said:
Andreas Reiff said:
Hey!

I have a managed c++ app (actually, the app is mixed managed/unmanaged,
but this happens in the managed part) that dynamically generates some
program in c# (with CSharpCodeProvider).

The problem now is that somehow I get runtime errors when trying to pass
a function (via delegates) into the c# code in order to make c# call back
that function. The message box says: "An unhandled exeption of type
'System.ArgumentException' occurred in mscorlib.dll Additional
information: The object with type my_delegate cannot be converted to type
my_delegate.' (Actually, this is a translation of my message.)
I have given both delegate declarations the same name in case this was
the error.


Your code ain't gonna work. Following is a small (Quick and dirty) sample
that may help you implement this in your code.
Compile and run, it should give you some idea's about what's wrong with
your code.

// C++ file : useDynDel.cpp
using namespace System;
using namespace System::Text;
using namespace Microsoft::CSharp;
using namespace System::CodeDom;
using namespace System::CodeDom::Compiler;
using namespace System::Reflection;
using namespace System::IO;

namespace Tester
{
public delegate void SampleDelegate(String ^s);
public interface class Command
{
void Execute(SampleDelegate^ dl);
};

bool BuildAndCompileCS()
{
StringBuilder^ sb = gcnew StringBuilder();
sb->Append("using System;" + System::Environment::NewLine);
sb->Append(System::Environment::NewLine);
sb->Append("namespace Tester" + System::Environment::NewLine);
sb->Append("{" + System::Environment::NewLine);
sb->Append(" public class TestClass : Tester.Command" +
System::Environment::NewLine);
sb->Append(" {" + System::Environment::NewLine);
sb->Append(" public TestClass()" + System::Environment::NewLine);
sb->Append(" {" + System::Environment::NewLine);
sb->Append(" }" + System::Environment::NewLine);
sb->Append(System::Environment::NewLine);
sb->Append(" public void Execute(SampleDelegate del) " +
System::Environment::NewLine);
sb->Append(" {" + System::Environment::NewLine);
sb->Append(" del(\"Hello from Delegate - This is a testmessage\");" +
System::Environment::NewLine);
sb->Append(" }" + System::Environment::NewLine);
sb->Append(" }" + System::Environment::NewLine);
sb->Append("}" + System::Environment::NewLine);
CSharpCodeProvider^ csp = gcnew CSharpCodeProvider();
ICodeCompiler^ cc = csp->CreateCompiler();
CompilerParameters^ cp = gcnew CompilerParameters();
cp->OutputAssembly = ".\\TestClass.dll";
cp->ReferencedAssemblies->Add("useDynDel.exe");
cp->WarningLevel = 3;
cp->CompilerOptions = "/target:library /o+";
cp->GenerateExecutable = false;
cp->GenerateInMemory = false;
Compiler::TempFileCollection^ tfc = gcnew TempFileCollection(".\\",
false);
CompilerResults^ cr = gcnew CompilerResults(tfc);

cr = cc->CompileAssemblyFromSource(cp, sb->ToString());

if (cr->Errors->Count > 0)
{
Console::WriteLine(cr->Errors[0]->ToString());
return false;
}
else

return true;
}

array<Byte>^ loadFile(String^ filename)
{
FileStream fs(filename, FileMode::Open);
array<Byte>^ buffer = gcnew array<Byte>((int) fs.Length);
fs.Read(buffer, 0, buffer->Length);
fs.Close();
return buffer;
}
void DelMethod(String^ s)
{
Console::WriteLine(s);
}

void Execute()
{

AppDomain^ newDomain = AppDomain::CurrentDomain;
array<Byte>^ rawAssembly = loadFile("TestClass.dll");
Assembly^ assembly = newDomain->Load(rawAssembly, nullptr);
SampleDelegate^ d1 = gcnew SampleDelegate(DelMethod);
Command^ testClass =
dynamic_cast<Command^>(assembly->CreateInstance("Tester.TestClass"));
if(testClass != nullptr)
testClass->Execute(d1);
}
}
int main(array<System::String ^> ^args)
{
if(Tester::BuildAndCompileCS())
Tester::Execute();
return 0;
}

Willy.
 
W

Willy Denoyette [MVP]

Andreas Reiff said:
I don't really understand it 100%ly right now, but I am working on it.

From what I think I understand, the difference is, that you use the calling assembly as a
reference and thus have the same delegate declaration in both assemblies?!

That's it.
Ohoh.. .net really is tricky..

Not really, types defined in different assemblies will yield different object *identities*
when instantiated, so, even if they "look the same", they are different. This is really
important to understand and is not about .NET only, all OO frameworks have this feature.
Anyhow, many thx for your solution, I suppose that I will have understood it probably
tomorrow (and actually just with copy + paste it is working already in my program).

Feel free to come back if you encounter other issues.

Willy.
 
B

Barry Kelly

Ben said:
a very small fraction
of C# programmers could write even a short C++ program.

That sounds like an absolute statement of capability! Careful!

-- Barry
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top