Question

First, why do I ask this question? Because the Windows CE platform I am stuck with does not support this attribute. So I need to do everything to my delegate (that is getting called from C++) that the attribute normally does.

In normal Windows I have

[UnmanagedFunctionPointer(CallingConvention.Cdecl)]        
public delegate UInt32 UnmanagedAuthenticationEvent(UInt32 authType, UInt32 numberToDisplay, IntPtr btAddress, ref UInt32 numberOfDigits)

And it works fine. When called from C++ the return value and the ref UInt32 numberOfDigits value are both properly received from the C# layer. Now I remove the attribute and use the Windows CE required approach of passing an unmanaged function pointer to C++ via PInvoke:

_authenticationEvent = new UnmanagedAuthenticationEvent(OnReceiveUnmanagedAuthenticationEvent);
_unmanagedAuthenticationEvent = Marshal.GetFunctionPointerForDelegate(_authenticationEvent);
CallRegisterForAuthenticationEvent(_uBTTransportPtr, _unmanagedAuthenticationEvent);

where 'CallRegisterForAuthenticationEvent(IntPtr, IntPtr)' is the PInvoke signature.

I have declared the following class 'variables'

private IntPtr _unmanagedAuthenticationEvent;
private Delegate _authenticationEvent;
private UInt32 _numberOfDigits; // the ref UInt32 passed back to C++
private UInt32 _matchValue;     // the return value

to protect my delegate function pointer AND the variables passed back to C++ from garbage collection. At first I didn't add the return value and ref UInt32 value and both were garbage in C++. After doing the above, the '_numberOfDigits' value is good but the return value is junk.

The function I am now calling from C++ looks as follows (at least to start with):

    #region OnReceiveUnmanagedAuthenticationEvent
    // This is the function called by the unmanaged code
    private UInt32 OnReceiveUnmanagedAuthenticationEvent(UInt32 authType, UInt32 numberToDisplay, IntPtr btAddress, ref UInt32 numberOfDigits)
    {
        byte[] byteAddress = new byte[6];
        Marshal.Copy(btAddress, byteAddress, 0, 6);
        string btAddressString = Common.StaticHelper.BluetoothAddressFromByteArray(byteAddress);

        switch (authType)
        {
            case 2: //Auth_BT_PIN:
            if(OnPinRequestEvent != null)
            {
                string pin = "";
                OnPinRequestEvent(ref pin, btAddressString);
                try
                {
                    _matchValue = UInt32.Parse(pin);
                    _numberOfDigits = (uint)pin.Length; // protect value from being GCed
                    numberOfDigits = _numberOfDigits;
                    return _matchValue;   // The case I am viewing returns here
                }
                catch(ArgumentNullException e)
                {
                    Console.WriteLine("Application entered a bad PIN value. Details " + e.Message);
                }
                catch(FormatException e)
                {
                    Console.WriteLine("Application entered a bad PIN value. Details " + e.Message);
                }
                catch(OverflowException e)
                {
                    Console.WriteLine("Application entered a bad PIN value. Details " + e.Message);
                }
            }
            break;

What am I missing that the return value is junk? I suppose I could return the value as a ref value in the parameters and solve my problem but I want to know how UnmanagedFunctionPointer does it so I can address other issues that might come up in this painful Windows CE implementation.

Was it helpful?

Solution

Turns out all it specifies is the function calling convention. On WindowsCE, there is only one calling convention so the need for this attribute is unnecessary and therefore is not present. Thus if one is trying to support both platforms, one will need a conditional compile on the use of this attribute.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top