Cannot pass a __delegate pointer to C++ function?

G

Guest

I have no trouble passing __delegate ptrs to native C functions in DLLs,
however when attempting to pass the __delegate ptr to a native C++ function
in a DLL I get the following runtime exception: An unhandled exception of
type 'System.EntryPointNotFoundException'.

This is confusing b/c if the entry point was not found, I would think I'd
get an unresolved symbol error during linking. Furthermore, I can
successfully pass void, int *, and function pointers, etc to C++ DLL
functions, but this error persists when attempting to pass a __delegate
pointer, which is used for callbacks to managed C++ functions.

Here is the relevant code snippet - the DLL code simply contains stubs of
the functions called here:

#include "stdafx.h"
#using <mscorlib.dll>

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

__delegate void testCB();
[DllImport("unmdll.dll")]
void fnunmdll_2(testCB *UpdateProgMeter);

int fnunmdll_0(void);
void fnunmdll_1(int *x);//testCB *UpdateProgMeter);

__gc class C1
{
public:
static void handleCB()
{
return;
}
};

int _tmain()
{
int k = 9;

//always works (as both C/C++)
fnunmdll_0();

//always works (as both C/C++)
fnunmdll_1(&k);

testCB *pCB = new testCB(0, &C1::handleCB);

//if this is a C++ function expecting a function pointer, the
aforementioned error occurs. If this is an extern "C" function, all is ok!
fnunmdll_2(pCB);

return 0;
}

As crazy as it sounds, I'm beginning to think this is not possible to do
with native C++ functions.

Thanks in advance, George
 
W

Willy Denoyette [MVP]

This has nothing to do with delegates, this is a C++ name mangling issue,
your DllImport functions (fnunmdll_2) must use C linkage exports, or an
appropriate .DEF file.
extern "C"
{
....

}

Willy.
 
G

Guest

Thanks for the post but do you know why there are no problems when passing
void or int *, even function pointers? Wouldn't they also have name mangling
issues? It seems strange that the only thing that fails is passing
__delegates.

Thanks again, George
 
G

Guest

Using a .DEF file to get rid of mangled names fixes the problem. extern C
also works of course but the .Def is better.

If anyone can explain why this worked all along for all non-delegate types,
i would really love to know. I'm not sure why name-mangling would affect the
function call based on argument type.

Thanks, George
 
W

Willy Denoyette [MVP]

glutz7878 said:
Thanks for the post but do you know why there are no problems when passing
void or int *, even function pointers? Wouldn't they also have name
mangling
issues? It seems strange that the only thing that fails is passing
__delegates.

Thanks again, George

No, the diference is that you are calling the function through PInvoke
([DllImport("unmdll.dll")]), that means you are dynamically binding against
the library and the entrypoint will be resolved at call time(that's why the
linker doesn't complain), while in the other cases you are statically
linking and the linker is able to resolve the functions called points.

Willy.
 
S

samm

Thank you for your help. It works fine, but I have weird problem. It works
only on one of my devices: HTC Touch phone. If I use HTC Mogul (the same
Windows Mobile 6.0, but less memory) it does not. I have the same .net CF 2.0
on both.


thanks again.



Passing managed delegates to native functions as callbacks
Willy Denoyette said:
glutz7878 said:
Thanks for the post but do you know why there are no problems when passing
void or int *, even function pointers? Wouldn't they also have name
mangling
issues? It seems strange that the only thing that fails is passing
__delegates.

Thanks again, George

No, the diference is that you are calling the function through PInvoke
([DllImport("unmdll.dll")]), that means you are dynamically binding against
the library and the entrypoint will be resolved at call time(that's why the
linker doesn't complain), while in the other cases you are statically
linking and the linker is able to resolve the functions called points.

Willy.
 
B

Ben Voigt [C++ MVP]

samm said:
Thank you for your help. It works fine, but I have weird problem. It
works only on one of my devices: HTC Touch phone. If I use HTC Mogul
(the same Windows Mobile 6.0, but less memory) it does not. I have
the same .net CF 2.0 on both.

C++ interop doesn't work in the compact framework, but
Marshal::GetFunctionPointerForDelegate does.

__delegate doesn't exist in .NET 2.0, that's .NET 1.x Managed Extensions for
C++ syntax

Explain what you're trying to accomplish and what you're doing so far and we
should be able to help you.
 
S

samm

Sure.

1. I have C++ dll which needs to register callback in my C# application.
This callback returns a string:


C# side:

public delegate void RegisterCallBackDelegateInt(System.IntPtr response);
....
RegisterCallBackDelegateInt Callback;
IntPtr callbackPointer;
....

Callback = new
RegisterCallBackDelegateInt(RegisterCallBackInt);
callbackPointer =
Marshal.GetFunctionPointerForDelegate(Callback);
GC.KeepAlive(callbackPointer);
ret = RegisterCallBack(callbackPointer);
....
public void RegisterCallBackInt(System.IntPtr response)
{
string myString = Marshal.PtrToStringUni(response);
Log("Callback: " + myString);
Marshal.FreeHGlobal(response);
}

C++ side

extern "C" {

__declspec( dllexport ) HRESULT RegisterCallBack(void(*CB)(BYTE *xmlResult) );

};

It works fine on Touch HTC phone, but not on Mogul.

2. The reason I do that is because I do not have ability to wrap my C++ dll
to C++ managed anymore. On win32 I can do that and my life is much better.

Thank you for your help.

Sam
 
B

Ben Voigt [C++ MVP]

samm said:
Sure.

1. I have C++ dll which needs to register callback in my C#
application.
This callback returns a string:


C# side:

public delegate void RegisterCallBackDelegateInt(System.IntPtr
response); ...
RegisterCallBackDelegateInt Callback;
IntPtr callbackPointer;
...

Callback = new
RegisterCallBackDelegateInt(RegisterCallBackInt);
callbackPointer =
Marshal.GetFunctionPointerForDelegate(Callback);
GC.KeepAlive(callbackPointer);
ret = RegisterCallBack(callbackPointer);
...
public void RegisterCallBackInt(System.IntPtr response)
{
string myString = Marshal.PtrToStringUni(response);
Log("Callback: " + myString);
Marshal.FreeHGlobal(response);
}

C++ side

extern "C" {

__declspec( dllexport ) HRESULT RegisterCallBack(void(*CB)(BYTE
*xmlResult) );

};

It works fine on Touch HTC phone, but not on Mogul.

2. The reason I do that is because I do not have ability to wrap my
C++ dll to C++ managed anymore. On win32 I can do that and my life is
much better.

Thank you for your help.

You are misusing GC.KeepAlive, so you will experience random failures
whenever a collection takes place before the callback is finished. Use
GCHandle.Alloc instead, on the delegate, not the native function pointer.
 
S

samm

Thank you.
Can you give me an example?



Ben Voigt said:
You are misusing GC.KeepAlive, so you will experience random failures
whenever a collection takes place before the callback is finished. Use
GCHandle.Alloc instead, on the delegate, not the native function pointer.
 
B

Ben Voigt [C++ MVP]

samm said:
Thank you.
Can you give me an example?

First, the runtime should do this for you if you do the DllImport right (see
the documentation for GCHandle.Alloc). But you can do it by hand.

Modifying your example:

RegisterCallBackDelegateInt Callback;
IntPtr callbackPointer;
Callback = new RegisterCallBackDelegateInt(RegisterCallBackInt);
callbackPointer = Marshal.GetFunctionPointerForDelegate(Callback);
ret = RegisterCallBack(callbackPointer);

GCHandle cbKeepAlive = GCHandle.Alloc(Callback);
....

// when the callback will no longer be used from native code
cbKeepAlive.Free();
 
S

samm

I added code you recommended. My application works fine, but if I start
another application and switch back to mine I get a crash.

If I stay in my app it works without any problems.
 
B

Ben Voigt [C++ MVP]

samm said:
I added code you recommended. My application works fine, but if I
start another application and switch back to mine I get a crash.

Switching applications usually triggers a GC.

Try changing GCHandle.Alloc(x) to GCHandle.Alloc(x, GCHandleType.Pinned)
 
S

samm

Does not work. If I switch without using callback no problems.
I am suprised Microsoft released the product without straight forward
feature of calling C++ dlls from .net. Managed C++ was working just fine for
me as a glue between .net and C++ unmanaged.
 
B

Ben Voigt [C++ MVP]

samm said:
Does not work. If I switch without using callback no problems.
I am suprised Microsoft released the product without straight forward
feature of calling C++ dlls from .net. Managed C++ was working just
fine for me as a glue between .net and C++ unmanaged.

C++ interop is great (at least starting with 2005), but you're on Compact
Framework so you can't use it.

Try taking out the GCHandle.Free entirely?
 
S

samm

I do not call free at all. I got interop file and llok what happens:
[pinvokeimpl][preservesig]
int Averquest.Averquest::SendRequest(System.Byte[] );
int (I4_VAL) SendRequest(unsigned char *(ARRAY_BLIT_LPARRAY) );

[preservesig][delegate]
void RegisterCallBackDelegateInt::Invoke(RegisterCallBackDelegateInt ,
IntPtr );
void (*)(INT_PTR (I_VAL) )
[preservesig][delegate]
void RegisterCallBackDelegateInt::Invoke(RegisterCallBackDelegateInt ,
IntPtr );
void (*)(INT_PTR (I_VAL) )
[preservesig][delegate]
void RegisterCallBackDelegateInt::Invoke(RegisterCallBackDelegateInt ,
IntPtr );
void (*)(INT_PTR (I_VAL) )
[preservesig][delegate]
void RegisterCallBackDelegateInt::Invoke(RegisterCallBackDelegateInt ,
IntPtr );
void (*)(INT_PTR (I_VAL) )
[pinvokeimpl][preservesig]
IntPtr SafeNativeMethods::LocalAlloc(int , int );
INT_PTR (I_VAL) LocalAlloc(int (I4_VAL) , int (I4_VAL) );

[pinvokeimpl][preservesig]
IntPtr SafeNativeMethods::LocalAlloc(int , int );
INT_PTR (I_VAL) LocalAlloc(int (I4_VAL) , int (I4_VAL) );

[pinvokeimpl][preservesig]
int Microsoft.WindowsMobile.Telephony.Phone::phoneMakeCall(IntPtr );
int (I4_VAL) PhoneMakeCall(INT_PTR (I_VAL) );

[pinvokeimpl][preservesig]
IntPtr SafeNativeMethods::LocalFree(IntPtr );
INT_PTR (I_VAL) LocalFree(INT_PTR (I_VAL) );

[pinvokeimpl][preservesig]
IntPtr SafeNativeMethods::LocalFree(IntPtr );
INT_PTR (I_VAL) LocalFree(INT_PTR (I_VAL) );
 

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