Hello all,
i have problems to implement a structure including a callback function
pointer correctly in C# .NET.
The structure is used as a parameter for a initializing function, which
is implemented in a C++ dll (unmanaged code) i want to use.
THEME: calling a DLL-function from .NET including a function pointer
within a class/structure - interop / marshal
First i made a declaration for the function:
[DllImport("mydll.dll", CharSet = CharSet.Ansi)]
private static extern System.Int32 Initialize(ref PARAM para);
//in the dll-documentation it is declared like this. long must now be
a int, the datatypes increased in .NET
//long Initialize( PARAM *para );
// and the structure is originally
//typedef struct
//{
// DWORD MacNo;
// DWORD OwnPort;
// DWORD IpAddr;
// DWORD Port;
// VOID (*CallBack)(DWORD, DWORD, VOID *);
/* Call back routine */
// WORD Timeout[20];
// WORD retry[20];
//} PARAM ;
then i defined the structure in my code as follows:
[StructLayout(LayoutKind.Sequential)]
public class PARAM
{
public System.Int32 nMacNo;
public System.Int32 nOwnPort;
public System.Int32 nIpAddr;
public System.Int32 nNCPort;
[MarshalAsAttribute(UnmanagedType.FunctionPtr)]
public CallbackFunctionHandler cb;
[MarshalAs(UnmanagedType.ByValArray, SizeConst=PARA_ARR_LEN)]
public System.Int16[] arrTimeout = new System.Int16[PARA_ARR_LEN];
[MarshalAs(UnmanagedType.ByValArray, SizeConst=PARA_ARR_LEN)]
public System.Int16[] arrRetry = new System.Int16[PARA_ARR_LEN];
}
and i created a delegate as a funtion pointer:
// member
public delegate void CallbackFunctionHandler(System.UInt32 iHandle,
System.UInt32 iEvent, IntPtr arg);
CallbackFunctionHandler callBackFunctionHandler = new
CallbackFunctionHandler(CallBack);
later on in my code i fill the structure with values and try to call
the initialize function
// PARAM para = new PARAM(); member
para.cb = callBackFunctionHandler;
//...and all other values
// call the function seems not be ok, but i get no error
int nResult = Initialize(ref para);
// and the function which should be called if data arrived
public static void CallBack(System.UInt32 iHandle, System.UInt32
iEvent, IntPtr arg)
I've tried now so many things and attributes, also with keyword
"unsafe", but i don't get it to work, the tread within the dll is not
started after invoking the initialize function.
It would be nice to know how a callback function pointer must be
marshalled to the unmanaged dll.
Any help would be appreciated.
Martin
Marcus Heege - 22 Nov 2006 14:18 GMT
At the first quick view, your code seems ok. What exactly is goin wrong?
What is the calling convention of CallBack?
Marcus
> Hello all,
> i have problems to implement a structure including a callback function
[quoted text clipped - 76 lines]
> Any help would be appreciated.
> Martin
ms - 24 Nov 2006 15:32 GMT
I found a solution now, but i don't have enough time to write it down
now.
I'll post it on monday or thuesday. Thanks.
ms - 27 Nov 2006 10:02 GMT
i had two problems with this code.
1) the callback-function was never called
2) at the end of the callback-function the program crashed
the solutions are rather simple
1) i changed 'class' PARAM to 'struct', or replace 'ref' with '[In]' in
Initialize function declaration. Using a 'ref class' passes double
reference (PARAM**), because class is already passed by reference
(unlike the struct, which is by default passed by value).
And i removed array initializers from the structure.
2) there was a stack corruption, because of __cdecl calling convention
of the callback function. There are several ways to solve this issue:
a) force library vendor to use __stdcall modifier for callbacks. This
would be best, but also very unlikely to happen.
b) use a wrapper library written in C++, which will provide
"translation" layer for the callbacks. You'll need header and .lib file
for the dll.
c) use a wrapper library written in managed C++. This is similar to b),
but it can provide better .NET-friendly interface, which may avoid the
need of interop code in the application.
d) manually add the cdecl modopt modifier to disassembled IL source and
re-compile it. This has to be done after each build, for example as a
part of post-build step, text manipulation can be automated. There are
some examples on the Internet...
e) switch to Visual Studio 2005. I've found (but not yet tested)
UnmanagedFunctionPointer attribute which can be used to mark a delegate
with cdecl calling convention
I decided to write a wrapper library in C++ (solution b) which i use
now for calling a "__stdcall Initialize"-function.
Hope this helps anybody out there.