.NET Framework - Callbacks from unmanaged to managed C++

Asked By DavidT on 02-Dec-07 03:24 AM
Hello,

at first, exuse if the following question is simple to solve, but i
normaly coding with C# and now have to use C++/CLI for one project.

My Problem is that i have to use a native c++ sdk to read pictures
from ip cameras. I get the native c++ decoder worked so far and now
try to send callbacks from unmanaged to managed code, to signal wenn a
picture is fully received.

To get a first view on that area i implemented a little test where i
extract a pointer from a delegate and sending the pointer to unmanaged
code. In the unmanaged function i use the callbackfunction (after
filling an array), jump back to to managed code and marshaling the
generated array to the managed world.
That little example is working fine in debugmode but as soon as i
running in releasemode the programm crashs and i dont know why.

So, at first my Code:
[CODE]
class umTest
{
private:
unsigned char * data;
public:
void Caller(int size, void callback(int, unsigned char*))
{
data = new unsigned char[size];
for(int i = 0; i < size; i++)
data[i] = i;
callback(size,data);
};

};

using namespace System;
using namespace System::Runtime::InteropServices;

public delegate void callback(int, unsigned char*);

ref class mTest
{
private:
array<unsigned char> ^destination;
int size;

public:
mTest()
{
callback ^d = gcnew callback(this,&mTest::callTest);
IntPtr ptr = Marshal::GetFunctionPointerForDelegate(d);
umTest *umT = new umTest();
[B]umT->Caller(5,
static_cast<void (__cdecl *)(int, unsigned char*)>
(ptr.ToPointer()));
this->print();
}

void callTest(int size, unsigned char* test)
{
this->size = size;
destination = gcnew array<unsigned char>(size);
Marshal::Copy(IntPtr(test),destination,0,size);
delete[](test);
test = NULL;
this->print();
};

void print()
{
for(int i = 0; i<size; i++)
Console::WriteLine("destination[{0}]={1}",i,destination[i]);
};
};
[/CODE]
When it runs in release mode, the callbackfunction is normally called
but after finishing the function, he is called again. On the
Marshal::Copy(...)-Line then the programm is crashing without any
exception...

I hope someone of you find the mistake, i have no idea anymore...
Thanks in advance

Greets,

David




Jochen Kalmbach [MVP] replied on 01-Dec-07 02:28 AM
Hi DavidT!



Normally callbacks to managed code are done via "gcroot" Template...
See:
http://groups.google.de/group/microsoft.public.dotnet.languages.vc/msg/577d07f3522b5a25?hl=de&

--
Greetings
Jochen

My blog about Win32 and .NET
http://blog.kalmbachnet.de/
DavidT replied on 02-Dec-07 03:24 AM
On 1 Dez., 08:28, "Jochen Kalmbach [MVP]" <nospam-

At first, thanks for your answer here and at the german c++-board. I
tried it now with gcroot but i have the problem (sorry, i realy new to
C++/CLI) that i can't set my managed class via a setter-method.

Here my code:

using namespace System;
using namespace System::Runtime::InteropServices;


ref class mClass
{
void callback()
{

};
};

class umClass
{
public:
msclr::auto_gcroot<mClass^> mClass;

void setCallback()
{

};
};

What is the type of my setCallback parameter? mClass*? pointers on
unmanaged classes are not allowed in C++/CLI, are they? mClass^?
Trakinghandles also not available in native code. In your example it
absolutly clear, but what have i to do in the new C++/CLI?

The other Problem is, it doesnt matter what i try, i get all the time
the following error:

Fehler	2	error C2327: 'umClass::mClass': Ist kein Typname, nicht
statisch und kein Enumerator
19	(engl. mClass is no typename, not statical and no enum)

What is my mistake?

Thanks again,

David
Jochen Kalmbach [MVP] replied on 01-Dec-07 09:23 AM
Hi DavidT!



void setCallback(mClass^ o)
{
mClass = o;
}

and

void doCallback()
{
mClass ^o;
o->callback();
}


--
Greetings
Jochen

My blog about Win32 and .NET
http://blog.kalmbachnet.de/
Jochen Kalmbach [MVP] replied on 01-Dec-07 09:26 AM
Upps... should be:

class umClass
{
public:
msclr::auto_gcroot<mClass^> _m;

void setCallback(mClass^ o)
{
_m = o;
};
void doCallback()
{
mClass ^o;
o = _m;
o->callback();
}
};

--
Greetings
Jochen

My blog about Win32 and .NET
http://blog.kalmbachnet.de/
DavidT replied on 02-Dec-07 03:24 AM
On 1 Dez., 15:23, "Jochen Kalmbach [MVP]" <nospam-

i splitted up both classes into different files.

When i do your changed i get the same error (your german, so i keep
translation:


Fehler	3	error C2327: 'umClass::mClass': Ist kein Typname, nicht
statisch und kein Enumerator 11

Fehler	4	error C2065: 'o': nichtdeklarierter Bezeichner		18

Fehler	5	error C2227: Links von "->callback" muss sich ein Zeiger auf
Klassen-/Struktur-/Union-/generischen Typ befinden.	19

Is there something wrong with my vs settings?!?

Thanks a lot,

David
DavidT replied on 02-Dec-07 03:24 AM
Fehler	3	error C2440: '=': 'msclr::auto_gcroot<_element_type>' kann
nicht in 'mClass ^' konvertiert werden	18


o = _m.get(); <- is it this?

Sorry, i don't wanna get you on your nerves but i absolutely running
out of concentration after weeks trying to pass this (obviously
simple) problem...

Thanks,

David
DavidT replied on 02-Dec-07 03:24 AM
Ok, it's working now! Thanks a lot, i still dunno where my initial
mistake was but thats not sooo important rigth now :)

A addition question to you, if allowed:

When i produce a unsigned char array in my native class and want to
copy the whole array to the unmanaged world and send it to a
memorystream, is it enough to submit the pointer from umngt to mngt an
copy the data to the stream? Or have i to marshal it first du a mngt-
array and then send to the memory-stream?

Thanks,

David
Jochen Kalmbach [MVP] replied on 01-Dec-07 10:25 AM
Hi DavidT!


You mean somthing like this:

array<Byte> ^arr;
Then you can pin it...

array<Byte> ^bDst = gcnew array<Byte>(10);
pin_ptr<Byte> pDstPinned = &bDst[0];

Then you can use the "pinned" array and access it like a native array...

For example:


using namespace System;

int main()
{
array<Byte> ^bDst = gcnew array<Byte>(10);

unsigned char *pSrc = new unsigned char[10];
for(size_t i=0; i<10; i++)
pSrc[i] = i;
{
pin_ptr<Byte> pDstPinned = &bDst[0];
memcpy(pDstPinned, pSrc, 10);
}

for each (Byte b in bDst)
Console::WriteLine(b);
}



--
Greetings
Jochen

My blog about Win32 and .NET
http://blog.kalmbachnet.de/