简体   繁体   中英

C# calling C++ 3rd party DLL (no source) raises exception - not PInvoke Compatible

I am using VS 2015 Community with an ASP.NET MVC Web Application that uses a 3rd party C++ DLL I do not have source code for. Documentation is very scarce as is any helpful communication with the authors of the 3rd party DLL.

I've asked a related SO Question and received a good answer from @Steven. I've modified my code according to his answer and am trying to make a successful call to the 3rd party C++ DLL. The code:

// Call DLL

MyDLLInput _DLLInput = new MyDLLInput();
{
    SomeList = new int[288],
    ...
    SomeInt = 22,
    SomeDbl = 1.45,
    ...
    PathtoData = "C:\\Some\\Path\\To\\Data"
};    

var ids = new int[] { 0, 12, 33, 67, 93 };
Array.Copy(ids, _DLLInput.SomeList, ids.Length);

// Call DLL Entry Point
MyDLLOutput _DLLOutput = MyDLL.Unit(_DLLInput);

Raises exception:

Method's type signature is not PInvoke compatible.

// C# Input STRUCT

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct MyDLLInput
{
    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 288)]
    public int[] SomeList;
    ...
    public int SomeInt;
    public double SomeDbl;

    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)]
    public string PathtoData;
};

// C# Output STRUCT

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct MyDLLOutput
{
    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 288)]
    public int[] SomeList;
    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 288)]
    public double[] SomeDblArray;
    ...
    public int SomeInt;        // Same as input
    public double SomeDbl;     // Same as input
}

// C# DLLImport

public class MyDLL
{
    [DllImport("My_DLL.dll",
        EntryPoint = "?Unit@@YA?AUDLLOutput@@UDLLInput@@@Z",
        CallingConvention = CallingConvention.Cdecl)]
    public static extern MyDLLOutput Unit(MyDLLInput UnitInput);
}

// C++ My_DLL.h

#define  EPS_API __declspec(dllexport) 

struct DLLInput
{
    int SomeList[288];

    int SomeInt;
    double SomeDbl;

    char PathtoData[256];
};

struct DLLOutput
{
    int SomeList[288];
    double SomeDblArray[288];
    ...
    int SomeInt;
    double SomeDbl;
};

EPS_API DLLOutput Unit(DLLInput UnitInput);

I think I must be close, but haven't been able to find any SO or Google results that help. Does anyone see what I'm doing wrong?

Expanding on what Ben has said.

You need to remove the const definitions to outside the interopable struct. By declaring a layout of the struct as sequential the compiler is expecting EXACTLY the same number of members, declared in EXACTLY the same order, with each type declared, EXACTLY the same size on both sides (ie in C++ and C#). Those const declarations add to the memory layout and so the signatures are not compatible.

char is not the same size in C# and C++, byte is (although they can change depending on the platform, both types are guaranteed at least 8 bits). You could marshall a byte array instead of a string , but either is fine.

To Create a byte array which is compatible with C++ char array (same size).

UTF8Encoding utf8 = new UTF8Encoding();
public byte[] PathToData = new byte[256];
PathToData = utf8.GetBytes(pathToDataString);

On another note though, if documentation is scarce and the authors are not helpful, personally I would think twice about using this 3rd party. Calling it is one thing, debugging it is another.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM