C#调用C ++第三方DLL(无源)引发异常 - 不是PInvoke兼容 [英] C# calling C++ 3rd party DLL (no source) raises exception - not PInvoke Compatible

查看:152
本文介绍了C#调用C ++第三方DLL(无源)引发异常 - 不是PInvoke兼容的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在使用VS 2015社区与使用第三方C ++ DLL的ASP.NET MVC Web应用程序没有源代码。文档是非常稀缺的,与第三方DLL的作者有任何有帮助的沟通。



我已经问过一个相关的 SO Question ,并从@Steven收到了一个很好的答案。我根据他的答案修改了我的代码,并试图成功地调用第三方C ++ DLL。代码:



//调用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);

//调用DLL入口点
MyDLLOutput _DLLOutput = MyDLL.Unit(_DLLInput);

引发例外:


方法的类型签名不是PInvoke兼容的。


// C#输入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)]
公共字符串PathtoData;
};

// C#输出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; //与输入相同
public double SomeDbl; //与输入
相同

// 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)

结构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);

我想我一定很接近,但还没有找到任何SO或Google结果帮帮我。有没有人看到我在做错什么?

解决方案

扩展Ben所说的内容。



您需要将const定义删除到可互换的结构之外。通过将结构体的布局声明为顺序,编译器期望完全相同数量的成员(以完全相同的顺序声明),每个类型都声明,两侧都是相同的大小(即C ++和C#)。那些 const 声明添加到内存布局,因此签名不兼容。



char 在C#和C ++中的大小不一样, byte 是(尽管它们可以根据平台而改变,两种类型都保证至少8位)。你可以编写一个字节数组,而不是一个字符串,但是两者都可以。



创建一个与C ++ char 数组(相同大小)兼容的字节数组。

  UTF8Encoding utf8 = new UTF8Encoding(); 
public byte [] PathToData = new byte [256];
PathToData = utf8.GetBytes(pathToDataString);另外还有一个注意事项,如果文档很少,作者没有帮助,我个人会三思而后行关于使用这个第三方。调用它是一回事,调试是另一回事。


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 (i.e 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.

这篇关于C#调用C ++第三方DLL(无源)引发异常 - 不是PInvoke兼容的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆