Passing method pointer

  • Thread starter Thread starter cody
  • Start date Start date
C

cody

I have the following given:

size_t (*StreamHandler)(const Image *,const void *,const size_t);

Image *ReadStream(StreamHandler);

size_t ImageLdr::MyStreamHandler(const MagickLib::Image *image,const
void *pixels, const size_t columns)
{
}

Now I want to call ReadStream in my code:

MagickLib::Image* img = ReadStream(&ImageLdr::MyStreamHandler);

But the compiler tells me "can't take address of
'MagickNet::ImageLdr::MyStreamHandler' unless creating delegate instance".

And when I create a delegate:

public delegate size_t SH(const MagickLib::Image *image,const void
*pixels, const size_t columns);
SH^ sh = gcnew SH(this, &ImageLdr::MyStreamHandler);
MagickLib::Image* img = ReadStream(sh);

Then it says: "cannot convert parameter 2 from 'MagickNet::SH ^' to
'MagickLib::StreamHandler'".

So how can I pass my method to this function?
 
cody said:
I have the following given:

size_t (*StreamHandler)(const Image *,const void *,const size_t);

Image *ReadStream(StreamHandler);

size_t ImageLdr::MyStreamHandler(const MagickLib::Image *image,const
void *pixels, const size_t columns)
{
}

Now I want to call ReadStream in my code:

MagickLib::Image* img = ReadStream(&ImageLdr::MyStreamHandler);

But the compiler tells me "can't take address of
'MagickNet::ImageLdr::MyStreamHandler' unless creating delegate
instance".

Well this is baloney. But, it's right that you can't use that function
there, because it is a non-static member function which expects a hidden
"this" parameter, so it is the wrong signature.
And when I create a delegate:

public delegate size_t SH(const MagickLib::Image *image,const void
*pixels, const size_t columns);
SH^ sh = gcnew SH(this, &ImageLdr::MyStreamHandler);
MagickLib::Image* img = ReadStream(sh);

Then it says: "cannot convert parameter 2 from 'MagickNet::SH ^' to
'MagickLib::StreamHandler'".

So how can I pass my method to this function?

You can either try to make your function static (if it doesn't use the this
pointer) in which case it would have the right signature, or use
Marshal::GetFunctionPointerForDelegate to create a trampoline that has the
right signature and adds the this pointer afterward (the trampoline is
machine code with the this pointer embedded in it).
 
Ben said:
Well this is baloney. But, it's right that you can't use that function
there, because it is a non-static member function which expects a hidden
"this" parameter, so it is the wrong signature.


You can either try to make your function static (if it doesn't use the this
pointer) in which case it would have the right signature, or use
Marshal::GetFunctionPointerForDelegate to create a trampoline that has the
right signature and adds the this pointer afterward (the trampoline is
machine code with the this pointer embedded in it).

Thanks for the reply!
I now managed it to get it compiled but at runtime the vs2008 debugger
tells me at the ReadStream call than an attempt was made to illegally
read memory:

typedef size_t FP(const MagickLib::Image *image,const void *pixels,
const size_t columns);

SH^ sh = gcnew SH(this, &ImageLdr::StreamHandler);
ReadStream((FP*)Marshal::GetFunctionPointerForDelegate(sh).ToPointer());
 
You can either try to make your function static (if it doesn't use
Thanks for the reply!
I now managed it to get it compiled but at runtime the vs2008 debugger
tells me at the ReadStream call than an attempt was made to illegally
read memory:

typedef size_t FP(const MagickLib::Image *image,const void *pixels,
const size_t columns);

SH^ sh = gcnew SH(this, &ImageLdr::StreamHandler);
ReadStream((FP*)Marshal::GetFunctionPointerForDelegate(sh).ToPointer());

Stuff sh in a gcroot or GCHandle so it doesn't get cleaned up by the garbage
collector while the native code has a copy of the pointer.
 
Ben said:
Stuff sh in a gcroot or GCHandle so it doesn't get cleaned up by the garbage
collector while the native code has a copy of the pointer.

I now made SH an instance variable of the containing class (which is
also a managed class) but the error remains. The illegal memory read
occures *before* the ReadStream() method returns. I've set a breakpoint
into the StreamHandler and it seems it is at least one time called.
 
cody said:
I now made SH an instance variable of the containing class (which is also
a managed class) but the error remains. The illegal memory read occures
*before* the ReadStream() method returns. I've set a breakpoint into the
StreamHandler and it seems it is at least one time called.

Probably a calling convention mismatch,
Marshal::GetFunctionPointerForDelegate always uses stdcall.

Can you write it like this?

size_t (__stdcall *StreamHandler)(const Image *,const void *,const size_t);
Image *ReadStream(StreamHandler);

Or is the definition of ReadStream fixed so you cannot change it?
 
Ben said:
Probably a calling convention mismatch,
Marshal::GetFunctionPointerForDelegate always uses stdcall.

Can you write it like this?

size_t (__stdcall *StreamHandler)(const Image *,const void *,const size_t);
Image *ReadStream(StreamHandler);

Or is the definition of ReadStream fixed so you cannot change it?

ReadStream is part of the ImageMagick library so I cannot change it (I
statically link to its .lib files if that should matter).
But I tried changing my MyStreamHandler function and got warning C4441:

"calling convention of '__stdcall ' ignored; '__clrcall ' used instead"

And the runtime error remains so I finally created a new *unmanaged*
class which now contains the MyStreamHandler function and I call that
from my *managed* class. And it now works! :)

Seems there is no way to use a function of a ref class to use as a
callback function in managed c++ (Although I remember that I
successfully used a C# method as callback of a native function some time
ago).

However it works now, thanks for all your hints and ideas, it helped me
very much to understand the things going on behind it :)
 
But I tried changing my MyStreamHandler function and got warning C4441:

"calling convention of '__stdcall ' ignored; '__clrcall ' used instead"

It's the trampoline that is __stdcall, not the member function of the ref
class.
And the runtime error remains so I finally created a new *unmanaged* class
which now contains the MyStreamHandler function and I call that from my
*managed* class. And it now works! :)

Because now you probably have a __cdecl function which is what the library
ReadStream call is expecting.
Seems there is no way to use a function of a ref class to use as a
callback function in managed c++ (Although I remember that I successfully
used a C# method as callback of a native function some time ago).

It's possible only if the native function wants a stdcall callback. I don't
know of any function for creating a cdecl -> clrcall trampoline (sometimes
also called a shim).
However it works now, thanks for all your hints and ideas, it helped me
very much to understand the things going on behind it :)

Glad you got past this problem. You're welcome.
 
Back
Top