首页 新闻 搜索 专区 学院

请指点一个在C#中调用C++动态链接库的问题

0
[已关闭问题] 关闭于 2017-05-16 19:43

在C++中是这样写的

#define MAX_DEVICE_NAME_LENGTH  32
typedef char(*pDeviceArray)[MAX_DEVICE_NAME_LENGTH];

BV_ZFY_API int BV_ZFY_devices(int* count, pDeviceArray *devices_list)
{
    GLOBAL_STATUS_CHECK();
    if (!count || !devices_list || !devices_list[0])
        return BVUSB_RESULT_E_INVALID_PARAM;
    if (BVUSB_Result_SUCCEEDED(g_Global.BVUsbGetDevices(count, devices_list)))
        return RESULT_FLAG_S_OK;
    else return RESULT_FLAG_E_FAILED;
}

测试:
char szDeviceName[24] = { 0 };
pDeviceArray devArray;
int iDevCount = -1;
iResult = BV_ZFY_devices(&iDevCount, &devArray);
if (RESULT_FLAG_S_OK != iResult)
{  
    cout << "ERROR" << endl;
    goto SYSTEM_PAUSE;
}

以上为C++书写的代码,这个程序后来被编译成了libBVUsb.dll动态链接库,以上代码片断是没有办法再修改的。

 

现在我需要在我的程序中调用它,我目前这样写的:

[DllImport("libBVUsb.dll", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall, EntryPoint = "BV_ZFY_devices")]
public static extern bool BV_ZFY_devices(ref int count, ref IntPtr devices_list);

传参:
int i = 0;
 IntPtr pt = Marshal.AllocHGlobal(1);
var result = BV_ZFY_devices(ref i, ref pt);  

于是,就出错了!

错误内容为:

在调用时出错了,出错内容为:
对 PInvoke 函数“WindowsFormsApplication1!WindowsFormsApplication1.Form1::BV_ZFY_devices”的调用导致堆栈不对称。原因可能是托管的 PInvoke 签名与非托管的目标签名不匹配。请检查 PInvoke 签名的调用约定和参数与非托管的目标签名是否匹配。

 

 

应该是参数传错了,那正确的应该如何传呢?谢谢大家!

C++
denli的主页 denli | 初学一级 | 园豆:17
提问于:2017-05-16 16:24
< >
分享
所有回答(1)
0

你好,正好最近我也做过类似的工作,在这里给你一些建议,在接口调用的时候尽量用基本类型,这样比较能够看到本质,另外对int类型的取地址和直接用参数关系应该不是很大,建议两端都用int类型即可,另外C#和C/C++类型传递,理论上说只用int和字符串类型就可以满足所有的条件了。在这里我把自己遇到问题的处理代码给你看一下吧,希望能有一定的借鉴意义。

/***********************************/

在这里我们要注意一点,在C#程序中我们调用DLL方法是要返回结果的,我们可能传递一些有值的参数进行运算获得自己需要的结果,也可能只想得到返回结果。我们不得不面对C/C++和C#的参数类型转换。在这里我们用到的类型有两种那就是整数类型int和字符串类型String,在满足这两种类型转换的基础上我们可以完成所有的参数传递功能。对于int类型,在两种语言中没有太大的变化,作为基本类型可以直接进行内存空间的读写。而对于String类型,我们在C/C++中虽然有string类型,可是两者的数据结构完全不同,我们需要使用char *来进行内存空间的读写。如果需要从C/C++端获得字符串类型的值,我们必须在C#端使用StringBuilder来作为接收返回值的内存空间,这是因为在C#之中String的内存空间在与其他字符串空间合并的时候并不是直接在原来的内存空间上合并,而是创建了新的内存空间,因此我们在C/C++端传递的返回值就接收不到,而因为StringBuilder的内存空间是连续的,会将字符串合并,所以我们需要使用StringBuilder来开辟内存空间。关于C/C++和C#的DLL的调用方法如下面的表格所示。

  表 C/C++和C#数据类型转换

条件

C/C++和C#中的函数声明

C#函数调用从DLL中获得int类型的返回值

C/C++:extern “C” ZYR_API int  __stdcall  fun();

C#:    static extern int  fun();

将int类型的值传递到C/C++的DLL端

C/C++:extern “C” ZYR_API int  __stdcall  fun(int para);

C#:    static extern int  fun(int para);

C#函数调用从DLL获得字符串类型的返回值

C/C++:extern “C” ZYR_API void __stdcall fun(char *para);

C#:    static extern void fun(StringBuilder myInfo);

将字符串类型的值传递到C/C++的DLL端

C/C++:extern “C” ZYR_API int __stdcall fun(char *para);

C#:    static extern int  fun(String para);

精心出精品 | 园豆:306 (菜鸟二级) | 2017-05-16 17:07
清除回答草稿
   您需要登录以后才能回答,未注册用户请先注册