3

Aha, this question is all over stack overflow so I've already proceeded to add CallingConvention = CallingConvention.Cdecl which has worked just fine for other libraries I've had to import but in this case, nothing changes and it still fails with the same error message.

The original code for this came out of a .net 3.5 project and works perfectly fine:

[DllImport("Compiled DSP.dll")]
private static extern int fnGetConfigParam(int nID, ref stParamInt pstParam);

[DllImport("Compiled DSP.dll")]
private static extern int fnGetConfigParam(int nID, ref stParamFloat pstParam);

The new project is a .net 4.0 project that is trying to call the same library. I've already added the calling convention:

[DllImport("Compiled DSP.dll", CallingConvention = CallingConvention.Cdecl)]
private static extern int fnGetConfigParam(int nID, ref stParamInt pstParam);

[DllImport("Compiled DSP.dll", CallingConvention = CallingConvention.Cdecl)]
private static extern int fnGetConfigParam(int nID, ref stParamFloat pstParam);

Both of the ref types are structs. When I try to run the same code that calls this function as the 3.5 project, I get the PInvoke error, even if I change the call to StdCall (as expected) I get the same error.

Any thoughts? I'm guessing that the struct is somehow interfering but using imports like this is not a common task for me so this is a blind guess. Time to ask google and poke around deeper on here.

EDIT : In case this is useful, here are the two structs passed as reference:

stParamInt:

    [StructLayout(LayoutKind.Sequential, Pack = 1)]
    private struct stParamInt           
    {
        public uint unID;
        public int nValue;
        public int nValueMin;
        public int nValueMax;
        public int nValueDef;
        public int nUnitsType;
        public int nUnits;

        public byte[] GetBytes()
        {
            byte[] result = new byte[0];
            List<byte> buf = new List<byte>();
            buf.AddRange(BitConverter.GetBytes(unID));
            buf.AddRange(BitConverter.GetBytes(nValue));
            buf.AddRange(BitConverter.GetBytes(nValueMin));
            buf.AddRange(BitConverter.GetBytes(nValueMax));
            buf.AddRange(BitConverter.GetBytes(nValueDef));
            buf.AddRange(BitConverter.GetBytes(nUnitsType));
            buf.AddRange(BitConverter.GetBytes(nUnits));
            result = buf.ToArray();
            return result;
        }

        public stParamInt(byte[] buf)
        {
            unID = BitConverter.ToUInt32(buf, 0);
            nValue = BitConverter.ToInt32(buf, 4);
            nValueMin = BitConverter.ToInt32(buf, 8);
            nValueMax = BitConverter.ToInt32(buf, 12);
            nValueDef = BitConverter.ToInt32(buf, 16);
            nUnitsType = BitConverter.ToInt32(buf, 20);
            nUnits = BitConverter.ToInt32(buf, 24);
        }
    };

stParamFloat:

    [StructLayout(LayoutKind.Sequential, Pack = 1)]
    private struct stParamFloat                 
    {
        public uint unID;
        public float fValue;
        public float fValueMin;
        public float fValueMax;
        public float fValueDef;
        public int nUnitsType;
        public int nUnits;

        public byte[] GetBytes()
        {
            byte[] result = new byte[0];
            List<byte> buf = new List<byte>();
            buf.AddRange(BitConverter.GetBytes(unID));
            buf.AddRange(BitConverter.GetBytes(fValue));
            buf.AddRange(BitConverter.GetBytes(fValueMin));
            buf.AddRange(BitConverter.GetBytes(fValueMax));
            buf.AddRange(BitConverter.GetBytes(fValueDef));
            buf.AddRange(BitConverter.GetBytes(nUnitsType));
            buf.AddRange(BitConverter.GetBytes(nUnits));
            result = buf.ToArray();
            return result;
        }

        public stParamFloat(byte[] buf)
        {
            unID = BitConverter.ToUInt32(buf, 0);
            fValue = BitConverter.ToSingle(buf, 4);
            fValueMin = BitConverter.ToSingle(buf, 8);
            fValueMax = BitConverter.ToSingle(buf, 12);
            fValueDef = BitConverter.ToSingle(buf, 16);
            nUnitsType = BitConverter.ToInt32(buf, 20);
            nUnits = BitConverter.ToInt32(buf, 24);
        }

    };

EDIT I managed to dig out the signature within the related dll, it's in C++ unmanaged code with the following method signature:

COMPILEDDSP_API int fnGetConfigParam(int nID, struct stPint *pstParam)
{
    return CONFIG_GetSetConfigParam(GET_PARAM, nID, pstParam, &g_stActiveCfg, sizeof(struct stActiveConfig)/sizeof(struct stPint), NULL);
}

Unfortunately for me I'm not especially fluent in standard C++ functions but the signature appears to be fine.

EDIT I found that it works prefectly fine if I just build and run it from my bin. If I try to run in debug mode through visual studio, it fails with this PInvoke error.

Mohgeroth
  • 1,617
  • 4
  • 32
  • 47
  • I did not realize DllImport can do overload resolution. How are stParamInt and stParamFloat defined, and do you use the same bit-ness on both flatforms (32 vs 64)? – 500 - Internal Server Error Mar 06 '13 at 18:50
  • Not the author of this code mind you, but I know that the current versions of our apps that use this (about 4 different apps) are all 32 bit applications that run off of .net 3.5 SP1. The .net 4.0 project is 32 bit as well since he has not redone anything to support 64 bit machines. – Mohgeroth Mar 06 '13 at 18:52
  • Nothing obviously wrong with your pinvoke declarations. The C code itself may have imbalanced the stack. Use a debugger and pay attention to the value of the ESP register when you step over CONFIG_GetSetConfigParam(). Debug + Windows + Registers. – Hans Passant Mar 06 '13 at 23:12
  • @Mohgeroth: please check the answer provided and if resolves your issue, please mark as answer. – IAbstract May 02 '13 at 21:30
  • This did not resolve the issue so it was not marked as the answer. – Mohgeroth May 03 '13 at 18:11

1 Answers1

1

Take a look of these answers

You can pass the ref structures with IntPtr, the importing would be

[DllImport("Compiled DSP.dll")]
private static extern int fnGetConfigParam(int nID, IntPtr pstParam);

and the test

var sizeParamInt=Marshal.SizeOf(typeof(stParamInt));
var marshalParamInt=Marshal.AllocHGlobal(sizeParamInt);
fnGetConfigParam(123, marshalParamInt);
var paramInt=(stParamInt)Marshal.PtrToStructure(marshalParamInt, typeof(stParamInt));
Marshal.FreeHGlobal(marshalParamInt);

Note that I didn't try for the calling conventions, you might need to test with your actual code.

Community
  • 1
  • 1
Ken Kin
  • 4,503
  • 3
  • 38
  • 76