时间:2019-04-01 标签:c#dllimport parameters shift

Posted

技术标签:

【中文标题】时间:2019-04-01 标签:c#dllimport parameters shift【英文标题】:c# dllimport parameters shift 【发布时间】:2012-08-24 08:06:37 【问题描述】:

我目前正在使用 C# 开发 Excel 插件,并且正在使用与 C++ 库的互操作。两者都是我开发的。 C++ 库不是 COM 组件,只是一个导出一些函数的 DLL。

我有一个 C# 函数,我想将一个数字和两个字符串传递给 C++ 函数,而 C++ 函数返回一个字符串。首先我尝试直接返回一个字符串,所以原型看起来像

//C#
[return: MarshalAs(UnmanagedType.LPStr)]
private static extern string Function([MarshalAs(UnmanagedType.I4)] int number,
                                      [MarshalAs(UnmanagedType.LPStr)]string str1,
                                      [MarshalAs(UnmanagedType.LPStr)] string str2);
//C++
extern "C" __declspec(dllexport) string Function(int, char*, char*);

(顺便说一句,我已经在 C# 中将此函数的调用约定设置为 Cdecl)

然后当我调用C#版本,进入C++(调试的时候),发现参数移位了,所以整数是指向第一个字符串的指针的值,第一个字符串是第二个字符串,第二个字符串指向其他地方。我也查了内存,发现参数没有按照_cdecl约定传递,整数是最后压入栈的(没问题),但是两个字符串的位置调换了。编译器添加了许多其他信息,因此我不想更深入地了解这一点。

不过后来我把返回字符串改成了参数,原型看起来像

//C#
private static extern void Function([MarshalAs(UnmanagedType.I4)]int number,
                                    [MarshalAs(UnmanagedType.LPStr)] string str1,
                                    [MarshalAs(UnmanagedType.LPStr)] string str2,
                                    [MarshalAs(UnmanagedType.LPStr), Out] StringBuilder returnStr);
//C++
extern "C" __declspec(dllexport) void Function(int, char*, char*, string&);

现在参数已正确传递。

所以我的第一个问题是,这是怎么发生的?

我的第二个问题是,即使使用第二种方法,我也无法让程序正确运行,程序从C++函数返回时崩溃(实际上在C++函数中,唯一要做的就是调用另一个成员函数一个类,所以函数就像是成员函数的包装器,而程序在返回成员函数的时候就崩溃了。由于warpper函数只是调用了成员函数,所以我不知道问题出在哪里)。

有人知道怎么做吗?所以这个函数的重点是取一个数字和两个字符串,然后返回另一个字符串。

提前致谢。

【问题讨论】:

【参考方案1】:

look at this article about the usage of stringbuilder in unmanaged code

试试这样的:

//C#
public string Function(int number, string str1, string str2)

    StringBuilder sbStr1 = new StringBuilder(str1);
    StringBuilder sbStr2 = new StringBuilder(str2);
    StringBuilder sbReturnStr = new StringBuilder(1024);
    Function(number, sbStr1 , sbStr2 , sbReturnStr , sbReturnStr.Capacity);
    return sbReturnStr.ToString();


//C# p/invoke
private static extern void Function(int number, StringBuilder str1, StringBuilder str2, StringBuilder returnStrBuffer, int size);

//C++
extern "C" __declspec (dllexport) void __stdcall Function(int number, const char* str1, const char* str2, char* returnStrBuffer, int size)

     //fill the returnStrBuffer
     strncpy(returnStrBuffer, "blablalb", size); //example!!

【讨论】:

谢谢,非常巧合的是我昨天提到了同一个博客的另一个问题,我发现这很有帮助!我还检查了您提供的那个,我发现这有点复杂。我将尝试使用同一博客的另一篇文章提到的方法,使用 CoTaskMemAlloc。如果它不起作用,我可以尝试最后一个选项。谢谢。 再次感谢您的更新!我刚刚阅读了博客,我意识到返回字符串的主要问题是堆的问题。我会试试你的版本和使用 AllocHGlobal 的版本。 嗨,codeteq,我试过你的版本,但我得到一个断言错误,缓冲区太小。我为字符串生成器分配了 1024 个字节,怎么会发生这种情况? OK,我发现问题了,我用的是strcpy_s,要复制的字符串的大小必须加1(包括终止的/0)。但奇怪的是,我在同一个 dll 的其他地方也使用了这个函数,但我错过了加 1,它起作用了……我只是不知道这一切是怎么发生的。

以上是关于时间:2019-04-01 标签:c#dllimport parameters shift的主要内容,如果未能解决你的问题,请参考以下文章

时间:2019-04-01 标签:c#com object can't get ref object values

时间:2019-04-01 标签:c#mongodb database collection find

时间:2019-04-01 标签:c#dllimport parameters shift

时间:2019-04-01 标签:c#swagger openapi erronous refs when nested classes

[Intern][2019.04.01]Linux 学习心得

参数中的数据值被复制到其他参数 c++, c#